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