2 This program is free software: you can redistribute it and/or modify
3 it under the terms of the GNU General Public License as published by
4 the Free Software Foundation, either version 2 of the License, or
5 (at your option) any later version.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
12 You should have received a copy of the GNU General Public License
13 along with this program. If not, see <http://www.gnu.org/licenses/>.
15 Copyright 2005 Michal Sojka <sojkam1@fel.cvut.cz>
16 Copyright 2005-2023 Pavel Pisa <pisa@cmp.felk.cvut.cz>
28 #include <sys/types.h>
32 #include <semaphore.h>
34 /* TODO: Handle the case where there are more canping slaves running
46 int sched_policy = SCHED_OTHER;
53 #define dbg(level, fmt, arg...) do {} while (0)
55 #define dbg(level, fmt, arg...) do { if (level <= DEBUG) { printf("candping: " fmt, ## arg); } } while (0)
58 #define NOT_INTERRUPTED_SYSCALL (errno != EINTR && errno != ERESTART)
59 #define IS_FINISH_FLAG() (finish_flag)
63 #define EXIT_BAD_PARAM 1
64 #define EXIT_CANNOT_OPEN 2
65 #define EXIT_FILTER_ERROR 3
67 #define EXIT_READ_ERROR 5
68 #define EXIT_WRITE_ERROR 6
69 #define EXIT_SELECT_ERROR 7
70 #define EXIT_FLUSH_ERROR 8
72 /* Global variables */
73 sig_atomic_t finish_flag = 0; /* Threads should terminate. */
74 sem_t finish_sem; /* Thread signals a termination */
78 int total_timeout = 0;
80 pthread_mutex_t mut_start = PTHREAD_MUTEX_INITIALIZER;
81 pthread_cond_t cond_start = PTHREAD_COND_INITIALIZER;
83 sem_t ready_sem; /* Thread is ready for execution */
86 /* Command line options */
87 char *option_device = "/dev/can0";
88 int option_masters = 0;
89 long int option_first_id = 1000;
90 int option_slaves = 0;
91 int option_verbose = 0; /* 0 - nothing, 1 - only global
92 * statistics, 2 - simple times, 3 -
94 int option_length = 8;
96 int option_wait_ms = 1000;
97 int option_timeout = 4;
98 int option_open_once = 0;
99 int option_synch_start = 0;
102 typedef struct threads {
106 typedef struct thread_data {
111 double mean; /* mean value of responses */
112 double moment2nd; /* used to compute variance of
114 int min, max; /* min/max response times */
115 int timeout; /* number of timeouts */
120 UL_LIST_CUST_DEC(thread_list, threads_t, thread_data_t, head, node);
122 threads_t master_threads;
123 threads_t slave_threads;
125 /* Subtract the `struct timeval' values X and Y, storing the result in
126 RESULT. Return 1 if the difference is negative, otherwise 0. */
128 int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
130 /* Perform the carry for the later subtraction by updating Y. */
131 if (x->tv_usec < y->tv_usec) {
132 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
133 y->tv_usec -= 1000000 * nsec;
136 if (x->tv_usec - y->tv_usec > 1000000) {
137 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
138 y->tv_usec += 1000000 * nsec;
142 /* Compute the time remaining to wait.
143 `tv_usec' is certainly positive. */
144 result->tv_sec = x->tv_sec - y->tv_sec;
145 result->tv_usec = x->tv_usec - y->tv_usec;
147 /* Return 1 if result is negative. */
148 return x->tv_sec < y->tv_sec;
151 void dbg_print_timeval(char *msg, struct timeval *tv)
154 printf("%s sec=%ld usec=%ld\n", msg, tv->tv_sec, tv->tv_usec);
156 void kill_all_threads(int signum)
160 ul_list_for_each(thread_list, &master_threads, td) {
161 pthread_kill(td->tid, signum);
163 ul_list_for_each(thread_list, &slave_threads, td) {
164 pthread_kill(td->tid, signum);
167 void term_handler(int signum)
169 dbg(1, "Thread %p got a signal %d\n", (void *)pthread_self(), signum);
170 if (!IS_FINISH_FLAG()) {
171 dbg(1, "Terminating threads\n");
174 kill_all_threads(signum);
178 void *master_thread(void *arg)
180 thread_data_t *td = (thread_data_t *)arg;
181 int ping_id = td->canid;
182 int pong_id = ping_id + 1;
183 struct canmsg_t pingmsg, pongmsg;
186 int canfd; /* File descriptor of CAN driver. */
188 fd_set rdset; /* read set for select syscall */
189 struct timeval timeout, pingtime, pongtime;
190 struct canfilt_t canfilt; /* filter for received messages */
192 if (!option_open_once) {
193 /* Open can driver */
194 if ((canfd = open(option_device, O_RDWR)) < 0) {
196 fprintf(stderr, "Error opening %s (for id %d)\n", option_device, ping_id);
197 exit(EXIT_CANNOT_OPEN);
200 canfd = global_canfd;
203 /* setup filtering of received messages */
204 memset(&canfilt, 0, sizeof(canfilt));
205 canfilt.mask = 0xfffffff;
206 canfilt.id = pong_id; /* pong responces with increased id */
207 ret = ioctl(canfd, CANQUE_FILTER, &canfilt);
209 perror("ioctl CANQUE_FILTER");
210 exit(EXIT_FILTER_ERROR);
212 ret = ioctl(canfd, CANQUE_FLUSH, NULL);
214 perror("ioctl CANQUE_QUEUE_FLUSH");
215 exit(EXIT_FLUSH_ERROR);
218 /* Prepare data structures for the select syscall. */
221 /* Signal that I'm ready */
222 sem_post(&ready_sem);
224 if (option_synch_start) {
225 /* Wait for other threads to initialize */
226 pthread_mutex_lock(&mut_start);
227 while (!start_flag) pthread_cond_wait(&cond_start, &mut_start);
228 pthread_mutex_unlock(&mut_start);
231 while (!IS_FINISH_FLAG() && (option_count == 0 || ping_count++ < option_count)) {
232 /* Send a ping message */
235 pingmsg.length = option_length;
236 for (i=0; i < option_length; i++) pingmsg.data[i] = i;
237 gettimeofday(&pingtime, NULL);
239 ret = write(canfd, &pingmsg, sizeof(pingmsg));
241 if (NOT_INTERRUPTED_SYSCALL) {
243 exit(EXIT_WRITE_ERROR);
248 /* Wait for a pong responce */
249 FD_SET (canfd, &rdset);
251 timeout.tv_sec = option_timeout;
254 ret = select(FD_SETSIZE, &rdset, NULL, NULL, &timeout);
256 /* Read the message */
258 ret = read(canfd, &pongmsg, sizeof(pongmsg));
260 if (NOT_INTERRUPTED_SYSCALL) {
262 exit(EXIT_READ_ERROR);
266 else { /* A pong message received */
268 gettimeofday(&pongtime, NULL);
270 fprintf(stderr, "read returned zero\n");
271 exit(EXIT_READ_ERROR);
273 if (pongmsg.id != pong_id) {
274 fprintf(stderr, "Filter error (expected: %d, received %ld)\n", pong_id, pongmsg.id);
275 exit(EXIT_FILTER_ERROR);
277 timeval_subtract(&pongtime, &pongtime, &pingtime);
278 time = pongtime.tv_sec*1000000 + pongtime.tv_usec;
279 switch (option_verbose) {
281 printf("%d:%ld\n", ping_id, time);
284 printf("Pong response for id %d received in %ld us\n", ping_id, time);
287 /* Update statistics */
290 td->mean * ((double)(td->count - 1) / td->count) +
291 (double)time / td->count;
293 td->moment2nd * ((double)(td->count - 1) / td->count) +
294 (double)time*time / td->count;
295 if (time > td->max) td->max = time;
296 if (time < td->min) td->min = time;
300 else if (ret == 0) { /* select */
301 if (option_verbose >= 2)
302 printf("Timeout encountered (id %d)\n", ping_id);
307 if (NOT_INTERRUPTED_SYSCALL) {
309 exit(EXIT_SELECT_ERROR);
313 usleep(option_wait_ms * 1000);
316 if (!option_open_once) {
317 /* Close the can driver */
321 sem_post(&finish_sem);
325 void start_masters(int masters, int first_id)
330 dbg(1, "Starting %d master threads\n", masters);
332 for (i = 0; i < masters; i++, id += 2) {
335 td = malloc(sizeof(*td));
337 printf("Can't allocate memory");
340 memset(td, 0, sizeof(*td));
342 td->min = 0x7fffffff;
343 /* TODO use mutexes and signal blocking */
344 thread_list_ins_tail(&master_threads, td);
345 pthread_create(&td->tid, NULL, master_thread, (void *)td);
346 dbg(2, "Master thread: %p\n", (void *)td->tid);
350 /* Wait for all threads beeing ready */
351 for (i = 0; i < masters; i++) sem_wait(&ready_sem);
354 pthread_mutex_lock(&mut_start);
356 pthread_cond_broadcast(&cond_start);
357 pthread_mutex_unlock(&mut_start);
360 void *slave_thread(void *arg)
362 thread_data_t *td = (thread_data_t *)arg;
363 int ping_id = td->canid;
364 int pong_id = ping_id + 1;
365 struct canmsg_t pingmsg, pongmsg;
367 int canfd; /* File descriptor of CAN driver. */
369 struct canfilt_t canfilt; /* filter for received messages */
371 if (!option_open_once) {
372 /* Open the CAN driver */
373 if ((canfd = open(option_device, O_RDWR)) < 0) {
375 printf("Error opening %s (for id %d)\n", option_device, ping_id);
376 exit(EXIT_CANNOT_OPEN);
379 canfd = global_canfd;
382 /* setup filtering of received messages */
383 memset(&canfilt, 0, sizeof(canfilt));
384 canfilt.mask = 0xfffffff;
385 canfilt.id = ping_id; /* receive only our ping messages */
386 ret = ioctl(canfd, CANQUE_FILTER, &canfilt);
388 perror("ioctl CANQUE_FILTER");
389 exit(EXIT_FILTER_ERROR);
391 /* If there are some messages already, delete them. These may
392 * not processed by our filter and thus may have a wrong
394 ret = ioctl(canfd, CANQUE_FLUSH, NULL);
396 perror("ioctl CANQUE_QUEUE_FLUSH");
397 exit(EXIT_FLUSH_ERROR);
400 /* Prepare a pong message */
402 pongmsg.id = pong_id;
403 pongmsg.length = option_length;
404 for (i=0; i < option_length; i++) pongmsg.data[i] = i;
406 while (!IS_FINISH_FLAG()) {
407 /* Receive a ping message */
409 ret = read(canfd, &pingmsg, sizeof(pingmsg));
411 if (NOT_INTERRUPTED_SYSCALL) {
412 printf("%d\n", errno);
414 exit(EXIT_READ_ERROR);
417 if (NOT_INTERRUPTED_SYSCALL && pingmsg.id != ping_id) {
418 fprintf(stderr, "Filter error (expected: %d, received %ld)\n", ping_id, pingmsg.id);
419 exit(EXIT_FILTER_ERROR);
421 /* Answer immendiately with a pong message */
422 if (NOT_INTERRUPTED_SYSCALL) {
423 ret = write(canfd, &pongmsg, sizeof(pongmsg));
425 if (NOT_INTERRUPTED_SYSCALL) {
427 exit(EXIT_WRITE_ERROR);
432 if (ret >= 0) total_count++;
434 if (option_verbose >= 2)
435 /* This drasticly slows down the pong
436 * response. Why??? */
437 printf("Replying to ping id %lu\n", pingmsg.id);
441 if (!option_open_once) {
442 /* Close can driver */
446 dbg(2, "Slave thread for id %d is going to finish\n", ping_id);
448 sem_post(&finish_sem);
452 void start_slaves(int slaves, int first_id)
457 dbg(1, "Starting %d slave threads\n", slaves);
459 for (i = 0; i < slaves; i++, id += 2) {
462 td = malloc(sizeof(*td));
464 printf("Can't allocate memory");
467 memset(td, 0, sizeof(*td));
469 /* TODO use mutexes and signal blocking */
470 thread_list_ins_tail(&slave_threads, td);
471 pthread_create(&td->tid, NULL, slave_thread, (void *)td);
472 dbg(2, "Slave thread: %p\n", (void *)td->tid);
477 int set_sched_policy_and_prio(int policy, int rtprio)
479 struct sched_param scheduling_parameters;
480 int maxprio=sched_get_priority_max(policy);
481 int minprio=sched_get_priority_min(policy);
483 if((rtprio < minprio) || (rtprio > maxprio)) {
484 fprintf(stderr, "The priority for requested policy is out of <%d, %d> range\n",
489 scheduling_parameters.sched_priority = rtprio;
491 if (0 != pthread_setschedparam(pthread_self(), policy, &scheduling_parameters)) {
492 perror("pthread_setschedparam error");
498 void print_help(void)
500 printf("Usage: canping -m <master threads> [other options]\n"
501 " canping -s <slave threads> [other options]\n\n"
503 " -c count how many messages each master sends\n"
504 " -d dev device (e.g. /dev/can1)\n"
505 " -h print this help\n"
506 " -i id id of first master message\n"
507 " -l length length of the messages (0..8)\n"
508 " -o open a device only once for all threads (doesn't work)\n" /* due to filters */
509 " -t timeout timeout in seconds (default 4 s)\n"
510 " -v be verbose (use more than once to increase verbosity)\n"
511 " -w ms wait ms miliseconds between sending pings\n"
512 " -y synchronize threads before start (doesn't test race conditions\n"
513 " between open and read/write)\n"
515 " -r run at realtime priority\n"
516 " -R P:prio run at realtime priority prio, policy RR or FF\n"
519 "Example: canping -m 10 -vv\n"
523 int parse_options(int argc, char *argv[])
528 while ((c = getopt (argc, argv, "c:d:hi:l:m:os:t:vw:yrR:")) != -1)
532 option_count = atoi(optarg);
535 option_device = optarg;
542 option_first_id = atoi(optarg);
545 option_length = atoi(optarg);
546 if (option_length > 8) option_length = 8;
547 if (option_length < 0) option_length = 0;
550 option_masters = atoi(optarg);
553 option_open_once = 1;
556 option_slaves = atoi(optarg);
559 option_timeout = atoi(optarg);
565 option_wait_ms = atoi(optarg);
568 option_synch_start = 1;
572 sched_policy = SCHED_FIFO;
573 sched_rtprio = (sched_get_priority_min(SCHED_FIFO) +
574 sched_get_priority_max(SCHED_FIFO)) / 2;
577 if(!isalpha(*optarg)) {
578 sched_policy = SCHED_FIFO;
579 } else if(!strncmp(optarg,"FF:",3)) {
580 sched_policy = SCHED_FIFO;
582 } else if(!strncmp(optarg,"RR:",3)) {
583 sched_policy = SCHED_RR;
586 fprintf (stderr, "Unknown policy %s\n", optarg);
587 exit(EXIT_BAD_PARAM);
589 sched_rtprio = atoi(optarg);
593 if (isprint (optopt))
594 fprintf (stderr, "Unknown option `-%c'.\n", optopt);
597 "Unknown option character `\\x%x'.\n",
601 exit(EXIT_BAD_PARAM);
603 if (!(option_masters || option_slaves) || (option_masters && option_slaves))
604 exit(EXIT_BAD_PARAM);
608 void print_stats(thread_data_t *td)
611 int count = td->count + td->timeout;
613 if (td->count >= 2) {
614 snprintf(std, 19, "%7.2f", sqrt((td->count/(td->count - 1)) *
615 (td->moment2nd - td->mean*td->mean)));
617 strncpy(std, "N/A", 19);
619 printf("Id %4ld: count = %5d"
620 " mean = %7.2f stddev = %s"
621 " min = %5d max = %5d [us]"
622 " loss = %d%% (%d)\n",
623 td->canid, count, td->mean,
626 (count > 0) ? 100 * td->timeout / count : 0, td->timeout
630 void wait_for_threads(void)
634 int thread_count = option_slaves + option_masters;
637 while (thread_count > 0) {
638 if (option_verbose != 1) {
639 ret = sem_wait(&finish_sem);
640 dbg(2, "Main thread sem_wait() exited with ret=%d.\n", ret);
642 /* If the sem_wait is successful i.e. not
643 * interrupted by a signal, decrease the
645 if (ret == 0) thread_count--;
647 if (!IS_FINISH_FLAG()) {
648 printf("Total count: %6d, Timeouts: %6d\r", total_count, total_timeout);
652 while (sem_trywait(&finish_sem) == 0) thread_count--;
655 if (option_verbose == 1) {
659 ul_list_for_each(thread_list, &master_threads, td) {
660 pthread_join(td->tid, &thread_ret);
661 dbg(2, "Master thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
664 ul_list_for_each(thread_list, &slave_threads, td) {
665 pthread_join(td->tid, &thread_ret);
666 dbg(2, "Slave thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
669 if (option_masters) printf("Summary statistics:\n");
671 ul_list_for_each_cut(thread_list, &master_threads, td) {
677 ul_list_for_each_cut(thread_list, &slave_threads, td)
681 int main(int argc, char *argv[])
683 parse_options(argc, argv);
686 if(sched_policy != SCHED_OTHER)
687 if(set_sched_policy_and_prio(sched_policy, sched_rtprio) <0)
688 exit(EXIT_BAD_PARAM);
692 thread_list_init_head(&master_threads);
693 thread_list_init_head(&slave_threads);
695 sem_init(&finish_sem, 0, 0);
696 sem_init(&ready_sem, 0, 0);
698 siginterrupt(SIGINT, 1);
699 signal(SIGINT, term_handler);
700 siginterrupt(SIGTERM, 1);
701 signal(SIGTERM, term_handler);
703 dbg(2, "Main thread: %p\n", (void *)pthread_self());
705 if (option_open_once) {
706 /* Open the CAN driver */
707 if ((global_canfd = open(option_device, O_RDWR)) < 0) {
709 printf("Error opening %s\n", option_device);
710 exit(EXIT_CANNOT_OPEN);
714 if (option_masters) start_masters(option_masters, option_first_id);
715 if (option_slaves) start_slaves(option_slaves, option_first_id);
719 if (option_open_once) {