xmodem.c

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