Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

rcx_comm.c

Go to the documentation of this file.
00001 /*
00002  *  rcx_comm.c
00003  *
00004  *  RCX communication routines.
00005  *
00006  *  The contents of this file are subject to the Mozilla Public License
00007  *  Version 1.0 (the "License"); you may not use this file except in
00008  *  compliance with the License. You may obtain a copy of the License at
00009  *  http://www.mozilla.org/MPL/
00010  *
00011  *  Software distributed under the License is distributed on an "AS IS"
00012  *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
00013  *  License for the specific language governing rights and limitations
00014  *  under the License.
00015  *
00016  *  The Original Code is Firmdl code, released October 3, 1998.
00017  *
00018  *  The Initial Developer of the Original Code is Kekoa Proudfoot.
00019  *  Portions created by Kekoa Proudfoot are Copyright (C) 1998, 1999
00020  *  Kekoa Proudfoot. All Rights Reserved.
00021  *
00022  *  Contributor(s): Kekoa Proudfoot <kekoa@graphics.stanford.edu>
00023  */
00024 
00025 /*  2002.04.01
00026  *
00027  *  Modifications to the original loader.c file in LegOS 0.2.4 include:
00028  *
00029  *  Hary D. Mahesan's update to support USB IR firmware downloading 
00030  *  using RCX 2.0's USB tower under WIN32 on Cygwin.
00031  *      <hdmahesa@engmail.uwaterloo.ca>
00032  *      <hmahesan@hotmail.com>
00033  *
00034  *  CVS inclusion, revision and modification by Paolo Masetti.
00035  *      <paolo.masetti@itlug.org>
00036  *
00037  */
00038 
00039 #include <sys/types.h>
00040 #include <sys/stat.h>
00041 #include <fcntl.h>
00042 #include <stdlib.h>
00043 #include <unistd.h>
00044 #include <termios.h>
00045 #include <sys/time.h>
00046 #include <stdio.h>
00047 #include <ctype.h>
00048 #include <string.h>
00049 
00050 #if defined(_WIN32)
00051   #include <windows.h>
00052 #else
00053   #include <sys/ioctl.h>
00054   #include <errno.h>
00055 #endif
00056 
00057 #include "rcx_comm.h"
00058 
00059 /* Defines */
00060 
00061 #define BUFFERSIZE  4096
00062 
00063 /* Globals */
00064 
00065 int __comm_debug = 0;
00066 extern int tty_usb; 
00067 
00068 /* Timer routines */
00069 
00070 typedef struct timeval timeval_t;
00071 
00072 #define tvupdate(tv)  gettimeofday(tv,NULL)
00073 #define tvsec(tv)     ((tv)->tv_sec)
00074 #define tvmsec(tv)    ((tv)->tv_usec * 1e-3)
00075 
00076 static float
00077 timer_reset(timeval_t *timer)
00078 {
00079     tvupdate(timer);
00080     return 0;
00081 }
00082 
00083 static float
00084 timer_read(timeval_t *timer)
00085 {
00086     timeval_t now;
00087     tvupdate(&now);
00088     return tvsec(&now) - tvsec(timer) + (tvmsec(&now) - tvmsec(timer)) * 1e-3;
00089 }
00090 
00091 void myperror(char *str) {
00092 #if defined(_WIN32)
00093     fprintf(stderr, "Error %lu: %s\n", (unsigned long) GetLastError(), str);
00094 #else
00095     perror(str);
00096 #endif
00097 }
00098 
00099 /* Timeout read routine */
00100 
00101 static int nbread (FILEDESCR fd, void *buf, int maxlen, int timeout)
00102 {
00103     char *bufp = (char *)buf;
00104     int len = 0;
00105 #if defined(LINUX) | defined(linux)
00106    int count;
00107    fd_set fds;
00108    struct timeval tv;
00109 #endif
00110 
00111     while (len < maxlen) {
00112 
00113 #if defined(_WIN32)
00114       if(tty_usb) {
00115           // USB Stuff here
00116           // We can't use Serial timeouts.
00117           DWORD count = 0;
00118           struct timeval timebegin ,timenow;
00119           unsigned long elapsed; // for timeout values
00120 
00121           gettimeofday(&timebegin,0); 
00122           while(count==0) {
00123                 ReadFile( fd, &bufp[len], maxlen - len, &count, NULL);
00124                 gettimeofday(&timenow,0);
00125                 elapsed = (timenow.tv_sec - timebegin.tv_sec ) + (timenow.tv_usec - timebegin.tv_usec);                         
00126                 if(elapsed > timeout) 
00127                         break;
00128           }
00129           if(count==0) {
00130                 if(__comm_debug) 
00131                         printf("Hary Mahesan - USB mode: nbread(len=%d, maxlen=%d) break...timed out\n", len, maxlen);
00132                 break;
00133           }
00134           len += count; //update len
00135       } else {
00136           // Serial port stuff now.
00137           DWORD count = 0;
00138           COMMTIMEOUTS CommTimeouts;
00139 
00140           GetCommTimeouts (fd, &CommTimeouts);
00141 
00142         // Change the COMMTIMEOUTS structure settings.
00143         CommTimeouts.ReadIntervalTimeout = MAXDWORD;
00144         CommTimeouts.ReadTotalTimeoutMultiplier = 0;
00145         CommTimeouts.ReadTotalTimeoutConstant = timeout;
00146         CommTimeouts.WriteTotalTimeoutMultiplier = 10;
00147         CommTimeouts.WriteTotalTimeoutConstant = 1000;
00148 
00149         // Set the time-out parameters for all read and write operations
00150         // on the port.
00151         SetCommTimeouts(fd, &CommTimeouts);
00152 
00153         if (ReadFile(fd, &bufp[len], maxlen - len, &count, NULL) == FALSE) {
00154             myperror("ReadFile");
00155               fprintf(stderr, "nb_read - error reading tty: %lu\n", (unsigned long) GetLastError());
00156               exit(1);
00157           }
00158 
00159         len += count;
00160 
00161         if (count == 0) {
00162                 if(__comm_debug) 
00163                         printf("Serial mode: nbread(len=%d, maxlen=%d) break...timed out\n", len, maxlen);
00164                 break;
00165         }
00166         }
00167 #else
00168      if (tty_usb == 1)
00169          {
00170             // LegoUSB doesn't work with select(), so just set a read
00171             // timeout and then later check to see if the read timed out
00172             // without reading data.
00173 
00174             ioctl(fd, _IOW('u', 0xc8, int), timeout);
00175          }
00176      else
00177          {
00178                 FD_ZERO(&fds);
00179                 FD_SET(fd, &fds);
00180 
00181                 tv.tv_sec = timeout / 1000;
00182                 tv.tv_usec = (timeout % 1000) * 1000;
00183 
00184                 if (select(fd+1, &fds, NULL, NULL, &tv) < 0) {
00185                     perror("select");
00186                     exit(1);
00187                 }
00188                 if (!FD_ISSET(fd, &fds))
00189                     break;
00190                 }
00191 
00192                 count = read(fd, &bufp[len], maxlen - len);
00193 
00194             // If no data was read from a USB tower and the read() timed out,
00195                 // go ahead and assume we are done reading data.
00196         if (tty_usb == 1 && count == -1 && errno == ETIMEDOUT) 
00197                 {
00198                         break;
00199                 }
00200         
00201                 if (count < 0) {
00202                     perror("read");
00203                     exit(1);
00204                 }
00205 
00206         len += count;
00207 #endif
00208 
00209     }
00210 
00211     return len;
00212 }
00213 
00214 /* discard all characters in the input queue of tty */
00215 static void rx_flush(FILEDESCR fd)
00216 {
00217 #if defined(_WIN32)
00218     if (tty_usb == 0) {
00219           PurgeComm(fd, PURGE_RXABORT | PURGE_RXCLEAR);
00220     } else {
00221       char echo[BUFFERSIZE];
00222       nbread(fd, echo, BUFFERSIZE, 200);
00223     }
00224 #else
00225     char echo[BUFFERSIZE];
00226     nbread(fd, echo, BUFFERSIZE, 200);
00227 #endif
00228 }
00229 
00230 int mywrite(FILEDESCR fd, const void *buf, size_t len) {
00231 #if defined(_WIN32)
00232     DWORD nBytesWritten=0;
00233     WriteFile(fd, buf, len, &nBytesWritten, NULL);
00234     return nBytesWritten;
00235 #else
00236    /* For usb tower, the driver, legousbtower, uses interrupt  */
00237    /* urb which can only carry up to 8 bytes at a time. To     */
00238    /* transmit more we have to check and send the rest.  It is */
00239    /* a good thing to check, so I'll make it general.          */
00240 
00241    int actual = 0;
00242    int rc;
00243    char * cptr;
00244    int retry = 1000;
00245 
00246    if (len < 1) return len;
00247    cptr = (char *) buf;
00248    while (actual < len) {
00249      rc = (long) write(fd, cptr+actual, len-actual);
00250      if (rc == -1) {
00251        if ((errno == EINTR) || (errno == EAGAIN))  {
00252          rc = 0;
00253          usleep(10);
00254          retry --;
00255        } else return -1;
00256      }
00257      actual += rc;
00258      if (retry < 1) return actual;
00259    }
00260    return len;
00261 #endif
00262 }
00263 
00264 /* RCX routines */
00265 
00266 FILEDESCR rcx_init(char *tty, int is_fast)
00267 {
00268     FILEDESCR fd;
00269 
00270 #if defined(_WIN32)
00271     DCB dcb;
00272 #else
00273     struct termios ios;
00274 #endif
00275 
00276     if (__comm_debug) printf("mode = %s\n", is_fast ? "fast" : "slow");
00277 
00278 #if defined(_WIN32)
00279         // have windows platform I/O
00280     if ((fd = CreateFile(tty, GENERIC_READ | GENERIC_WRITE,
00281                               0, NULL, OPEN_EXISTING,
00282                               0, NULL)) == INVALID_HANDLE_VALUE) {
00283                 fprintf(stderr, "Error %lu: Opening %s\n", (unsigned long) GetLastError(), tty);
00284                 exit(1);
00285     }
00286 
00287     //These settings don't apply to the USB tower, so if{} them out.
00288     if(tty_usb==0) {
00289         // Serial settings
00290         FillMemory(&dcb, sizeof(dcb), 0);
00291         if (!GetCommState(fd, &dcb)) {  // get current DCB
00292         // Error in GetCommState
00293           myperror("GetCommState");
00294         exit(1);
00295         } else {
00296           dcb.ByteSize = 8;
00297           dcb.Parity   = (is_fast ? 0 : 1);     // 0-4=no,odd,even,mark,space
00298           dcb.StopBits = 0;                     // 0,1,2 = 1, 1.5, 2
00299           dcb.fBinary  = TRUE ;
00300           dcb.fParity  = (is_fast ? FALSE : TRUE) ;
00301           dcb.fAbortOnError = FALSE ;
00302           dcb.BaudRate = (is_fast ? CBR_4800 : CBR_2400);       // Update DCB rate.
00303 
00304           // Set new state.
00305           if (!SetCommState(fd, &dcb)) {
00306             // Error in SetCommState. Possibly a problem with the communications
00307           // port handle or a problem with the DCB structure itself.
00308             myperror("SetCommState");
00309             exit(1);
00310         }
00311         } // GetCommState
00312     } // usb
00313 
00314 #else
00315         // have linux platform I/O
00316     if ((fd = open(tty, O_RDWR)) < 0) {
00317                 fprintf(stderr,"ERROR: tty=%s, ",tty);
00318                 perror("open");
00319                 exit(1);
00320     }
00321 
00322     if (tty_usb == 0){
00323             if (!isatty(fd)) {
00324                         close(fd);
00325                         fprintf(stderr, "%s: not a tty\n", tty);
00326                         exit(1);
00327             }
00328 
00329             memset(&ios, 0, sizeof(ios));
00330 
00331             ios.c_cflag = CREAD | CLOCAL | CS8 | (is_fast ? 0 : PARENB | PARODD);
00332         
00333             cfsetispeed(&ios, is_fast ? B4800 : B2400);
00334             cfsetospeed(&ios, is_fast ? B4800 : B2400);
00335 
00336             if (tcsetattr(fd, TCSANOW, &ios) == -1) {
00337                         perror("tcsetattr");
00338                         exit(1);
00339             }
00340     }
00341 #endif
00342 
00343     return fd;
00344 }
00345 
00346 void rcx_close(FILEDESCR fd)
00347 {
00348 #if defined(_WIN32)
00349     CloseHandle(fd);
00350 #else
00351     close(fd);
00352 #endif
00353 }
00354 
00355 int rcx_wakeup_tower (FILEDESCR fd, int timeout)
00356 {
00357     char msg[] = { 0x10, 0xfe, 0x10, 0xfe };
00358     char keepalive = 0xff;
00359     char buf[BUFFERSIZE];
00360     timeval_t timer;
00361     int count = 0;
00362     int len;
00363 
00364     // First, I send a KeepAlive Byte to settle IR Tower...
00365     mywrite(fd, &keepalive, 1);
00366     usleep(20000);
00367     rx_flush(fd);
00368 
00369     timer_reset(&timer);
00370 
00371     do {
00372         if (__comm_debug) {
00373             printf("writelen = %d\n", sizeof(msg));
00374             hexdump("W", msg, sizeof(msg));
00375         }
00376         if (mywrite(fd, msg, sizeof(msg)) != sizeof(msg)) {
00377             myperror("write");
00378             exit(1);
00379         }
00380         count += len = nbread(fd, buf, BUFFERSIZE, 50);
00381         if (len == sizeof(msg) && !memcmp(buf, msg, sizeof(msg)))
00382             return RCX_OK; /* success */
00383         if (__comm_debug) {
00384             printf("recvlen = %d\n", len);
00385             hexdump("R", buf, len);
00386         }
00387         rx_flush(fd);
00388     } while (timer_read(&timer) < (float)timeout / 1000.0f);
00389 
00390     if (!count)
00391         return RCX_NO_TOWER; /* tower not responding */
00392     else
00393         return RCX_BAD_LINK; /* bad link */
00394 }
00395 
00396 /* Hexdump routine */
00397 
00398 #define LINE_SIZE   16
00399 #define GROUP_SIZE  4
00400 #define UNPRINTABLE '.'
00401 
00402 void hexdump(char *prefix, void *buf, int len)
00403 {
00404     unsigned char *b = (unsigned char *)buf;
00405     int i, j, w;
00406 
00407     for (i = 0; i < len; i += w) {
00408         w = len - i;
00409         if (w > LINE_SIZE)
00410             w = LINE_SIZE;
00411         if (prefix)
00412             printf("%s ", prefix);
00413         printf("%04x: ", i);
00414         for (j = 0; j < w; j++, b++) {
00415             printf("%02x ", *b);
00416             if ((j + 1) % GROUP_SIZE == 0)
00417                 putchar(' ');
00418         }
00419         putchar('\n');
00420     }
00421 }
00422 
00423 
00424 int rcx_send (FILEDESCR fd, void *buf, int len, int use_comp)
00425 {
00426     char *bufp = (char *)buf;
00427     char buflen = len;
00428     char msg[BUFFERSIZE];
00429     char echo[BUFFERSIZE];
00430     int msglen, echolen;
00431     int sum;
00432     int tty_usb_echo = 0; // USB do not echos
00433 
00434     /* Encode message */
00435 
00436     msglen = 0;
00437     sum = 0;
00438 
00439     if (use_comp) {
00440         msg[msglen++] = 0x55;
00441         msg[msglen++] = 0xff;
00442         msg[msglen++] = 0x00;
00443         while (buflen--) {
00444             msg[msglen++] = *bufp;
00445             msg[msglen++] = (~*bufp) & 0xff;
00446             sum += *bufp++;
00447         }
00448         msg[msglen++] = sum;
00449         msg[msglen++] = ~sum;
00450     }
00451     else {
00452         msg[msglen++] = 0xff;
00453         while (buflen--) {
00454             msg[msglen++] = *bufp;
00455             sum += *bufp++;
00456         }
00457         msg[msglen++] = sum;
00458     }
00459 
00460     /* Send message */
00461 
00462     if (mywrite(fd, msg, msglen) != msglen) {
00463         myperror("write");
00464         exit(1);
00465     }
00466 
00467     /* Receive echo */
00468 
00469     // USB Tower doesn't echo
00470     if(tty_usb == tty_usb_echo) {
00471 
00472       echolen = nbread(fd, echo, msglen, 100);
00473 
00474       if (__comm_debug) {
00475           printf("msglen = %d, echolen = %d\n", msglen, echolen);
00476           hexdump("C", echo, echolen);
00477       }
00478 
00479       /* Check echo */
00480       /* Ignore data, since rcx might send ack even if echo data is wrong */
00481 
00482       if (echolen != msglen /* || memcmp(echo, msg, msglen) */ ) {
00483           /* Flush connection if echo is bad */
00484           rx_flush(fd);
00485           return RCX_BAD_ECHO;
00486       }
00487     } // USB
00488 
00489     return len;
00490 }
00491 
00492 int rcx_recv (FILEDESCR fd, void *buf, int maxlen, int timeout, int use_comp)
00493 {
00494     char *bufp = (char *)buf;
00495     unsigned char msg[BUFFERSIZE];
00496     int msglen;
00497     int sum;
00498     int pos;
00499     int len;
00500 
00501     /* Receive message */
00502 
00503     msglen = nbread(fd, msg, BUFFERSIZE, timeout);
00504 
00505     if (__comm_debug) {
00506         printf("recvlen = %d\n", msglen);
00507         hexdump("R", msg, msglen);
00508     }
00509 
00510     /* Check for message */
00511 
00512     if (!msglen)
00513         return RCX_NO_RESPONSE;
00514 
00515     /* Verify message */
00516 
00517     if (use_comp) {
00518         if (msglen < 5 || (msglen - 3) % 2 != 0)
00519             return RCX_BAD_RESPONSE;
00520 
00521         if (msg[0] != 0x55 || msg[1] != 0xff || msg[2] != 0x00)
00522             return RCX_BAD_RESPONSE;
00523 
00524         for (sum = 0, len = 0, pos = 3; pos < msglen - 2; pos += 2) {
00525             if (msg[pos] != ((~msg[pos+1]) & 0xff))
00526                 return RCX_BAD_RESPONSE;
00527             sum += msg[pos];
00528             if (len < maxlen)
00529                 bufp[len++] = msg[pos];
00530         }
00531 
00532         if (msg[pos] != ((~msg[pos+1]) & 0xff))
00533             return RCX_BAD_RESPONSE;
00534 
00535         if (msg[pos] != (sum & 0xff))
00536             return RCX_BAD_RESPONSE;
00537 
00538         /* Success */
00539         return len;
00540     }
00541     else {
00542         if (msglen < 4)
00543             return RCX_BAD_RESPONSE;
00544 
00545         if (msg[0] != 0x55 || msg[1] != 0xff || msg[2] != 0x00)
00546             return RCX_BAD_RESPONSE;
00547 
00548         for (sum = 0, len = 0, pos = 3; pos < msglen - 1; pos++) {
00549             sum += msg[pos];
00550             if (len < maxlen)
00551                 bufp[len++] = msg[pos];
00552         }
00553 
00554         /* Return success if checksum matches */
00555         if (msg[pos] == (sum & 0xff))
00556             return len;
00557 
00558         /* Failed.  Possibly a 0xff byte queued message? (unlock firmware) */
00559         for (sum = 0, len = 0, pos = 3; pos < msglen - 2; pos++) {
00560           sum += msg[pos];
00561           if (len < maxlen)
00562             bufp[len++] = msg[pos];
00563         }
00564 
00565         /* Return success if checksum matches */
00566         if (msg[pos] == (sum & 0xff))
00567           return len;
00568 
00569         /* Failed.  Possibly a long message? */
00570         /* Long message if opcode is complemented and checksum okay */
00571         /* If long message, checksum does not include opcode complement */
00572         for (sum = 0, len = 0, pos = 3; pos < msglen - 1; pos++) {
00573             if (pos == 4) {
00574                 if (msg[3] != ((~msg[4]) & 0xff))
00575                     return RCX_BAD_RESPONSE;
00576             }
00577             else {
00578                 sum += msg[pos];
00579                 if (len < maxlen)
00580                     bufp[len++] = msg[pos];
00581             }
00582         }
00583 
00584         if (msg[pos] != (sum & 0xff))
00585             return RCX_BAD_RESPONSE;
00586 
00587         /* Success */
00588         return len;
00589     }
00590 }
00591 
00592 int rcx_sendrecv (FILEDESCR fd, void *send, int slen, void *recv, int rlen,
00593                   int timeout, int retries, int use_comp)
00594 {
00595     int status = 0;
00596 
00597     if (__comm_debug) printf("sendrecv %d:\n", slen);
00598 
00599     while (retries--) {
00600         if ((status = rcx_send(fd, send, slen, use_comp)) < 0) {
00601             if (__comm_debug) printf("status = %s\n", rcx_strerror(status));
00602             continue;
00603         }
00604         if ((status = rcx_recv(fd, recv, rlen, timeout, use_comp)) < 0) {
00605             if (__comm_debug) printf("status = %s\n", rcx_strerror(status));
00606             continue;
00607         }
00608         break;
00609     }
00610 
00611     if (__comm_debug) {
00612         if (status > 0)
00613             printf("status = %s\n", rcx_strerror(0));
00614         else
00615             printf("status = %s\n", rcx_strerror(status));
00616     }
00617 
00618     return status;
00619 }
00620 
00621 int rcx_is_alive (FILEDESCR fd, int use_comp)
00622 {
00623     unsigned char send[1] = { 0x10 };
00624     unsigned char recv[1];
00625 
00626     return (rcx_sendrecv(fd, send, 1, recv, 1, 50, 5, use_comp) == 1);
00627 }
00628 
00629 char *rcx_strerror (int error)
00630 {
00631     switch (error) {
00632     case RCX_OK: return "no error";
00633     case RCX_NO_TOWER: return "tower not responding";
00634     case RCX_BAD_LINK: return "bad ir link";
00635     case RCX_BAD_ECHO: return "bad ir echo";
00636     case RCX_NO_RESPONSE: return "no response from rcx";
00637     case RCX_BAD_RESPONSE: return "bad response from rcx";
00638     default: return "unknown error";
00639     }
00640 }
00641 

Generated on Fri Feb 25 08:02:40 2005 for brickos by  doxygen 1.3.9.1