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 extern int SendCanMsg(canmsg_t * msg);
39 extern int ReceiveCanMsg(canmsg_t * msg);
40 extern int ReadInput(int fd);
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 */
52 #define MSG_CAN_UNKNOWN -1
53 #define MSG_CAN_NONE 0
56 #define CAN_MSG_SIZE sizeof(canmsg_t)
57 #define CAN_DEV "/dev/can0"
64 #define WARN(fmt...) (fprintf(stderr,"WARN:" fmt),fprintf(stderr,"\n"))
65 #define ERR(fmt...) (fprintf(stderr,"ERROR:" fmt),fprintf(stderr,"\n"))
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
71 #define DMSG(fmt...) (fprintf(stderr,"DBG:" fmt),fprintf(stderr,"\n"))
77 /** global variables */
79 unsigned short int myport;
80 unsigned short int toport;
81 struct sockaddr_in myaddr, fromaddr, recvaddr, toaddr;
91 long tmLastVerified=0;
92 long tmNow; // current time, global, will be updated in many places
93 long tmLastSentReceived=0;
95 struct timeval tvSelectTimeout;
104 gettimeofday(&tvNow,NULL);
105 return (tmNow=TM_HZ*(tvNow.tv_sec-tsStarted) + (TM_HZ*tvNow.tv_usec)/1000000);
107 #error when using 32 bit signed int TM_HZ can not be greater than 2147
112 gettimeofday(&tvNow,NULL);
113 return ( tvNow.tv_sec + tvNow.tv_usec/1000000.0);
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)
123 for(iLength = strlen(str)-1; iLength >=0; iLength --) {
124 //DMSG("RmNL LineLength:>%i<", iLength);
125 if (isspace(str[iLength])) {
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));
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];
154 unsigned int recvaddrsize = sizeof(recvaddr);
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);
163 memcpy(msg, recvbuf, size);
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"
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");
180 handling command line options, calling functions, main program loop
181 @param argc, char * argv[] - command line options
184 int main(int argc, char * argv[]) {
185 struct timeval tvTimeoutTmp;
192 myport = DEFAULT_CAN_SERVER_PORT;
193 toport = DEFAULT_CAN_CLIENT_PORT;
194 fdNet = fdCanDev = fdIn = -1;
199 tsStarted = tvNow.tv_sec;
200 tvSelectTimeout.tv_sec = 0;
201 tvSelectTimeout.tv_usec = 500000; // wake up every 0.5 s even without data
203 while((opt=getopt(argc, argv, "io:p:ch"))>0) {
205 case 'i': // interactive or stdin
208 case 'o': // log file
209 fLog = fopen(optarg,"a");
212 sscanf(optarg,"%hi", &myport);
215 fdCanDev = open(optarg,O_RDWR/*|O_NONBLOCK - select supported*/);
230 fprintf(stderr, "can-proxy v0.7.1-pi3.5 (C) 2002 BFAD GmbH http://www.getembedded.de/ (GPL) \n");
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));
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) ;
245 toaddr.sin_family=AF_INET;
246 toaddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);;
247 toaddr.sin_port = htons(toport);
249 bind(fdNet,(struct sockaddr*) &myaddr,sizeof(myaddr));
253 ERR("1.CAN-PROXY main fdCanDev not valid:%d, exiting", fdCanDev);
258 tvTimeoutTmp = tvSelectTimeout;
262 FD_SET(fdCanDev, &readsel);
263 if(fdCanDev+1>fdSelectMax)
264 fdSelectMax = fdCanDev+1;
267 FD_SET(fdNet, &readsel);
268 if(fdNet+1>fdSelectMax)
269 fdSelectMax = fdNet+1;
272 FD_SET(fdIn, &readsel);
273 if(fdIn+1>fdSelectMax)
274 fdSelectMax = fdIn+1;
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
283 if(gettime()-time0 < SleepUntil) {
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
294 if(fdIn>=0 && FD_ISSET(fdIn, &readsel)) {
304 logging messages to/from CAN
305 @param dir - direction
306 @param fout - file descriptor for output
307 @param msg - CanMsgPerma
310 int LogMsg(char * dir, FILE *fout, canmsg_t * msg) {
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]);
325 fprintf(stdout, "\n");
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]);
343 /** PERMA CAN utility functions */
346 sending messages to CAN
347 @param msg - CanMsgPerma
350 int SendCanMsg(canmsg_t * msg) {
353 msg->flags = MSG_EXT;
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
368 receiving messages from CAN
369 @param msg - CanMsgPerma
372 int ReceiveCanMsg(canmsg_t * msg) {
375 // fdCanDev = open(CAN_DEV,O_RDWR | O_NONBLOCK);
379 ret = read(fdCanDev,msg, CAN_MSG_SIZE);
380 if(ret == CAN_MSG_SIZE) {
381 LogMsg("RECV", fLog, msg);
382 tmLastSentReceived = tmNow;
385 // we can receive 0 here
386 if(ret == 0 || (ret == -1 && errno == EAGAIN)) {
389 DMSG("received %d bytes",ret);
395 @param buf - string buffer
396 @param val - alias value
399 int GetNumber(char * buf, int * val) {
403 if(sscanf(buf,"%i", val)==1) {
409 /** @name BuildCanMsg
410 building the can message from buf string to msg
412 @param msg - CanMsgPerma
415 int BuildCanMsg(char * buf, canmsg_t *msg) {
419 buf = strtok(buf, " \t");
422 buf = strtok(NULL, " \t");
423 GetNumber(buf, &val);
427 buf = strtok(NULL, " \t");
428 GetNumber(buf, &val);
431 for(i=0;(buf = strtok(NULL, " \t")); i++) {
433 GetNumber(buf, &val);
443 extern int ReadCommand(char *buf);
447 processing a script or from stdin
448 @param fd - file descriptor
451 int ReadInput(int fd) {
454 ret = read(fd, buf, LINE_L);
456 for(j=i; (j<ret) && (buf[j] != '\n'); j++); // find NL
462 fprintf(stderr, "too big input file\n");
470 reading and handling commands from buf string
471 @param buf - string buffer, 1.char tells about what is to be done:
474 a - assign (simple aliasing mechanism)
480 int ReadCommand(char *buf) {
481 static int usSleep=20000;
485 buf[LINE_L-1] = '\0';
486 if(RemoveNL(buf) == 0)
487 return 0; // empty line
489 switch(tolower(buf[0])) {
491 DMSG("1.CAN-PROXY ReadCommand received QUIT command, exiting");
498 printf("TIME %8.3f\n", gettime()-time0);
502 ptr = strtok(NULL, " \t");
504 sscanf(ptr,"%i", &usSleep);
505 SleepUntil = gettime()-time0 + usSleep/1000000.0;
508 if(BuildCanMsg(buf, &msg) == 0) {
511 fprintf(stderr,"wrong message syntax: %s\n", buf);