00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
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
00165 #if !defined(_WIN32)
00166 fd_set fds;
00167
00168
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
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
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
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
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
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
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;
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
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
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
00487 if (buffer[0]) tty=buffer;
00488 if (!tty) tty = getenv(TTY_VARIABLE);
00489 if (!tty) tty = DEFAULTTTY;
00490
00491
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";
00498 }
00499 #elif defined(LINUX) || defined(linux)
00500
00501
00502
00503
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
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
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;
00550 if(lnp_assured_write(buffer,2,rcxaddr,srcport)) {
00551 fputs("error deleting program\n",stderr);
00552 return -1;
00553 }
00554
00555
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;
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;
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
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;
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 }