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;
95 long tmDurationLocked;
96 long tmLastVerified=0;
97 long tmNow; // current time, global, will be updated in many places
98 long tmLastSentReceived=0;
100 struct timeval tvSelectTimeout;
108 gettimeofday(&tvNow,NULL);
109 return (tmNow=TM_HZ*(tvNow.tv_sec-tsStarted) + (TM_HZ*tvNow.tv_usec)/1000000);
111 #error when using 32 bit signed int TM_HZ can not be greater than 2147
116 gettimeofday(&tvNow,NULL);
117 return ( tvNow.tv_sec + tvNow.tv_usec/1000000.0);
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)
127 for(iLength = strlen(str)-1; iLength >=0; iLength --) {
128 //DMSG("RmNL LineLength:>%i<", iLength);
129 if (isspace(str[iLength])) {
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));
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];
158 int recvaddrsize = sizeof(recvaddr);
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);
167 memcpy(msg, recvbuf, size);
174 handling command line options, calling functions, main program loop
175 @param argc, char * argv[] - command line options
178 int main(int argc, char * argv[]) {
179 struct timeval tvTimeoutTmp;
186 myport = DEFAULT_CAN_SERVER_PORT;
187 toport = DEFAULT_CAN_CLIENT_PORT;
188 fdNet = fdCanDev = fdIn = -1;
192 tmWhenLocked = tmDurationLocked = 0;
194 tsStarted = tvNow.tv_sec;
195 tvSelectTimeout.tv_sec = 0;
196 tvSelectTimeout.tv_usec = 500000; // wake up every 0.5 s even without data
198 while((opt=getopt(argc, argv, "io:p:c:l"))>0) {
200 case 'i': // interactive or stdin
203 case 'o': // log file
204 fLog = fopen(optarg,"a");
207 sscanf(optarg,"%hi", &myport);
210 fdCanDev = open(optarg,O_RDWR/*|O_NONBLOCK - select supported*/);
212 case 'l': // lock the bus during dispense
224 fprintf(stderr, "can-proxy v0.7.1-pi3.3 (C) 2002 BFAD GmbH http://www.getembedded.de/ (GPL) \n");
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));
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) ;
239 toaddr.sin_family=AF_INET;
240 toaddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);;
241 toaddr.sin_port = htons(toport);
243 bind(fdNet,(struct sockaddr*) &myaddr,sizeof(myaddr));
247 ERR("1.CAN-PROXY main fdCanDev not valid:%d, exiting", fdCanDev);
252 tvTimeoutTmp = tvSelectTimeout;
256 FD_SET(fdCanDev, &readsel);
257 if(fdCanDev+1>fdSelectMax)
258 fdSelectMax = fdCanDev+1;
261 FD_SET(fdNet, &readsel);
262 if(fdNet+1>fdSelectMax)
263 fdSelectMax = fdNet+1;
266 FD_SET(fdIn, &readsel);
267 if(fdIn+1>fdSelectMax)
268 fdSelectMax = fdIn+1;
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
277 if(gettime()-time0 < SleepUntil) {
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
288 if(fdIn>=0 && FD_ISSET(fdIn, &readsel)) {
291 // usleep(20000); //can driver does select(), no need for delay
297 logging messages to/from CAN
298 @param dir - direction
299 @param fout - file descriptor for output
300 @param msg - CanMsgPerma
303 int LogMsg(char * dir, FILE *fout, canmsg_t * msg) {
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]);
318 fprintf(stdout, "\n");
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]);
336 /** PERMA CAN utility functions */
339 sending messages to CAN
340 @param msg - CanMsgPerma
343 int SendCanMsg(canmsg_t * msg) {
346 msg->flags = MSG_EXT;
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
361 receiving messages from CAN
362 @param msg - CanMsgPerma
365 int ReceiveCanMsg(canmsg_t * msg) {
368 // fdCanDev = open(CAN_DEV,O_RDWR | O_NONBLOCK);
373 ret = read(fdCanDev,msg, CAN_MSG_SIZE);
374 if(ret == CAN_MSG_SIZE) {
375 LogMsg("RECV", fLog, msg);
376 tmLastSentReceived = tmNow;
379 // we can receive 0 here
380 if(ret == 0 || (ret == -1 && errno == EAGAIN)) {
383 DMSG("received %d bytes",ret);
389 @param buf - string buffer
390 @param val - alias value
393 int GetNumber(char * buf, int * val) {
397 if(sscanf(buf,"%i", val)==1) {
403 /** @name BuildCanMsg
404 building the can message from buf string to msg
406 @param msg - CanMsgPerma
409 int BuildCanMsg(char * buf, canmsg_t *msg) {
413 buf = strtok(buf, " \t");
416 buf = strtok(NULL, " \t");
417 GetNumber(buf, &val);
421 buf = strtok(NULL, " \t");
422 GetNumber(buf, &val);
425 for(i=0;(buf = strtok(NULL, " \t")); i++) {
427 GetNumber(buf, &val);
437 extern int ReadCommand(char *buf);
441 processing a script or from stdin
442 @param fd - file descriptor
445 int ReadInput(int fd) {
448 ret = read(fd, buf, LINE_L);
450 for(j=i; (j<ret) && (buf[j] != '\n'); j++); // find NL
456 fprintf(stderr, "too big input file\n");
464 reading and handling commands from buf string
465 @param buf - string buffer, 1.char tells about what is to be done:
468 a - assign (simple aliasing mechanism)
474 int ReadCommand(char *buf) {
475 static int usSleep=20000;
479 buf[LINE_L-1] = '\0';
480 if(RemoveNL(buf) == 0)
481 return 0; // empty line
483 switch(tolower(buf[0])) {
485 DMSG("1.CAN-PROXY ReadCommand received QUIT command, exiting");
492 printf("TIME %8.3f\n", gettime()-time0);
496 ptr = strtok(NULL, " \t");
498 sscanf(ptr,"%i", &usSleep);
499 SleepUntil = gettime()-time0 + usSleep/1000000.0;
503 printf("bus locking is %s", (bBusLock ? "ON" : "OFF"));
504 ptr = strtok(NULL, " \t");
506 sscanf(ptr,"%i", &bBusLock);
507 printf(" switched to %s\n", (bBusLock ? "ON" : "OFF"));
510 if(BuildCanMsg(buf, &msg) == 0) {
513 fprintf(stderr,"wrong message syntax: %s\n", buf);