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

loader.c

Go to the documentation of this file.
00001 
00006 /*
00007  *  The contents of this file are subject to the Mozilla Public License
00008  *  Version 1.0 (the "License"); you may not use this file except in
00009  *  compliance with the License. You may obtain a copy of the License at
00010  *  http://www.mozilla.org/MPL/
00011  *
00012  *  Software distributed under the License is distributed on an "AS IS"
00013  *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
00014  *  License for the specific language governing rights and limitations
00015  *  under the License.
00016  *
00017  *  The Original Code is legOS code, released October 2, 1999.
00018  *
00019  *  The Initial Developer of the Original Code is Markus L. Noga.
00020  *  Portions created by Markus L. Noga are Copyright (C) 1999
00021  *  Markus L. Noga. All Rights Reserved.
00022  *
00023  *  Contributor(s): everyone discussing LNP at LUGNET
00024  */
00025 
00026 /*  2000.05.01
00027  *
00028  *  Modifications to the original loader.c file for LegOS 0.2.2 include:
00029  *
00030  *  Paolo Masetti's patches to eliminate "Invalid Argument" error and
00031  *  to get rid of several compiler warnings:
00032  *     <paolo.masetti@itlug.org>
00033  *     http://www.lugnet.com/robotics/rcx/legos/?n=619
00034  *
00035  *  Markus L. Noga's solution to Cygwin's failure to define the O_ASYNC symbol:
00036  *     <markus@noga.de>
00037  *     http://www.lugnet.com/robotics/rcx/legos/?n=439
00038  *
00039  *  Paolo Masetti's adaptation for definitive porting to Win32. No more errors in
00040  *  serial communication and conditional compile for Winnt (cygnwin).
00041  *  Several addition to dll option to better support user. Execute dll without
00042  *  arguments to get help.
00043  *     <paolo.masetti@itlug.org>
00044  */
00045 
00046 /*  2002.04.01
00047  *
00048  *  Modifications to the original loader.c file in LegOS 0.2.4 include:
00049  *
00050  *  Hary D. Mahesan's update to support USB IR firmware downloading 
00051  *  using RCX 2.0's USB tower under WIN32 on Cygwin.
00052  *      <hdmahesa@engmail.uwaterloo.ca>
00053  *      <hmahesan@hotmail.com>
00054  *
00055  *  CVS inclusion, revision and modification by Paolo Masetti.
00056  *      <paolo.masetti@itlug.org>
00057  *
00058  */
00059 
00060 #include <stdio.h>
00061 #include <stdlib.h>
00062 #include <unistd.h>
00063 #include <string.h>
00064 #include <fcntl.h>
00065 #include <sys/time.h>
00066 #include <sys/types.h>
00067 #include <signal.h>
00068 #include <errno.h>
00069 
00070 #if defined(_WIN32)
00071   #include <windows.h>
00072 #endif
00073 
00074 #if (defined(__unix__) || defined(unix)) && !defined(USG)
00075 #include <sys/param.h>
00076 #endif
00077 
00078 #include <sys/lnp.h>
00079 #include <sys/lnp-logical.h>
00080 
00081 #include "rcxtty.h"
00082 #include "keepalive.h"
00083 #include <lx.h>
00084 
00085 #define MAX_DATA_CHUNK 0xf8       
00086 #define XMIT_RETRIES   5          
00087 #define REPLY_TIMEOUT  750000     
00088 #define BYTE_TIME      (1000*LNP_BYTE_TIME) 
00089 
00090 #define PROG_MIN        1
00091 #define PROG_MAX        8
00092 
00093 #define ADDR_MIN        0
00094 #define ADDR_MAX        15
00095 
00096 #define DEFAULT_DEST    0
00097 #define DEFAULT_PROGRAM PROG_MIN
00098 #define DEFAULT_SRCPORT 0
00099 #define DEFAULT_PRIORITY 10
00100 
00101 typedef enum {
00102   CMDacknowledge,               
00103   CMDdelete,            
00104   CMDcreate,            
00105   CMDoffsets,           
00106   CMDdata,              
00107   CMDrun,               
00108   CMDirmode,                    
00109   CMDsethost,                   
00110   CMDlast               
00111 } packet_cmd_t;
00112 
00113 #if (defined(__sun__) && defined(__svr4__)) || defined(BSD)     // Solaris||BSD
00114 #undef HAVE_GETOPT_LONG
00115 #else
00116 #define HAVE_GETOPT_LONG 1
00117 #endif
00118 
00119 #ifdef HAVE_GETOPT_LONG
00120 #include <getopt.h>
00121 
00122 static const struct option long_options[]={
00123   {"rcxaddr",required_argument,0,'r'},
00124   {"program",required_argument,0,'p'},
00125   {"delete", required_argument,0,'d'},
00126   {"srcport",required_argument,0,'s'},
00127   {"tty",    required_argument,0,'t'},
00128   {"irmode", required_argument,0,'i'},
00129   {"node"  , required_argument,0,'n'},
00130   {"execute",no_argument      ,0,'e'},
00131   {"verbose",no_argument      ,0,'v'},
00132   {0        ,0                ,0,0  }
00133 };
00134 
00135 #else // HAVE_GETOPT_LONG
00136 
00137 #define getopt_long(ac, av, opt, lopt, lidx) (getopt((ac), (av), (opt)))
00138 
00139 #endif // HAVE_GETOPT_LONG
00140 
00141 volatile int receivedAck=0;
00142 
00143 volatile unsigned short relocate_to=0;
00144 
00145 unsigned int  rcxaddr = DEFAULT_DEST,
00146               prog    = DEFAULT_PROGRAM,
00147               srcport = DEFAULT_SRCPORT,
00148               hostaddr = DEFAULT_DEST,
00149                   irmode  = -1;
00150   
00151 int run_flag=0;
00152 int verbose_flag=0;
00153 int tty_usb=0;
00154 int pdelete_flag=0;
00155 int hostaddr_flag=0;
00156 
00157 void io_handler(void);
00158 
00162 int lnp_logical_write(const void *data, size_t length) {
00163 
00164 // With Win32 we are using Blocking Write by default
00165 #if !defined(_WIN32)
00166   fd_set fds;
00167 
00168   // wait for transmission
00169   //
00170   do {
00171     FD_ZERO(&fds);
00172     FD_SET(rcxFD(),&fds);
00173   } while(select(rcxFD()+1,NULL,&fds,NULL,NULL)<1);
00174 #endif
00175 
00176   // transmit
00177   //
00178 #if defined(LINUX) || defined(linux)
00179    if (tty_usb == 0)
00180 #endif
00181   keepaliveRenew();
00182 
00183   return mywrite(rcxFD(), data, length)!=length;
00184 }
00185 
00187 
00189 int lnp_assured_write(const unsigned char *data, unsigned char length,
00190                       unsigned char dest, unsigned char srcport) {
00191   int i;
00192   struct timeval timeout,now;
00193   unsigned long total,elapsed;
00194   
00195   for(i=0; i<XMIT_RETRIES; i++) {
00196     receivedAck=0;
00197     
00198     lnp_addressing_write(data,length,dest,srcport);
00199     
00200     gettimeofday(&timeout,0);
00201     total=REPLY_TIMEOUT+length*BYTE_TIME;
00202     elapsed=0;
00203 
00204     do {
00205 #if defined(_WIN32)
00206       io_handler();
00207 #else
00208       struct timeval tv;
00209       fd_set fds;
00210 
00211       FD_ZERO(&fds);
00212       FD_SET(rcxFD(), &fds);
00213 
00214       tv.tv_sec = (total - elapsed) / 1000000;
00215       tv.tv_usec = (total - elapsed) % 1000000;
00216       select(rcxFD() + 1, &fds, NULL, NULL, &tv);
00217       if (FD_ISSET(rcxFD(), &fds))
00218         io_handler();
00219 #endif
00220  
00221       gettimeofday(&now,0);
00222       elapsed=1000000*(now.tv_sec  - timeout.tv_sec ) +
00223                        now.tv_usec - timeout.tv_usec;
00224       
00225     } while((!receivedAck) && (elapsed < total));
00226     
00227     if(i || !receivedAck)
00228       if(verbose_flag)
00229         fprintf(stderr,"try %d: ack:%d\n",i,receivedAck);
00230     
00231     if(receivedAck)
00232       return 0;    
00233    }
00234   
00235   return -1;
00236 }
00237 
00238 void io_handler(void) {
00239   
00240     static struct timeval last={0,0};
00241     struct timeval now;
00242     unsigned long diff;
00243     
00244     unsigned char buffer[256];
00245 #if defined(_WIN32)
00246     DWORD len=0;
00247     int i;
00248 #else
00249     int len,i;
00250 #endif
00251     
00252     gettimeofday(&now,0);
00253     diff= 1000000*now .tv_sec + now .tv_usec - 
00254          (1000000*last.tv_sec + last.tv_usec);
00255     
00256     if(diff> 10000*LNP_BYTE_TIMEOUT) {
00257       if(verbose_flag)
00258         fprintf(stderr,"\n#time %lu ",diff);
00259       lnp_integrity_reset();
00260     }
00261 #if defined(_WIN32)
00262     // Remember, USB support only in WIN32 environments.
00263     if (tty_usb == 0) {
00264         ReadFile(rcxFD(), buffer, sizeof(buffer), &len, NULL);
00265     } else {
00266         struct timeval timeout, timenow;
00267         unsigned long total, elapsed;
00268         
00269         gettimeofday(&timeout,0);
00270         total = REPLY_TIMEOUT+(long)sizeof(buffer)*BYTE_TIME;
00271         while(len == 0) {
00272                 ReadFile(rcxFD(), buffer, sizeof(buffer), &len, NULL);
00273                 gettimeofday(&timenow, 0);
00274                 // calculate elapsed time as usual
00275                 elapsed=1000000*(timenow.tv_sec - timeout.tv_sec ) + (timenow.tv_usec - timeout.tv_usec);
00276                 if(elapsed > total)
00277                         break;
00278         }
00279     }
00280 #else
00281     len=read(rcxFD(),buffer,sizeof(buffer));
00282 #endif
00283     for(i=0; i<len; i++) {
00284       if(verbose_flag)
00285         fprintf(stderr,"%02x ",buffer[i]);
00286       lnp_integrity_byte(buffer[i]);
00287     }
00288     gettimeofday(&last,0);
00289 }
00290 
00291 void LNPinit(const char *tty) {
00292   struct timeval timeout,now;
00293   long diff;
00294 
00295 #if !defined(_WIN32)
00296   unsigned char buffer[256];
00297 #endif
00298     
00299   // initialize RCX communications
00300   //
00301   if (verbose_flag) fputs("opening tty...\n", stderr);
00302 #ifdef CONF_LNP_FAST
00303   rcxInit(tty, 1);
00304 #else
00305   rcxInit(tty, 0);
00306 #endif
00307 
00308   if (rcxFD() == BADFILE) {
00309     myperror("opening tty");
00310     exit(1);
00311   }
00312 
00313 #if defined(LINUX) || defined(linux)
00314   if (tty_usb == 0) {
00315 #endif
00316      
00317   keepaliveInit();
00318    
00319   // wait for IR to settle
00320   // 
00321   gettimeofday(&timeout,0);
00322   do {    
00323     usleep(100000);
00324     gettimeofday(&now,0);
00325     diff=1000000*(now.tv_sec  - timeout.tv_sec ) +
00326                   now.tv_usec - timeout.tv_usec;
00327       
00328   } while(diff < 100000);
00329 #if defined(LINUX) || defined(linux)
00330   }
00331 #endif
00332 #if defined(_WIN32)
00333   PurgeComm(rcxFD(), PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
00334 #else
00335 #if defined(LINUX) || defined(linux)
00336    if (tty_usb == 0)
00337 #endif
00338   read(rcxFD(),buffer,256);
00339 #endif
00340 }
00341     
00342 void ahandler(const unsigned char *data,unsigned char len,unsigned char src) {
00343   if(*data==CMDacknowledge) {
00344     receivedAck=1;
00345     if(len==8) {
00346       // offset packet
00347       //
00348       relocate_to=(data[2]<<8)|data[3];
00349     }
00350   }
00351 }
00352     
00353 void lnp_download(const lx_t *lx) {
00354   unsigned char buffer[256+3];
00355   
00356   size_t i,chunkSize,totalSize=lx->text_size+lx->data_size;
00357 
00358   if(verbose_flag)
00359     fputs("\ndata ",stderr);
00360 
00361   buffer[0]=CMDdata;
00362   buffer[1]=prog-1;
00363 
00364   for(i=0; i<totalSize; i+=chunkSize) {
00365     chunkSize=totalSize-i;
00366     if(chunkSize>MAX_DATA_CHUNK)
00367       chunkSize=MAX_DATA_CHUNK;
00368 
00369     buffer[2]= i >> 8;
00370     buffer[3]= i &  0xff;
00371     memcpy(buffer+4,lx->text + i,chunkSize);
00372     if(lnp_assured_write(buffer,chunkSize+4,rcxaddr,srcport)) {
00373       fputs("error downloading program\n",stderr);
00374       exit(-1);
00375     }
00376   }
00377 }
00378 
00379 int main(int argc, char **argv) {
00380   lx_t lx;          // the brickOS executable
00381   char *filename;
00382   int opt;
00383 #ifdef HAVE_GETOPT_LONG
00384   int option_index;
00385 #endif
00386   unsigned char buffer[256+3]="";
00387   char *tty=NULL;
00388 
00389   while((opt=getopt_long(argc, argv, "r:p:d:s:t:i:n:ev",
00390                         (struct option *)long_options, &option_index) )!=-1) {
00391     switch(opt) {
00392       case 'e':
00393         run_flag=1;
00394         break;
00395       case 'r':
00396         sscanf(optarg,"%d",&rcxaddr);
00397                 if (rcxaddr > ADDR_MAX || rcxaddr < ADDR_MIN) {
00398                         fprintf(stderr, "LNP host address not in range 0..15\n");
00399                         return -1;
00400                 }
00401                 rcxaddr = (rcxaddr << 4) & CONF_LNP_HOSTMASK;
00402         break;
00403       case 'p':
00404         sscanf(optarg,"%d",&prog);
00405         break;
00406       case 's':
00407         sscanf(optarg,"%d",&srcport);
00408                 if (srcport > ADDR_MAX || srcport < ADDR_MIN) {
00409                         fprintf(stderr, "LNP port number not in range 0..15\n");
00410                         return -1;
00411                 }
00412         break;
00413       case 't':
00414         sscanf(optarg,"%s",buffer);
00415         break;
00416       case 'i':
00417         sscanf(optarg,"%x",&irmode);
00418         break;
00419       case 'd':
00420         sscanf(optarg,"%d",&prog);
00421         pdelete_flag=1;
00422         break;
00423       case 'n':
00424         sscanf(optarg,"%d",&hostaddr);
00425                 if (hostaddr > ADDR_MAX || hostaddr < ADDR_MIN) {
00426                         fprintf(stderr, "LNP host address not in range 0..15\n");
00427                         return -1;
00428                 }
00429         hostaddr_flag=1;
00430         break;
00431       case 'v':
00432         verbose_flag=1;
00433         break;
00434     }
00435   }           
00436   
00437   if (prog > PROG_MAX || prog < PROG_MIN) {
00438     fprintf(stderr, "Program not in range 1..8\n");
00439     return -1;
00440   }
00441 
00442   // load executable
00443   //      
00444   if(((argc-optind < 1) && !(pdelete_flag || hostaddr_flag)) ||
00445          ((argc-optind > 0 ) && (pdelete_flag || hostaddr_flag)))
00446   {
00447     char *usage_string =
00448         "Options:\n"
00449         "  -p<prognum>  , --program=<prognum>   set destination program to <prognum>\n"
00450         "  -r<rcxaddr>  , --rcxaddr=<rcxaddr>   send to RCX host address <rcxaddr>\n"
00451         "  -s<srcport>  , --srcport=<srcport>   send to RCX source port <srcport>\n"
00452         "  -t<comport>  , --tty=<comport>       set IR Tower com port <comport>\n"
00453 #if defined(_WIN32)
00454         "  -t<usb>      , --tty=<usb>           set IR Tower USB mode \n"
00455 #else
00456         "                                       (if \"usb\" is in the port, use USB mode)\n"
00457 #endif
00458         "  -i<0/1>      , --irmode=<0/1>        set IR mode near(0)/far(1) on RCX\n"
00459         "  -e           , --execute             execute program after download\n"
00460         "  -v           , --verbose             verbose mode\n"
00461         "\nCommands:\n"
00462         "  -d<prognum>  , --delete=<prognum>    delete program <prognum> from memory\n"
00463         "  -n<hostaddr> , --node=<hostaddr>     set LNP host address in brick\n"
00464         "\n"
00465         "Default COM port or USB support can be set using environment variable RCXTTY.\n"
00466         "Eg:\tset RCXTTY=COM2\n"
00467            "\tset RCXTTY=USB\n"
00468         "\n"
00469         ;
00470 
00471         fprintf(stderr, "usage: %s [options] [command | file.lx]\n", argv[0]);
00472         fprintf(stderr, usage_string);
00473 
00474     return -1;
00475   }
00476 
00477   // Ignore filename if -dn or -na given
00478   if (!(pdelete_flag || hostaddr_flag)) {
00479     filename=argv[optind++];
00480     if(lx_read(&lx,filename)) {
00481       fprintf(stderr,"unable to load brickOS executable from %s.\n",filename);
00482       return -1;
00483         }
00484   }
00485 
00486   // Moved tty device setting for a new option on command line
00487   if (buffer[0]) tty=buffer;
00488   if (!tty) tty = getenv(TTY_VARIABLE);
00489   if (!tty) tty = DEFAULTTTY;
00490 
00491   // Check if USB IR tower is selected.
00492 #if defined(_WIN32)
00493   if (stricmp(tty, "usb")==0) {
00494         tty_usb = 1;
00495         if(verbose_flag)
00496                 fputs("\n\n Hary Mahesan - LEGO USB IR Tower Mode\n\n",stderr);
00497         tty="\\\\.\\legotower1"; // Set the correct usb tower if you have more than one (unlikely).
00498   }
00499 #elif defined(LINUX) || defined(linux)
00500    /* If the tty string contains "usb", e.g. /dev/usb/lego0, we */
00501    /* assume it is the USB tower.  /dev/usb/lego0 is the default name of */
00502    /* the device in the LegoUSB (http://legousb.sourceforge.net) project. */
00503    /* If yours doesn't contain the "usb" string, just link it. */
00504    if (strstr(tty,"usb") !=0) {
00505        tty_usb=1;
00506        if (verbose_flag)
00507           fputs("\nC.P. Chan & Tyler Akins - USB IR Tower Mode for Linux.\n",stderr);
00508    }
00509 #endif
00510 
00511   LNPinit(tty);
00512 
00513   if (verbose_flag)
00514     fputs("\nLNP Initialized...\n",stderr);
00515 
00516   lnp_addressing_set_handler(0,ahandler);
00517 
00518   if(verbose_flag)  
00519     fprintf(stderr,"loader hostaddr=0x%02x hostmask=0x%02x portmask=0x%02x\n",
00520             rcxaddr & 0x00ff, LNP_HOSTMASK & 0x00ff, srcport & 0x00ff);
00521 
00522   // Set IR mode
00523   if (irmode != -1) {
00524     buffer[0]=CMDirmode;
00525     buffer[1]=irmode;
00526     if(lnp_assured_write(buffer,2,rcxaddr,srcport)) {
00527       fputs("error setting IR mode to far\n",stderr);
00528       return -1;
00529     }
00530   }
00531   
00532   // Set LNP host address
00533   if (hostaddr_flag) {
00534     if(verbose_flag)
00535           fputs("\nset LNP host address", stderr);
00536         buffer[0] = CMDsethost;
00537         buffer[1] = hostaddr;
00538         if(lnp_assured_write(buffer,2,rcxaddr,srcport)) {
00539       fputs("error setting host address\n",stderr);
00540       return -1;
00541         }
00542         fprintf(stderr, "LNP host address set to %d\n", hostaddr);
00543         return 0;
00544   }
00545   
00546   if(verbose_flag)
00547     fputs("\ndelete",stderr);
00548   buffer[0]=CMDdelete;
00549   buffer[1]=prog-1; //       prog 0
00550   if(lnp_assured_write(buffer,2,rcxaddr,srcport)) {
00551     fputs("error deleting program\n",stderr);
00552     return -1;
00553   }
00554 
00555   // All done if -dn given
00556   if (pdelete_flag) {
00557     fprintf(stderr, "P%d deleted\n", prog);
00558         return 0;
00559   }
00560 
00561   if(verbose_flag)
00562     fputs("\ncreate ",stderr);
00563   buffer[ 0]=CMDcreate;
00564   buffer[ 1]=prog-1; //       prog 0
00565   buffer[ 2]=lx.text_size>>8;
00566   buffer[ 3]=lx.text_size & 0xff;
00567   buffer[ 4]=lx.data_size>>8;
00568   buffer[ 5]=lx.data_size & 0xff;
00569   buffer[ 6]=lx.bss_size>>8;
00570   buffer[ 7]=lx.bss_size & 0xff;
00571   buffer[ 8]=lx.stack_size>>8;
00572   buffer[ 9]=lx.stack_size & 0xff;
00573   buffer[10]=lx.offset >> 8;    // start offset from text segment
00574   buffer[11]=lx.offset & 0xff; 
00575   buffer[12]=DEFAULT_PRIORITY;
00576   if(lnp_assured_write(buffer,13,rcxaddr,srcport)) {
00577     fputs("error creating program\n",stderr);
00578     return -1;
00579   }
00580 
00581   // relocation target address in relocate_to
00582   //
00583   lx_relocate(&lx,relocate_to);
00584 
00585   lnp_download(&lx);
00586 
00587   fprintf(stderr, "\n");
00588 
00589   if (run_flag) {
00590     if(verbose_flag)
00591       fputs("\nrun ",stderr);
00592     buffer[0]=CMDrun;
00593     buffer[1]=prog-1; //       prog 0
00594     if(lnp_assured_write(buffer,2,rcxaddr,srcport)) {
00595       fputs("error running program\n",stderr);
00596       return -1;
00597     }
00598   }
00599       
00600   return 0;
00601 }

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