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