X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=mware%2Fxmodem.c;h=030783d36369d17649c8523e5268fbb339df37d2;hb=e5523f6098d2f4d51c838459ce83423152cc8692;hp=5e296ab3c8167546401a9137a959f2097fe36e58;hpb=e0e6c8a308489f2f9679a6bacb91cd035e1a7445;p=bertos.git diff --git a/mware/xmodem.c b/mware/xmodem.c old mode 100755 new mode 100644 index 5e296ab3..030783d3 --- a/mware/xmodem.c +++ b/mware/xmodem.c @@ -1,127 +1,97 @@ -/*! +/** * \file * * \brief X-Modem serial transmission protocol (implementation) * - * Suppots the CRC-16 and 1K-blocks variants of the standard. + * Supports the CRC-16 and 1K-blocks variants of the standard. * \see ymodem.txt for the protocol description. * + * \todo Break xmodem_send() and xmodem_recv() in smaller functions. + * + * \todo Maybe convert drv/ser.c to the KFile interface for symmetry and + * flexibility. + * * \version $Id$ * \author Bernardo Innocenti + * \author Francesco Sacchi */ -/* - * $Log$ - * Revision 1.4 2004/08/12 23:35:50 bernie - * Replace a handmade loop with memset(). - * - * Revision 1.3 2004/08/12 23:34:36 bernie - * Replace if/else with continue to reduce indentation level. - * - * Revision 1.2 2004/08/12 23:24:07 bernie - * Rename UPDCRC() to UPDCRC16(). - * - * Revision 1.1 2004/08/11 19:54:22 bernie - * Import XModem protocol into DevLib. - * - */ #include "xmodem.h" +#include +#include /* for memset() */ #include -#include -#include -#include -#include +#include +#include + -#include /* for memset() */ -/*! +/** * \name Protocol control codes * \{ */ -#define XM_SOH 0x01 /*!< Start Of Header (128-byte block) */ -#define XM_STX 0x02 /*!< Start Of Header (1024-byte block) */ -#define XM_EOT 0x04 /*!< End Of Transmission */ -#define XM_ACK 0x06 /*!< Acknowledge block */ -#define XM_NAK 0x15 /*!< Negative Acknowledge */ -#define XM_C 0x43 /*!< Request CRC-16 transmission */ -#define XM_CAN 0x18 /*!< CANcel transmission */ +#define XM_SOH 0x01 /**< Start Of Header (128-byte block) */ +#define XM_STX 0x02 /**< Start Of Header (1024-byte block) */ +#define XM_EOT 0x04 /**< End Of Transmission */ +#define XM_ACK 0x06 /**< Acknowledge block */ +#define XM_NAK 0x15 /**< Negative Acknowledge */ +#define XM_C 0x43 /**< Request CRC-16 transmission */ +#define XM_CAN 0x18 /**< CANcel transmission */ /*\}*/ -#define XM_MAXRETRIES 15 /*!< Max retries before giving up */ -#define XM_MAXCRCRETRIES 7 /*!< Max retries before switching to BCC */ -#define XM_BUFSIZE 1024 /*!< Size of block buffer */ +#define XM_MAXRETRIES 15 /**< Max retries before giving up */ +#define XM_MAXCRCRETRIES 7 /**< Max retries before switching to BCC */ - -#if (ARCH & ARCH_BOOT) - #include "kbdhw.h" - #if (ARCH & ARCH_SLIM) - #define CHECK_ABORT KEYPRESSED_STOP - #elif (ARCH & ARCH_SARF) - #define CHECK_ABORT KEYPRESSED_ESC - #endif +#if CONFIG_XMODEM_1KCRC == 1 + #define XM_BUFSIZE 1024 /**< 1024 bytes of block buffer */ #else - #include "kbd.h" - #if (ARCH & ARCH_SLIM) - #define CHECK_ABORT (kbd_getchar() == K_STOP) - #elif (ARCH & ARCH_SARF) - #define CHECK_ABORT (kbd_getchar() == K_ESC) - #endif -#endif /* ARCH_BOOT */ - - -/*! Buffer to hold a block of data */ -static char block_buffer[XM_BUFSIZE]; - - -/*! - * Decode serial driver errors and print - * them on the display. - */ -static void SerialError(int retries) -{ - serstatus_t err, status; - - /* Get serial error code and reset it */ - status = ser_getstatus(); - ser_setstatus(0); - - /* Mostra tutti gli errori in sequenza */ - for (err = 0; status != 0; status >>= 1, err++) - { - /* Se il bit dell'errore e' settato */ - if (status & 1) - { - lcd_printf(0, 3, LCD_FILL, "%s %d", serial_errors[err], retries); - buz_beep(200); - timer_delay(500); - } - } -} + #define XM_BUFSIZE 128 /**< 128 bytes of block buffer */ +#endif -/*! - * Reset previous serial errors and flush the receive buffer - * (set a short timeout to speed up purge) +#if CONFIG_XMODEM_RECV +/** + * \brief Receive a file using the XModem protocol. + * + * \param port Serial port to use for transfer + * \param fd Destination file + * + * \note This function allocates a large amount of stack (\see XM_BUFSIZE). */ -static void FlushSerial(void) -{ - ser_setstatus(0); - ser_settimeouts(200, SER_DEFTXTIMEOUT); - while (ser_getchar() != EOF) {} - ser_settimeouts(SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT); - ser_setstatus(0); -} - - -bool xmodem_recv(KFile *fd) +bool xmodem_recv(struct Serial *port, KFile *fd) { + char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */ int c, i, blocksize; int blocknr = 0, last_block_done = 0, retries = 0; char *buf; @@ -131,20 +101,18 @@ bool xmodem_recv(KFile *fd) bool usecrc = true; - lcd_printf(0, 2, LCD_FILL, "Starting Transfer..."); - lcd_clear(); + XMODEM_PROGRESS("Starting Transfer...\n"); purge = true; - ser_settimeouts(SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT); - ser_setstatus(0); + ser_setstatus(port, 0); /* Send initial NAK to start transmission */ for(;;) { - if (CHECK_ABORT) + if (XMODEM_CHECK_ABORT) { - ser_putchar(XM_CAN); - ser_putchar(XM_CAN); - lcd_printf(0, 2, LCD_FILL, "Transfer aborted"); + ser_putchar(XM_CAN, port); + ser_putchar(XM_CAN, port); + XMODEM_PROGRESS("Transfer aborted\n"); return false; } @@ -156,17 +124,17 @@ bool xmodem_recv(KFile *fd) { purge = false; - if (ser_getstatus()) - SerialError(retries); + if (ser_getstatus(port)) + XMODEM_PROGRESS("Retries %d\n", retries); - FlushSerial(); + ser_resync(port, 200); retries++; if (retries >= XM_MAXRETRIES) { - ser_putchar(XM_CAN); - ser_putchar(XM_CAN); - lcd_printf(0, 2, LCD_FILL, "Transfer aborted"); + ser_putchar(XM_CAN, port); + ser_putchar(XM_CAN, port); + XMODEM_PROGRESS("Transfer aborted\n"); return false; } @@ -175,236 +143,256 @@ bool xmodem_recv(KFile *fd) { if (retries < XM_MAXCRCRETRIES) { - lcd_printf(0, 2, LCD_FILL, "Request Tx (CRC)"); - ser_putchar(XM_C); + XMODEM_PROGRESS("Request Tx (CRC)\n"); + ser_putchar(XM_C, port); } else { /* Give up with CRC and fall back to checksum */ usecrc = false; - lcd_printf(0, 2, LCD_FILL, "Request Tx (BCC)"); - ser_putchar(XM_NAK); + XMODEM_PROGRESS("Request Tx (BCC)\n"); + ser_putchar(XM_NAK, port); } } else - ser_putchar(XM_NAK); + ser_putchar(XM_NAK, port); } - switch (ser_getchar()) + switch (ser_getchar(port)) { - case XM_STX: /* Start of header (1024-byte block) */ - blocksize = 1024; - goto getblock; - - case XM_SOH: /* Start of header (128-byte block) */ - blocksize = 128; + #if XM_BUFSIZE >= 1024 + case XM_STX: /* Start of header (1024-byte block) */ + blocksize = 1024; + goto getblock; + #endif + + case XM_SOH: /* Start of header (128-byte block) */ + blocksize = 128; + /* Needed to avoid warning if XM_BUFSIZE < 1024 */ + goto getblock; + getblock: + /* Get block number */ + c = ser_getchar(port); + + /* Check complemented block number */ + if ((~c & 0xff) != ser_getchar(port)) + { + XMODEM_PROGRESS("Bad blk (%d)\n", c); + purge = true; + break; + } - getblock: - /* Get block number */ - c = ser_getchar(); + /* Determine which block is being sent */ + if (c == (blocknr & 0xff)) + /* Last block repeated */ + XMODEM_PROGRESS("Repeat blk %d\n", blocknr); + else if (c == ((blocknr + 1) & 0xff)) + /* Next block */ + XMODEM_PROGRESS("Recv blk %d\n", ++blocknr); + else + { + /* Sync lost */ + XMODEM_PROGRESS("Sync lost (%d/%d)\n", c, blocknr); + purge = true; + break; + } - /* Check complemented block number */ - if ((~c & 0xff) != ser_getchar()) + buf = block_buffer; /* Reset pointer to start of buffer */ + checksum = 0; + crc = 0; + for (i = 0; i < blocksize; i++) + { + if ((c = ser_getchar(port)) == EOF) { - lcd_printf(0, 3, LCD_FILL, "Bad blk (%d)", c); purge = true; break; } - /* Determine which block is being sent */ - if (c == (blocknr & 0xff)) - /* Last block repeated */ - lcd_printf(0, 2, LCD_FILL, "Repeat blk %d", blocknr); - else if (c == ((blocknr + 1) & 0xff)) - /* Next block */ - lcd_printf(0, 2, LCD_FILL, "Recv blk %d", ++blocknr); + /* Store in buffer */ + *buf++ = (char)c; + + /* Calculate block checksum or CRC */ + if (usecrc) + crc = UPDCRC16(c, crc); else - { - /* Sync lost */ - lcd_printf(0, 3, LCD_FILL, "Sync lost (%d/%d)", c, blocknr); - purge = true; - break; - } + checksum += (char)c; + } - buf = block_buffer; /* Reset pointer to start of buffer */ - checksum = 0; - crc = 0; - for (i = 0; i < blocksize; i++) - { - if ((c = ser_getchar()) == EOF) - { - purge = true; - break; - } - - /* Store in buffer */ - *buf++ = (char)c; - - /* Calculate block checksum or CRC */ - if (usecrc) - crc = UPDCRC16(c, crc); - else - checksum += (char)c; - } + if (purge) + break; - if (purge) - break; + /* Get the checksum byte or the CRC-16 MSB */ + if ((c = ser_getchar(port)) == EOF) + { + purge = true; + break; + } + + if (usecrc) + { + crc = UPDCRC16(c, crc); - /* Get the checksum byte or the CRC-16 MSB */ - if ((c = ser_getchar()) == EOF) + /* Get CRC-16 LSB */ + if ((c = ser_getchar(port)) == EOF) { purge = true; break; } - if (usecrc) - { - crc = UPDCRC16(c, crc); + crc = UPDCRC16(c, crc); - /* Get CRC-16 LSB */ - if ((c = ser_getchar()) == EOF) - { - purge = true; - break; - } - - crc = UPDCRC16(c, crc); - - if (crc) - { - lcd_printf(0, 3, LCD_FILL, "Bad CRC: %04x", crc); - purge = true; - break; - } - } - /* Compare the checksum */ - else if (c != checksum) + if (crc) { - lcd_printf(0, 3, LCD_FILL, "Bad sum: %04x/%04x", checksum, c); + XMODEM_PROGRESS("Bad CRC: %04x\n", crc); purge = true; break; } + } + /* Compare the checksum */ + else if (c != checksum) + { + XMODEM_PROGRESS("Bad sum: %04x/%04x\n", checksum, c); + purge = true; + break; + } - /* - * Avoid flushing the same block twice. - * This could happen when the sender does not receive our - * acknowledge and resends the same block. - */ - if (last_block_done < blocknr) + /* + * Avoid flushing the same block twice. + * This could happen when the sender does not receive our + * acknowledge and resends the same block. + */ + if (last_block_done < blocknr) + { + /* Call user function to flush the buffer */ + if (fd->write(fd, block_buffer, blocksize)) { - /* Call user function to flush the buffer */ - if (fd->write(fd, block_buffer, blocksize)) - { - /* Acknowledge block and clear error counter */ - ser_putchar(XM_ACK); - retries = 0; - last_block_done = blocknr; - } - else - { - /* User callback failed: abort transfer immediately */ - retries = XM_MAXRETRIES; - purge = true; - } + /* Acknowledge block and clear error counter */ + ser_putchar(XM_ACK, port); + retries = 0; + last_block_done = blocknr; } - break; + else + { + /* User callback failed: abort transfer immediately */ + retries = XM_MAXRETRIES; + purge = true; + } + } + break; - case XM_EOT: /* End of transmission */ - ser_putchar(XM_ACK); - lcd_printf(0, 2, LCD_FILL, "Transfer completed"); - return true; + case XM_EOT: /* End of transmission */ + ser_putchar(XM_ACK, port); + XMODEM_PROGRESS("Transfer completed\n"); + return true; - case EOF: /* Timeout or serial error */ - purge = true; - break; + case EOF: /* Timeout or serial error */ + purge = true; + break; - default: - lcd_printf(0, 3, LCD_FILL, "Skipping garbage"); - purge = true; - break; + default: + XMODEM_PROGRESS("Skipping garbage\n"); + purge = true; + break; } } /* End forever */ } +#endif -bool xmodem_send(KFile *fd) +#if CONFIG_XMODEM_SEND +/** + * \brief Transmit some data using the XModem protocol. + * + * \param port Serial port to use for transfer + * \param fd Source file + * + * \note This function allocates a large amount of stack for + * the XModem transfer buffer (\see XM_BUFSIZE). + */ +bool xmodem_send(struct Serial *port, KFile *fd) { + char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */ size_t size = -1; int blocknr = 1, retries = 0, c, i; bool proceed, usecrc = false; uint16_t crc; uint8_t sum; + /* + * Reading a block can be very slow, so we read the first block early + * to avoid receiving double XM_C char. + * This could happen if we check for XM_C and then read the block, giving + * the receiving device time to send another XM_C char misinterpretating + * the blocks sent. + */ + size = fd->read(fd, block_buffer, XM_BUFSIZE); - ser_settimeouts(SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT); - ser_setstatus(0); - FlushSerial(); - lcd_printf(0, 2, LCD_FILL, "Wait remote host"); + ser_setstatus(port, 0); + ser_purge(port); + XMODEM_PROGRESS("Wait remote host\n"); for(;;) { proceed = false; do { - if (CHECK_ABORT) + if (XMODEM_CHECK_ABORT) return false; - switch (c = ser_getchar()) + switch (c = ser_getchar(port)) { - case XM_NAK: - case XM_C: - if (blocknr == 1) - { - if (c == XM_C) - { - lcd_printf(0, 2, LCD_FILL, "Tx start (CRC)"); - usecrc = true; - } - else - lcd_printf(0, 2, LCD_FILL, "Tx start (BCC)"); - - /* Call user function to read in one block */ - size = fd->read(fd, block_buffer, XM_BUFSIZE); - } - else - lcd_printf(0, 2, LCD_FILL, "Resend blk %d", blocknr); - proceed = true; - break; - - case XM_ACK: - /* End of transfer? */ - if (!size) - return true; + case XM_NAK: + XMODEM_PROGRESS("Resend blk %d\n", blocknr); + proceed = true; + break; - /* Call user function to read in one block */ - size = fd->read(fd, block_buffer, XM_BUFSIZE); - blocknr++; - retries = 0; - proceed = true; - lcd_printf(0, 2, LCD_FILL, "Send blk %d", blocknr); - break; + case XM_C: + if (c == XM_C) + { + XMODEM_PROGRESS("Tx start (CRC)\n"); + usecrc = true; + } + else + XMODEM_PROGRESS("Tx start (BCC)\n"); - case EOF: - retries++; - SerialError(retries); - if (retries <= XM_MAXRETRIES) - break; - /* falling through! */ + proceed = true; + break; - case XM_CAN: - lcd_printf(0, 2, LCD_FILL, "Transfer aborted"); - return false; + case XM_ACK: + /* End of transfer? */ + if (!size) + return true; + + /* Call user function to read in one block */ + size = fd->read(fd, block_buffer, XM_BUFSIZE); + XMODEM_PROGRESS("Send blk %d\n", blocknr); + blocknr++; + retries = 0; + proceed = true; + break; - default: - lcd_printf(0, 3, LCD_FILL, "Skipping garbage"); + case EOF: + ser_setstatus(port, 0); + retries++; + XMODEM_PROGRESS("Retries %d\n", retries); + if (retries <= XM_MAXRETRIES) break; + /* falling through! */ + + case XM_CAN: + XMODEM_PROGRESS("Transfer aborted\n"); + return false; + + default: + XMODEM_PROGRESS("Skipping garbage\n"); + break; } } while (!proceed); if (!size) { - ser_putchar(XM_EOT); + ser_putchar(XM_EOT, port); continue; } @@ -412,16 +400,20 @@ bool xmodem_send(KFile *fd) memset(block_buffer + size, 0xFF, XM_BUFSIZE - size); /* Send block header (STX, blocknr, ~blocknr) */ - ser_putchar(XM_STX); - ser_putchar(blocknr & 0xFF); - ser_putchar(~blocknr & 0xFF); + #if XM_BUFSIZE == 128 + ser_putchar(XM_SOH, port); + #else + ser_putchar(XM_STX, port); + #endif + ser_putchar(blocknr & 0xFF, port); + ser_putchar(~blocknr & 0xFF, port); /* Send block and compute its CRC/checksum */ sum = 0; crc = 0; for (i = 0; i < XM_BUFSIZE; i++) { - ser_putchar(block_buffer[i]); + ser_putchar(block_buffer[i], port); crc = UPDCRC16(block_buffer[i], crc); sum += block_buffer[i]; } @@ -431,10 +423,11 @@ bool xmodem_send(KFile *fd) { crc = UPDCRC16(0, crc); crc = UPDCRC16(0, crc); - ser_putchar(crc >> 8); - ser_putchar(crc & 0xFF); + ser_putchar(crc >> 8, port); + ser_putchar(crc & 0xFF, port); } else - ser_putchar(sum); + ser_putchar(sum, port); } } +#endif