From d71313fa58a64903e3b00d6d1cc5dfa1cb77d6aa Mon Sep 17 00:00:00 2001 From: surenyi Date: Sun, 4 Aug 2019 13:39:05 +0800 Subject: [PATCH] reimple ymodem_recv() in lib/ymodem.c Signed-off-by: surenyi --- lib/serial_ymodem.c | 7 +- lib/ymodem.c | 423 +++++++++++++++++++++------------- lib/ymodem.h | 2 +- targets/stm32f429-disc/cmds.c | 21 +- targets/stm32f429-disc/main.c | 2 +- 5 files changed, 279 insertions(+), 176 deletions(-) diff --git a/lib/serial_ymodem.c b/lib/serial_ymodem.c index 694c740..9b1fdfd 100644 --- a/lib/serial_ymodem.c +++ b/lib/serial_ymodem.c @@ -25,11 +25,7 @@ static int do_getc(struct ymodem_xfer *xfer, uint8_t *c, int timeout) static int do_putc(struct ymodem_xfer *xfer, uint8_t c) { struct serial_ymodem *modem = (struct serial_ymodem *)xfer; - int nr; - - do { - nr = serial_send_buffered(modem->uart, &c, 1); - } while (nr != 1); + serial_send_blocking(modem->uart, &c, 1); return 0; } @@ -69,6 +65,7 @@ int serial_ymodem_recv(struct serial *uart, void *user, .user = user }; + serial_rx_buffer_clear(uart); return ymodem_recv(&modem.xfer); } diff --git a/lib/ymodem.c b/lib/ymodem.c index 1714584..d44a80c 100644 --- a/lib/ymodem.c +++ b/lib/ymodem.c @@ -40,24 +40,79 @@ static int put_byte (struct ymodem_xfer *uart, uint8_t c) } /** - * @brief Receive a packet from sender + * @brief Update CRC16 for input byte + * @param CRC input value + * @param input byte + * @retval None + */ +static uint16_t crc16(uint16_t crcIn, uint8_t byte) +{ + uint32_t crc = crcIn; + uint32_t in = byte | 0x100; + + do { + crc <<= 1; + in <<= 1; + if(in & 0x100) + ++crc; + if(crc & 0x10000) + crc ^= 0x1021; + } while (!(in & 0x10000)); + return crc & 0xffffu; +} + + +/** + * @brief Cal CRC16 for YModem Packet * @param data * @param length - * @param timeout - * 0: end of transmission - * -1: abort by sender - * >0: packet length - * @retval 0: normally return - * -1: timeout or packet error - * 1: abort by user + * @retval None + */ +static uint16_t do_crc16(const uint8_t* data, uint32_t size) +{ + uint32_t crc = 0; + const uint8_t* dataEnd = data+size; + + while(data < dataEnd) + crc = crc16(crc, *data++); + + crc = crc16(crc, 0); + crc = crc16(crc, 0); + + return crc&0xffffu; +} + +struct rxdescr { + struct ymodem_xfer *xfer; + + int last_error; + + int errors; + int eot; + int abort; + + uint8_t seq; + + char fname[FILE_NAME_LENGTH + 1]; + char fsize[FILE_SIZE_LENGTH + 1]; + + uint8_t data[PACKET_1K_SIZE + PACKET_OVERHEAD]; +}; + +/* + * recv a ymodem frame over xfer. + * + * return : < 0 on error. + * : = 0 on eot or abort. + * : > 0 packet size. */ -static int do_recv(struct ymodem_xfer *uart, uint8_t *data, int *length, uint32_t timeout) +static int recv_frame(struct rxdescr *rx, uint32_t timeout) { - uint16_t i, packet_size; uint8_t c; - *length = 0; + uint16_t ccs, rcs; + int i = 0, packet_size; - if (get_byte(uart, &c, timeout) != 0) { + if (get_byte(rx->xfer, &c, timeout) != 0) { return -1; } @@ -69,140 +124,225 @@ static int do_recv(struct ymodem_xfer *uart, uint8_t *data, int *length, uint32_ packet_size = PACKET_1K_SIZE; break; case EOT: + ++rx->eot; return 0; case CA: - if ((get_byte(uart, &c, timeout) == 0) && (c == CA)) { - *length = -1; + if ((get_byte(rx->xfer, &c, timeout) == 0) && (c == CA)) { + ++rx->abort; return 0; } else { return -1; } case ABORT1: case ABORT2: - return 1; + ++rx->abort; + return 0; default: return -1; } - *data = c; - for (i = 1; i < (packet_size + PACKET_OVERHEAD); i ++) { - if (get_byte(uart, data + i, timeout) != 0) { - return -1; + rx->data[i++] = c; + for (i = 1; i < (packet_size + PACKET_OVERHEAD); i++) { + if (get_byte(rx->xfer, rx->data + i, timeout) != 0) { + return -2; } } - if (data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff)) { - return -1; + if (rx->data[PACKET_SEQNO_INDEX] != ((rx->data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff)) { + return -3; } - *length = packet_size; - return 0; + + /* crc in packet */ + rcs = rx->data[packet_size + PACKET_OVERHEAD - 2]; + rcs <<= 8; + rcs |= rx->data[packet_size + PACKET_OVERHEAD - 1]; + + /* computed crc from data */ + ccs = do_crc16(&rx->data[PACKET_HEADER], packet_size); + if (ccs != rcs) { +#ifdef YMODEM_DEBUG + printf("crc mismatch: %04x, expected: %04x\n", ccs, rcs); +#endif + return -4; + } + + return (packet_size); } -/* - * @brief Receive a file using the ymodem protocol. - */ int ymodem_recv(struct ymodem_xfer *uart) { - char filename[FILE_NAME_LENGTH + 1], file_size[FILE_SIZE_LENGTH + 1]; - uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], *file_ptr; - int i, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0; + struct rxdescr *rx; + uint8_t *fp; + int pkt_size, i; + unsigned int fsize, recv_bytes = 0; + + if (!uart->on_info || !uart->on_data) { +#ifdef YMODEM_DEBUG + printf("please set `on_info` and `on_data`\n"); +#endif + return -1; + } + /* allocate enough memory. */ + rx = malloc(sizeof *rx); + if (rx == NULL) { +#ifdef YMODEM_DEBUG + printf("%s: out of memory.\n", __func__); +#endif + return -1; + } + memset(rx, 0, sizeof *rx); + rx->xfer = uart; - session_done = 0; - session_begin = 0; - errors = 0; - while (session_done == 0) { - packets_received = 0; - file_done = 0; - while (file_done == 0) { - switch (do_recv(uart, packet_data, &packet_length, NAK_TIMEOUT)) { - case 0: - errors = 0; - switch (packet_length) { - /* Abort by sender */ - case -1: - put_byte(uart, ACK); - return 0; - /* End of transmission */ - case 0: - put_byte(uart, ACK); - file_done = 1; - break; - /* Normal packet */ - default: - if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff)) { - put_byte(uart, NAK); - } else { - if (packets_received == 0) { - /* Filename packet */ - if (packet_data[PACKET_HEADER] != 0) { - /* Filename packet has valid data */ - for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);) { - filename[i++] = *file_ptr++; - } - filename[i++] = '\0'; - for (i = 0, file_ptr++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);) { - file_size[i++] = *file_ptr++; - } - file_size[i++] = '\0'; - size = strtoul(file_size, NULL, 0); - - /* Test the size of the image to be sent */ - /* Image size is greater than Flash size */ - if (uart->on_info && uart->on_info(uart, filename, size)) { /* error */ - /* End session */ - put_byte(uart, CA); - put_byte(uart, CA); - return -1; - } - put_byte(uart, ACK); - put_byte(uart, CRC16); - } else {/* Filename packet is empty, end session */ - put_byte(uart, ACK); - file_done = 1; - session_done = 1; - break; - } - } else { /* Data packet */ - /* Write received data in Flash */ - if (uart->on_data) { - if (uart->on_data(uart, packet_data + PACKET_HEADER, packet_length)) { /* An error occurred while writing to Flash memory */ - /* End session */ - put_byte(uart, CA); - put_byte(uart, CA); - return -2; - } else { - put_byte(uart, ACK); - } - } else { - put_byte(uart, CA); - put_byte(uart, CA); - } - - } - packets_received++; - session_begin = 1; - } - } - break; - case 1: - put_byte(uart, CA); - put_byte(uart, CA); - return -3; - default: - if (session_begin > 0) { - errors ++; - } - if (errors > MAX_ERRORS) { - put_byte(uart, CA); - put_byte(uart, CA); - return 0; +retry: + /* wait to start */ + while ((pkt_size = recv_frame(rx, NAK_TIMEOUT)) <= 0) { + put_byte(uart, WANTCRC); + } + + if (rx->data[PACKET_SEQNO_INDEX] != 0) { /* seq mismatch */ + put_byte(uart, NAK); + goto retry; + } + + fp = &rx->data[PACKET_HEADER]; + /* packet 0, filename packet. */ + if (*fp == 0) { /* have no filename, it's not a vlid ymodem first packet */ + put_byte(uart, CA); + put_byte(uart, CA); +#ifdef YMODEM_DEBUG + printf("no filename retry\n"); +#endif + goto retry; + } + + /* send req 0 response */ + put_byte(uart, ACK); + put_byte(uart, WANTCRC); + + /* parse filename */ + i = 0; + while ((*fp != 0) && (i < FILE_NAME_LENGTH)) { + rx->fname[i++] = *fp++; + } + rx->fname[i++] = '\0'; + + /* parse file size */ + i = 0; + ++fp; + while ((*fp != ' ') && (i < FILE_SIZE_LENGTH)) { + rx->fsize[i++] = *fp++; + } + rx->fsize[i++] = '\0'; + fsize = strtoul(rx->fsize, NULL, 0); + + /* call file info callback */ + rx->last_error = uart->on_info(uart, rx->fname, fsize); + +#ifdef YMODEM_DEBUG + printf("%s : (%d) bytes (last_error %d)\n", rx->fname, fsize, rx->last_error); +#endif + + /* initialize ymodem state */ + rx->eot = 0; + rx->abort = 0; + rx->errors = 0; + rx->seq = 1; + + /* start to recv file contents */ + fp = &rx->data[PACKET_HEADER]; + for (;;) { + pkt_size = recv_frame(rx, NAK_TIMEOUT); + + if (rx->abort > 0) { +#ifdef YMODEM_DEBUG + printf("abort\n"); +#endif + break; + } + + if (rx->last_error) { + put_byte(uart, CA); + put_byte(uart, CA); +#ifdef YMODEM_DEBUG + printf("last_error break\n"); +#endif + break; + } + + if (pkt_size < 0) { + ++rx->errors; + if (rx->errors >= MAX_ERRORS) { + put_byte(uart, CA); + put_byte(uart, CA); +#ifdef YMODEM_DEBUG + printf("errors break\n"); +#endif + break; + } else { + put_byte(uart, NAK); +#ifdef YMODEM_DEBUG + printf("send NAK\n"); +#endif + continue; + } + } + + rx->errors = 0; + + if (rx->eot == 2 && rx->data[PACKET_SEQNO_INDEX] == 0) { +#ifdef YMODEM_DEBUG + printf("end of ymodem protocol.\n"); +#endif + put_byte(uart, ACK); + break; + } + + if (pkt_size > 0) { /* on data */ + if (rx->data[PACKET_SEQNO_INDEX] == 0) { /* all zeros */ + for (i = 0; i < pkt_size; ++i) { + if (fp[i] != 0) { + break; } - put_byte(uart, CRC16); + } + if ((i == pkt_size) && rx->eot > 0) { /* end of stream */ + put_byte(uart, ACK); break; + } + } + + if (rx->seq != rx->data[PACKET_SEQNO_INDEX]) { + put_byte(uart, NAK); +#ifdef YMODEM_DEBUG + printf("data phase: seq mismatch %d, expected %d\n", rx->data[PACKET_SEQNO_INDEX], rx->seq); +#endif + } else { + put_byte(uart, ACK); + + rx->last_error = uart->on_data(uart, fp, pkt_size); + + ++rx->seq; + recv_bytes += pkt_size; + } + continue; + } + + /* eot */ + if (pkt_size == 0) { + if (rx->eot == 1) { + put_byte(uart, NAK); + continue; + } + + if (rx->eot == 2) { + put_byte(uart, ACK); + put_byte(uart, WANTCRC); } } } - return size; + + free(rx); + + return (recv_bytes); } /** @@ -275,49 +415,6 @@ static void prepare_packet(uint8_t *sourceBuf, uint8_t *data, uint8_t pktNo, uin } } -/** - * @brief Update CRC16 for input byte - * @param CRC input value - * @param input byte - * @retval None - */ -static uint16_t crc16(uint16_t crcIn, uint8_t byte) -{ - uint32_t crc = crcIn; - uint32_t in = byte | 0x100; - - do { - crc <<= 1; - in <<= 1; - if(in & 0x100) - ++crc; - if(crc & 0x10000) - crc ^= 0x1021; - } while (!(in & 0x10000)); - return crc & 0xffffu; -} - - -/** - * @brief Cal CRC16 for YModem Packet - * @param data - * @param length - * @retval None - */ -static uint16_t do_crc16(const uint8_t* data, uint32_t size) -{ - uint32_t crc = 0; - const uint8_t* dataEnd = data+size; - - while(data < dataEnd) - crc = crc16(crc, *data++); - - crc = crc16(crc, 0); - crc = crc16(crc, 0); - - return crc&0xffffu; -} - /** * @brief Cal Check sum for YModem Packet * @param data diff --git a/lib/ymodem.h b/lib/ymodem.h index 1caf6e0..fc14516 100644 --- a/lib/ymodem.h +++ b/lib/ymodem.h @@ -25,7 +25,7 @@ extern "C" { #define ACK (0x06) /* acknowledge */ #define NAK (0x15) /* negative acknowledge */ #define CA (0x18) /* two of these in succession aborts transfer */ -#define CRC16 (0x43) /* 'C' == 0x43, request 16-bit CRC */ +#define WANTCRC (0x43) /* 'C' == 0x43, request 16-bit CRC */ #define ABORT1 (0x41) /* 'A' == 0x41, abort by user */ #define ABORT2 (0x61) /* 'a' == 0x61, abort by user */ diff --git a/targets/stm32f429-disc/cmds.c b/targets/stm32f429-disc/cmds.c index 83a7abf..8858a0e 100644 --- a/targets/stm32f429-disc/cmds.c +++ b/targets/stm32f429-disc/cmds.c @@ -3,6 +3,7 @@ #include #include "cmd.h" #include "efs.h" +#include "serial.h" #include "ymodem.h" struct fileinfo { @@ -11,7 +12,7 @@ struct fileinfo { size_t size; }; -static int ry_on_info(void *xfer, const char *name, unsigned int size) +static int rx_on_info(void *xfer, const char *name, unsigned int size) { struct fileinfo *s = xfer; @@ -24,15 +25,17 @@ static int ry_on_info(void *xfer, const char *name, unsigned int size) return 0; } -static int ry_on_data(void *xfer, const void *data, unsigned int len) +static int rx_on_data(void *xfer, const void *data, unsigned int _len) { struct fileinfo *s = xfer; + int len = _len; s->recv += len; - if (s->recv > s->size) { + if (s->size > 0 && (s->recv > s->size)) { len -= (s->recv - s->size); } - efs_write(s->fh, data, len); + if (len > 0) + efs_write(s->fh, data, len); return 0; } @@ -55,11 +58,17 @@ static int do_efs(cmd_tbl_t s, int argc, char *argv[]) printf(" rm\tdelete file\n"); } else if (strcmp(argv[1], "rx") == 0) { memset(&fi, 0, sizeof fi); - err = stdio_ymodem_recv(&fi, ry_on_info, ry_on_data); +#if 0 + serial_setup(&serial2, CONSOLE_BAUDRATE, 8, SERIAL_STOPBITS_1); + serial_ymodem_recv(&serial2, &fi, rx_on_info, rx_on_data); + serial_close(&serial2); +#else + stdio_ymodem_recv(&fi, rx_on_info, rx_on_data); +#endif if (fi.fh > 0) { efs_close(fi.fh); } - printf("\n\t\nreceive %d bytes\n", err); + printf("receive %d bytes\n", fi.size); } else if (strcmp(argv[1], "rm") == 0 && argc >= 3) { efs_remove(argv[2]); } else if (strcmp(argv[1], "format") == 0) { diff --git a/targets/stm32f429-disc/main.c b/targets/stm32f429-disc/main.c index 949df05..aa8e998 100644 --- a/targets/stm32f429-disc/main.c +++ b/targets/stm32f429-disc/main.c @@ -139,7 +139,7 @@ int main() iis_start_tx(&i2s0); -// efs_mount(); + efs_mount(); bkp_sram_write(MAGIC_POS, MAGIC_APP); printf("build: %s (%s)\r\n", __DATE__, __TIME__);