1 //#############################################################################
3 @author: T.Motylewski@bfad.de
4 Operating System: LINUX
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.
12 //#############################################################################
15 //-----------------------------------------------------------------------------
17 //-----------------------------------------------------------------------------
22 #include <sys/types.h>
23 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <netinet/tcp.h>
34 #define DEFAULT_CAN_SERVER_PORT 3007
35 #define DEFAULT_CAN_CLIENT_PORT 3008
36 #define MAX_MSG_SIZE 4000
38 typedef struct canmsg_t canmsg_t;
40 extern int SendCanMsg(canmsg_t * msg);
41 extern int ReceiveCanMsg(canmsg_t * msg);
42 extern int ReadInput(int fd);
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 */
54 #define MSG_CAN_UNKNOWN -1
55 #define MSG_CAN_NONE 0
58 #define CAN_MSG_SIZE sizeof(canmsg_t)
59 #define CAN_DEV "/dev/can0"
66 #define WARN(fmt...) (fprintf(stderr,"WARN:" fmt),fprintf(stderr,"\n"))
67 #define ERR(fmt...) (fprintf(stderr,"ERROR:" fmt),fprintf(stderr,"\n"))
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
73 #define DMSG(fmt...) (fprintf(stderr,"DBG:" fmt),fprintf(stderr,"\n"))
79 /** global variables */
81 unsigned short int myport;
82 unsigned short int toport;
83 struct sockaddr_in myaddr, fromaddr, recvaddr, toaddr;
93 long tmLastVerified=0;
94 long tmNow; // current time, global, will be updated in many places
95 long tmLastSentReceived=0;
97 struct timeval tvSelectTimeout;
106 gettimeofday(&tvNow,NULL);
107 return (tmNow=TM_HZ*(tvNow.tv_sec-tsStarted) + (TM_HZ*tvNow.tv_usec)/1000000);
109 #error when using 32 bit signed int TM_HZ can not be greater than 2147
114 gettimeofday(&tvNow,NULL);
115 return ( tvNow.tv_sec + tvNow.tv_usec/1000000.0);
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)
125 for(iLength = strlen(str)-1; iLength >=0; iLength --) {
126 //DMSG("RmNL LineLength:>%i<", iLength);
127 if (isspace(str[iLength])) {
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));
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];
156 int recvaddrsize = sizeof(recvaddr);
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);
165 memcpy(msg, recvbuf, size);
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"
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");
182 handling command line options, calling functions, main program loop
183 @param argc, char * argv[] - command line options
186 int main(int argc, char * argv[]) {
187 struct timeval tvTimeoutTmp;
194 myport = DEFAULT_CAN_SERVER_PORT;
195 toport = DEFAULT_CAN_CLIENT_PORT;
196 fdNet = fdCanDev = fdIn = -1;
201 tsStarted = tvNow.tv_sec;
202 tvSelectTimeout.tv_sec = 0;
203 tvSelectTimeout.tv_usec = 500000; // wake up every 0.5 s even without data
205 while((opt=getopt(argc, argv, "io:p:ch"))>0) {
207 case 'i': // interactive or stdin
210 case 'o': // log file
211 fLog = fopen(optarg,"a");
214 sscanf(optarg,"%hi", &myport);
217 fdCanDev = open(optarg,O_RDWR/*|O_NONBLOCK - select supported*/);
232 fprintf(stderr, "can-proxy v0.7.1-pi3.5 (C) 2002 BFAD GmbH http://www.getembedded.de/ (GPL) \n");
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));
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) ;
247 toaddr.sin_family=AF_INET;
248 toaddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);;
249 toaddr.sin_port = htons(toport);
251 bind(fdNet,(struct sockaddr*) &myaddr,sizeof(myaddr));
255 ERR("1.CAN-PROXY main fdCanDev not valid:%d, exiting", fdCanDev);
260 tvTimeoutTmp = tvSelectTimeout;
264 FD_SET(fdCanDev, &readsel);
265 if(fdCanDev+1>fdSelectMax)
266 fdSelectMax = fdCanDev+1;
269 FD_SET(fdNet, &readsel);
270 if(fdNet+1>fdSelectMax)
271 fdSelectMax = fdNet+1;
274 FD_SET(fdIn, &readsel);
275 if(fdIn+1>fdSelectMax)
276 fdSelectMax = fdIn+1;
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
285 if(gettime()-time0 < SleepUntil) {
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
296 if(fdIn>=0 && FD_ISSET(fdIn, &readsel)) {
306 logging messages to/from CAN
307 @param dir - direction
308 @param fout - file descriptor for output
309 @param msg - CanMsgPerma
312 int LogMsg(char * dir, FILE *fout, canmsg_t * msg) {
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]);
327 fprintf(stdout, "\n");
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]);
345 /** PERMA CAN utility functions */
348 sending messages to CAN
349 @param msg - CanMsgPerma
352 int SendCanMsg(canmsg_t * msg) {
355 msg->flags = MSG_EXT;
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
370 receiving messages from CAN
371 @param msg - CanMsgPerma
374 int ReceiveCanMsg(canmsg_t * msg) {
377 // fdCanDev = open(CAN_DEV,O_RDWR | O_NONBLOCK);
382 ret = read(fdCanDev,msg, CAN_MSG_SIZE);
383 if(ret == CAN_MSG_SIZE) {
384 LogMsg("RECV", fLog, msg);
385 tmLastSentReceived = tmNow;
388 // we can receive 0 here
389 if(ret == 0 || (ret == -1 && errno == EAGAIN)) {
392 DMSG("received %d bytes",ret);
398 @param buf - string buffer
399 @param val - alias value
402 int GetNumber(char * buf, int * val) {
406 if(sscanf(buf,"%i", val)==1) {
412 /** @name BuildCanMsg
413 building the can message from buf string to msg
415 @param msg - CanMsgPerma
418 int BuildCanMsg(char * buf, canmsg_t *msg) {
422 buf = strtok(buf, " \t");
425 buf = strtok(NULL, " \t");
426 GetNumber(buf, &val);
430 buf = strtok(NULL, " \t");
431 GetNumber(buf, &val);
434 for(i=0;(buf = strtok(NULL, " \t")); i++) {
436 GetNumber(buf, &val);
446 extern int ReadCommand(char *buf);
450 processing a script or from stdin
451 @param fd - file descriptor
454 int ReadInput(int fd) {
457 ret = read(fd, buf, LINE_L);
459 for(j=i; (j<ret) && (buf[j] != '\n'); j++); // find NL
465 fprintf(stderr, "too big input file\n");
473 reading and handling commands from buf string
474 @param buf - string buffer, 1.char tells about what is to be done:
477 a - assign (simple aliasing mechanism)
483 int ReadCommand(char *buf) {
484 static int usSleep=20000;
488 buf[LINE_L-1] = '\0';
489 if(RemoveNL(buf) == 0)
490 return 0; // empty line
492 switch(tolower(buf[0])) {
494 DMSG("1.CAN-PROXY ReadCommand received QUIT command, exiting");
501 printf("TIME %8.3f\n", gettime()-time0);
505 ptr = strtok(NULL, " \t");
507 sscanf(ptr,"%i", &usSleep);
508 SleepUntil = gettime()-time0 + usSleep/1000000.0;
511 if(BuildCanMsg(buf, &msg) == 0) {
514 fprintf(stderr,"wrong message syntax: %s\n", buf);