]> rtime.felk.cvut.cz Git - canping.git/blob - src/vca_canping.c
21239768bae5f50ab7295f984ca4e69f68de5c3a
[canping.git] / src / vca_canping.c
1 /**************************************************************************/
2 /* File: vca_canping.c - utility to test CAN functionality and throughput */
3 /*                                                                        */
4 /* LibVCA - Versatile CAN/CANopen API library                             */
5 /* Copyright (C) 2005-2006 Michal Sojka, DCE FEE CTU Prague               */
6 /* Copyright (C) 2006-2009 Pavel Pisa <pisa@cmp.felk.cvut.cz>             */
7 /*                                                                        */
8 /* LibVCA is free software; you can redistribute it and/or modify it      */
9 /* under terms of the GNU General Public License as published by the      */
10 /* Free Software Foundation; either version 2, or (at your option) any    */
11 /* later version.  LinCAN is distributed in the hope that it will be      */
12 /* useful, but WITHOUT ANY WARRANTY; without even the implied warranty    */
13 /* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU    */
14 /* General Public License for more details. You should have received a    */
15 /* copy of the GNU General Public License along with LinCAN; see file     */
16 /* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave,  */
17 /* Cambridge, MA 02139, USA.                                              */
18 /**************************************************************************/
19
20 #include <string.h>
21 #include <math.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <pthread.h>
28 #include <signal.h>
29 #include <sys/types.h>
30 #include <sys/time.h>
31 #include <ul_list.h>
32 #include <errno.h>
33 #include <semaphore.h>
34
35 /* TODO: Handle the case where there are more canping slaves running
36  * on one CAN bus. */
37
38 #include <can_vca.h>
39
40 //#define DEBUG 1
41 //#define DEBUG 2
42 #define WITH_RTPRIO
43
44 #ifdef WITH_RTPRIO
45 #include <sched.h>
46
47 int sched_policy = SCHED_OTHER;
48 int sched_rtprio;
49
50 #endif
51
52
53 #ifndef DEBUG
54 #define dbg(level, fmt, arg...) do {} while (0)
55 #else
56 #define dbg(level, fmt, arg...) do { if (level <= DEBUG) { printf("candping: " fmt, ## arg); } } while (0)
57 #endif
58
59 #define NOT_INTERRUPTED_SYSCALL (errno != EINTR && errno != ERESTART)
60 #define IS_FINISH_FLAG() (finish_flag)
61
62 /* Exit codes */
63 #define EXIT_OK 0
64 #define EXIT_BAD_PARAM 1
65 #define EXIT_CANNOT_OPEN 2
66 #define EXIT_FILTER_ERROR 3
67 #define EXIT_NO_MEM 4
68 #define EXIT_READ_ERROR 5
69 #define EXIT_WRITE_ERROR 6
70 #define EXIT_SELECT_ERROR 7
71 #define EXIT_FLUSH_ERROR 8
72
73 /* Global variables */
74 sig_atomic_t finish_flag = 0;   /* Threads should terminate. */
75 sem_t finish_sem;               /* Thread signals a termination */
76 vca_handle_t global_vcah = VCA_HANDLE_INVALID;
77
78 int total_count = 0;
79 int total_timeout = 0;
80
81 pthread_mutex_t mut_start = PTHREAD_MUTEX_INITIALIZER;
82 pthread_cond_t cond_start = PTHREAD_COND_INITIALIZER;
83 int start_flag = 0;
84 sem_t ready_sem;                /* Thread is ready for execution */
85
86
87 /* Command line options */
88 char *option_device = "/dev/can0";
89 int option_masters = 0;
90 long int option_first_id = 1000;
91 int option_slaves = 0;
92 int option_verbose = 0;         /* 0 - nothing, 1 - only global
93                                  * statistics, 2 - simple times, 3 -
94                                  * verbose times */
95 int option_length = 8;
96 int option_count = 0;
97 int option_wait_ms = 1000;
98 int option_timeout = 4;
99 int option_open_once = 0;
100 int option_synch_start = 0;
101
102 /* Lists */
103 typedef struct threads {
104         ul_list_head_t head;
105 } threads_t;
106
107 typedef struct thread_data {
108         pthread_t tid;
109         long int canid;
110
111         int count;
112         double mean;            /* mean value of responses */
113         double moment2nd;       /* used to compute variance of
114                                  * responses */
115         int min, max;           /* min/max response times */
116         int timeout;            /* number of timeouts */
117
118         ul_list_node_t node;
119 } thread_data_t;
120
121 UL_LIST_CUST_DEC(thread_list, threads_t, thread_data_t, head, node);
122
123 threads_t master_threads;
124 threads_t slave_threads;
125
126 /* Subtract the `struct timeval' values X and Y, storing the result in
127    RESULT.  Return 1 if the difference is negative, otherwise 0.  */
128      
129 int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
130 {
131         /* Perform the carry for the later subtraction by updating Y. */
132         if (x->tv_usec < y->tv_usec) {
133                 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
134                 y->tv_usec -= 1000000 * nsec;
135                 y->tv_sec += nsec;
136         }
137         if (x->tv_usec - y->tv_usec > 1000000) {
138                 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
139                 y->tv_usec += 1000000 * nsec;
140                 y->tv_sec -= nsec;
141         }
142      
143         /* Compute the time remaining to wait.
144            `tv_usec' is certainly positive. */
145         result->tv_sec = x->tv_sec - y->tv_sec;
146         result->tv_usec = x->tv_usec - y->tv_usec;
147      
148         /* Return 1 if result is negative. */
149         return x->tv_sec < y->tv_sec;
150 }
151
152 void dbg_print_timeval(char *msg, struct timeval *tv)
153 {
154
155         printf("%s sec=%ld usec=%ld\n", msg, tv->tv_sec, tv->tv_usec);
156 }
157 void kill_all_threads(int signum)
158 {
159         thread_data_t *td;
160                 
161         ul_list_for_each(thread_list, &master_threads, td) {
162                 pthread_kill(td->tid, signum);
163         }
164         ul_list_for_each(thread_list, &slave_threads, td) {
165                 pthread_kill(td->tid, signum);
166         }
167 }
168 void term_handler(int signum)
169 {
170         dbg(1, "Thread %p got a signal %d\n", (void *)pthread_self(), signum);
171         if (!IS_FINISH_FLAG()) {
172                 dbg(1, "Terminating threads\n");
173                 finish_flag = 1;
174
175                 kill_all_threads(signum);
176         }
177 }
178
179 void *master_thread(void *arg)
180 {
181         thread_data_t *td = (thread_data_t *)arg;
182         int ping_id = td->canid;
183         int pong_id = ping_id + 1;
184         struct canmsg_t pingmsg, pongmsg;
185         int ping_count = 0;
186         int ret = 0;
187         vca_handle_t vcah = VCA_HANDLE_INVALID; /* File descriptor of CAN driver. */
188         int i;
189         fd_set rdset;           /* read set for select syscall */
190         struct timeval timeout, pingtime, pongtime;
191         struct canfilt_t canfilt; /* filter for received messages */
192
193         if (!option_open_once) {
194                 /* Open can driver */
195                 if(vca_open_handle(&vcah, option_device, NULL, 0) < 0) {
196                         perror("open");
197                         fprintf(stderr, "Error opening %s (for id %d)\n", option_device, ping_id);
198                         exit(EXIT_CANNOT_OPEN); 
199                 }
200         } else {
201                 vcah = global_vcah;
202         }
203         
204         /* setup filtering of received messages */
205         memset(&canfilt, 0, sizeof(canfilt));
206         canfilt.mask = 0xfffffff;
207         canfilt.id = pong_id;   /* pong responces with increased id */
208         ret = vca_set_filt(vcah, &canfilt);
209         if(ret<0) {
210                 perror("ioctl CANQUE_FILTER");
211                 exit(EXIT_FILTER_ERROR);
212         }
213         ret = vca_queue_flush(vcah, 0);
214         if(ret<0) {
215                 perror("ioctl CANQUE_QUEUE_FLUSH");
216                 exit(EXIT_FLUSH_ERROR);
217         }
218         
219         /* Prepare data structures for the select syscall. */
220         FD_ZERO (&rdset);
221
222         /* Signal that I'm ready */
223         sem_post(&ready_sem);
224
225         if (option_synch_start) {
226                 /* Wait for other threads to initialize */
227                 pthread_mutex_lock(&mut_start);
228                 while (!start_flag) pthread_cond_wait(&cond_start, &mut_start);
229                 pthread_mutex_unlock(&mut_start);
230         }
231
232         while (!IS_FINISH_FLAG() && (option_count == 0 || ping_count++ < option_count)) {
233                 /* Send a ping message */
234                 pingmsg.flags=0;
235                 pingmsg.id=ping_id;
236                 pingmsg.length = option_length;
237                 for (i=0; i < option_length; i++) pingmsg.data[i] = i;
238                 gettimeofday(&pingtime, NULL);
239
240                 ret = vca_send_msg_seq(vcah, &pingmsg, 1);
241                 if (ret < 0) {
242                         if (NOT_INTERRUPTED_SYSCALL) {
243                                 perror("write");
244                                 exit(EXIT_WRITE_ERROR);
245                         }
246                         else continue;
247                 }
248                 
249                 /* Wait for a pong responce */
250                 FD_SET (vca_h2fd(vcah), &rdset);
251      
252                 timeout.tv_sec = option_timeout;
253                 timeout.tv_usec = 0;
254      
255                 ret = select(FD_SETSIZE, &rdset, NULL, NULL, &timeout);
256                 if (ret > 0) {
257                         /* Read the message */
258                         pongmsg.flags=0;
259                         ret = vca_rec_msg_seq(vcah, &pongmsg, 1);
260                         if (ret < 0) {
261                                 if (NOT_INTERRUPTED_SYSCALL) {
262                                         perror("read");
263                                         exit(EXIT_READ_ERROR);
264                                 }
265                                 else continue;
266                         }
267                         else {  /* A pong message received */
268                                 long int time;
269                                 gettimeofday(&pongtime, NULL);
270                                 if (ret == 0) {
271                                         fprintf(stderr, "read returned zero\n");
272                                         exit(EXIT_READ_ERROR);
273                                 }
274                                 if (pongmsg.id != pong_id) {
275                                         fprintf(stderr, "Filter error (expected: %d, received %ld)\n", pong_id, pongmsg.id);
276                                         exit(EXIT_FILTER_ERROR);
277                                 }
278                                 timeval_subtract(&pongtime, &pongtime, &pingtime);
279                                 time = pongtime.tv_sec*1000000 + pongtime.tv_usec;
280                                 switch (option_verbose) {
281                                 case 2:
282                                         printf("%d:%ld\n", ping_id, time); 
283                                         break;
284                                 case 3:
285                                         printf("Pong response for id %d received in %ld us\n", ping_id, time);
286                                         break;
287                                 }
288                                 /* Update statistics */
289                                 td->count++;
290                                 td->mean = 
291                                         td->mean * ((double)(td->count - 1) / td->count) + 
292                                         (double)time / td->count;
293                                 td->moment2nd = 
294                                         td->moment2nd * ((double)(td->count - 1) / td->count) + 
295                                         (double)time*time  / td->count;
296                                 if (time > td->max) td->max = time;
297                                 if (time < td->min) td->min = time;
298                                 total_count++;
299                         } /* read */
300                 } 
301                 else if (ret == 0) { /* select */
302                         if (option_verbose >= 2) 
303                                 printf("Timeout encountered (id %d)\n", ping_id);
304                         td->timeout++;
305                         total_timeout++;
306                 } 
307                 else {
308                         if (NOT_INTERRUPTED_SYSCALL) {
309                                 perror("select");
310                                 exit(EXIT_SELECT_ERROR);
311                         }
312                         else continue;
313                 }
314                 usleep(option_wait_ms * 1000);
315         }
316
317         if (!option_open_once) {
318                 /* Close the can driver */
319                 vca_close_handle(vcah);
320         }
321
322         sem_post(&finish_sem);
323         return (void *)ret;
324 }
325
326 void start_masters(int masters, int first_id)
327 {
328         int id = first_id;
329         int i;
330
331         dbg(1, "Starting %d master threads\n", masters);
332         
333         for (i = 0; i < masters; i++, id += 2) {
334                 thread_data_t *td;
335                 
336                 td = malloc(sizeof(*td));
337                 if (!td) {
338                         printf("Can't allocate memory");
339                         exit(EXIT_NO_MEM);
340                 }
341                 memset(td, 0, sizeof(*td));
342                 td->canid = id;
343                 td->min = 0x7fffffff;
344                 /* TODO use mutexes and signal blocking */
345                 thread_list_ins_tail(&master_threads, td);
346                 pthread_create(&td->tid, NULL, master_thread, (void *)td);
347                 dbg(2, "Master thread: %p\n", (void *)td->tid);
348         }
349
350
351         /* Wait for all threads beeing ready */
352         for (i = 0; i < masters; i++) sem_wait(&ready_sem);
353
354         /* Start threads */
355         pthread_mutex_lock(&mut_start);
356         start_flag = 1;
357         pthread_cond_broadcast(&cond_start);
358         pthread_mutex_unlock(&mut_start);
359 }
360
361 void *slave_thread(void *arg)
362 {
363         thread_data_t *td = (thread_data_t *)arg;
364         int ping_id = td->canid;
365         int pong_id = ping_id + 1;
366         struct canmsg_t pingmsg, pongmsg;
367         int ret = 0;
368         vca_handle_t vcah = VCA_HANDLE_INVALID; /* File descriptor of CAN driver. */
369         int i;
370         struct canfilt_t canfilt; /* filter for received messages */
371
372         if (!option_open_once) {
373                 /* Open the CAN driver */
374                 if(vca_open_handle(&vcah, option_device, NULL, 0) < 0) {
375                         perror("open");
376                         printf("Error opening %s (for id %d)\n", option_device, ping_id);
377                         exit(EXIT_CANNOT_OPEN); 
378                 }
379         } else {
380                 vcah = global_vcah;
381         }
382         
383         /* setup filtering of received messages */
384         memset(&canfilt, 0, sizeof(canfilt));
385         canfilt.mask = 0xfffffff;
386         canfilt.id = ping_id;   /* receive only our ping messages */
387         ret = vca_set_filt(vcah, &canfilt);
388         if (ret < 0) {
389                 perror("ioctl CANQUE_FILTER");
390                 exit(EXIT_FILTER_ERROR);
391         }
392         /* If there are some messages already, delete them. These may
393          * not processed by our filter and thus may have a wrong
394          * ID. */
395         ret = vca_queue_flush(vcah, 0);
396         if (ret < 0) {
397                 perror("ioctl CANQUE_QUEUE_FLUSH");
398                 exit(EXIT_FLUSH_ERROR);
399         }
400         
401         /* Prepare a pong message */
402         pongmsg.flags = 0;
403         pongmsg.id = pong_id;
404         pongmsg.length = option_length;
405         for (i=0; i < option_length; i++) pongmsg.data[i] = i;
406
407         while (!IS_FINISH_FLAG()) {
408                 /* Receive a ping message */
409                 pingmsg.flags=0;
410                 ret = vca_rec_msg_seq(vcah, &pingmsg, 1);
411                 if (ret < 0) {
412                         if (NOT_INTERRUPTED_SYSCALL) {
413                                 printf("%d\n", errno);
414                                 perror("read");
415                                 exit(EXIT_READ_ERROR);
416                         }
417                 }
418                 if (NOT_INTERRUPTED_SYSCALL && pingmsg.id != ping_id) {
419                         fprintf(stderr, "Filter error (expected: %d, received %ld)\n", ping_id, pingmsg.id);
420                         exit(EXIT_FILTER_ERROR);
421                 }
422                 /* Answer immendiately with a pong message */
423                 if (NOT_INTERRUPTED_SYSCALL) {
424                         ret = vca_send_msg_seq(vcah, &pongmsg, 1);
425                         if (ret < 0) {
426                                 if (NOT_INTERRUPTED_SYSCALL) {
427                                         perror("write");
428                                         exit(EXIT_WRITE_ERROR);
429                                 }
430                         }
431                 }
432
433                 if (ret >= 0) total_count++;
434
435                 if (option_verbose >= 2)
436                         /* This drasticly slows down the pong
437                          * response. Why??? */
438                         printf("Replying to ping id %lu\n", pingmsg.id);
439
440         }
441
442         if (!option_open_once) {
443                 /* Close can driver */
444                 vca_close_handle(vcah);
445         }
446
447         dbg(2, "Slave thread for id %d is going to finish\n", ping_id);
448
449         sem_post(&finish_sem);
450         return (void *)ret;
451 }
452      
453 void start_slaves(int slaves, int first_id)
454 {
455         int id = first_id;
456         int i;
457
458         dbg(1, "Starting %d slave threads\n", slaves);
459         
460         for (i = 0; i < slaves; i++, id += 2) {
461                 thread_data_t *td;
462                 
463                 td = malloc(sizeof(*td));
464                 if (!td) {
465                         printf("Can't allocate memory");
466                         exit(EXIT_NO_MEM);
467                 }
468                 memset(td, 0, sizeof(*td));
469                 td->canid = id;
470                 /* TODO use mutexes and signal blocking */
471                 thread_list_ins_tail(&slave_threads, td);
472                 pthread_create(&td->tid, NULL, slave_thread, (void *)td);
473                 dbg(2, "Slave thread: %p\n", (void *)td->tid);
474         }
475 }
476
477 #ifdef WITH_RTPRIO
478 int set_sched_policy_and_prio(int policy, int rtprio)
479 {
480         struct sched_param scheduling_parameters;
481         int maxprio=sched_get_priority_max(policy);
482         int minprio=sched_get_priority_min(policy);
483
484         if((rtprio < minprio) || (rtprio > maxprio)) {
485                 fprintf(stderr, "The priority for requested policy is out of <%d, %d> range\n",
486                                 minprio, maxprio);
487                 return -1;
488         }
489
490         scheduling_parameters.sched_priority = rtprio;
491
492         if (0 != pthread_setschedparam(pthread_self(), policy, &scheduling_parameters)) {
493                 perror("pthread_setschedparam error");
494         }
495         return 0;
496 }
497 #endif
498
499 void print_help(void)
500 {
501         printf("Usage: canping -m <master threads> [other options]\n"
502                "       canping -s <slave threads> [other options]\n\n"
503                "Other options:\n"
504                "  -c count      how many messages each master sends\n"
505                "  -d dev        device (e.g. /dev/can1)\n"
506                "  -h            print this help\n"
507                "  -i id         id of first master message\n"
508                "  -l length     length of the messages (0..8)\n"
509                "  -o            open a device only once for all threads (doesn't work)\n" /* due to filters */
510                "  -t timeout    timeout in seconds (default 4 s)\n"
511                "  -v            be verbose (use more than once to increase verbosity)\n"
512                "  -w ms         wait ms miliseconds between sending pings\n"
513                "  -y            synchronize threads before start (doesn't test race conditions\n"
514                "                between open and read/write)\n"
515 #ifdef WITH_RTPRIO
516                "  -r            run at realtime priority\n"
517                "  -R P:prio     run at realtime priority prio, policy RR or FF\n"
518 #endif
519                "\n"
520                "Example: canping -m 10 -vv\n"
521                 );
522 }
523      
524 int parse_options(int argc, char *argv[])
525 {
526         int c;
527         
528         opterr = 0;
529         while ((c = getopt (argc, argv, "c:d:hi:l:m:os:t:vw:yrR:")) != -1)
530                 switch (c)
531                 {
532                 case 'c':
533                         option_count = atoi(optarg);
534                         break;
535                 case 'd':
536                         option_device = optarg;
537                         break;
538                 case 'h':
539                         print_help();
540                         exit(EXIT_OK);
541                         break;
542                 case 'i':
543                         option_first_id = atoi(optarg);
544                         break;
545                 case 'l':
546                         option_length = atoi(optarg);
547                         if (option_length > 8) option_length = 8;
548                         if (option_length < 0) option_length = 0;
549                         break;
550                 case 'm':
551                         option_masters = atoi(optarg);
552                         break;
553                 case 'o':
554                         option_open_once = 1;
555                         break;
556                 case 's':
557                         option_slaves = atoi(optarg);
558                         break;
559                 case 't':
560                         option_timeout = atoi(optarg);
561                         break;
562                 case 'v':
563                         option_verbose++;
564                         break;
565                 case 'w':
566                         option_wait_ms = atoi(optarg);
567                         break;
568                 case 'y':
569                         option_synch_start = 1;
570                         break;
571 #ifdef WITH_RTPRIO
572                 case 'r':
573                         sched_policy = SCHED_FIFO;
574                         sched_rtprio = (sched_get_priority_min(SCHED_FIFO) +
575                                         sched_get_priority_max(SCHED_FIFO)) / 2;
576                         break;
577                 case 'R':
578                         if(!isalpha(*optarg)) {
579                                 sched_policy = SCHED_FIFO;
580                         } else if(!strncmp(optarg,"FF:",3)) {
581                                 sched_policy = SCHED_FIFO;
582                                 optarg += 3;
583                         } else if(!strncmp(optarg,"RR:",3)) {
584                                 sched_policy = SCHED_RR;
585                                 optarg += 3;
586                         } else {
587                                 fprintf (stderr, "Unknown policy %s\n", optarg);
588                                 exit(EXIT_BAD_PARAM);
589                         }
590                         sched_rtprio = atoi(optarg);
591                         break;
592 #endif
593                 case '?':
594                         if (isprint (optopt))
595                                 fprintf (stderr, "Unknown option `-%c'.\n", optopt);
596                         else
597                                 fprintf (stderr,
598                                          "Unknown option character `\\x%x'.\n",
599                                          optopt);
600                         return 1;
601                 default:
602                         exit(EXIT_BAD_PARAM);
603                 }
604         if (!(option_masters || option_slaves) || (option_masters && option_slaves))
605                 exit(EXIT_BAD_PARAM);
606         return 0;
607 }
608
609 void print_stats(thread_data_t *td)
610 {
611         char std[20];
612         int count = td->count + td->timeout;
613         
614         if (td->count >= 2) {
615                 snprintf(std, 19, "%7.2f", sqrt((td->count/(td->count - 1)) * 
616                                                 (td->moment2nd - td->mean*td->mean)));
617         } else {
618                 strncpy(std, "N/A", 19);
619         }
620         printf("Id %4ld: count = %5d"
621                " mean = %7.2f stddev = %s"
622                " min = %5d max = %5d [us]"
623                " loss = %d%% (%d)\n",
624                td->canid, count, td->mean, 
625                std,
626                td->min, td->max,
627                (count > 0) ? 100 * td->timeout / count : 0, td->timeout
628                 );
629 }
630
631 void wait_for_threads(void)
632 {
633         thread_data_t *td;
634         void *thread_ret;
635         int thread_count = option_slaves + option_masters;
636         int ret;
637
638         while (thread_count > 0) {
639                 if (option_verbose != 1) {
640                         ret = sem_wait(&finish_sem);
641                         dbg(2, "Main thread sem_wait() exited with ret=%d.\n", ret);
642
643                         /* If the sem_wait is successful i.e. not
644                          * interrupted by a signal, decrease the
645                          * thread_count*/
646                         if (ret == 0) thread_count--; 
647                 } else {
648                         if (!IS_FINISH_FLAG()) {
649                                 printf("Total count: %6d, Timeouts: %6d\r", total_count, total_timeout);
650                                 fflush(stdout);
651                                 usleep(1000000);
652                         }
653                         while (sem_trywait(&finish_sem) == 0) thread_count--;
654                 }
655         }
656         if (option_verbose == 1) {
657                 printf("\n");
658         }
659
660         ul_list_for_each(thread_list, &master_threads, td) {
661                 pthread_join(td->tid, &thread_ret);
662                 dbg(2, "Master thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
663         }
664
665         ul_list_for_each(thread_list, &slave_threads, td) {
666                 pthread_join(td->tid, &thread_ret);
667                 dbg(2, "Slave thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
668         }
669
670         if (option_masters) printf("Summary statistics:\n");
671
672         ul_list_for_each_cut(thread_list, &master_threads, td) {
673                 print_stats(td);
674                 free(td);
675         }
676         
677
678         ul_list_for_each_cut(thread_list, &slave_threads, td) 
679                 free(td);
680 }
681
682 int main(int argc, char *argv[])
683 {
684         parse_options(argc, argv);
685
686 #ifdef WITH_RTPRIO
687         if(sched_policy != SCHED_OTHER)
688                 if(set_sched_policy_and_prio(sched_policy, sched_rtprio) <0)
689                         exit(EXIT_BAD_PARAM);
690 #endif
691
692
693         thread_list_init_head(&master_threads);
694         thread_list_init_head(&slave_threads);
695
696         sem_init(&finish_sem, 0, 0);
697         sem_init(&ready_sem, 0, 0);
698
699         siginterrupt(SIGINT, 1);
700         signal(SIGINT, term_handler);
701         siginterrupt(SIGTERM, 1);
702         signal(SIGTERM, term_handler);
703
704         dbg(2, "Main thread: %p\n", (void *)pthread_self());
705
706         if (option_open_once) {
707                 /* Open the CAN driver */
708                 if(vca_open_handle(&global_vcah, option_device, NULL, 0) < 0) {
709                         perror("open");
710                         printf("Error opening %s\n", option_device);
711                         exit(EXIT_CANNOT_OPEN); 
712                 }
713         }
714
715         if (option_masters) start_masters(option_masters, option_first_id);
716         if (option_slaves) start_slaves(option_slaves, option_first_id);
717
718         wait_for_threads();
719
720         if (option_open_once) {
721                 vca_close_handle(global_vcah);
722         }
723
724         return 0;
725 }