xmodem.c

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