From ed046ec2e30d22f13729f02739a5aa80806d72ec Mon Sep 17 00:00:00 2001 From: bernie Date: Wed, 11 Aug 2004 19:54:22 +0000 Subject: [PATCH] Import XModem protocol into DevLib. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@132 38d2e660-2303-0410-9eaa-f027e97ec537 --- mware/xmodem.c | 430 +++++++++++++++++++++++++++++++++++++++++++++++++ mware/xmodem.h | 30 ++++ 2 files changed, 460 insertions(+) create mode 100755 mware/xmodem.c create mode 100755 mware/xmodem.h diff --git a/mware/xmodem.c b/mware/xmodem.c new file mode 100755 index 00000000..ba38810b --- /dev/null +++ b/mware/xmodem.c @@ -0,0 +1,430 @@ +/*! + * \file + * + * \brief X-Modem serial transmission protocol (implementation) + * + * Suppots the CRC-16 and 1K-blocks variants of the standard. + * \see ymodem.txt for the protocol description. + * + * \version $Id$ + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/08/11 19:54:22 bernie + * Import XModem protocol into DevLib. + * + */ + +#include "xmodem.h" + +#include +#include +#include +#include +#include + + +/*! + * \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_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 */ + + +#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 +#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); + } + } +} + + +/*! + * Reset previous serial errors and flush the receive buffer + * (set a short timeout to speed up purge) + */ +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) +{ + int c, i, blocksize; + int blocknr = 0, last_block_done = 0, retries = 0; + char *buf; + uint8_t checksum; + uint16_t crc; + bool purge = false; + bool usecrc = true; + + + lcd_printf(0, 2, LCD_FILL, "Starting Transfer..."); + lcd_clear(); + purge = true; + ser_settimeouts(SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT); + ser_setstatus(0); + + /* Send initial NAK to start transmission */ + for(;;) + { + if (CHECK_ABORT) + { + ser_putchar(XM_CAN); + ser_putchar(XM_CAN); + lcd_printf(0, 2, LCD_FILL, "Transfer aborted"); + return false; + } + + /* + * Discard incoming input until a timeout occurs, then send + * a NAK to the transmitter. + */ + if (purge) + { + purge = false; + + if (ser_getstatus()) + SerialError(retries); + + FlushSerial(); + retries++; + + if (retries >= XM_MAXRETRIES) + { + ser_putchar(XM_CAN); + ser_putchar(XM_CAN); + lcd_printf(0, 2, LCD_FILL, "Transfer aborted"); + return false; + } + + /* Transmission start? */ + if (blocknr == 0) + { + if (retries < XM_MAXCRCRETRIES) + { + lcd_printf(0, 2, LCD_FILL, "Request Tx (CRC)"); + ser_putchar(XM_C); + } + 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); + } + } + else + ser_putchar(XM_NAK); + } + + switch (ser_getchar()) + { + case XM_STX: /* Start of header (1024-byte block) */ + blocksize = 1024; + goto getblock; + + case XM_SOH: /* Start of header (128-byte block) */ + blocksize = 128; + + getblock: + /* Get block number */ + c = ser_getchar(); + + /* Check complemented block number */ + if ((~c & 0xff) != ser_getchar()) + { + 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); + else + { + /* Sync lost */ + lcd_printf(0, 3, LCD_FILL, "Sync lost (%d/%d)", c, blocknr); + purge = true; + break; + } + + 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 = UPDCRC(c, crc); + else + checksum += (char)c; + } + + if (purge) + break; + + /* Get the checksum byte or the CRC-16 MSB */ + if ((c = ser_getchar()) == EOF) + { + purge = true; + break; + } + + if (usecrc) + { + crc = UPDCRC(c, crc); + + /* Get CRC-16 LSB */ + if ((c = ser_getchar()) == EOF) + { + purge = true; + break; + } + + crc = UPDCRC(c, crc); + + if (crc) + { + lcd_printf(0, 3, LCD_FILL, "Bad CRC: %04x", crc); + purge = true; + break; + } + } + /* Compare the checksum */ + else if (c != checksum) + { + lcd_printf(0, 3, LCD_FILL, "Bad sum: %04x/%04x", 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) + { + /* 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; + } + } + break; + + case XM_EOT: /* End of transmission */ + ser_putchar(XM_ACK); + lcd_printf(0, 2, LCD_FILL, "Transfer completed"); + return true; + + case EOF: /* Timeout or serial error */ + purge = true; + break; + + default: + lcd_printf(0, 3, LCD_FILL, "Skipping garbage"); + purge = true; + break; + } + } /* End forever */ +} + + +bool xmodem_send(KFile *fd) +{ + size_t size = -1; + int blocknr = 1, retries = 0, c, i; + bool proceed, usecrc = false; + uint16_t crc; + uint8_t sum; + + + ser_settimeouts(SER_DEFRXTIMEOUT, SER_DEFTXTIMEOUT); + ser_setstatus(0); + FlushSerial(); + lcd_printf(0, 2, LCD_FILL, "Wait remote host"); + + for(;;) + { + proceed = false; + do + { + if (CHECK_ABORT) + return false; + + switch (c = ser_getchar()) + { + 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; + + /* 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 EOF: + retries++; + SerialError(retries); + if (retries <= XM_MAXRETRIES) + break; + /* falling through! */ + + case XM_CAN: + lcd_printf(0, 2, LCD_FILL, "Transfer aborted"); + return false; + + default: + lcd_printf(0, 3, LCD_FILL, "Skipping garbage"); + break; + } + } + while (!proceed); + + if (!size) + ser_putchar(XM_EOT); + else + { + /* Pad block with 0xFF if it's partially full */ + if (size < XM_BUFSIZE) + for (i = size; i < XM_BUFSIZE; i++) + block_buffer[i] = (char)0xFF; + + /* Send block header (STX, blocknr, ~blocknr) */ + ser_putchar(XM_STX); + ser_putchar(blocknr & 0xFF); + ser_putchar(~blocknr & 0xFF); + + /* Send block and compute its CRC/checksum */ + sum = 0; + crc = 0; + for (i = 0; i < XM_BUFSIZE; i++) + { + ser_putchar(block_buffer[i]); + crc = UPDCRC(block_buffer[i], crc); + sum += block_buffer[i]; + } + + /* Send CRC/Checksum */ + if (usecrc) + { + crc = UPDCRC(0, crc); + crc = UPDCRC(0, crc); + ser_putchar(crc >> 8); + ser_putchar(crc & 0xFF); + } + else + ser_putchar(sum); + } + } +} diff --git a/mware/xmodem.h b/mware/xmodem.h new file mode 100755 index 00000000..0cad6832 --- /dev/null +++ b/mware/xmodem.h @@ -0,0 +1,30 @@ +/*! + * \file + * + * \brief X-Modem serial transmission protocol (interface) + * + * \version $Id$ + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/08/11 19:54:22 bernie + * Import XModem protocol into DevLib. + * + */ +#ifndef MWARE_XMODEM_H +#define MWARE_XMODEM_H + +/* fwd decl */ +struct _KFile; + +bool xmodem_recv(struct _KFile *fd); +bool xmodem_send(struct _KFile *fd); + +#endif /* MWARE_XMODEM_H */ + -- 2.25.1