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