S.F.T. XMODEM Library  1.0
xmodem.c
Go to the documentation of this file.
00001 
00002 //                                                                          //
00003 //                                      _                                   //
00004 //         __  __ _ __ ___    ___    __| |  ___  _ __ ___      ___          //
00005 //         \ \/ /| '_ ` _ \  / _ \  / _` | / _ \| '_ ` _ \    / __|         //
00006 //          >  < | | | | | || (_) || (_| ||  __/| | | | | | _| (__          //
00007 //         /_/\_\|_| |_| |_| \___/  \__,_| \___||_| |_| |_|(_)\___|         //
00008 //                                                                          //
00009 //                                                                          //
00011 //                                                                          //
00012 //          Copyright (c) 2012 by S.F.T. Inc. - All rights reserved         //
00013 //  Use, copying, and distribution of this software are licensed according  //
00014 //    to the GPLv2, LGPLv2, or BSD license, as appropriate (see COPYING)    //
00015 //                                                                          //
00017 
00018 
00019 // XMODEM adapted for arduino and POSIX systems.  Windows code incomplete
00020 
00021 #include "xmodem.h"
00022 
00023 // internal structure definitions
00024 
00025 // Windows requires a different way of specifying structure packing
00026 #ifdef WIN32
00027 #define PACKED
00028 #pragma pack(push,1)
00029 #else // POSIX, ARDUINO
00030 #define PACKED __attribute__((__packed__))
00031 #endif // WIN32 vs THE REST OF THE WORLD
00032 
00033 #define _SOH_ 1 /* start of packet - note XMODEM-1K uses '2' */
00034 #define _EOT_ 4
00035 #define _ENQ_ 5
00036 #define _ACK_ 6
00037 #define _NAK_ 21 /* NAK character */
00038 #define _CAN_ 24 /* CAN character CTRL+X */
00039 
00061 typedef struct _XMODEM_BUF_
00062 {
00063    char cSOH;                   
00064    unsigned char aSEQ, aNotSEQ; 
00065    char aDataBuf[128];          
00066    unsigned char bCheckSum;     
00067 } PACKED XMODEM_BUF;
00068 
00084 typedef struct _XMODEMC_BUF_
00085 {
00086    char cSOH;                   
00087    unsigned char aSEQ, aNotSEQ; 
00088    char aDataBuf[128];          
00089    unsigned short wCRC;         
00090 } PACKED XMODEMC_BUF;
00091 
00092 #ifdef WIN32
00093 // restore default packing
00094 #pragma pack(pop)
00095 #endif // WIN32
00096 
00119 typedef struct _XMODEM_
00120 {
00121   SERIAL_TYPE ser;     
00122   FILE_TYPE file;      
00123 
00124   union
00125   {
00126     XMODEM_BUF xbuf;   
00127     XMODEMC_BUF xcbuf; 
00128   } buf;               
00129 
00130   unsigned char bCRC;  
00131 
00132 } XMODEM;
00133 
00134 
00135 #ifdef DEBUG_CODE
00136 static char szERR[32]; // place for error messages, up to 16 characters
00137 
00138 const char *XMGetError(void)
00139 {
00140   return szERR;
00141 }
00142 #endif // DEBUG_CODE
00143 
00144 #if defined(STAND_ALONE) && defined(DEBUG_CODE)
00145 void debug_dump_buffer(int iDir, const void *pBuf, int cbBuf)
00146 {
00147 int i1, i2;
00148 const unsigned char *p1, *p2;
00149 
00150   if(cbBuf <= 0)
00151   {
00152     return;
00153   }
00154 
00155   p1 = p2 = (const unsigned char *)pBuf;
00156 
00157   for(i1=0, i2=0; i1 <= cbBuf; i1++, p1++)
00158   {
00159     if(!i1 || i2 >= 16 || i1 == cbBuf)
00160     {
00161       if(i1)
00162       {
00163         while(i2 < 16)
00164         {
00165           fputs("    ", stderr); // fill up spaces where data would be
00166           i2++;
00167         }
00168 
00169         fputs(" : ", stderr);
00170 
00171         while(p2 < p1)
00172         {
00173           if(*p2 >= 32 && *p2 <= 127)
00174           {
00175             fputc(*p2, stderr);
00176           }
00177           else
00178           {
00179             fputc('.', stderr);
00180           }
00181 
00182           p2++;
00183         }
00184 
00185         fputc('\n', stderr);
00186       }
00187 
00188       if(!i1 && iDir > 0)
00189       {
00190         fputs("--> ", stderr);
00191       }
00192       else if(!i1 && iDir < 0)
00193       {
00194         fputs("<-- ", stderr);
00195       }
00196       else
00197       {
00198         fputs("    ", stderr);
00199       }
00200 
00201       i2 = 0;
00202       p2 = p1; // make sure
00203     }
00204 
00205     if(i1 < cbBuf)
00206     {
00207       if(!i2)
00208       {
00209         fprintf(stderr, "%02x: %02x", i1, *p1);
00210       }
00211       else
00212       {
00213         fprintf(stderr, ", %02x", *p1);
00214       }
00215 
00216       i2++;
00217     }
00218   }
00219 
00220   fputc('\n', stderr);
00221   fflush(stderr);
00222 }
00223 #endif // STAND_ALONE, DEBUG_CODE
00224 
00225 
00226 //char iBinaryTransfer = 0, iDisableRXOVER = 0;
00227 
00236 unsigned char CalcCheckSum(const char *lpBuf, short cbBuf)
00237 {
00238 short iC, i1;
00239 
00240   iC = 0;
00241 
00242   for(i1 = 0; i1 < cbBuf; i1++)
00243   {
00244     iC += lpBuf[i1];
00245   }
00246 
00247   return (unsigned char)(iC & 0xff);
00248 }
00249 
00259 static unsigned short my_htons(unsigned short sVal)
00260 {
00261   union
00262   {
00263     unsigned char aVal[2];
00264     unsigned short sVal;
00265   } a, b;
00266 
00267   // tweeked for size and speed.  enjoy.
00268 
00269   b.sVal = sVal;
00270 
00271 #ifdef ARDUINO
00272 
00273   a.aVal[0] = b.aVal[1]; // no math involved, pre-optimized code
00274   a.aVal[1] = b.aVal[0];
00275 
00276 #else
00277 
00278   a.aVal[0] = (unsigned char)(sVal >> 8); // less optimized but universal code
00279   a.aVal[1] = (unsigned char)(sVal & 0xff);
00280 
00281 #endif // ARDUINO
00282 
00283   return a.sVal;
00284 }
00285 
00296 unsigned short CalcCRC(const char *lpBuf, short cbBuf)
00297 {
00298 unsigned short wCRC;
00299 short i1, i2, iAX;
00300 char cAL;
00301 
00302   // ** this function returns 2-byte string containing
00303   // ** the CRC calculation result, as high endian
00304 
00305   wCRC = 0;
00306 
00307   for(i1=0; i1 < cbBuf; i1++)
00308   {
00309     cAL = lpBuf[i1];
00310 
00311     iAX = (unsigned short)cAL << 8;
00312 
00313     wCRC = iAX ^ wCRC;
00314 
00315     for(i2=0; i2 < 8; i2++)
00316     {
00317       iAX = wCRC;
00318 
00319       if(iAX & 0x8000)
00320       {
00321         wCRC <<= 1;
00322         wCRC ^= 0x1021;
00323       }
00324       else
00325       {
00326         wCRC <<= 1;
00327       }
00328     }
00329   }
00330 
00331   return my_htons(wCRC);
00332 }
00333 
00334 //void WaitASecond()
00335 //{
00336 //#ifdef ARDUINO
00337 //  delay(1000);
00338 //#elif defined(WIN32)
00339 //  Sleep(1000);
00340 //#else //
00341 //  usleep(1000000);
00342 //#endif // ARDUINO
00343 //}
00344 
00345 #ifndef ARDUINO
00346 #ifdef WIN32
00347 #define MyMillis GetTickCount
00348 #else // WIN32
00349 
00363 unsigned long MyMillis(void)
00364 {
00365 struct timeval tv;
00366 
00367   gettimeofday(&tv, NULL); // 2nd parameter is obsolete anyway
00368 
00369   // NOTE:  this won't roll over the way 'GetTickCount' does in WIN32 so I'll truncate it
00370   //        down to a 32-bit value to make it happen.  Everything that uses 'MyGetTickCount'
00371   //        must handle this rollover properly using 'int' and not 'long' (or cast afterwards)
00372   return((unsigned int)((unsigned long)tv.tv_sec * 1000L + (unsigned long)tv.tv_usec / 1000L));
00373 }
00374 #endif // WIN32
00375 #endif // ARDUINO
00376 
00377 //Function GenerateSEQ (wSeq%) As String
00378 //
00379 //   GenerateSEQ = Chr$(wSeq%) + Chr$(Not (wSeq%) And &HFF)
00380 //
00381 //End Function
00382 
00394 void GenerateSEQ(XMODEM_BUF *pBuf, unsigned char bSeq)
00395 {
00396   pBuf->aSEQ = bSeq;
00397   pBuf->aNotSEQ = ~bSeq;
00398 }
00399 
00411 void GenerateSEQC(XMODEMC_BUF *pBuf, unsigned char bSeq)
00412 {
00413   pBuf->aSEQ = bSeq;
00414   pBuf->aNotSEQ = (255 - bSeq);//~bSeq; these should be the same but for now I do this...
00415 }
00416 
00435 short GetXmodemBlock(SERIAL_TYPE ser, char *pBuf, short cbSize)
00436 {
00437 unsigned long ulCur;
00438 short cb1;
00439 char *p1;
00440 
00441 // ** This function obtains a buffer of 'wSize%' bytes,      **
00442 // ** waiting a maximum of 5 seconds (of silence) to get it. **
00443 // ** It returns this block as a string of 'wSize%' bytes,   **
00444 // ** or a zero length string on error.                      **
00445 
00446 //   iDisableRXOVER% = 1; // bug workaround
00447 
00448 #ifdef ARDUINO
00449 short i1;
00450 
00451   p1 = pBuf;
00452   cb1 = 0;
00453 
00454   ulCur = millis();
00455   ser->setTimeout(SILENCE_TIMEOUT); // 5 seconds [of silence]
00456 
00457   for(i1=0; i1 < cbSize; i1++)
00458   {
00459     if(ser->readBytes(p1, 1) != 1) // 5 seconds of "silence" is what fails this
00460     {
00461       break;
00462     }
00463 
00464     cb1++;
00465     p1++;
00466 
00467     if((millis() - ulCur) > (unsigned long)(10L * SILENCE_TIMEOUT)) // 10 times SILENCE TIMEOUT for TOTAL TIMEOUT
00468     {
00469       break; // took too long, I'm going now
00470     }
00471   }
00472 
00473 #elif defined(WIN32)
00474 
00475 #error no win32 code yet
00476 
00477 #else // POSIX
00478 int i1, i2;
00479 unsigned long ulStart;
00480 
00481 
00482   if(fcntl(ser, F_SETFL, O_NONBLOCK) == -1)
00483   {
00484     static int iFailFlag = 0;
00485 
00486     if(!iFailFlag)
00487     {
00488       fprintf(stderr, "Warning:  'fcntl(O_NONBLOCK)' failed, errno = %d\n", errno);
00489       fflush(stderr);
00490       iFailFlag = 1;
00491     }
00492   }
00493 
00494   p1 = pBuf;
00495   cb1 = 0;
00496 
00497   ulStart = ulCur = MyMillis();
00498 
00499   for(i1=0; i1 < cbSize; i1++)
00500   {
00501     while((i2 = read(ser, p1, 1)) != 1)
00502     {
00503       if(i2 < 0 && errno != EAGAIN)
00504       {
00505         // read error - exit now
00506 //        return cb1; // how many bytes I actually read
00507         goto the_end;
00508       }
00509       else
00510       {
00511         usleep(1000); // 1 msec
00512 
00513         if((MyMillis() - ulCur) > SILENCE_TIMEOUT || // too much silence?
00514            (MyMillis() - ulStart) > 10 * SILENCE_TIMEOUT) // too long for transfer
00515         {
00516 //          return cb1; // finished (return how many bytes I actually read)
00517           goto the_end;
00518         }
00519       }
00520     }
00521 
00522     // here it succeeds
00523 
00524     cb1++;
00525     p1++;
00526 
00527     if((MyMillis() - ulStart) > 10 * SILENCE_TIMEOUT) // 10 times SILENCE TIMEOUT for TOTAL TIMEOUT
00528     {
00529       break; // took too long, I'm going now
00530     }
00531   }
00532 
00533 the_end:
00534 
00535 #ifdef STAND_ALONE
00536   fprintf(stderr, "GetXmodemBlock - request %d, read %d  errno=%d\n", cbSize, cb1, errno);
00537   fflush(stderr);
00538 #ifdef DEBUG_CODE
00539   debug_dump_buffer(-1, pBuf, cb1);
00540 #endif // DEBUG_CODE
00541 #endif // STAND_ALONE
00542 
00543 #endif // ARDUINO
00544 
00545   return cb1; // what I actually read
00546 }
00547 
00558 int WriteXmodemChar(SERIAL_TYPE ser, unsigned char bVal)
00559 {
00560 int iRval;
00561 #ifdef ARDUINO
00562 
00563   iRval = ser->write(bVal);
00564 //  ser->flush(); // force sending it
00565 
00566 #elif defined(WIN32)
00567 
00568 #error no win32 code yet
00569 
00570 #else // POSIX
00571 char buf[2]; // use size of '2' to avoid warnings about array size of '1'
00572 
00573   if(fcntl(ser, F_SETFL, 0) == -1) // set blocking mode
00574   {
00575     static int iFailFlag = 0;
00576 
00577     if(!iFailFlag)
00578     {
00579       fprintf(stderr, "Warning:  'fcntl(O_NONBLOCK)' failed, errno = %d\n", errno);
00580       iFailFlag = 1;
00581     }
00582   }
00583 
00584   buf[0] = bVal; // in case args are passed by register
00585 
00586   iRval = write(ser, buf, 1);
00587 
00588 #if defined(STAND_ALONE) && defined(DEBUG_CODE)
00589   fprintf(stderr, "WriteXmodemChar - returns %d\n", iRval);
00590   if(iRval > 0)
00591   {
00592     debug_dump_buffer(1, buf, 1);
00593   }
00594 #endif // STAND_ALONE, DEBUG_CODE
00595 #endif // ARDUINO
00596 
00597   return iRval;
00598 }
00599 
00611 int WriteXmodemBlock(SERIAL_TYPE ser, const void *pBuf, int cbSize)
00612 {
00613 int iRval;
00614 #ifdef ARDUINO
00615 
00616   iRval = ser->write((const uint8_t *)pBuf, cbSize);
00617 //  ser->flush(); // force sending it before returning
00618 
00619 #elif defined(WIN32)
00620 
00621 #error no win32 code yet
00622 
00623 #else // POSIX
00624 
00625 
00626   if(fcntl(ser, F_SETFL, 0) == -1) // set blocking mode
00627   {
00628     static int iFailFlag = 0;
00629 
00630     if(!iFailFlag)
00631     {
00632       fprintf(stderr, "Warning:  'fcntl(O_NONBLOCK)' failed, errno = %d\n", errno);
00633       fflush(stderr);
00634       iFailFlag = 1;
00635     }
00636   }
00637 
00638   iRval = write(ser, pBuf, cbSize);
00639 
00640 #if defined(STAND_ALONE) && defined(DEBUG_CODE)
00641   fprintf(stderr, "\r\nWriteXmodemBlock - returns %d\n", iRval);
00642   fflush(stderr);
00643 
00644   if(iRval > 0)
00645   {
00646     debug_dump_buffer(1, pBuf, cbSize);
00647   }
00648 #endif // STAND_ALONE, DEBUG_CODE
00649 #endif
00650 
00651   return iRval;
00652 }
00653 
00666 void XModemFlushInput(SERIAL_TYPE ser)
00667 {
00668 unsigned long ulStart;
00669 #ifdef ARDUINO
00670 
00671   ulStart = millis();
00672 
00673   do
00674   {
00675     if(ser->available())
00676     {
00677       ser->read(); // don't care about the data
00678       ulStart = millis(); // reset time
00679     }
00680     else
00681     {
00682       delay(1);
00683     }
00684 
00685   } while((millis() - ulStart) < 1000);
00686 
00687 #elif defined(WIN32)
00688 
00689 #error no win32 code yet
00690 
00691 #else // POSIX
00692 int i1;
00693 #ifdef DEBUG_CODE
00694 unsigned char buf[16];
00695 int cbBuf;
00696 #else // DEBUG_CODE
00697 unsigned char buf[2];
00698 #endif // DEBUG_CODE  
00699 
00700   if(fcntl(ser, F_SETFL, O_NONBLOCK) == -1)
00701   {
00702     static int iFailFlag = 0;
00703 
00704     if(!iFailFlag)
00705     {
00706       fprintf(stderr, "Warning:  'fcntl(O_NONBLOCK)' failed, errno = %d\n", errno);
00707       iFailFlag = 1;
00708     }
00709   }
00710 
00711   ulStart = MyMillis();
00712 #ifdef DEBUG_CODE
00713   cbBuf = 0;
00714 #endif // DEBUG_CODE  
00715   while((MyMillis() - ulStart) < 1000)
00716   {
00717 #ifdef DEBUG_CODE
00718     i1 = read(ser, &(buf[cbBuf]), 1);
00719 #else // DEBUG_CODE  
00720     i1 = read(ser, buf, 1);
00721 #endif // DEBUG_CODE  
00722     if(i1 == 1)
00723     {
00724 #if defined(STAND_ALONE) && defined(DEBUG_CODE)
00725       cbBuf++;
00726       if(cbBuf >= sizeof(buf))
00727       {
00728         debug_dump_buffer(-1, buf, cbBuf);
00729         cbBuf = 0;
00730       }
00731 #endif // STAND_ALONE, DEBUG_CODE
00732       ulStart = MyMillis();
00733     }
00734     else
00735     {
00736       usleep(1000);
00737     }
00738   }
00739 
00740 #if defined(STAND_ALONE) && defined(DEBUG_CODE)
00741   if(cbBuf > 0)
00742   {
00743     debug_dump_buffer(-1, buf, cbBuf);
00744   }
00745 #endif // STAND_ALONE, DEBUG_CODE
00746   
00747 #endif // ARDUINO
00748 }
00749 
00758 void XmodemTerminate(XMODEM *pX)
00759 {
00760   XModemFlushInput(pX->ser);
00761 
00762   // TODO:  close files?
00763 }
00764 
00765 
00775 short ValidateSEQ(XMODEM_BUF *pX, unsigned char bSeq)
00776 {
00777   return pX->aSEQ != 255 - pX->aNotSEQ || // ~(pX->aNotSEQ) ||
00778          pX->aSEQ != bSeq; // returns TRUE if not valid
00779 }
00780 
00790 short ValidateSEQC(XMODEMC_BUF *pX, unsigned char bSeq)
00791 {
00792   return pX->aSEQ != 255 - pX->aNotSEQ || // ~(pX->aNotSEQ) ||
00793          pX->aSEQ != bSeq; // returns TRUE if not valid
00794 }
00795 
00811 int ReceiveXmodem(XMODEM *pX)
00812 {
00813 int ecount, ec2;
00814 long etotal, filesize, block;
00815 unsigned char cY; // the char to send in response to a packet
00816 // NOTE:  to allow debugging the CAUSE of an xmodem block's failure, i1, i2, and i3
00817 //        are assigned to function return values and reported in error messages.
00818 #ifdef DEBUG_CODE
00819 short i1, i2, i3;
00820 #define DEBUG_I1 i1 =
00821 #define DEBUG_I2 i2 =
00822 #define DEBUG_I3 i3 =
00823 #else // DEBUG_CODE
00824 #define DEBUG_I1 /*normally does nothing*/
00825 #define DEBUG_I2 /*normally does nothing*/
00826 #define DEBUG_I3 /*normally does nothing*/
00827 #endif // DEBUG_CODE
00828 
00829   ecount = 0;
00830   etotal = 0;
00831   filesize = 0;
00832   block = 1;
00833 
00834   // ** already got the first 'SOH' character on entry to this function **
00835 
00836   //   Form2.Show 0      '** modeless show of form2 (CANSEND) **
00837   //   Form2!Label1.FloodType = 0
00838   //   Form2.Caption = "* XMODEM(Checksum) BINARY RECEIVE *"
00839   //   Form2!Label1.Caption = "Errors: 0  Bytes: 0"
00840 
00841   pX->buf.xbuf.cSOH = (char)1; // assumed already got this, put into buffer
00842 
00843   do
00844   {
00845     if(!pX->bCRC &&
00846        ((DEBUG_I1 GetXmodemBlock(pX->ser, ((char *)&(pX->buf.xbuf)) + 1, sizeof(pX->buf.xbuf) - 1))
00847         != sizeof(pX->buf.xbuf) - 1 ||
00848         (DEBUG_I2 ValidateSEQ(&(pX->buf.xbuf), block & 255)) ||
00849         (DEBUG_I3 CalcCheckSum(pX->buf.xbuf.aDataBuf, sizeof(pX->buf.xbuf.aDataBuf)) != pX->buf.xbuf.bCheckSum)))
00850     {
00851       // did not receive properly
00852       // TODO:  deal with repeated packet, sequence number for previous packet
00853 
00854 #ifdef DEBUG_CODE
00855       sprintf(szERR,"A%ld,%d,%d,%d,%d,%d",block,i1,i2,i3,pX->buf.xbuf.aSEQ, pX->buf.xbuf.aNotSEQ);
00856 //#ifdef STAND_ALONE
00857 //      fprintf(stderr, "TEMPORARY (csum):  seq=%x, ~seq=%x  i1=%d, i2=%d, i3=%d\n", pX->buf.xbuf.aSEQ, pX->buf.xbuf.aNotSEQ, i1, i2, i3);
00858 //#endif // STAND_ALONE
00859 #endif // DEBUG_CODE
00860 
00861       XModemFlushInput(pX->ser);  // necessary to avoid problems
00862 
00863       cY = _NAK_; // send NAK (to get the checksum version)
00864       ecount ++; // for this packet
00865       etotal ++;
00866     }
00867     else if(pX->bCRC &&
00868             ((DEBUG_I1 GetXmodemBlock(pX->ser, ((char *)&(pX->buf.xcbuf)) + 1, sizeof(pX->buf.xcbuf) - 1))
00869              != sizeof(pX->buf.xcbuf) - 1 ||
00870             (DEBUG_I2 ValidateSEQC(&(pX->buf.xcbuf), block & 255)) ||
00871             (DEBUG_I3 CalcCRC(pX->buf.xcbuf.aDataBuf, sizeof(pX->buf.xbuf.aDataBuf)) != pX->buf.xcbuf.wCRC)))
00872     {
00873       // did not receive properly
00874       // TODO:  deal with repeated packet, sequence number for previous packet
00875 
00876 #ifdef DEBUG_CODE
00877       sprintf(szERR,"B%ld,%d,%d,%d,%d,%d",block,i1,i2,i3,pX->buf.xcbuf.aSEQ, pX->buf.xcbuf.aNotSEQ);
00878 //#ifdef STAND_ALONE
00879 //      fprintf(stderr, "TEMPORARY (CRC):  seq=%x, ~seq=%x  i1=%d, i2=%d, i3=%d\n", pX->buf.xcbuf.aSEQ, pX->buf.xcbuf.aNotSEQ, i1, i2, i3);
00880 //#endif // STAND_ALONE
00881 #endif // DEBUG_CODE
00882 
00883       XModemFlushInput(pX->ser);  // necessary to avoid problems
00884 
00885       if(block > 1)
00886       {
00887         cY = _NAK_; // TODO do I need this?
00888       }
00889       else
00890       {
00891         cY = 'C'; // send 'CRC' NAK (the character 'C') (to get the CRC version)
00892       }
00893       ecount ++; // for this packet
00894       etotal ++;
00895     }
00896     else
00897     {
00898 #ifdef ARDUINO
00899       if(pX->file.write((const uint8_t *)&(pX->buf.xbuf.aDataBuf), sizeof(pX->buf.xbuf.aDataBuf)) != sizeof(pX->buf.xbuf.aDataBuf))
00900       {
00901         return -2; // write error on output file
00902       }
00903 #else // ARDUINO
00904       if(write(pX->file, &(pX->buf.xbuf.aDataBuf), sizeof(pX->buf.xbuf.aDataBuf)) != sizeof(pX->buf.xbuf.aDataBuf))
00905       {
00906         XmodemTerminate(pX);
00907         return -2; // write error on output file
00908       }
00909 #endif // ARDUINO
00910       cY = _ACK_; // send ACK
00911       block ++;
00912       filesize += sizeof(pX->buf.xbuf.aDataBuf); // TODO:  need method to avoid extra crap at end of file
00913       ecount = 0; // zero out error count for next packet
00914     }
00915 
00916 #ifdef STAND_ALONE
00917     fprintf(stderr, "block %ld  %ld bytes  %d errors\r\n", block, filesize, ecount);
00918 #endif // STAND_ALONE
00919 
00920     ec2 = 0;   //  ** error count #2 **
00921 
00922     while(ecount < TOTAL_ERROR_COUNT && ec2 < ACK_ERROR_COUNT) // ** loop to get SOH or EOT character **
00923     {
00924       WriteXmodemChar(pX->ser, cY); // ** output appropriate command char **
00925      
00926       if(GetXmodemBlock(pX->ser, &(pX->buf.xbuf.cSOH), 1) == 1)
00927       {
00928         if(pX->buf.xbuf.cSOH == _CAN_) // ** CTRL-X 'CAN' - terminate
00929         {
00930           XmodemTerminate(pX);
00931           return 1; // terminated
00932         }
00933         else if(pX->buf.xbuf.cSOH == _EOT_) // ** EOT - end
00934         {
00935           WriteXmodemChar(pX->ser, _ACK_); // ** send an ACK (most XMODEM protocols expect THIS)
00936 //          WriteXmodemChar(pX->ser, _ENQ_); // ** send an ENQ
00937 
00938           return 0; // I am done
00939         }
00940         else if(pX->buf.xbuf.cSOH == _SOH_) // ** SOH - sending next packet
00941         {
00942           break; // leave this loop
00943         }
00944         else
00945         {
00946           // TODO:  deal with repeated packet, i.e. previous sequence number
00947 
00948           XModemFlushInput(pX->ser);  // necessary to avoid problems (since the character was unexpected)
00949           // if I was asking for the next block, and got an unexpected character, do a NAK; otherwise,
00950           // just repeat what I did last time
00951 
00952           if(cY == _ACK_) // ACK
00953           {
00954             cY = _NAK_; // NACK
00955           }
00956 
00957           ec2++;
00958         }
00959       }
00960       else
00961       {
00962         ecount++; // increase total error count, and try writing the 'ACK' or 'NACK' again
00963       }
00964     }
00965 
00966     if(ec2 >= ACK_ERROR_COUNT) // wasn't able to get a packet
00967     {
00968       break;
00969     }
00970 
00971   } while(ecount < TOTAL_ERROR_COUNT);
00972 
00973   XmodemTerminate(pX);
00974   return 1; // terminated
00975 }
00976 
00977 
00994 int SendXmodem(XMODEM *pX)
00995 {
00996 int ecount, ec2;
00997 short i1;
00998 long etotal, filesize, filepos, block;
00999 
01000 
01001   ecount = 0;
01002   etotal = 0;
01003   filesize = 0;
01004   filepos = 0;
01005   block = 1;
01006 
01007   pX->bCRC = 0; // MUST ASSIGN TO ZERO FIRST or XMODEM-CHECKSUM may not work properly
01008 
01009   // ** already got first 'NAK' character on entry as pX->buf.xbuf.cSOH  **
01010 
01011 #ifdef ARDUINO
01012 
01013   filesize = pX->file.size();
01014 
01015 #else // ARDUINO
01016 
01017   filesize = (long)lseek(pX->file, 0, SEEK_END);
01018   if(filesize < 0) // not allowed
01019   {
01020 #ifdef STAND_ALONE
01021     fputs("SendXmodem fail (file size)\n", stderr);
01022 #endif // STAND_ALONE
01023     return -1;
01024   }
01025 
01026   lseek(pX->file, 0, SEEK_SET); // position at beginning
01027 
01028 #endif // ARDUINO
01029 
01030   do
01031   {
01032     // ** depending on type of transfer, place the packet
01033     // ** into pX->buf with all fields appropriately filled.
01034 
01035     if(filepos >= filesize) // end of transfer
01036     {
01037       for(i1=0; i1 < 8; i1++)
01038       {
01039         WriteXmodemChar(pX->ser, _EOT_); // ** send an EOT marking end of transfer
01040 
01041         if(GetXmodemBlock(pX->ser, &(pX->buf.xbuf.cSOH), 1) != 1) // this takes up to 5 seconds
01042         {
01043           // nothing returned - try again?
01044           // break; // for now I loop, uncomment to bail out
01045         }
01046         else if(pX->buf.xbuf.cSOH == _ENQ_    // an 'ENQ' (apparently some expect this)
01047                 || pX->buf.xbuf.cSOH == _ACK_ // an 'ACK' (most XMODEM implementations expect this)
01048                 || pX->buf.xbuf.cSOH == _CAN_) // CTRL-X = TERMINATE
01049         {
01050           // both normal and 'abnormal' termination.
01051           break;
01052         }
01053       }
01054 
01055       XmodemTerminate(pX);
01056 
01057 #ifdef STAND_ALONE
01058       fprintf(stderr, "SendXmodem return %d\n", i1 >= 8 ? 1 : 0);
01059 #endif // STAND_ALONE
01060       return i1 >= 8 ? 1 : 0; // return 1 if receiver choked on the 'EOT' marker, else 0 for 'success'
01061     }
01062 
01063 //  TODO:  progress indicator [can be LCD for arduino, blinky lights, ???  and of course stderr for everyone else]
01064 //  If filesize& <> 0 Then Form2!Label1.FloodPercent = 100 * filepos& / filesize&
01065 
01066 #ifdef STAND_ALONE
01067     fprintf(stderr, "block %ld  %ld of %ld bytes  %d errors\r\n", block, filepos, filesize, ecount);
01068 #endif // STAND_ALONE
01069 
01070     if(pX->buf.xbuf.cSOH != 'C' // XMODEM CRC
01071        && pX->buf.xbuf.cSOH != (char)_NAK_) // NAK
01072     {
01073       // increase error count, bail if it's too much
01074 
01075       ec2++;
01076     }
01077 
01078 
01079 #ifdef ARDUINO
01080     pX->file.seek(filepos); // in case I'm doing a 'retry' and I have to re-read part of the file
01081 #else  // ARDUINO
01082     lseek(pX->file, filepos, SEEK_SET); // same reason as above
01083 #endif // ARDUINO
01084 
01085     // fortunately, xbuf and xcbuf are the same through the end of 'aDataBuf' so
01086     // I can read the file NOW using 'xbuf' for both CRC and CHECKSUM versions
01087 
01088     if((filesize - filepos) >= sizeof(pX->buf.xbuf.aDataBuf))
01089     {
01090 #ifdef ARDUINO
01091       i1 = pX->file.read(pX->buf.xbuf.aDataBuf, sizeof(pX->buf.xcbuf.aDataBuf));
01092 #else  // ARDUINO
01093       i1 = read(pX->file, pX->buf.xbuf.aDataBuf, sizeof(pX->buf.xcbuf.aDataBuf));
01094 #endif // ARDUINO
01095 
01096       if(i1 != sizeof(pX->buf.xcbuf.aDataBuf))
01097       {
01098         // TODO:  read error - send a ctrl+x ?
01099       }
01100     }
01101     else
01102     {
01103       memset(pX->buf.xcbuf.aDataBuf, '\x1a', sizeof(pX->buf.xcbuf.aDataBuf)); // fill with ctrl+z which is what the spec says
01104 #ifdef ARDUINO
01105       i1 = pX->file.read(pX->buf.xbuf.aDataBuf, filesize - filepos);
01106 #else  // ARDUINO
01107       i1 = read(pX->file, pX->buf.xbuf.aDataBuf, filesize - filepos);
01108 #endif // ARDUINO
01109 
01110       if(i1 != (filesize - filepos))
01111       {
01112         // TODO:  read error - send a ctrl+x ?
01113       }
01114     }
01115 
01116     if(pX->buf.xbuf.cSOH == 'C' ||  // XMODEM CRC 'NAK' (first time only, typically)
01117        ((pX->buf.xbuf.cSOH == _ACK_ || pX->buf.xbuf.cSOH == _NAK_) && pX->bCRC)) // identifies ACK/NACK with XMODEM CRC
01118     {
01119       pX->bCRC = 1; // make sure (only matters the first time, really)
01120 
01121       // calculate the CRC, assign to the packet, and then send it
01122 
01123       pX->buf.xcbuf.cSOH = 1; // must send SOH as 1st char
01124       pX->buf.xcbuf.wCRC = CalcCRC(pX->buf.xcbuf.aDataBuf, sizeof(pX->buf.xcbuf.aDataBuf));
01125 
01126       GenerateSEQC(&(pX->buf.xcbuf), block);
01127 
01128       // send it
01129 
01130       i1 = WriteXmodemBlock(pX->ser, &(pX->buf.xcbuf), sizeof(pX->buf.xcbuf));
01131       if(i1 != sizeof(pX->buf.xcbuf)) // write error
01132       {
01133         // TODO:  handle write error (send ctrl+X ?)
01134       }
01135     }
01136     else if(pX->buf.xbuf.cSOH == _NAK_ || // 'NAK' (checksum method, may also be with CRC method)
01137             (pX->buf.xbuf.cSOH == _ACK_ && !pX->bCRC)) // identifies ACK with XMODEM CHECKSUM
01138     {
01139       pX->bCRC = 0; // make sure (this ALSO allows me to switch modes on error)
01140 
01141       // calculate the CHECKSUM, assign to the packet, and then send it
01142 
01143       pX->buf.xbuf.cSOH = 1; // must send SOH as 1st char
01144       pX->buf.xbuf.bCheckSum = CalcCheckSum(pX->buf.xbuf.aDataBuf, sizeof(pX->buf.xbuf.aDataBuf));
01145 
01146       GenerateSEQ(&(pX->buf.xbuf), block);
01147 
01148       // send it
01149 
01150       i1 = WriteXmodemBlock(pX->ser, &(pX->buf.xbuf), sizeof(pX->buf.xbuf));
01151       if(i1 != sizeof(pX->buf.xbuf)) // write error
01152       {
01153         // TODO:  handle write error (send ctrl+X ?)
01154       }
01155     }
01156 
01157     ec2 = 0;
01158 
01159     while(ecount < TOTAL_ERROR_COUNT && ec2 < ACK_ERROR_COUNT) // loop to get ACK or NACK
01160     {
01161       if(GetXmodemBlock(pX->ser, &(pX->buf.xbuf.cSOH), 1) == 1)
01162       {
01163         if(pX->buf.xbuf.cSOH == _CAN_) // ** CTRL-X - terminate
01164         {
01165           XmodemTerminate(pX);
01166 
01167           return 1; // terminated
01168         }
01169         else if(pX->buf.xbuf.cSOH == _NAK_ || // ** NACK
01170                 pX->buf.xbuf.cSOH == 'C') // ** CRC NACK
01171         {
01172           break;  // exit inner loop and re-send packet
01173         }
01174         else if(pX->buf.xbuf.cSOH == _ACK_) // ** ACK - sending next packet
01175         {
01176           filepos += sizeof(pX->buf.xbuf.aDataBuf);
01177           block++; // increment file position and block count
01178 
01179           break; // leave inner loop, send NEXT packet
01180         }
01181         else
01182         {
01183           XModemFlushInput(pX->ser);  // for now, do this here too
01184           ec2++;
01185         }
01186       }
01187       else
01188       {
01189         ecount++; // increase total error count, then loop back and re-send packet
01190         break;
01191       }
01192     }
01193 
01194     if(ec2 >= ACK_ERROR_COUNT)
01195     {
01196       break;  // that's it, I'm done with this
01197     }
01198 
01199   } while(ecount < TOTAL_ERROR_COUNT * 2); // twice error count allowed for sending
01200     
01201 // TODO: progress indicator
01202 //   If filesize& <> 0 And filepos& <= filesize& Then
01203 //      Form2!Label1.FloodPercent = 100 * filepos& / filesize&
01204 //   Else
01205 //      Form2!Label1.FloodPercent = 100
01206 //   End If
01207 
01208 
01209    // ** at this point it is important to indicate the errors
01210    // ** and flush all buffers, and terminate process!
01211 
01212   XmodemTerminate(pX);
01213 #ifdef STAND_ALONE
01214   fputs("SendXmodem fail (total error count)\n", stderr);
01215 #endif // STAND_ALONE
01216   return -2; // exit on error
01217 }
01218 
01219 
01232 int XReceiveSub(XMODEM *pX)
01233 {
01234 int i1;
01235 
01236   // start with CRC mode [try 8 times to get CRC]
01237 
01238   pX->bCRC = 1;
01239 
01240   for(i1=0; i1 < 8; i1++)
01241   {
01242     WriteXmodemChar(pX->ser, 'C'); // start with NAK for XMODEM CRC
01243      
01244     if(GetXmodemBlock(pX->ser, &(pX->buf.xbuf.cSOH), 1) == 1)
01245     {
01246       if(pX->buf.xbuf.cSOH == _SOH_) // SOH - packet is on its way
01247       {
01248         return ReceiveXmodem(pX);
01249       }
01250       else if(pX->buf.xbuf.cSOH == _EOT_) // an EOT [blank file?  allow this?]
01251       {
01252         return 0; // for now, do this
01253       }
01254       else if(pX->buf.xbuf.cSOH == _CAN_) // cancel
01255       {
01256         return 1; // canceled
01257       }
01258     }
01259   }    
01260 
01261   pX->bCRC = 0;
01262 
01263   // try again, this time using XMODEM CHECKSUM
01264   for(i1=0; i1 < 8; i1++)
01265   {
01266     WriteXmodemChar(pX->ser, _NAK_); // switch to NAK for XMODEM Checksum
01267      
01268     if(GetXmodemBlock(pX->ser, &(pX->buf.xbuf.cSOH), 1) == 1)
01269     {
01270       if(pX->buf.xbuf.cSOH == _SOH_) // SOH - packet is on its way
01271       {
01272         return ReceiveXmodem(pX);
01273       }
01274       else if(pX->buf.xbuf.cSOH == _EOT_) // an EOT [blank file?  allow this?]
01275       {
01276         return 0; // for now, do this
01277       }
01278       else if(pX->buf.xbuf.cSOH == _CAN_) // cancel
01279       {
01280         return 1; // canceled
01281       }
01282     }
01283   }    
01284   
01285 
01286   XmodemTerminate(pX);
01287 
01288   return -3; // fail
01289 }
01290 
01291 
01304 int XSendSub(XMODEM *pX)
01305 {
01306 unsigned long ulStart;
01307 
01308   // waiting up to 30 seconds for transfer to start.  this is part of the spec?
01309 
01310 
01311 #ifdef ARDUINO
01312   ulStart = millis();
01313 #else // ARDUINO
01314   ulStart = MyMillis();
01315 #endif // ARDUINO
01316 
01317   do
01318   {
01319     if(GetXmodemBlock(pX->ser, &(pX->buf.xbuf.cSOH), 1) == 1)
01320     {
01321       if(pX->buf.xbuf.cSOH == 'C' || // XMODEM CRC
01322          pX->buf.xbuf.cSOH == _NAK_) // NAK - XMODEM CHECKSUM
01323       {
01324 #ifdef STAND_ALONE
01325         fprintf(stderr, "Got %d, continuing\n", pX->buf.xbuf.cSOH);
01326 #endif // STAND_ALONE
01327         return SendXmodem(pX);
01328       }
01329       else if(pX->buf.xbuf.cSOH == _CAN_) // cancel
01330       {
01331 #ifdef STAND_ALONE
01332         fputs("XSendSub fail (cancel)\n", stderr);
01333 #endif // STAND_ALONE
01334         return 1; // canceled
01335       }
01336     }
01337   }    
01338 #ifdef ARDUINO
01339   while((short)(millis() - ulStart) < 30000);   // 30 seconds
01340 #else // ARDUINO
01341   while((int)(MyMillis() - ulStart) < 30000);
01342 #endif // ARDUINO
01343 
01344   XmodemTerminate(pX);
01345 
01346 #ifdef STAND_ALONE
01347   fputs("XSendSub fail (timeout)\n", stderr);
01348 #endif // STAND_ALONE
01349   return -3; // fail
01350 }
01351 
01352 //typedef struct _XMODEM_
01353 //{
01354 //  SERIAL_TYPE ser;
01355 //  FILE_TYPE file;
01356 //
01357 //  union
01358 //  {
01359 //    XMODEM_BUF xbuf;
01360 //    XMODEMC_BUF xcbuf;
01361 //  } buf; // 133 bytes
01362 //
01363 //  unsigned char bCRC; // non-zero for CRC, zero for checksum
01364 //
01365 //}  __attribute__((__packed__)) XMODEM;
01366 
01367 #ifdef ARDUINO
01368 
01369 short XReceive(SDClass *pSD, HardwareSerial *pSer, const char *szFilename)
01370 {
01371 short iRval;
01372 XMODEM xx;
01373 
01374   memset(&xx, 0, sizeof(xx));
01375 
01376   xx.ser = pSer;
01377 
01378   if(pSD->exists((char *)szFilename))
01379   {
01380     pSD->remove((char *)szFilename);
01381   }
01382 
01383   xx.file = pSD->open((char *)szFilename, FILE_WRITE);
01384   if(!xx.file)
01385   {
01386     return -9; // can't create file
01387   }
01388 
01389   iRval = XReceiveSub(&xx);  
01390 
01391   xx.file.close();
01392 
01393   if(iRval)
01394   {
01395     WriteXmodemChar(pSer, _CAN_); // cancel (make sure)
01396 
01397     pSD->remove((char *)szFilename); // delete file on error
01398   }
01399 
01400   return iRval;
01401 }
01402 
01403 int XSend(SDClass *pSD, HardwareSerial *pSer, const char *szFilename)
01404 {
01405 short iRval;
01406 XMODEM xx;
01407 
01408   memset(&xx, 0, sizeof(xx));
01409 
01410   xx.ser = pSer;
01411 
01412   xx.file = pSD->open(szFilename, FILE_READ);
01413   if(!xx.file)
01414   {
01415     return -9; // can't open file
01416   }
01417 
01418   iRval = XSendSub(&xx);  
01419 
01420   xx.file.close();
01421 
01422   return iRval;
01423 }
01424 
01425 #else // ARDUINO
01426 
01427 int XReceive(SERIAL_TYPE hSer, const char *szFilename, int nMode)
01428 {
01429 int iRval;
01430 XMODEM xx;
01431 #ifndef ARDUINO
01432 int iFlags;
01433 #endif // !ARDUINO
01434 
01435 #ifdef DEBUG_CODE
01436   szERR[0]=0;
01437 #endif // DEBUG_CODE
01438   memset(&xx, 0, sizeof(xx));
01439 
01440   xx.ser = hSer;
01441 
01442   unlink(szFilename); // make sure it does not exist, first
01443   xx.file = open(szFilename, O_CREAT | O_TRUNC | O_WRONLY, nMode);
01444 
01445   if(!xx.file)
01446   {
01447 #ifdef STAND_ALONE
01448     fprintf(stderr, "XReceive fail \"%s\"  errno=%d\n", szFilename, errno);
01449 #endif // STAND_ALONE
01450     return -9; // can't create file
01451   }
01452 
01453 #ifndef ARDUINO
01454   iFlags = fcntl(hSer, F_GETFL);
01455 #endif // !ARDUINO
01456 
01457   iRval = XReceiveSub(&xx);  
01458 
01459 #ifndef ARDUINO
01460   if(iFlags == -1 || fcntl(hSer, F_SETFL, iFlags) == -1)
01461   {
01462     fprintf(stderr, "Warning:  'fcntl' call to restore flags failed, errno=%d\n", errno);
01463   }
01464 #endif // !ARDUINO
01465 
01466   close(xx.file);
01467 
01468   if(iRval)
01469   {
01470     unlink(szFilename); // delete file on error
01471   }
01472 
01473 #ifdef STAND_ALONE
01474   fprintf(stderr, "XReceive returns %d\n", iRval);
01475 #endif // STAND_ALONE
01476   return iRval;
01477 }
01478 
01479 int XSend(SERIAL_TYPE hSer, const char *szFilename)
01480 {
01481 int iRval;
01482 XMODEM xx;
01483 #ifndef ARDUINO
01484 int iFlags;
01485 #endif // !ARDUINO
01486 
01487 #ifdef DEBUG_CODE
01488   szERR[0]=0;
01489 #endif // DEBUG_CODE
01490   memset(&xx, 0, sizeof(xx));
01491 
01492   xx.ser = hSer;
01493 
01494   xx.file = open(szFilename, O_RDONLY, 0);
01495 
01496   if(!xx.file)
01497   {
01498 #ifdef STAND_ALONE
01499     fprintf(stderr, "XSend fail \"%s\"  errno=%d\n", szFilename, errno);
01500 #endif // STAND_ALONE
01501     return -9; // can't open file
01502   }
01503 
01504 #ifndef ARDUINO
01505   iFlags = fcntl(hSer, F_GETFL);
01506 #endif // !ARDUINO
01507 
01508   iRval = XSendSub(&xx);  
01509 
01510   if(iFlags == -1 || fcntl(hSer, F_SETFL, iFlags) == -1)
01511   {
01512     fprintf(stderr, "Warning:  'fcntl' call to restore flags failed, errno=%d\n", errno);
01513   }
01514 
01515   close(xx.file);
01516 
01517 #ifdef STAND_ALONE
01518   fprintf(stderr, "XSend returning %d\n", iRval);
01519 #endif // STAND_ALONE
01520   return iRval;
01521 }
01522 
01523 #endif // ARDUINO
01524 
01525 
01526 
01527 #ifdef STAND_ALONE
01528 
01529 static const char szSER[]="/dev/ttyU0";
01530 
01531 #include <termios.h>
01532 
01547 void ttyconfig(int iFile, int iBaud, int iParity, int iBits, int iStop)
01548 {
01549 int i1;
01550 struct termios sIOS;
01551 
01552   i1 = fcntl(iFile, F_GETFL);
01553 
01554   i1 |= O_NONBLOCK; // i1 &= ~O_NONBLOCK); // turn OFF non-blocking?
01555 
01556   fcntl(iFile, F_SETFL, i1);
01557 
01558   if(!tcgetattr(iFile, &sIOS))
01559   {
01560     cfsetspeed(&sIOS, iBaud);
01561     sIOS.c_cflag &= ~(CSIZE|PARENB|CS5|CS6|CS7|CS8);
01562     sIOS.c_cflag |= iBits == 5 ? CS5 : iBits == 6 ? CS6 : iBits == 7 ? CS7 : CS8; // 8 is default
01563     if(iStop == 2)
01564     {
01565       sIOS.c_cflag |= CSTOPB;
01566     }
01567     else
01568     {
01569       sIOS.c_cflag &= ~CSTOPB;
01570     }
01571 
01572     sIOS.c_cflag &= ~CRTSCTS; // hardware flow control _DISABLED_ (so I can do the reset)
01573     sIOS.c_cflag |= CLOCAL; // ignore any modem status lines
01574 
01575     if(!iParity)
01576     {
01577       sIOS.c_cflag &= ~(PARENB | PARODD);
01578     }
01579     else if(iParity > 0) // odd
01580     {
01581       sIOS.c_cflag |= (PARENB | PARODD);
01582     }
01583     else // even (negative)
01584     {
01585       sIOS.c_cflag &= PARODD;
01586       sIOS.c_cflag |= PARENB;
01587     }
01588 
01589 //    sIOS.c_iflag |= IGNCR; // ignore CR
01590 
01591     // do not translate characters or xon/xoff and ignore break
01592     sIOS.c_iflag &= ~(IGNBRK | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY | IMAXBEL | ISTRIP); // turn these off
01593 
01594 #if defined(__FreeBSD__)
01595     sIOS.c_oflag &= ~(OPOST | ONLCR | OCRNL | TABDLY | ONOEOT | ONOCR | ONLRET); // FreeBSD version
01596 #else // Linux? YMMV
01597     sIOS.c_oflag &= ~(OPOST | ONLCR | OCRNL | TABDLY | ONOCR | ONLRET); // turn these off too (see man termios)
01598 #endif // FBSD vs Linux
01599 
01600     // make sure echoing is disabled and control chars aren't translated or omitted
01601 #if defined(__FreeBSD__)
01602     sIOS.c_lflag &= ~(ECHO | ECHOKE | ECHOE | ECHONL | ECHOPRT | ECHOCTL | ICANON | IEXTEN | ISIG | ALTWERASE);
01603 #else // Linux? YMMV
01604     sIOS.c_lflag &= ~(ECHO | ECHOKE | ECHOE | ECHONL | ECHOPRT | ECHOCTL | ICANON | IEXTEN | ISIG);
01605 #endif // FBSD vs Linux
01606     sIOS.c_cc[VMIN] = 0;  // ensures no 'grouping' of input
01607     sIOS.c_cc[VTIME] = 0; // immediate return
01608 
01609     if(tcsetattr(iFile, TCSANOW, &sIOS))
01610     {
01611       fprintf(stderr, "error %d setting attributes\n", errno);
01612     }
01613   }
01614   else
01615   {
01616     fprintf(stderr, "error %d getting attributes\n", errno);
01617   }
01618 }
01619 
01630 void reset_arduino(int iFile)
01631 {
01632 unsigned int sFlags;
01633 unsigned long ulStart;
01634 int i1;
01635 
01636 // toggle the RTS and DTR high, low, then high - so much easier via POSIX-compatible OS!
01637 
01638   ioctl(iFile, TIOCMGET, &sFlags);
01639 
01640   sFlags &= ~(TIOCM_DTR | TIOCM_RTS); // the high to low transition discharges the capacitor (signal is inverted on board)
01641   if(ioctl(iFile, TIOCMSET, &sFlags) < 0)
01642   {
01643     fprintf(stderr, "WARNING:  ioctl() returns < 0, errno=%d (%xH)\n", errno, errno);
01644   }
01645 
01646   usleep(250000); // avrdude does this for 50 msecs, my change has it at 50msecs
01647 
01648   sFlags |= TIOCM_DTR | TIOCM_RTS; // leave it in THIS state when I'm done
01649   if(ioctl(iFile, TIOCMSET, &sFlags) < 0)
01650   {
01651     fprintf(stderr, "WARNING:  ioctl() returns < 0, errno=%d (%xH)\n", errno, errno);
01652   }
01653 
01654   usleep(50000); // avrdude does this for 50 msecs (no change)
01655 
01656   ulStart = MyMillis();
01657 
01658   // flush whatever is there, (5 seconds)
01659 
01660   while((MyMillis() - ulStart) < 5000)
01661   {
01662     i1 = read(iFile, &i1, 1);
01663     if(i1 == 1)
01664     {
01665       ulStart = MyMillis();
01666     }
01667     else
01668     {
01669       usleep(1000);
01670     }
01671   }
01672 
01673 }
01674 
01675 int main(int argc, char *argv[])
01676 {
01677 int hSer;
01678 char tbuf[256];
01679 int i1, iSR = 0;
01680 
01681 
01682   if(argc < 3)
01683   {
01684     fputs("Usage:  [prog] [S|R] filename\n", stderr);
01685     return 1;
01686   }
01687 
01688   if(argv[1][0] == 'R' || argv[1][1]=='r')
01689   {
01690     iSR = -1;
01691   }
01692   else if(argv[1][0] == 'S' || argv[1][1]=='s')
01693   {
01694     iSR = 1;
01695   }
01696   else if(argv[1][0] == 'X' || argv[1][1]=='x')
01697   {
01698     iSR = 0; // test function
01699   }
01700   else
01701   {
01702     fputs("Usage:  [prog] [S|R] filename     (b)\n", stderr);
01703     return 1;
01704   }
01705 
01706   hSer = open(szSER, (O_RDWR | O_NONBLOCK), 0);
01707   if(hSer == -1)
01708   {
01709     fprintf(stderr, "Unable to open \"%s\" errno=%d\n", szSER, errno);
01710     return 3;
01711   }
01712 
01713   fputs("TTYCONFIG\n", stderr);
01714   ttyconfig(hSer, 9600, 0, 8, 1);
01715 
01716   reset_arduino(hSer);
01717 
01718   fprintf(stderr, "Sleeping for 10 seconds to allow reset\n");
01719 
01720 //  usleep(10000000);
01721   for(i1=0; i1 < 10; i1++)
01722   {
01723     XModemFlushInput(hSer);  
01724   }
01725 
01726   for(i1=0; i1 < 3; i1++)
01727   {
01728     sprintf(tbuf, "X%c%s", argv[1][0], argv[2]);
01729 
01730     fprintf(stderr, "writing: \"%s\"\n", tbuf);
01731     strcat(tbuf, "\r");
01732     WriteXmodemBlock(hSer, tbuf, strlen(tbuf));
01733 
01734     fputs("flush input\n", stderr);
01735     XModemFlushInput(hSer);  
01736 
01737     // wait for an LF response
01738 
01739     if(iSR > 0)
01740     {
01741       fputs("XSEND\n", stderr);
01742       if(XSend(hSer, argv[2]))
01743       {
01744         fputs("ERROR\n", stderr);
01745       }
01746       else
01747       {
01748         fputs("SUCCESS!\n", stderr);
01749         i1 = 0;
01750         break;
01751       }
01752     }
01753     else if(iSR < 0)
01754     {
01755       fputs("XRECEIVE\n", stderr);
01756       if(XReceive(hSer, argv[2], 0664))
01757       {
01758         fputs("ERROR\n", stderr);
01759       }
01760       else
01761       {
01762         fputs("SUCCESS!\n", stderr);
01763         i1 = 0;
01764         break;
01765       }
01766     }
01767     else
01768     {
01769       // test function
01770       XModemFlushInput(hSer); // continue doing this
01771       break; // done (once only)
01772     }
01773   }
01774 
01775   fputs("EXIT\n", stderr);
01776   close(hSer);
01777 
01778   return i1 ? 1 : -1;
01779 }
01780 #endif // STAND_ALONE
01781