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