BeRTOS
|
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