xmodem.c

Go to the documentation of this file.
00001 
00048 #include "xmodem.h"
00049 
00050 #include "cfg/cfg_xmodem.h"
00051 #include <cfg/debug.h>
00052 
00053 #include <algo/crc.h>
00054 
00055 #include <string.h> /* for memset() */
00056 
00061 #define XM_SOH  0x01  
00062 #define XM_STX  0x02  
00063 #define XM_EOT  0x04  
00064 #define XM_ACK  0x06  
00065 #define XM_NAK  0x15  
00066 #define XM_C    0x43  
00067 #define XM_CAN  0x18  
00068 /*\}*/
00069 
00070 #if CONFIG_XMODEM_1KCRC == 1
00071     #define XM_BUFSIZE       1024  
00072 #else
00073     #define XM_BUFSIZE       128   
00074 #endif
00075 
00076 
00077 #if CONFIG_XMODEM_RECV
00078 
00086 bool xmodem_recv(KFile *ch, KFile *fd)
00087 {
00088     char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
00089     int c, i, blocksize;
00090     int blocknr = 0, last_block_done = 0, retries = 0;
00091     char *buf;
00092     uint8_t checksum;
00093     uint16_t crc;
00094     bool purge = false;
00095     bool usecrc = true;
00096 
00097 
00098     XMODEM_PROGRESS("Starting Transfer...\n");
00099     purge = true;
00100     kfile_clearerr(ch);
00101 
00102     /* Send initial NAK to start transmission */
00103     for(;;)
00104     {
00105         if (XMODEM_CHECK_ABORT)
00106         {
00107             kfile_putc(XM_CAN, ch);
00108             kfile_putc(XM_CAN, ch);
00109             XMODEM_PROGRESS("Transfer aborted\n");
00110             return false;
00111         }
00112 
00113         /*
00114          * Discard incoming input until a timeout occurs, then send
00115          * a NAK to the transmitter.
00116          */
00117         if (purge)
00118         {
00119             purge = false;
00120 
00121             if (kfile_error(ch))
00122                 XMODEM_PROGRESS("Retries %d\n", retries);
00123 
00124             kfile_resync(ch, 200);
00125             retries++;
00126 
00127             if (retries >= CONFIG_XMODEM_MAXRETRIES)
00128             {
00129                 kfile_putc(XM_CAN, ch);
00130                 kfile_putc(XM_CAN, ch);
00131                 XMODEM_PROGRESS("Transfer aborted\n");
00132                 return false;
00133             }
00134 
00135             /* Transmission start? */
00136             if (blocknr == 0)
00137             {
00138                 if (retries < CONFIG_XMODEM_MAXCRCRETRIES)
00139                 {
00140                     XMODEM_PROGRESS("Request Tx (CRC)\n");
00141                     kfile_putc(XM_C, ch);
00142                 }
00143                 else
00144                 {
00145                     /* Give up with CRC and fall back to checksum */
00146                     usecrc = false;
00147                     XMODEM_PROGRESS("Request Tx (BCC)\n");
00148                     kfile_putc(XM_NAK, ch);
00149                 }
00150             }
00151             else
00152                 kfile_putc(XM_NAK, ch);
00153         }
00154 
00155         switch (kfile_getc(ch))
00156         {
00157         #if XM_BUFSIZE >= 1024
00158         case XM_STX:  /* Start of header (1024-byte block) */
00159             blocksize = 1024;
00160             goto getblock;
00161         #endif
00162 
00163         case XM_SOH:  /* Start of header (128-byte block) */
00164             blocksize = 128;
00165             /* Needed to avoid warning if XM_BUFSIZE < 1024 */
00166 
00167         getblock:
00168             /* Get block number */
00169             c = kfile_getc(ch);
00170 
00171             /* Check complemented block number */
00172             if ((~c & 0xff) != kfile_getc(ch))
00173             {
00174                 XMODEM_PROGRESS("Bad blk (%d)\n", c);
00175                 purge = true;
00176                 break;
00177             }
00178 
00179             /* Determine which block is being sent */
00180             if (c == (blocknr & 0xff))
00181                 /* Last block repeated */
00182                 XMODEM_PROGRESS("Repeat blk %d\n", blocknr);
00183             else if (c == ((blocknr + 1) & 0xff))
00184                 /* Next block */
00185                 XMODEM_PROGRESS("Recv blk %d\n", ++blocknr);
00186             else
00187             {
00188                 /* Sync lost */
00189                 XMODEM_PROGRESS("Sync lost (%d/%d)\n", c, blocknr);
00190                 purge = true;
00191                 break;
00192             }
00193 
00194             buf = block_buffer; /* Reset pointer to start of buffer */
00195             checksum = 0;
00196             crc = 0;
00197             for (i = 0; i < blocksize; i++)
00198             {
00199                 if ((c = kfile_getc(ch)) == EOF)
00200                 {
00201                     purge = true;
00202                     break;
00203                 }
00204 
00205                 /* Store in buffer */
00206                 *buf++ = (char)c;
00207 
00208                 /* Calculate block checksum or CRC */
00209                 if (usecrc)
00210                     crc = UPDCRC16(c, crc);
00211                 else
00212                     checksum += (char)c;
00213             }
00214 
00215             if (purge)
00216                 break;
00217 
00218             /* Get the checksum byte or the CRC-16 MSB */
00219             if ((c = kfile_getc(ch)) == EOF)
00220             {
00221                 purge = true;
00222                 break;
00223             }
00224 
00225             if (usecrc)
00226             {
00227                 crc = UPDCRC16(c, crc);
00228 
00229                 /* Get CRC-16 LSB */
00230                 if ((c = kfile_getc(ch)) == EOF)
00231                 {
00232                     purge = true;
00233                     break;
00234                 }
00235 
00236                 crc = UPDCRC16(c, crc);
00237 
00238                 if (crc)
00239                 {
00240                     XMODEM_PROGRESS("Bad CRC: %04x\n", crc);
00241                     purge = true;
00242                     break;
00243                 }
00244             }
00245             /* Compare the checksum */
00246             else if (c != checksum)
00247             {
00248                 XMODEM_PROGRESS("Bad sum: %04x/%04x\n", checksum, c);
00249                 purge = true;
00250                 break;
00251             }
00252 
00253             /*
00254              * Avoid flushing the same block twice.
00255              * This could happen when the sender does not receive our
00256              * acknowledge and resends the same block.
00257              */
00258             if (last_block_done < blocknr)
00259             {
00260                 /* Call user function to flush the buffer */
00261                 if (kfile_write(fd, block_buffer, blocksize))
00262                 {
00263                     /* Acknowledge block and clear error counter */
00264                     kfile_putc(XM_ACK, ch);
00265                     retries = 0;
00266                     last_block_done = blocknr;
00267                 }
00268                 else
00269                 {
00270                     /* User callback failed: abort transfer immediately */
00271                     retries = CONFIG_XMODEM_MAXRETRIES;
00272                     purge = true;
00273                 }
00274             }
00275             break;
00276 
00277         case XM_EOT:    /* End of transmission */
00278             kfile_putc(XM_ACK, ch);
00279             XMODEM_PROGRESS("Transfer completed\n");
00280             return true;
00281 
00282         case EOF: /* Timeout or serial error */
00283             purge = true;
00284             break;
00285 
00286         default:
00287             XMODEM_PROGRESS("Skipping garbage\n");
00288             purge = true;
00289             break;
00290         }
00291     } /* End forever */
00292 }
00293 #endif
00294 
00295 
00296 #if CONFIG_XMODEM_SEND
00297 
00306 bool xmodem_send(KFile *ch, KFile *fd)
00307 {
00308     char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
00309     size_t size = -1;
00310     int blocknr = 1, retries = 0, c, i;
00311     bool proceed, usecrc = false;
00312     uint16_t crc;
00313     uint8_t sum;
00314 
00315     /*
00316      * Reading a block can be very slow, so we read the first block early
00317      * to avoid receiving double XM_C char.
00318      * This could happen if we check for XM_C and then read the block, giving
00319      * the receiving device time to send another XM_C char misinterpretating
00320      * the blocks sent.
00321      */
00322     size = kfile_read(fd, block_buffer, XM_BUFSIZE);
00323 
00324     kfile_clearerr(ch);
00325     XMODEM_PROGRESS("Wait remote host\n");
00326 
00327     for(;;)
00328     {
00329         proceed = false;
00330         do
00331         {
00332             if (XMODEM_CHECK_ABORT)
00333                 return false;
00334 
00335             switch (c = kfile_getc(ch))
00336             {
00337             case XM_NAK:
00338                 XMODEM_PROGRESS("Resend blk %d\n", blocknr);
00339                 proceed = true;
00340                 break;
00341 
00342             case XM_C:
00343                 if (c == XM_C)
00344                 {
00345                     XMODEM_PROGRESS("Tx start (CRC)\n");
00346                     usecrc = true;
00347                 }
00348                 else
00349                     XMODEM_PROGRESS("Tx start (BCC)\n");
00350 
00351                 proceed = true;
00352                 break;
00353 
00354             case XM_ACK:
00355                 /* End of transfer? */
00356                 if (!size)
00357                     return true;
00358 
00359                 /* Call user function to read in one block */
00360                 size = kfile_read(fd, block_buffer, XM_BUFSIZE);
00361                 XMODEM_PROGRESS("Send blk %d\n", blocknr);
00362                 blocknr++;
00363                 retries = 0;
00364                 proceed = true;
00365                 break;
00366 
00367             case EOF:
00368                 kfile_clearerr(ch);
00369                 retries++;
00370                 XMODEM_PROGRESS("Retries %d\n", retries);
00371                 if (retries <= CONFIG_XMODEM_MAXRETRIES)
00372                     break;
00373                 /* falling through! */
00374 
00375             case XM_CAN:
00376                 XMODEM_PROGRESS("Transfer aborted\n");
00377                 return false;
00378 
00379             default:
00380                 XMODEM_PROGRESS("Skipping garbage\n");
00381                 break;
00382             }
00383         }
00384         while (!proceed);
00385 
00386         if (!size)
00387         {
00388             kfile_putc(XM_EOT, ch);
00389             continue;
00390         }
00391 
00392         /* Pad block with 0xFF if it's partially full */
00393         memset(block_buffer + size, 0xFF, XM_BUFSIZE - size);
00394 
00395         /* Send block header (STX, blocknr, ~blocknr) */
00396         #if XM_BUFSIZE == 128
00397             kfile_putc(XM_SOH, ch);
00398         #else
00399             kfile_putc(XM_STX, ch);
00400         #endif
00401         kfile_putc(blocknr & 0xFF, ch);
00402         kfile_putc(~blocknr & 0xFF, ch);
00403 
00404         /* Send block and compute its CRC/checksum */
00405         sum = 0;
00406         crc = 0;
00407         for (i = 0; i < XM_BUFSIZE; i++)
00408         {
00409             kfile_putc(block_buffer[i], ch);
00410             crc = UPDCRC16(block_buffer[i], crc);
00411             sum += block_buffer[i];
00412         }
00413 
00414         /* Send CRC/Checksum */
00415         if (usecrc)
00416         {
00417             crc = UPDCRC16(0, crc);
00418             crc = UPDCRC16(0, crc);
00419             kfile_putc(crc >> 8, ch);
00420             kfile_putc(crc & 0xFF, ch);
00421         }
00422         else
00423             kfile_putc(sum, ch);
00424     }
00425 }
00426 #endif