]> rtime.felk.cvut.cz Git - canping.git/blob - src/vca_canping.c
50c19df83b249af54cf4af51d984ee6795f24f7c
[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-2009 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 #include <stdbool.h>
35 #include <error.h>
36
37 /* TODO: Handle the case where there are more canping slaves running
38  * on one CAN bus. */
39
40 #include <can_vca.h>
41
42 //#define DEBUG 1
43 //#define DEBUG 2
44 #define WITH_RTPRIO
45
46 #ifdef WITH_RTPRIO
47 #include <sched.h>
48 #include <sys/mman.h>
49
50 int sched_policy = SCHED_OTHER;
51 int sched_rtprio;
52
53 #endif
54
55
56 #ifndef DEBUG
57 #define dbg(level, fmt, arg...) do {} while (0)
58 #else
59 #define dbg(level, fmt, arg...) do { if (level <= DEBUG) { printf("candping: " fmt, ## arg); } } while (0)
60 #endif
61
62 #define NOT_INTERRUPTED_SYSCALL (errno != EINTR && errno != ERESTART)
63 #define IS_FINISH_FLAG() (finish_flag)
64
65 /* Exit codes */
66 #define EXIT_OK 0
67 #define EXIT_BAD_PARAM 1
68 #define EXIT_CANNOT_OPEN 2
69 #define EXIT_FILTER_ERROR 3
70 #define EXIT_NO_MEM 4
71 #define EXIT_READ_ERROR 5
72 #define EXIT_WRITE_ERROR 6
73 #define EXIT_SELECT_ERROR 7
74 #define EXIT_FLUSH_ERROR 8
75
76 /* Global variables */
77 sig_atomic_t finish_flag = 0;   /* Threads should terminate. */
78 sem_t finish_sem;               /* Thread signals a termination */
79 vca_handle_t global_vcah = VCA_HANDLE_INVALID;
80
81 int total_count = 0;
82 int total_timeout = 0;
83
84 pthread_mutex_t mut_start = PTHREAD_MUTEX_INITIALIZER;
85 pthread_cond_t cond_start = PTHREAD_COND_INITIALIZER;
86 int start_flag = 0;
87 sem_t ready_sem;                /* Thread is ready for execution */
88
89
90 /* Command line options */
91 char *option_device = "/dev/can0";
92 int option_masters = 0;
93 long int option_first_id = 1000;
94 int option_slaves = 0;
95 int option_verbose = 0;         /* 0 - nothing, 1 - only global
96                                  * statistics, 2 - simple times, 3 -
97                                  * verbose times */
98 int option_length = 8;
99 int option_count = 0;
100 int option_wait_ms = 1000;
101 int option_timeout = 4;
102 int option_open_once = 0;
103 int option_synch_start = 0;
104 bool option_background = false;
105 char *option_histogram = NULL;
106 int option_msg_to_ignore = 0;
107 char *option_save_all_times = NULL;
108 /* Lists */
109 typedef struct threads {
110         ul_list_head_t head;
111 } threads_t;
112
113 struct histogram {
114         unsigned *data;
115         unsigned allocated;
116         unsigned resolution;
117 };
118
119 typedef struct thread_data {
120         pthread_t tid;
121         long int canid;
122
123         int count;
124         double mean;            /* mean value of responses */
125         double moment2nd;       /* used to compute variance of
126                                  * responses */
127         int min, max;           /* min/max response times */
128         int timeout;            /* number of timeouts */
129
130         struct histogram hist;
131         unsigned *times;        /* Array of all measured times */
132
133         ul_list_node_t node;
134 } thread_data_t;
135
136 UL_LIST_CUST_DEC(thread_list, threads_t, thread_data_t, head, node);
137
138 threads_t master_threads;
139 threads_t slave_threads;
140
141 int histogram_init(struct histogram *h,
142                    unsigned max_value,
143                    unsigned resolution)
144 {
145         size_t mem;
146         h->allocated = max_value/resolution + 1;
147         h->resolution = resolution;
148         mem = h->allocated*sizeof(*h->data);
149         h->data = malloc(mem);
150         if (h->data) {
151                 memset(h->data, 0, mem);
152                 return 0;
153         } else
154                 return -1;
155 }
156
157 void histogram_add(struct histogram *h, unsigned value)
158 {
159         unsigned index = value / h->resolution;
160         if (index >= h->allocated)
161                 index = h->allocated - 1;
162         h->data[index]++;
163 }
164
165 void histogram_fprint(struct histogram *h, FILE *f)
166 {
167         unsigned long long sum = 0, cum;
168         unsigned i;
169         for (i = 0; i < h->allocated; i++)
170                 sum += h->data[i];
171         cum = sum;
172         for (i = 0; i < h->allocated; i++) {
173                 if (h->data[i] != 0) {
174                         if (!getenv("CANPING_MS"))
175                                 fprintf(f, "%d %lld\n", i*h->resolution, cum);
176                         else
177                                 fprintf(f, "%g %lld\n", 1e-3*(i*h->resolution), cum);
178                 }
179                 cum -= h->data[i];
180         }
181 }
182
183 /* Subtract the `struct timeval' values X and Y, storing the result in
184    RESULT.  Return 1 if the difference is negative, otherwise 0.  */
185      
186 int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
187 {
188         /* Perform the carry for the later subtraction by updating Y. */
189         if (x->tv_usec < y->tv_usec) {
190                 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
191                 y->tv_usec -= 1000000 * nsec;
192                 y->tv_sec += nsec;
193         }
194         if (x->tv_usec - y->tv_usec > 1000000) {
195                 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
196                 y->tv_usec += 1000000 * nsec;
197                 y->tv_sec -= nsec;
198         }
199      
200         /* Compute the time remaining to wait.
201            `tv_usec' is certainly positive. */
202         result->tv_sec = x->tv_sec - y->tv_sec;
203         result->tv_usec = x->tv_usec - y->tv_usec;
204      
205         /* Return 1 if result is negative. */
206         return x->tv_sec < y->tv_sec;
207 }
208
209 void dbg_print_timeval(char *msg, struct timeval *tv)
210 {
211
212         printf("%s sec=%ld usec=%ld\n", msg, tv->tv_sec, tv->tv_usec);
213 }
214 void kill_all_threads(int signum)
215 {
216         thread_data_t *td;
217                 
218         ul_list_for_each(thread_list, &master_threads, td) {
219                 pthread_kill(td->tid, signum);
220         }
221         ul_list_for_each(thread_list, &slave_threads, td) {
222                 pthread_kill(td->tid, signum);
223         }
224 }
225 void term_handler(int signum)
226 {
227         dbg(1, "Thread %p got a signal %d\n", (void *)pthread_self(), signum);
228         if (!IS_FINISH_FLAG()) {
229                 dbg(1, "Terminating threads\n");
230                 finish_flag = 1;
231
232                 kill_all_threads(signum);
233         }
234 }
235
236 void *master_thread(void *arg)
237 {
238         thread_data_t *td = (thread_data_t *)arg;
239         int ping_id = td->canid;
240         int pong_id = ping_id + 1;
241         struct canmsg_t pingmsg, pongmsg;
242         int ping_count = 0;
243         int ret = 0;
244         vca_handle_t vcah = VCA_HANDLE_INVALID; /* File descriptor of CAN driver. */
245         int i;
246         fd_set rdset;           /* read set for select syscall */
247         struct timeval timeout, pingtime, pongtime;
248         struct canfilt_t canfilt; /* filter for received messages */
249
250         if (!option_open_once) {
251                 /* Open the CAN driver and disable (D) reception of
252                  * all messages until we setup a filter below. */
253                 if(vca_open_handle(&vcah, option_device, "D", 0) < 0) {
254                         perror("open");
255                         fprintf(stderr, "Error opening %s (for id %d)\n", option_device, ping_id);
256                         exit(EXIT_CANNOT_OPEN); 
257                 }
258         } else {
259                 vcah = global_vcah;
260         }
261         
262         /* setup filtering of received messages */
263         memset(&canfilt, 0, sizeof(canfilt));
264         canfilt.mask = 0xfffffff;
265         canfilt.id = pong_id;   /* pong responces with increased id */
266         ret = vca_set_filt(vcah, &canfilt);
267         if(ret<0) {
268                 perror("ioctl CANQUE_FILTER");
269                 exit(EXIT_FILTER_ERROR);
270         }
271         ret = vca_queue_flush(vcah, 0);
272         if(ret<0) {
273                 perror("ioctl CANQUE_QUEUE_FLUSH");
274                 exit(EXIT_FLUSH_ERROR);
275         }
276
277         if (option_histogram)
278                 histogram_init(&td->hist, 1000*1000/*max [us]*/, 5/*resolution [us]*/);
279
280         if (option_save_all_times) {
281                 td->times = malloc(sizeof(*td->times)*option_count);
282                 if (!td->times)
283                         error(1, errno, "malloc times");
284         }
285         
286         /* Prepare data structures for the select syscall. */
287         FD_ZERO (&rdset);
288
289         /* Signal that I'm ready */
290         sem_post(&ready_sem);
291
292         if (option_synch_start) {
293                 /* Wait for other threads to initialize */
294                 pthread_mutex_lock(&mut_start);
295                 while (!start_flag) pthread_cond_wait(&cond_start, &mut_start);
296                 pthread_mutex_unlock(&mut_start);
297         }
298
299         while (!IS_FINISH_FLAG() && (option_count == 0 || ping_count++ < option_count + option_msg_to_ignore)) {
300                 /* Send a ping message */
301                 pingmsg.flags=0;
302                 pingmsg.id=ping_id;
303                 pingmsg.length = option_length;
304                 for (i=0; i < option_length; i++) pingmsg.data[i] = i;
305                 gettimeofday(&pingtime, NULL);
306
307                 ret = vca_send_msg_seq(vcah, &pingmsg, 1);
308                 if (ret < 0) {
309                         if (NOT_INTERRUPTED_SYSCALL) {
310                                 perror("write");
311                                 exit(EXIT_WRITE_ERROR);
312                         }
313                         else continue;
314                 }
315                 
316                 /* Wait for a pong responce */
317                 FD_SET (vca_h2fd(vcah), &rdset);
318      
319                 timeout.tv_sec = option_timeout;
320                 timeout.tv_usec = 0;
321      
322                 ret = select(FD_SETSIZE, &rdset, NULL, NULL, &timeout);
323                 if (ret > 0) {
324                         /* Read the message */
325                         pongmsg.flags=0;
326                         ret = vca_rec_msg_seq(vcah, &pongmsg, 1);
327                         if (ret < 0) {
328                                 if (NOT_INTERRUPTED_SYSCALL) {
329                                         perror("read");
330                                         exit(EXIT_READ_ERROR);
331                                 }
332                                 else continue;
333                         }
334                         else {  /* A pong message received */
335                                 long int time;
336                                 gettimeofday(&pongtime, NULL);
337                                 if (ret == 0) {
338                                         fprintf(stderr, "read returned zero\n");
339                                         exit(EXIT_READ_ERROR);
340                                 }
341                                 if (pongmsg.id != pong_id) {
342                                         fprintf(stderr, "Filter error (expected: %d, received %ld)\n", pong_id, pongmsg.id);
343                                         exit(EXIT_FILTER_ERROR);
344                                 }
345                                 timeval_subtract(&pongtime, &pongtime, &pingtime);
346                                 time = pongtime.tv_sec*1000000 + pongtime.tv_usec;
347                                 switch (option_verbose) {
348                                 case 2:
349                                         printf("%d:%ld\n", ping_id, time); 
350                                         break;
351                                 case 3:
352                                         printf("Pong response %d for id %d received in %ld us\n", ping_count, ping_id, time);
353                                         break;
354                                 }
355                                 /* Update statistics */
356                                 if (ping_count > option_msg_to_ignore) {
357                                         td->count++;
358                                         td->mean = 
359                                                 td->mean * ((double)(td->count - 1) / td->count) + 
360                                                 (double)time / td->count;
361                                         td->moment2nd = 
362                                                 td->moment2nd * ((double)(td->count - 1) / td->count) + 
363                                                 (double)time*time  / td->count;
364                                         if (time > td->max) td->max = time;
365                                         if (time < td->min) td->min = time;
366                                         if (option_histogram)
367                                                 histogram_add(&td->hist, time);
368                                         if (option_save_all_times)
369                                                 td->times[ping_count - 1 - option_msg_to_ignore] = time;
370                                 }
371                                 total_count++;
372                         } /* read */
373                 } 
374                 else if (ret == 0) { /* select */
375                         if (option_verbose >= 2) 
376                                 printf("Timeout encountered (id %d)\n", ping_id);
377                         td->timeout++;
378                         total_timeout++;
379                 } 
380                 else {
381                         if (NOT_INTERRUPTED_SYSCALL) {
382                                 perror("select");
383                                 exit(EXIT_SELECT_ERROR);
384                         }
385                         else continue;
386                 }
387                 usleep(option_wait_ms * 1000);
388         }
389
390         if (!option_open_once) {
391                 /* Close the can driver */
392                 vca_close_handle(vcah);
393         }
394
395         if (option_histogram) {
396                 FILE *f;
397                 char buf[100];
398                 snprintf(buf, sizeof(buf), "%s-%d.dat", option_histogram, ping_id);
399                 f = fopen(buf, "w");
400                 histogram_fprint(&td->hist, f);
401                 fclose(f);
402         }
403
404         if (option_save_all_times) {
405                 FILE *f;
406                 char buf[100];
407                 int i;
408                 snprintf(buf, sizeof(buf), "%s-%d.dat", option_save_all_times, ping_id);
409                 f = fopen(buf, "w");
410                 for (i=0; i<option_count; i++) {
411                         if (!getenv("CANPING_MS")) 
412                                 fprintf(f, "%u\n", td->times[i]);
413                         else
414                                 fprintf(f, "%g\n", 1e-3*td->times[i]);
415                 }
416                 fclose(f);
417         }
418
419         sem_post(&finish_sem);
420         return (void *)ret;
421 }
422
423 void start_masters(int masters, int first_id)
424 {
425         int id = first_id;
426         int i;
427
428         dbg(1, "Starting %d master threads\n", masters);
429         
430         for (i = 0; i < masters; i++, id += 2) {
431                 thread_data_t *td;
432                 
433                 td = malloc(sizeof(*td));
434                 if (!td) {
435                         printf("Can't allocate memory");
436                         exit(EXIT_NO_MEM);
437                 }
438                 memset(td, 0, sizeof(*td));
439                 td->canid = id;
440                 td->min = 0x7fffffff;
441                 /* TODO use mutexes and signal blocking */
442                 thread_list_ins_tail(&master_threads, td);
443                 pthread_create(&td->tid, NULL, master_thread, (void *)td);
444                 dbg(2, "Master thread: %p\n", (void *)td->tid);
445         }
446
447
448         /* Wait for all threads beeing ready */
449         for (i = 0; i < masters; i++) sem_wait(&ready_sem);
450
451         /* Start threads */
452         pthread_mutex_lock(&mut_start);
453         start_flag = 1;
454         pthread_cond_broadcast(&cond_start);
455         pthread_mutex_unlock(&mut_start);
456 }
457
458 void *slave_thread(void *arg)
459 {
460         thread_data_t *td = (thread_data_t *)arg;
461         int ping_id = td->canid;
462         int pong_id = ping_id + 1;
463         struct canmsg_t pingmsg, pongmsg;
464         int ret = 0;
465         vca_handle_t vcah = VCA_HANDLE_INVALID; /* File descriptor of CAN driver. */
466         int i;
467         struct canfilt_t canfilt; /* filter for received messages */
468
469         if (!option_open_once) {
470                 /* Open the CAN driver and disable (D) reception of
471                  * all messages until we setup a filter below. */
472                 if(vca_open_handle(&vcah, option_device, "D", 0) < 0) {
473                         perror("open");
474                         printf("Error opening %s (for id %d)\n", option_device, ping_id);
475                         exit(EXIT_CANNOT_OPEN); 
476                 }
477         } else {
478                 vcah = global_vcah;
479         }
480         
481         /* setup filtering of received messages */
482         memset(&canfilt, 0, sizeof(canfilt));
483         canfilt.mask = 0xfffffff;
484         canfilt.id = ping_id;   /* receive only our ping messages */
485         ret = vca_set_filt(vcah, &canfilt);
486         if (ret < 0) {
487                 perror("ioctl CANQUE_FILTER");
488                 exit(EXIT_FILTER_ERROR);
489         }
490         /* If there are some messages already, delete them. These may
491          * not processed by our filter and thus may have a wrong
492          * ID. */
493         ret = vca_queue_flush(vcah, 0);
494         if (ret < 0) {
495                 perror("ioctl CANQUE_QUEUE_FLUSH");
496                 exit(EXIT_FLUSH_ERROR);
497         }
498         
499         /* Prepare a pong message */
500         pongmsg.flags = 0;
501         pongmsg.id = pong_id;
502         pongmsg.length = option_length;
503         for (i=0; i < option_length; i++) pongmsg.data[i] = i;
504
505         /* Signal that I'm ready */
506         sem_post(&ready_sem);
507
508         while (!IS_FINISH_FLAG()) {
509                 /* Receive a ping message */
510                 pingmsg.flags=0;
511                 ret = vca_rec_msg_seq(vcah, &pingmsg, 1);
512                 if (ret < 0) {
513                         if (NOT_INTERRUPTED_SYSCALL) {
514                                 printf("%d\n", errno);
515                                 perror("read");
516                                 exit(EXIT_READ_ERROR);
517                         }
518                 }
519                 if (NOT_INTERRUPTED_SYSCALL && pingmsg.id != ping_id) {
520                         fprintf(stderr, "Filter error (expected: %d, received %ld)\n", ping_id, pingmsg.id);
521                         exit(EXIT_FILTER_ERROR);
522                 }
523                 /* Answer immendiately with a pong message */
524                 if (NOT_INTERRUPTED_SYSCALL) {
525                         ret = vca_send_msg_seq(vcah, &pongmsg, 1);
526                         if (ret < 0) {
527                                 if (NOT_INTERRUPTED_SYSCALL) {
528                                         perror("write");
529                                         exit(EXIT_WRITE_ERROR);
530                                 }
531                         }
532                 }
533
534                 if (ret >= 0) total_count++;
535
536                 if (option_verbose >= 2)
537                         /* This drasticly slows down the pong
538                          * response. Why??? */
539                         printf("Replying to ping id %lu\n", pingmsg.id);
540
541         }
542
543         if (!option_open_once) {
544                 /* Close can driver */
545                 vca_close_handle(vcah);
546         }
547
548         dbg(2, "Slave thread for id %d is going to finish\n", ping_id);
549
550         sem_post(&finish_sem);
551         return (void *)ret;
552 }
553      
554 void start_slaves(int slaves, int first_id)
555 {
556         int id = first_id;
557         int i;
558
559         dbg(1, "Starting %d slave threads\n", slaves);
560         
561         for (i = 0; i < slaves; i++, id += 2) {
562                 thread_data_t *td;
563                 
564                 td = malloc(sizeof(*td));
565                 if (!td) {
566                         printf("Can't allocate memory");
567                         exit(EXIT_NO_MEM);
568                 }
569                 memset(td, 0, sizeof(*td));
570                 td->canid = id;
571                 /* TODO use mutexes and signal blocking */
572                 thread_list_ins_tail(&slave_threads, td);
573                 pthread_create(&td->tid, NULL, slave_thread, (void *)td);
574                 dbg(2, "Slave thread: %p\n", (void *)td->tid);
575         }
576
577         /* Wait for all threads beeing ready */
578         for (i = 0; i < slaves; i++) sem_wait(&ready_sem);
579 }
580
581 #ifdef WITH_RTPRIO
582 int set_sched_policy_and_prio(int policy, int rtprio)
583 {
584         struct sched_param scheduling_parameters;
585         int maxprio=sched_get_priority_max(policy);
586         int minprio=sched_get_priority_min(policy);
587
588         if((rtprio < minprio) || (rtprio > maxprio)) {
589                 fprintf(stderr, "The priority for requested policy is out of <%d, %d> range\n",
590                                 minprio, maxprio);
591                 return -1;
592         }
593
594         scheduling_parameters.sched_priority = rtprio;
595
596         if (0 != pthread_setschedparam(pthread_self(), policy, &scheduling_parameters)) {
597                 perror("pthread_setschedparam error");
598         }
599         return 0;
600 }
601 #endif
602
603 void print_help(void)
604 {
605         printf("Usage: canping -m <master threads> [other options]\n"
606                "       canping -s <slave threads> [other options]\n\n"
607                "Other options:\n"
608                "  -b            go to background (fork) after initialization, prints child PID\n"
609                "  -c count      how many messages each master sends\n"
610                "  -d dev        device (e.g. /dev/can1)\n"
611                "  -e prefix     save all measured times in file <prefix>-<id>.dat\n"
612                "  -g prefix     store cumulative histogram in file <prefix>-<id>.dat\n"
613                "  -h            print this help\n"
614                "  -i id         id of first master message\n"
615                "  -l length     length of the messages (0..8)\n"
616                "  -n number     ignore first <number> messages\n"
617                "  -o            open a device only once for all threads (doesn't work)\n" /* due to filters */
618                "  -t timeout    timeout in seconds (default 4 s)\n"
619                "  -v            be verbose (use more than once to increase verbosity)\n"
620                "  -w ms         wait ms miliseconds between sending pings\n"
621                "  -y            synchronize threads before start (doesn't test race conditions\n"
622                "                between open and read/write)\n"
623 #ifdef WITH_RTPRIO
624                "  -r            run at realtime priority\n"
625                "  -R P:prio     run at realtime priority prio, policy RR or FF\n"
626 #endif
627                "\n"
628                "Example: canping -m 10 -vv\n"
629                 );
630 }
631      
632 int parse_options(int argc, char *argv[])
633 {
634         int c;
635         
636         opterr = 0;
637         while ((c = getopt (argc, argv, "bc:d:e:g:hi:l:m:n:os:t:vw:yrR:")) != -1)
638                 switch (c)
639                 {
640                 case 'b':
641                         option_background = true;
642                         break;
643                 case 'c':
644                         option_count = atoi(optarg);
645                         break;
646                 case 'd':
647                         option_device = optarg;
648                         break;
649                 case 'e':
650                         option_save_all_times = optarg;
651                         break;
652                 case 'g':
653                         option_histogram = optarg;
654                         break;
655                 case 'h':
656                         print_help();
657                         exit(EXIT_OK);
658                         break;
659                 case 'i':
660                         option_first_id = atoi(optarg);
661                         break;
662                 case 'l':
663                         option_length = atoi(optarg);
664                         if (option_length > 8) option_length = 8;
665                         if (option_length < 0) option_length = 0;
666                         break;
667                 case 'm':
668                         option_masters = atoi(optarg);
669                         break;
670                 case 'n':
671                         option_msg_to_ignore = atoi(optarg);
672                         break;
673                 case 'o':
674                         option_open_once = 1;
675                         break;
676                 case 's':
677                         option_slaves = atoi(optarg);
678                         break;
679                 case 't':
680                         option_timeout = atoi(optarg);
681                         break;
682                 case 'v':
683                         option_verbose++;
684                         break;
685                 case 'w':
686                         option_wait_ms = atoi(optarg);
687                         break;
688                 case 'y':
689                         option_synch_start = 1;
690                         break;
691 #ifdef WITH_RTPRIO
692                 case 'r':
693                         sched_policy = SCHED_FIFO;
694                         sched_rtprio = (sched_get_priority_min(SCHED_FIFO) +
695                                         sched_get_priority_max(SCHED_FIFO)) / 2;
696                         break;
697                 case 'R':
698                         if(!isalpha(*optarg)) {
699                                 sched_policy = SCHED_FIFO;
700                         } else if(!strncmp(optarg,"FF:",3)) {
701                                 sched_policy = SCHED_FIFO;
702                                 optarg += 3;
703                         } else if(!strncmp(optarg,"RR:",3)) {
704                                 sched_policy = SCHED_RR;
705                                 optarg += 3;
706                         } else {
707                                 fprintf (stderr, "Unknown policy %s\n", optarg);
708                                 exit(EXIT_BAD_PARAM);
709                         }
710                         sched_rtprio = atoi(optarg);
711                         break;
712 #endif
713                 case '?':
714                         if (isprint (optopt))
715                                 fprintf (stderr, "Unknown option `-%c'.\n", optopt);
716                         else
717                                 fprintf (stderr,
718                                          "Unknown option character `\\x%x'.\n",
719                                          optopt);
720                         return 1;
721                 default:
722                         exit(EXIT_BAD_PARAM);
723                 }
724         if (!(option_masters || option_slaves) || (option_masters && option_slaves))
725                 exit(EXIT_BAD_PARAM);
726         return 0;
727 }
728
729 void print_stats(thread_data_t *td)
730 {
731         char std[20];
732         int count = td->count + td->timeout;
733         
734         if (td->count >= 2) {
735                 snprintf(std, 19, "%7.2f", sqrt((td->count/(td->count - 1)) * 
736                                                 (td->moment2nd - td->mean*td->mean)));
737         } else {
738                 strncpy(std, "N/A", 19);
739         }
740         printf("Id %4ld: count = %5d"
741                " mean = %7.2f stddev = %s"
742                " min = %5d max = %5d [us]"
743                " loss = %d%% (%d)\n",
744                td->canid, count, td->mean, 
745                std,
746                td->min, td->max,
747                (count > 0) ? 100 * td->timeout / count : 0, td->timeout
748                 );
749 }
750
751 void wait_for_threads(void)
752 {
753         thread_data_t *td;
754         void *thread_ret;
755         int thread_count = option_slaves + option_masters;
756         int ret;
757
758         while (thread_count > 0) {
759                 if (option_verbose != 1) {
760                         ret = sem_wait(&finish_sem);
761                         dbg(2, "Main thread sem_wait() exited with ret=%d.\n", ret);
762
763                         /* If the sem_wait is successful i.e. not
764                          * interrupted by a signal, decrease the
765                          * thread_count*/
766                         if (ret == 0) thread_count--; 
767                 } else {
768                         if (!IS_FINISH_FLAG()) {
769                                 printf("Total count: %6d, Timeouts: %6d\r", total_count, total_timeout);
770                                 fflush(stdout);
771                                 usleep(1000000);
772                         }
773                         while (sem_trywait(&finish_sem) == 0) thread_count--;
774                 }
775         }
776         if (option_verbose == 1) {
777                 printf("\n");
778         }
779
780         ul_list_for_each(thread_list, &master_threads, td) {
781                 pthread_join(td->tid, &thread_ret);
782                 dbg(2, "Master thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
783         }
784
785         ul_list_for_each(thread_list, &slave_threads, td) {
786                 pthread_join(td->tid, &thread_ret);
787                 dbg(2, "Slave thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
788         }
789
790         if (option_masters) printf("Summary statistics:\n");
791
792         ul_list_for_each_cut(thread_list, &master_threads, td) {
793                 print_stats(td);
794                 free(td);
795         }
796         
797
798         ul_list_for_each_cut(thread_list, &slave_threads, td) 
799                 free(td);
800 }
801
802 int main(int argc, char *argv[])
803 {
804         parse_options(argc, argv);
805
806 #ifdef WITH_RTPRIO
807         if(sched_policy != SCHED_OTHER) {
808                 if(set_sched_policy_and_prio(sched_policy, sched_rtprio) <0)
809                         exit(EXIT_BAD_PARAM);
810                 mlockall(MCL_CURRENT | MCL_FUTURE);
811         }
812 #endif
813
814
815         thread_list_init_head(&master_threads);
816         thread_list_init_head(&slave_threads);
817
818         sem_init(&finish_sem, 0, 0);
819         sem_init(&ready_sem, 0, 0);
820
821         siginterrupt(SIGINT, 1);
822         signal(SIGINT, term_handler);
823         siginterrupt(SIGTERM, 1);
824         signal(SIGTERM, term_handler);
825
826         dbg(2, "Main thread: %p\n", (void *)pthread_self());
827
828         if (option_open_once) {
829                 /* Open the CAN driver */
830                 if(vca_open_handle(&global_vcah, option_device, NULL, 0) < 0) {
831                         perror("open");
832                         printf("Error opening %s\n", option_device);
833                         exit(EXIT_CANNOT_OPEN); 
834                 }
835         }
836
837         sem_t *child_ready;
838         int fork_ret = 0;
839         if ((child_ready = sem_open("canping", O_CREAT)) == NULL)
840                 error(1, errno, "sem_open");
841         if (option_background) {
842                 /* Go to background when everything is ready */
843                 fork_ret = fork();
844                 if (fork_ret < 0)
845                         error(1, errno, "Cannot go to background");
846                 if (fork_ret == 0) {
847                         int devnull = open("/dev/null", O_WRONLY);
848                         dup2(devnull, 1);
849                 }
850         }
851
852         if (fork_ret == 0) {
853                 /* Child */
854                 if (option_masters) start_masters(option_masters, option_first_id);
855                 if (option_slaves) start_slaves(option_slaves, option_first_id);
856                 sem_post(child_ready);
857         } else {
858                 /* Parent */
859                 sem_wait(child_ready);
860                 printf("%d\n", fork_ret);
861                 exit(0);
862         }
863         
864         wait_for_threads();
865
866         if (option_open_once) {
867                 vca_close_handle(global_vcah);
868         }
869
870         return 0;
871 }