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