1 /**************************************************************************/
2 /* File: vca_canping.c - utility to test CAN functionality and throughput */
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> */
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 /**************************************************************************/
29 #include <sys/types.h>
33 #include <semaphore.h>
35 /* TODO: Handle the case where there are more canping slaves running
47 int sched_policy = SCHED_OTHER;
54 #define dbg(level, fmt, arg...) do {} while (0)
56 #define dbg(level, fmt, arg...) do { if (level <= DEBUG) { printf("candping: " fmt, ## arg); } } while (0)
59 #define NOT_INTERRUPTED_SYSCALL (errno != EINTR && errno != ERESTART)
60 #define IS_FINISH_FLAG() (finish_flag)
64 #define EXIT_BAD_PARAM 1
65 #define EXIT_CANNOT_OPEN 2
66 #define EXIT_FILTER_ERROR 3
68 #define EXIT_READ_ERROR 5
69 #define EXIT_WRITE_ERROR 6
70 #define EXIT_SELECT_ERROR 7
71 #define EXIT_FLUSH_ERROR 8
73 /* Global variables */
74 sig_atomic_t finish_flag = 0; /* Threads should terminate. */
75 sem_t finish_sem; /* Thread signals a termination */
76 vca_handle_t global_vcah = VCA_HANDLE_INVALID;
79 int total_timeout = 0;
81 pthread_mutex_t mut_start = PTHREAD_MUTEX_INITIALIZER;
82 pthread_cond_t cond_start = PTHREAD_COND_INITIALIZER;
84 sem_t ready_sem; /* Thread is ready for execution */
87 /* Command line options */
88 char *option_device = "/dev/can0";
89 int option_masters = 0;
90 long int option_first_id = 1000;
91 int option_slaves = 0;
92 int option_verbose = 0; /* 0 - nothing, 1 - only global
93 * statistics, 2 - simple times, 3 -
95 int option_length = 8;
97 int option_wait_ms = 1000;
98 int option_timeout = 4;
99 int option_open_once = 0;
100 int option_synch_start = 0;
103 typedef struct threads {
107 typedef struct thread_data {
112 double mean; /* mean value of responses */
113 double moment2nd; /* used to compute variance of
115 int min, max; /* min/max response times */
116 int timeout; /* number of timeouts */
121 UL_LIST_CUST_DEC(thread_list, threads_t, thread_data_t, head, node);
123 threads_t master_threads;
124 threads_t slave_threads;
126 /* Subtract the `struct timeval' values X and Y, storing the result in
127 RESULT. Return 1 if the difference is negative, otherwise 0. */
129 int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
131 /* Perform the carry for the later subtraction by updating Y. */
132 if (x->tv_usec < y->tv_usec) {
133 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
134 y->tv_usec -= 1000000 * nsec;
137 if (x->tv_usec - y->tv_usec > 1000000) {
138 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
139 y->tv_usec += 1000000 * nsec;
143 /* Compute the time remaining to wait.
144 `tv_usec' is certainly positive. */
145 result->tv_sec = x->tv_sec - y->tv_sec;
146 result->tv_usec = x->tv_usec - y->tv_usec;
148 /* Return 1 if result is negative. */
149 return x->tv_sec < y->tv_sec;
152 void dbg_print_timeval(char *msg, struct timeval *tv)
155 printf("%s sec=%ld usec=%ld\n", msg, tv->tv_sec, tv->tv_usec);
157 void kill_all_threads(int signum)
161 ul_list_for_each(thread_list, &master_threads, td) {
162 pthread_kill(td->tid, signum);
164 ul_list_for_each(thread_list, &slave_threads, td) {
165 pthread_kill(td->tid, signum);
168 void term_handler(int signum)
170 dbg(1, "Thread %p got a signal %d\n", (void *)pthread_self(), signum);
171 if (!IS_FINISH_FLAG()) {
172 dbg(1, "Terminating threads\n");
175 kill_all_threads(signum);
179 void *master_thread(void *arg)
181 thread_data_t *td = (thread_data_t *)arg;
182 int ping_id = td->canid;
183 int pong_id = ping_id + 1;
184 struct canmsg_t pingmsg, pongmsg;
187 vca_handle_t vcah = VCA_HANDLE_INVALID; /* File descriptor of CAN driver. */
189 fd_set rdset; /* read set for select syscall */
190 struct timeval timeout, pingtime, pongtime;
191 struct canfilt_t canfilt; /* filter for received messages */
193 if (!option_open_once) {
194 /* Open can driver */
195 if(vca_open_handle(&vcah, option_device, NULL, 0) < 0) {
197 fprintf(stderr, "Error opening %s (for id %d)\n", option_device, ping_id);
198 exit(EXIT_CANNOT_OPEN);
204 /* setup filtering of received messages */
205 memset(&canfilt, 0, sizeof(canfilt));
206 canfilt.mask = 0xfffffff;
207 canfilt.id = pong_id; /* pong responces with increased id */
208 ret = vca_set_filt(vcah, &canfilt);
210 perror("ioctl CANQUE_FILTER");
211 exit(EXIT_FILTER_ERROR);
213 ret = vca_queue_flush(vcah, 0);
215 perror("ioctl CANQUE_QUEUE_FLUSH");
216 exit(EXIT_FLUSH_ERROR);
219 /* Prepare data structures for the select syscall. */
222 /* Signal that I'm ready */
223 sem_post(&ready_sem);
225 if (option_synch_start) {
226 /* Wait for other threads to initialize */
227 pthread_mutex_lock(&mut_start);
228 while (!start_flag) pthread_cond_wait(&cond_start, &mut_start);
229 pthread_mutex_unlock(&mut_start);
232 while (!IS_FINISH_FLAG() && (option_count == 0 || ping_count++ < option_count)) {
233 /* Send a ping message */
236 pingmsg.length = option_length;
237 for (i=0; i < option_length; i++) pingmsg.data[i] = i;
238 gettimeofday(&pingtime, NULL);
240 ret = vca_send_msg_seq(vcah, &pingmsg, 1);
242 if (NOT_INTERRUPTED_SYSCALL) {
244 exit(EXIT_WRITE_ERROR);
249 /* Wait for a pong responce */
250 FD_SET (vca_h2fd(vcah), &rdset);
252 timeout.tv_sec = option_timeout;
255 ret = select(FD_SETSIZE, &rdset, NULL, NULL, &timeout);
257 /* Read the message */
259 ret = vca_rec_msg_seq(vcah, &pongmsg, 1);
261 if (NOT_INTERRUPTED_SYSCALL) {
263 exit(EXIT_READ_ERROR);
267 else { /* A pong message received */
269 gettimeofday(&pongtime, NULL);
271 fprintf(stderr, "read returned zero\n");
272 exit(EXIT_READ_ERROR);
274 if (pongmsg.id != pong_id) {
275 fprintf(stderr, "Filter error (expected: %d, received %ld)\n", pong_id, pongmsg.id);
276 exit(EXIT_FILTER_ERROR);
278 timeval_subtract(&pongtime, &pongtime, &pingtime);
279 time = pongtime.tv_sec*1000000 + pongtime.tv_usec;
280 switch (option_verbose) {
282 printf("%d:%ld\n", ping_id, time);
285 printf("Pong response for id %d received in %ld us\n", ping_id, time);
288 /* Update statistics */
291 td->mean * ((double)(td->count - 1) / td->count) +
292 (double)time / td->count;
294 td->moment2nd * ((double)(td->count - 1) / td->count) +
295 (double)time*time / td->count;
296 if (time > td->max) td->max = time;
297 if (time < td->min) td->min = time;
301 else if (ret == 0) { /* select */
302 if (option_verbose >= 2)
303 printf("Timeout encountered (id %d)\n", ping_id);
308 if (NOT_INTERRUPTED_SYSCALL) {
310 exit(EXIT_SELECT_ERROR);
314 usleep(option_wait_ms * 1000);
317 if (!option_open_once) {
318 /* Close the can driver */
319 vca_close_handle(vcah);
322 sem_post(&finish_sem);
326 void start_masters(int masters, int first_id)
331 dbg(1, "Starting %d master threads\n", masters);
333 for (i = 0; i < masters; i++, id += 2) {
336 td = malloc(sizeof(*td));
338 printf("Can't allocate memory");
341 memset(td, 0, sizeof(*td));
343 td->min = 0x7fffffff;
344 /* TODO use mutexes and signal blocking */
345 thread_list_ins_tail(&master_threads, td);
346 pthread_create(&td->tid, NULL, master_thread, (void *)td);
347 dbg(2, "Master thread: %p\n", (void *)td->tid);
351 /* Wait for all threads beeing ready */
352 for (i = 0; i < masters; i++) sem_wait(&ready_sem);
355 pthread_mutex_lock(&mut_start);
357 pthread_cond_broadcast(&cond_start);
358 pthread_mutex_unlock(&mut_start);
361 void *slave_thread(void *arg)
363 thread_data_t *td = (thread_data_t *)arg;
364 int ping_id = td->canid;
365 int pong_id = ping_id + 1;
366 struct canmsg_t pingmsg, pongmsg;
368 vca_handle_t vcah = VCA_HANDLE_INVALID; /* File descriptor of CAN driver. */
370 struct canfilt_t canfilt; /* filter for received messages */
372 if (!option_open_once) {
373 /* Open the CAN driver */
374 if(vca_open_handle(&vcah, option_device, NULL, 0) < 0) {
376 printf("Error opening %s (for id %d)\n", option_device, ping_id);
377 exit(EXIT_CANNOT_OPEN);
383 /* setup filtering of received messages */
384 memset(&canfilt, 0, sizeof(canfilt));
385 canfilt.mask = 0xfffffff;
386 canfilt.id = ping_id; /* receive only our ping messages */
387 ret = vca_set_filt(vcah, &canfilt);
389 perror("ioctl CANQUE_FILTER");
390 exit(EXIT_FILTER_ERROR);
392 /* If there are some messages already, delete them. These may
393 * not processed by our filter and thus may have a wrong
395 ret = vca_queue_flush(vcah, 0);
397 perror("ioctl CANQUE_QUEUE_FLUSH");
398 exit(EXIT_FLUSH_ERROR);
401 /* Prepare a pong message */
403 pongmsg.id = pong_id;
404 pongmsg.length = option_length;
405 for (i=0; i < option_length; i++) pongmsg.data[i] = i;
407 while (!IS_FINISH_FLAG()) {
408 /* Receive a ping message */
410 ret = vca_rec_msg_seq(vcah, &pingmsg, 1);
412 if (NOT_INTERRUPTED_SYSCALL) {
413 printf("%d\n", errno);
415 exit(EXIT_READ_ERROR);
418 if (NOT_INTERRUPTED_SYSCALL && pingmsg.id != ping_id) {
419 fprintf(stderr, "Filter error (expected: %d, received %ld)\n", ping_id, pingmsg.id);
420 exit(EXIT_FILTER_ERROR);
422 /* Answer immendiately with a pong message */
423 if (NOT_INTERRUPTED_SYSCALL) {
424 ret = vca_send_msg_seq(vcah, &pongmsg, 1);
426 if (NOT_INTERRUPTED_SYSCALL) {
428 exit(EXIT_WRITE_ERROR);
433 if (ret >= 0) total_count++;
435 if (option_verbose >= 2)
436 /* This drasticly slows down the pong
437 * response. Why??? */
438 printf("Replying to ping id %lu\n", pingmsg.id);
442 if (!option_open_once) {
443 /* Close can driver */
444 vca_close_handle(vcah);
447 dbg(2, "Slave thread for id %d is going to finish\n", ping_id);
449 sem_post(&finish_sem);
453 void start_slaves(int slaves, int first_id)
458 dbg(1, "Starting %d slave threads\n", slaves);
460 for (i = 0; i < slaves; i++, id += 2) {
463 td = malloc(sizeof(*td));
465 printf("Can't allocate memory");
468 memset(td, 0, sizeof(*td));
470 /* TODO use mutexes and signal blocking */
471 thread_list_ins_tail(&slave_threads, td);
472 pthread_create(&td->tid, NULL, slave_thread, (void *)td);
473 dbg(2, "Slave thread: %p\n", (void *)td->tid);
478 int set_sched_policy_and_prio(int policy, int rtprio)
480 struct sched_param scheduling_parameters;
481 int maxprio=sched_get_priority_max(policy);
482 int minprio=sched_get_priority_min(policy);
484 if((rtprio < minprio) || (rtprio > maxprio)) {
485 fprintf(stderr, "The priority for requested policy is out of <%d, %d> range\n",
490 scheduling_parameters.sched_priority = rtprio;
492 if (0 != pthread_setschedparam(pthread_self(), policy, &scheduling_parameters)) {
493 perror("pthread_setschedparam error");
499 void print_help(void)
501 printf("Usage: canping -m <master threads> [other options]\n"
502 " canping -s <slave threads> [other options]\n\n"
504 " -c count how many messages each master sends\n"
505 " -d dev device (e.g. /dev/can1)\n"
506 " -h print this help\n"
507 " -i id id of first master message\n"
508 " -l length length of the messages (0..8)\n"
509 " -o open a device only once for all threads (doesn't work)\n" /* due to filters */
510 " -t timeout timeout in seconds (default 4 s)\n"
511 " -v be verbose (use more than once to increase verbosity)\n"
512 " -w ms wait ms miliseconds between sending pings\n"
513 " -y synchronize threads before start (doesn't test race conditions\n"
514 " between open and read/write)\n"
516 " -r run at realtime priority\n"
517 " -R P:prio run at realtime priority prio, policy RR or FF\n"
520 "Example: canping -m 10 -vv\n"
524 int parse_options(int argc, char *argv[])
529 while ((c = getopt (argc, argv, "c:d:hi:l:m:os:t:vw:yrR:")) != -1)
533 option_count = atoi(optarg);
536 option_device = optarg;
543 option_first_id = atoi(optarg);
546 option_length = atoi(optarg);
547 if (option_length > 8) option_length = 8;
548 if (option_length < 0) option_length = 0;
551 option_masters = atoi(optarg);
554 option_open_once = 1;
557 option_slaves = atoi(optarg);
560 option_timeout = atoi(optarg);
566 option_wait_ms = atoi(optarg);
569 option_synch_start = 1;
573 sched_policy = SCHED_FIFO;
574 sched_rtprio = (sched_get_priority_min(SCHED_FIFO) +
575 sched_get_priority_max(SCHED_FIFO)) / 2;
578 if(!isalpha(*optarg)) {
579 sched_policy = SCHED_FIFO;
580 } else if(!strncmp(optarg,"FF:",3)) {
581 sched_policy = SCHED_FIFO;
583 } else if(!strncmp(optarg,"RR:",3)) {
584 sched_policy = SCHED_RR;
587 fprintf (stderr, "Unknown policy %s\n", optarg);
588 exit(EXIT_BAD_PARAM);
590 sched_rtprio = atoi(optarg);
594 if (isprint (optopt))
595 fprintf (stderr, "Unknown option `-%c'.\n", optopt);
598 "Unknown option character `\\x%x'.\n",
602 exit(EXIT_BAD_PARAM);
604 if (!(option_masters || option_slaves) || (option_masters && option_slaves))
605 exit(EXIT_BAD_PARAM);
609 void print_stats(thread_data_t *td)
612 int count = td->count + td->timeout;
614 if (td->count >= 2) {
615 snprintf(std, 19, "%7.2f", sqrt((td->count/(td->count - 1)) *
616 (td->moment2nd - td->mean*td->mean)));
618 strncpy(std, "N/A", 19);
620 printf("Id %4ld: count = %5d"
621 " mean = %7.2f stddev = %s"
622 " min = %5d max = %5d [us]"
623 " loss = %d%% (%d)\n",
624 td->canid, count, td->mean,
627 (count > 0) ? 100 * td->timeout / count : 0, td->timeout
631 void wait_for_threads(void)
635 int thread_count = option_slaves + option_masters;
638 while (thread_count > 0) {
639 if (option_verbose != 1) {
640 ret = sem_wait(&finish_sem);
641 dbg(2, "Main thread sem_wait() exited with ret=%d.\n", ret);
643 /* If the sem_wait is successful i.e. not
644 * interrupted by a signal, decrease the
646 if (ret == 0) thread_count--;
648 if (!IS_FINISH_FLAG()) {
649 printf("Total count: %6d, Timeouts: %6d\r", total_count, total_timeout);
653 while (sem_trywait(&finish_sem) == 0) thread_count--;
656 if (option_verbose == 1) {
660 ul_list_for_each(thread_list, &master_threads, td) {
661 pthread_join(td->tid, &thread_ret);
662 dbg(2, "Master thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
665 ul_list_for_each(thread_list, &slave_threads, td) {
666 pthread_join(td->tid, &thread_ret);
667 dbg(2, "Slave thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
670 if (option_masters) printf("Summary statistics:\n");
672 ul_list_for_each_cut(thread_list, &master_threads, td) {
678 ul_list_for_each_cut(thread_list, &slave_threads, td)
682 int main(int argc, char *argv[])
684 parse_options(argc, argv);
687 if(sched_policy != SCHED_OTHER)
688 if(set_sched_policy_and_prio(sched_policy, sched_rtprio) <0)
689 exit(EXIT_BAD_PARAM);
693 thread_list_init_head(&master_threads);
694 thread_list_init_head(&slave_threads);
696 sem_init(&finish_sem, 0, 0);
697 sem_init(&ready_sem, 0, 0);
699 siginterrupt(SIGINT, 1);
700 signal(SIGINT, term_handler);
701 siginterrupt(SIGTERM, 1);
702 signal(SIGTERM, term_handler);
704 dbg(2, "Main thread: %p\n", (void *)pthread_self());
706 if (option_open_once) {
707 /* Open the CAN driver */
708 if(vca_open_handle(&global_vcah, option_device, NULL, 0) < 0) {
710 printf("Error opening %s\n", option_device);
711 exit(EXIT_CANNOT_OPEN);
715 if (option_masters) start_masters(option_masters, option_first_id);
716 if (option_slaves) start_slaves(option_slaves, option_first_id);
720 if (option_open_once) {
721 vca_close_handle(global_vcah);