]> rtime.felk.cvut.cz Git - sojka/can-utils.git/blob - candump.c
Initial import of llcf-2.0-pre2
[sojka/can-utils.git] / candump.c
1 /*
2  *  $Id: candump.c,v 2.0 2006/04/13 10:37:20 ethuerm Exp $
3  */
4
5 /*
6  * candump.c
7  *
8  * Copyright (c) 2002-2005 Volkswagen Group Electronic Research
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions, the following disclaimer and
16  *    the referenced file 'COPYING'.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of Volkswagen nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * Alternatively, provided that this notice is retained in full, this
25  * software may be distributed under the terms of the GNU General
26  * Public License ("GPL") version 2 as distributed in the 'COPYING'
27  * file from the main directory of the linux kernel source.
28  *
29  * The provided data structures and external interfaces from this code
30  * are not restricted to be used by modules with a GPL compatible license.
31  *
32  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
35  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
36  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
38  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
39  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
40  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
42  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
43  * DAMAGE.
44  *
45  * Send feedback to <llcf@volkswagen.de>
46  *
47  */
48
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <string.h>
53 #include <signal.h>
54 #include <ctype.h>
55 #include <libgen.h>
56 #include <time.h>
57
58 #include <sys/time.h>
59 #include <sys/types.h>
60 #include <sys/socket.h>
61 #include <sys/ioctl.h>
62 #include <sys/uio.h>
63
64 #include "af_can.h"
65 #include "raw.h"
66 #include "terminal.h"
67
68 #define USE_RECVFROM /* use read() or recvfrom() syscall */
69
70 #define MAXDEV 6 /* change sscanf()'s manually if changed here */
71 #define ANYDEV "any"
72 #define ANL "\r\n" /* newline in ASC mode */
73
74 #define BOLD    ATTBOLD
75 #define RED     ATTBOLD FGRED
76 #define GREEN   ATTBOLD FGGREEN
77 #define YELLOW  ATTBOLD FGYELLOW
78 #define BLUE    ATTBOLD FGBLUE
79 #define MAGENTA ATTBOLD FGMAGENTA
80 #define CYAN    ATTBOLD FGCYAN
81
82 static const char col_on [MAXDEV][19] = {BOLD, MAGENTA, GREEN, BLUE, CYAN, RED};
83 static const char col_off [] = ATTRESET;
84
85 #define MAXANI 8
86 const char anichar[MAXANI] = {'|', '/', '-', '\\', '|', '/', '-', '\\'};
87
88 extern int optind, opterr, optopt;
89
90 static int      running = 1;
91
92 void print_usage(char *prg)
93 {
94     fprintf(stderr, "Usage: %s [can-interfaces]\n", prg);
95     fprintf(stderr, "Options: -m <mask>   (default 0x00000000)\n");
96     fprintf(stderr, "         -v <value>  (default 0x00000000)\n");
97     fprintf(stderr, "         -i <0|1>    (inv_filter)\n");
98     fprintf(stderr, "         -t <type>   (timestamp: Absolute/Delta/Zero)\n");
99     fprintf(stderr, "         -c          (color mode)\n");
100     fprintf(stderr, "         -s <level>  (silent mode - 1: animation 2: nothing)\n");
101     fprintf(stderr, "         -b <can>    (bridge mode - send received frames to <can>)\n");
102     fprintf(stderr, "         -a          (create ASC compatible output)\n");
103     fprintf(stderr, "         -1          (increment interface numbering in ASC mode)\n");
104     fprintf(stderr, "         -A          (enable ASCII output)\n");
105     fprintf(stderr, "\n");
106     fprintf(stderr, "When using more than one CAN interface the options\n");
107     fprintf(stderr, "m/v/i have comma seperated values e.g. '-m 0,7FF,0'\n");
108     fprintf(stderr, "Use interface name '%s' to receive from all can-interfaces\n", ANYDEV);
109 }
110
111 void sigterm(int signo)
112 {
113     running = 0;
114 }
115
116 int main(int argc, char **argv)
117 {
118     fd_set rdfs;
119     int s[MAXDEV];
120     int bridge = 0;
121     canid_t mask[MAXDEV] = {0};
122     canid_t value[MAXDEV] = {0};
123     int inv_filter[MAXDEV] = {0};
124     char devname[MAXDEV][IFNAMSIZ];
125     unsigned char timestamp = 0;
126     unsigned char silent = 0;
127     unsigned char silentani = 0;
128     unsigned char color = 0;
129     unsigned char ascii = 0;
130     unsigned char asc = 0;
131     unsigned char asc_inc_channel = 0;
132     int max_devname_len = 0;
133     int opt, ret;
134     int currmax = 1; /* we assume at least one can bus ;-) */
135     struct sockaddr_can addr;
136     struct can_filter rfilter;
137     struct can_frame frame;
138     int nbytes, i, j;
139     struct ifreq ifr;
140
141     time_t currtime;
142     struct timeval tv, last_tv;
143
144     signal(SIGTERM, sigterm);
145     signal(SIGHUP, sigterm);
146     signal(SIGINT, sigterm);
147
148     last_tv.tv_sec = 0; /* init */
149
150     while ((opt = getopt(argc, argv, "m:v:i:b:s:ca1At:")) != -1) {
151         switch (opt) {
152         case 'm':
153             i = sscanf(optarg, "%x,%x,%x,%x,%x,%x",
154                        &mask[0], &mask[1], &mask[2],
155                        &mask[3], &mask[4], &mask[5]);
156             if (i > currmax)
157                 currmax = i;
158             break;
159
160         case 'v':
161             i = sscanf(optarg, "%x,%x,%x,%x,%x,%x",
162                        &value[0], &value[1], &value[2],
163                        &value[3], &value[4], &value[5]);
164             if (i > currmax)
165                 currmax = i;
166             break;
167
168         case 'i':
169             i = sscanf(optarg, "%d,%d,%d,%d,%d,%d",
170                        &inv_filter[0], &inv_filter[1], &inv_filter[2],
171                        &inv_filter[3], &inv_filter[4], &inv_filter[5]);
172             if (i > currmax)
173                 currmax = i;
174             break;
175
176         case 'b':
177             if (strlen(optarg) >= IFNAMSIZ) {
178                 printf("Name of CAN device '%s' is too long!\n\n", optarg);
179                 return 1;
180             }
181             else {
182                 if ((bridge = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
183                     perror("bridge socket");
184                     return 1;
185                 }
186                 addr.can_family = AF_CAN;
187                 strcpy(ifr.ifr_name, optarg);
188                 if (ioctl(bridge, SIOCGIFINDEX, &ifr) < 0)
189                     perror("SIOCGIFINDEX");
190                 addr.can_ifindex = ifr.ifr_ifindex;
191                 
192                 if (!addr.can_ifindex) {
193                     perror("invalid bridge interface");
194                     return 1;
195                 }
196
197                 if (bind(bridge, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
198                     perror("bridge bind");
199                     return 1;
200                 }
201             }
202             break;
203             
204         case 's':
205             silent = atoi(optarg);
206             break;
207
208         case 'c':
209             color = 1;
210             break;
211
212         case 'a':
213             asc = 1;
214             break;
215
216         case '1':
217             asc_inc_channel = 1;
218             break;
219
220         case 'A':
221             ascii = 1;
222             break;
223
224         case 't':
225             timestamp = optarg[0];
226             if ((timestamp != 'a') && (timestamp != 'A') && (timestamp != 'd') && (timestamp != 'z')) {
227                 printf("%s: unknown timestamp mode '%c' - ignored\n",
228                        basename(argv[0]), optarg[0]);
229                 timestamp = 0;
230             }
231             break;
232
233         case '?':
234             break;
235
236         default:
237             fprintf(stderr, "Unknown option %c\n", opt);
238             break;
239         }
240     }
241
242     if (optind == argc) {
243         print_usage(basename(argv[0]));
244         exit(0);
245     }
246         
247     /* count in options higher than device count ? */
248     if (optind + currmax > argc) {
249         printf("low count of CAN devices!\n");
250         return 1;
251     }
252
253     currmax = argc - optind; /* find real number of CAN devices */
254
255     if (currmax > MAXDEV) {
256         printf("More than %d CAN devices!\n", MAXDEV);
257         return 1;
258     }
259
260     for (i=0; i<currmax; i++) {
261
262 #ifdef DEBUG
263         printf("open %d '%s' m%08X v%08X i%d.\n",
264                i, argv[optind+i], mask[i], value[i], inv_filter[i]);
265 #endif
266
267         if ((s[i] = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
268             perror("socket");
269             return 1;
270         }
271
272         if (mask[i] || value[i]) {
273
274             if (!(asc)) /* this output is not asc compatible! */
275                 printf("CAN ID filter[%d] for %s set to mask = %08X, value = %08X %s\n",
276                        i, argv[optind+i], mask[i], value[i],
277                        (inv_filter[i]) ? "(inv_filter)" : "");
278
279             rfilter.can_id   = value[i];
280             rfilter.can_mask = mask[i];
281             if (inv_filter[i])
282                 rfilter.can_id |= CAN_INV_FILTER;
283
284             setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
285         }
286
287         j = strlen(argv[optind+i]);
288
289         if (!(j < IFNAMSIZ)) {
290             printf("name of CAN device '%s' is too long!\n", argv[optind+i]);
291             return 1;
292         }
293
294         strcpy(devname[i], argv[optind+i]);
295
296         if (j > max_devname_len)
297             max_devname_len = j; /* for nice printing */
298
299         addr.can_family = AF_CAN;
300
301         if (strcmp(ANYDEV, argv[optind+i])) {
302             strcpy(ifr.ifr_name, argv[optind+i]);
303             if (ioctl(s[i], SIOCGIFINDEX, &ifr) < 0)
304                 perror("SIOCGIFINDEX");
305             addr.can_ifindex = ifr.ifr_ifindex;
306         }
307         else
308             addr.can_ifindex = 0; /* any can interface */
309
310         if (bind(s[i], (struct sockaddr *)&addr, sizeof(addr)) < 0) {
311             perror("bind");
312             return 1;
313         }
314     }
315
316     if (asc) {
317         char datestring[40];
318
319         /* print banner for ASC mode */
320
321         if (timestamp != 'd') /* delta time is allowed, else ... */
322             timestamp = 'z'; /* ASC-files always start with zero time */
323
324         if (time(&currtime) == (time_t)-1) {
325             perror("time");
326             return 1;
327         }
328         strncpy(datestring, ctime(&currtime), 39); /* copy to private buffer */
329         datestring[strlen(datestring)-1] = 0; /* chop off trailing newline */
330         printf("date %s%s", datestring, ANL); /* print with own new line representation */
331
332         printf("base hex  timestamps %s%s", (timestamp == 'd')?"relative":"absolute", ANL);
333         printf("no internal events logged%s", ANL);
334         fflush(stdout);
335     }
336
337     while (running) {
338
339         FD_ZERO(&rdfs);
340
341         for (i=0; i<currmax; i++)
342             FD_SET(s[i], &rdfs);
343
344         if ((ret = select(s[currmax-1]+1, &rdfs, NULL, NULL, NULL)) < 0) {
345             //perror("select");
346             running = 0;
347             continue;
348         }
349
350         for (i=0; i<currmax; i++) {
351
352             if (FD_ISSET(s[i], &rdfs)) {
353
354 #ifdef USE_RECVFROM
355                 socklen_t len = sizeof(addr);
356                 if ((nbytes = recvfrom(s[i], &frame, sizeof(struct can_frame), 0, (struct sockaddr*)&addr, &len)) < 0) {
357 #else
358                 if ((nbytes = read(s[i], &frame, sizeof(struct can_frame))) < 0) {
359 #endif
360                     perror("read");
361                     return 1;
362                 } else if (nbytes < sizeof(struct can_frame)) {
363                     fprintf(stderr, "read: incomplete CAN frame\n");
364                     return 1;
365                 } else {
366                     if (bridge) {
367                         if ((nbytes = write(bridge, &frame, sizeof(struct can_frame))) < 0) {
368                             perror("bridge write");
369                             return 1;
370                         } else if (nbytes < sizeof(struct can_frame)) {
371                             fprintf(stderr, "bridge write: incomplete CAN frame\n");
372                             return 1;
373                         }
374                     }
375                     
376                     if (silent){
377                       if (silent == 1)
378                         printf("%c\b", anichar[silentani%=MAXANI]), silentani++;
379                     }
380                     else {
381                       
382                         switch (timestamp) {
383
384                         case 'a': /* absolute with timestamp */
385                             if (ioctl(s[i], SIOCGSTAMP, &tv) < 0)
386                                 perror("SIOCGSTAMP");
387                             if (asc)
388                                 printf("%4ld.%04ld ", tv.tv_sec, tv.tv_usec/100);
389                             else
390                                 printf("(%ld.%06ld) ", tv.tv_sec, tv.tv_usec);
391                             break;
392
393                         case 'A': /* absolute with date */
394                             {
395                                 struct tm tm;
396                                 char timestring[25];
397                                 if (ioctl(s[i], SIOCGSTAMP, &tv) < 0)
398                                     perror("SIOCGSTAMP");
399                                 tm = *localtime(&tv.tv_sec);
400                                 strftime(timestring, 24, "%Y-%m-%d %H:%M:%S", &tm);
401                                 if (asc)
402                                     printf("%s.%04ld ", timestring, tv.tv_usec/100);
403                                 else
404                                     printf("(%s.%06ld) ", timestring, tv.tv_usec);
405                             }
406                             break;
407
408                         case 'd': /* delta */
409                         case 'z': /* starting with zero */
410                             {
411                                 struct timeval diff;
412
413                                 if (ioctl(s[i], SIOCGSTAMP, &tv) < 0)
414                                     perror("SIOCGSTAMP");
415                                 if (last_tv.tv_sec == 0)   /* first init */
416                                     last_tv = tv;
417                                 diff.tv_sec  = tv.tv_sec  - last_tv.tv_sec;
418                                 diff.tv_usec = tv.tv_usec - last_tv.tv_usec;
419                                 if (diff.tv_usec < 0)
420                                     diff.tv_sec--, diff.tv_usec += 1000000;
421                                 if (diff.tv_sec < 0)
422                                     diff.tv_sec = diff.tv_usec = 0;
423                                 if (asc)
424                                     printf("%4ld.%04ld ", diff.tv_sec, diff.tv_usec/100);
425                                 else
426                                     printf("(%ld.%06ld) ", diff.tv_sec, diff.tv_usec);
427                                 
428                                 if (timestamp == 'd')
429                                     last_tv = tv; /* update for delta calculation */
430                             }
431                             break;
432
433                         default: /* no timestamp output */
434                             break;
435                         }
436
437                         if (asc) {
438                             char id[10];
439
440                             printf("%-2d ", i + asc_inc_channel); /* channel number - left aligned */
441
442                             sprintf(id, "%X%c", frame.can_id & CAN_EFF_MASK,
443                                     (frame.can_id & CAN_EFF_FLAG)?'x':' ');
444                             printf("%-15s Rx   ", id);
445
446                             if (frame.can_id & CAN_RTR_FLAG)
447                                 printf("r"); /* RTR frame: nothing else to print */
448                             else {
449                                 printf("d %d ", frame.can_dlc); /* data frame */
450
451                                 for (j = 0; j < frame.can_dlc; j++) {
452                                     printf("%02X ", frame.data[j]);
453                                 }
454                             }
455                             printf("%s", ANL);
456                         }
457                         else {
458                             printf(" %s",(color)?col_on[i]:"");
459 #ifdef USE_RECVFROM
460                             ifr.ifr_ifindex = addr.can_ifindex;
461                             if (ioctl(s[i], SIOCGIFNAME, &ifr) < 0)
462                                 perror("SIOCGIFNAME");
463
464                             if (max_devname_len < strlen(ifr.ifr_name))
465                                 max_devname_len = strlen(ifr.ifr_name);
466
467                             printf("%*s", max_devname_len, ifr.ifr_name);
468 #else
469                             printf("%*s", max_devname_len, devname[i]); /* device name */
470 #endif
471                             printf("%s  ",(color)?col_off:"");
472                             if (frame.can_id & CAN_EFF_FLAG)
473                                 printf("%8X  ", frame.can_id & CAN_EFF_MASK);
474                             else
475                                 printf("%3X  ", frame.can_id & CAN_SFF_MASK);
476
477                             printf("[%d] ", frame.can_dlc);
478
479                             for (j = 0; j < frame.can_dlc; j++) {
480                                 printf("%02X ", frame.data[j]);
481                             }
482                             if (ascii) {
483                                 printf("%*s", 3*(8-frame.can_dlc)+3, "'");
484                                 for (j = 0; j < frame.can_dlc; j++)
485                                     if ((frame.data[j] > 0x1F) && (frame.data[j] < 0x7F))
486                                         putchar(frame.data[j]);
487                                     else
488                                         putchar('.');
489                                 printf("' ");
490                             }
491                             if (frame.can_id & CAN_RTR_FLAG)
492                                 printf("remote request");
493                             printf("\n");
494                         }
495                     }
496                     fflush(stdout);
497                 }
498             }
499         }
500     }
501
502     for (i=0; i<currmax; i++)
503         close(s[i]);
504
505     if (bridge)
506       close(bridge);
507
508     return 0;
509 }