]> rtime.felk.cvut.cz Git - lincan.git/blob - lincan/utils/can-proxy.c
Updated BFAD contributed can-proxy from latest changes by T.Motylewski
[lincan.git] / lincan / utils / can-proxy.c
1 //#############################################################################
2 /** @file                               can-proxy.c
3  @author:               T.Motylewski@bfad.de
4  Operating System:      LINUX
5  Compiler:                      gcc
6  Description:           gateway CAN-UDP.
7 receives all CAN packets, prints them and may save them to a file.
8 may send packets with ID, len, data entered from keyboard or from text file.
9 may send to CAN packets received over UDP
10 will forward packets from CAN to the most recent IP/UDP address.
11 */
12 //#############################################################################
13
14
15 //-----------------------------------------------------------------------------
16 //                                  INCLUDES
17 //-----------------------------------------------------------------------------
18
19 #include <stdio.h>
20 #include "can.h"
21 #include <sys/time.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netdb.h>
25 #include <netinet/in.h>
26 #include <netinet/tcp.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <fcntl.h>
33
34 #define DEFAULT_CAN_SERVER_PORT 3007
35 #define DEFAULT_CAN_CLIENT_PORT 3008
36 #define MAX_MSG_SIZE 4000
37
38 typedef struct canmsg_t canmsg_t;
39
40 extern int SendCanMsg(canmsg_t * msg);
41 extern int ReceiveCanMsg(canmsg_t * msg);
42 extern int ReadInput(int fd);
43
44
45 /** tm means time in units of 1/100 s since program started (tsStarted)
46  * tm time stays positive for 24.8 days after system boot.
47  * It can be redefined by changing TM_HZ value to something else than 1000.
48  * Always use TM_HZ when comparing time with real seconds.
49  * The code should work after "time wraparound" but if something happens you know why.
50  * ts means time in sec */
51 #define TM_HZ 1000
52
53
54 #define MSG_CAN_UNKNOWN -1
55 #define MSG_CAN_NONE 0
56 #define TRUE 1
57 #define FALSE 0
58 #define CAN_MSG_SIZE sizeof(canmsg_t)
59 #define CAN_DEV "/dev/can0"
60
61 enum BusStateFlags {
62         BusReset = 1,
63         BusLocked = 1<<1
64 };
65
66 #define WARN(fmt...) (fprintf(stderr,"WARN:" fmt),fprintf(stderr,"\n"))
67 #define ERR(fmt...) (fprintf(stderr,"ERROR:" fmt),fprintf(stderr,"\n"))
68
69 #ifdef DEBUG
70         //#define DMSG(fmt...) (fprintf(stderr,"DBG:" fmt),fprintf(stderr," %f\n",gettime()))
71         //very usefull for debugging data change after some miliseconds combined with dumpnc.c
72
73         #define DMSG(fmt...) (fprintf(stderr,"DBG:" fmt),fprintf(stderr,"\n"))
74 #else
75         #define DMSG(fmt...)
76 #endif
77
78
79 /** global variables */
80
81 unsigned short int myport;
82 unsigned short int toport;
83 struct sockaddr_in myaddr, fromaddr, recvaddr, toaddr;
84
85
86 int fdCanDev;
87 int fdNet;
88 int fdError;
89 int fdIn;
90 FILE * fLog;
91 long iBusFlags;
92 time_t tsStarted;
93 long tmLastVerified=0;
94 long tmNow;  // current time, global, will be updated in many places
95 long tmLastSentReceived=0;
96 struct timeval tvNow;
97 struct timeval tvSelectTimeout;
98 double time0;
99 double SleepUntil=0;
100
101 int quiet = 0;
102 int use_select = 0;
103
104
105 long tmGet() {
106         gettimeofday(&tvNow,NULL);
107         return (tmNow=TM_HZ*(tvNow.tv_sec-tsStarted) + (TM_HZ*tvNow.tv_usec)/1000000);
108 #if TM_HZ>2147
109 #error when using 32 bit signed int TM_HZ can not be greater than 2147
110 #endif
111 }
112
113 double gettime() {
114         gettimeofday(&tvNow,NULL);
115         return ( tvNow.tv_sec + tvNow.tv_usec/1000000.0);
116 }
117
118 /**
119         removes '\r\n' and all spaces and tabs from the end of string str
120         @param str: input/output string
121         @return final string length (without terminating \0) */
122 int RemoveNL(char *str)
123 {
124         int iLength;
125         for(iLength = strlen(str)-1; iLength >=0; iLength --) {
126                 //DMSG("RmNL LineLength:>%i<", iLength);
127                 if (isspace(str[iLength])) {
128                         str[iLength] = '\0';
129                 }
130                 else
131                         break;
132         }
133         return iLength+1;
134 }
135
136
137 /**
138         sending data over tcp/ip
139         @param fd       - file descriptor for connection
140         @param msg      - pointer to message to be written
141         @param size     - size of message
142         @return result of sendto */
143 int WriteNet(int fd, void * msg, int size) {
144         return sendto(fd, msg, size, 0, (struct sockaddr*)&toaddr, sizeof(toaddr));
145 }
146
147 /**
148         reading data over tcp/ip
149         @param fd       - file descriptor for connection
150         @param msg      - pointer to message to be read
151         @param size     - size of message
152         @return result of recvfrom */
153 int ReadNet(int fd, void * msg, int size) {
154         char recvbuf[MAX_MSG_SIZE];
155         int ret;
156         int recvaddrsize = sizeof(recvaddr);
157
158         // setting recvaddr is required for non-connected sockets
159         memcpy(&recvaddr, &fromaddr, sizeof(recvaddr));
160         ret = recvfrom(fd, recvbuf, MAX_MSG_SIZE, 0, (struct sockaddr*)& recvaddr, &recvaddrsize);
161 //  DMSG("NET: %d", ret);
162         if(ret>size)
163                 ret = size;
164         if(ret>0)
165                 memcpy(msg, recvbuf, size);
166         return ret;
167 }
168
169 void show_usage(void) {
170         printf("can-proxy options:\n"
171         "-i     : intercative (send CAN packets typed by user)\n"
172         "-o file.log : log all traffic to a file\n"
173         "-p port : send CAN packets arriving at UDP port\n"
174         "-c     : use select() for CAN (driver can-0.7.1-pi3.4 or newer)\n"
175         "-q     : quiet\n"
176         "-h     : this help\n"
177         "UDP arriving at specified port causes can-proxy to forward all CAN\n"
178         "traffic back to the sender, establishing bi-directional communication.\n");
179 }
180
181 /**
182         handling command line options, calling functions, main program loop 
183         @param argc, char * argv[]      - command line options
184         @return  0                                      - OK
185                         -1                                      - ERROR */
186 int main(int argc, char * argv[]) {
187         struct timeval tvTimeoutTmp;
188         int fdSelectMax;
189 //      int ret;
190         int opt;
191         fd_set readsel;
192         canmsg_t canmsg;
193
194         myport = DEFAULT_CAN_SERVER_PORT;
195         toport = DEFAULT_CAN_CLIENT_PORT;
196         fdNet = fdCanDev = fdIn = -1;
197         fLog = NULL;
198         
199         iBusFlags = 0;
200         time0 = gettime();
201         tsStarted = tvNow.tv_sec;
202         tvSelectTimeout.tv_sec = 0;
203         tvSelectTimeout.tv_usec = 500000; // wake up every 0.5 s even without data
204
205         while((opt=getopt(argc, argv, "io:p:ch"))>0) {
206                 switch(opt) {
207                 case 'i': // interactive or stdin
208                         fdIn = 0;
209                         break;
210                 case 'o': // log file
211                         fLog = fopen(optarg,"a");
212                         break;
213                 case 'p':
214                         sscanf(optarg,"%hi", &myport);
215                         break;
216                 case 'c':
217                         fdCanDev = open(optarg,O_RDWR/*|O_NONBLOCK - select supported*/);
218                         use_select++;
219                         break;
220                 case 'q':
221                         quiet ++;
222                         break;
223                 case 'h':
224                         show_usage();
225                         break;
226                 default:
227                         break;
228                 }
229         }
230
231         if(!quiet)
232                 fprintf(stderr, "can-proxy v0.7.1-pi3.5 (C) 2002 BFAD GmbH http://www.getembedded.de/ (GPL) \n");
233
234         
235         if(fdCanDev<0)
236                 fdCanDev = open(CAN_DEV,O_RDWR|O_NONBLOCK);
237         fdNet = socket(AF_INET,SOCK_DGRAM,0);
238         memset(&myaddr, 0, sizeof(myaddr));
239         memset(&fromaddr, 0, sizeof(fromaddr));
240         memset(&toaddr, 0, sizeof(toaddr));
241
242         memset(& canmsg, 0, sizeof(canmsg));
243         myaddr.sin_family=AF_INET;
244         myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
245         myaddr.sin_port = htons(myport) ;
246
247         toaddr.sin_family=AF_INET;
248         toaddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);;
249         toaddr.sin_port = htons(toport);
250
251         bind(fdNet,(struct sockaddr*) &myaddr,sizeof(myaddr));
252
253         if(fdCanDev< 0 ) {
254                 perror(CAN_DEV);
255                 ERR("1.CAN-PROXY main fdCanDev not valid:%d, exiting", fdCanDev);
256                 exit(1);
257         }
258
259         while(1) {
260                 tvTimeoutTmp = tvSelectTimeout;
261                 fdSelectMax=0;
262                 FD_ZERO(&readsel);
263                 if(fdCanDev>=0){
264                         FD_SET(fdCanDev, &readsel);
265                         if(fdCanDev+1>fdSelectMax)
266                                 fdSelectMax = fdCanDev+1;
267                 }
268                 if(fdNet>=0) {
269                          FD_SET(fdNet, &readsel);
270                          if(fdNet+1>fdSelectMax)
271                                  fdSelectMax = fdNet+1;
272                 }
273                 if(fdIn>=0) {
274                          FD_SET(fdIn, &readsel);
275                          if(fdIn+1>fdSelectMax)
276                                  fdSelectMax = fdIn+1;
277                 }
278                 select(fdSelectMax, &readsel, NULL, NULL, &tvTimeoutTmp);
279                 if(fdCanDev>=0 && FD_ISSET(fdCanDev, &readsel)) {
280                         if(ReceiveCanMsg(&canmsg) == 0) {
281                                 WriteNet(fdNet, &canmsg, sizeof(canmsg));
282                                 continue; // reading CAN has priority
283                         }
284                 }
285                 if(gettime()-time0 < SleepUntil) {
286                         continue;
287                 }
288                 if(fdNet>=0 && FD_ISSET(fdNet,&readsel)) {
289                         ReadNet(fdNet, &canmsg, sizeof(canmsg));
290                         // TODO: this will work for a single client
291                         // multiple clients should probably use broadcast
292                         //  for now we just reply to the most recent address
293                         toaddr = recvaddr;
294                         SendCanMsg(&canmsg);
295                 }
296                 if(fdIn>=0 && FD_ISSET(fdIn, &readsel)) {
297                         ReadInput(fdIn);
298                 }
299                 if(!use_select)
300                         usleep(20000);
301         }
302         return 0;       
303 }
304
305 /**
306         logging messages to/from CAN
307         @param dir      - direction
308         @param fout - file descriptor for output
309         @param msg      - CanMsgPerma
310         @return  0                                      - OK
311                         -1                                      - ERROR */
312 int LogMsg(char * dir, FILE *fout, canmsg_t * msg) {
313         int i;
314         double t;
315
316         t = gettime()-time0;
317         if(msg->length < 0)
318                 msg->length = 0;
319         if(msg->length > 8)
320                 msg->length = 8;
321
322         fprintf(stdout,"%s %8.3f  id=%08lx n=%d", 
323                 dir, gettime()-time0, msg->id, msg->length);
324         for(i=0; i< msg->length; i++) {
325                 fprintf(stdout, " %02x",  msg->data[i]);
326         }
327         fprintf(stdout, "\n");
328         fflush(stdout);
329
330         if(!fout) 
331                 return 0;
332
333         fprintf(fout,"%s %8.3f  id=%08lx n=%d", 
334                 dir, gettime()-time0, msg->id, msg->length);
335         for(i=0; i< msg->length; i++) {
336                 fprintf(fout, " %02x",  msg->data[i]);
337         }
338         fprintf(fout, "\n");
339         fflush(fout);
340
341         return 0;
342 }
343
344
345 /** PERMA CAN utility functions */
346
347 /**
348         sending messages to CAN
349         @param msg      - CanMsgPerma
350         @return  0                                      - OK
351                         -1                                      - ERROR */
352 int SendCanMsg(canmsg_t * msg) {
353         int ret;
354
355         msg->flags = MSG_EXT;
356         msg->cob = 0;
357         tmGet();
358         ret=write(fdCanDev, msg, CAN_MSG_SIZE);
359         LogMsg("SEND", fLog, msg);
360         tmLastSentReceived = tmNow;
361         if( ret != CAN_MSG_SIZE) {
362                 perror("sending to CAN");
363 //TODO: send to error_ico
364                 return ret;
365         }
366         return 0;
367 }
368         
369 /**
370         receiving messages from CAN
371         @param msg      - CanMsgPerma
372         @return  0                                      - OK
373                         -1                                      - ERROR */
374 int ReceiveCanMsg(canmsg_t * msg) {
375         int ret;
376 //      double t;
377 //      fdCanDev = open(CAN_DEV,O_RDWR | O_NONBLOCK);
378
379         msg->flags = 0;
380         msg->cob = 0;
381         msg->timestamp = 0;
382         ret = read(fdCanDev,msg, CAN_MSG_SIZE);
383         if(ret == CAN_MSG_SIZE) {       
384                 LogMsg("RECV", fLog, msg);
385                 tmLastSentReceived = tmNow;
386                 return 0;
387         }
388         // we can receive 0 here
389         if(ret == 0 || (ret == -1 && errno == EAGAIN)) {
390                 return -EAGAIN;
391         }
392         DMSG("received %d bytes",ret);
393         return ret;
394 }
395
396 /** @name GetNumber
397         aliasing: deassign
398         @param buf      - string buffer
399         @param val      - alias value
400         @return  0                                      - OK
401                         -1                                      - ERROR */
402 int GetNumber(char * buf, int * val) {
403         if(!buf) {
404                 return -1;
405         }
406         if(sscanf(buf,"%i", val)==1) {
407                 return 0;
408         }
409         return -1;
410 }
411
412 /** @name BuildCanMsg
413         building the can message from buf string to msg
414         @param buf      - 
415         @param msg      - CanMsgPerma
416         @return  0                                      - OK
417                         -1                                      - ERROR */
418 int BuildCanMsg(char * buf, canmsg_t *msg) {
419         int val;
420         int i;
421
422         buf = strtok(buf, " \t");
423
424         val = msg->id;
425         buf = strtok(NULL, " \t");
426         GetNumber(buf, &val);
427         msg->id = val;
428
429         val = msg->length;
430         buf = strtok(NULL, " \t");
431         GetNumber(buf, &val);
432         msg->length = val;
433
434         for(i=0;(buf = strtok(NULL, " \t")); i++) {
435                 val = msg->data[i];
436                 GetNumber(buf, &val);
437                 msg->data[i] = val;
438         }
439         return 0;
440 }
441
442
443 #define LINE_L 66000
444 char buf[LINE_L];
445
446 extern int ReadCommand(char *buf);
447
448
449 /**
450         processing a script or from stdin
451         @param fd       - file descriptor
452         @return  0                                      - OK
453                         -1                                      - ERROR */
454 int ReadInput(int fd) {
455         int ret;
456         int i,j;
457         ret = read(fd, buf, LINE_L);
458         for(i=0; i< ret; ) {
459                 for(j=i; (j<ret) && (buf[j] != '\n'); j++); // find NL
460                 if(j<ret) {
461                         buf[j] = '\0';
462                         ReadCommand(buf+i);
463                         i=j+1;
464                 } else {
465                         fprintf(stderr, "too big input file\n");
466                         i=j;
467                 }
468         }
469         return 0;
470 }
471
472 /**
473         reading and handling commands from buf string
474         @param buf      - string buffer, 1.char tells about what is to be done: 
475                                                                  w - write 
476                                                                  s - sleep
477                                                                  a - assign (simple aliasing mechanism)
478                                                                  r - reset timer
479                                                                  l - lock the bus
480                                                                  q - quit application
481         @return  0                                      - OK
482                         -1                                      - ERROR */
483 int ReadCommand(char *buf) {
484         static int usSleep=20000;
485         char * ptr;
486         static canmsg_t msg;
487
488         buf[LINE_L-1] = '\0';
489         if(RemoveNL(buf) == 0)
490                 return 0; // empty line
491
492         switch(tolower(buf[0])) {
493         case 'q':
494                 DMSG("1.CAN-PROXY ReadCommand received QUIT command, exiting");
495                 exit(0);
496                 break;
497         case 'r':
498                 time0 = gettime();
499                 break;
500         case 't':
501                 printf("TIME %8.3f\n", gettime()-time0);
502                 break;
503         case 's':
504                 strtok(buf, " \t");
505                 ptr = strtok(NULL, " \t");
506                 if(ptr)
507                         sscanf(ptr,"%i", &usSleep);
508                 SleepUntil = gettime()-time0 + usSleep/1000000.0;
509                 break;
510         case 'w':
511                 if(BuildCanMsg(buf, &msg) == 0) {
512                         SendCanMsg(&msg);
513                 } else {
514                         fprintf(stderr,"wrong message syntax: %s\n", buf);
515                 }
516                 break;
517         }
518         return 0;
519 }
520