Browse Source

reimple ymodem_recv() in lib/ymodem.c

Signed-off-by: surenyi <surenyi82@163.com>
master
surenyi 5 years ago
parent
commit
d71313fa58
  1. 7
      lib/serial_ymodem.c
  2. 381
      lib/ymodem.c
  3. 2
      lib/ymodem.h
  4. 19
      targets/stm32f429-disc/cmds.c
  5. 2
      targets/stm32f429-disc/main.c

7
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) static int do_putc(struct ymodem_xfer *xfer, uint8_t c)
{ {
struct serial_ymodem *modem = (struct serial_ymodem *)xfer; struct serial_ymodem *modem = (struct serial_ymodem *)xfer;
int nr; serial_send_blocking(modem->uart, &c, 1);
do {
nr = serial_send_buffered(modem->uart, &c, 1);
} while (nr != 1);
return 0; return 0;
} }
@ -69,6 +65,7 @@ int serial_ymodem_recv(struct serial *uart, void *user,
.user = user .user = user
}; };
serial_rx_buffer_clear(uart);
return ymodem_recv(&modem.xfer); return ymodem_recv(&modem.xfer);
} }

381
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 data
* @param length * @param length
* @param timeout * @retval None
* 0: end of transmission
* -1: abort by sender
* >0: packet length
* @retval 0: normally return
* -1: timeout or packet error
* 1: abort by user
*/ */
static int do_recv(struct ymodem_xfer *uart, uint8_t *data, int *length, uint32_t timeout) 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 recv_frame(struct rxdescr *rx, uint32_t timeout)
{ {
uint16_t i, packet_size;
uint8_t c; 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; 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; packet_size = PACKET_1K_SIZE;
break; break;
case EOT: case EOT:
++rx->eot;
return 0; return 0;
case CA: case CA:
if ((get_byte(uart, &c, timeout) == 0) && (c == CA)) { if ((get_byte(rx->xfer, &c, timeout) == 0) && (c == CA)) {
*length = -1; ++rx->abort;
return 0; return 0;
} else { } else {
return -1; return -1;
} }
case ABORT1: case ABORT1:
case ABORT2: case ABORT2:
return 1; ++rx->abort;
return 0;
default: default:
return -1; return -1;
} }
*data = c; rx->data[i++] = c;
for (i = 1; i < (packet_size + PACKET_OVERHEAD); i++) { for (i = 1; i < (packet_size + PACKET_OVERHEAD); i++) {
if (get_byte(uart, data + i, timeout) != 0) { if (get_byte(rx->xfer, rx->data + i, timeout) != 0) {
return -1; return -2;
} }
} }
if (data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff)) { if (rx->data[PACKET_SEQNO_INDEX] != ((rx->data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff)) {
return -1; 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) int ymodem_recv(struct ymodem_xfer *uart)
{ {
char filename[FILE_NAME_LENGTH + 1], file_size[FILE_SIZE_LENGTH + 1]; struct rxdescr *rx;
uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], *file_ptr; uint8_t *fp;
int i, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0; 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; retry:
session_begin = 0; /* wait to start */
errors = 0; while ((pkt_size = recv_frame(rx, NAK_TIMEOUT)) <= 0) {
while (session_done == 0) { put_byte(uart, WANTCRC);
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);) { if (rx->data[PACKET_SEQNO_INDEX] != 0) { /* seq mismatch */
file_size[i++] = *file_ptr++; put_byte(uart, NAK);
goto retry;
} }
file_size[i++] = '\0';
size = strtoul(file_size, NULL, 0);
/* Test the size of the image to be sent */ fp = &rx->data[PACKET_HEADER];
/* Image size is greater than Flash size */ /* packet 0, filename packet. */
if (uart->on_info && uart->on_info(uart, filename, size)) { /* error */ if (*fp == 0) { /* have no filename, it's not a vlid ymodem first packet */
/* End session */
put_byte(uart, CA); put_byte(uart, CA);
put_byte(uart, CA); put_byte(uart, CA);
return -1; #ifdef YMODEM_DEBUG
printf("no filename retry\n");
#endif
goto retry;
} }
/* send req 0 response */
put_byte(uart, ACK); put_byte(uart, ACK);
put_byte(uart, CRC16); put_byte(uart, WANTCRC);
} else {/* Filename packet is empty, end session */
put_byte(uart, ACK); /* parse filename */
file_done = 1; i = 0;
session_done = 1; 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; break;
} }
} else { /* Data packet */
/* Write received data in Flash */ if (rx->last_error) {
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);
put_byte(uart, CA); put_byte(uart, CA);
return -2; #ifdef YMODEM_DEBUG
} else { printf("last_error break\n");
put_byte(uart, ACK); #endif
break;
} }
} else {
if (pkt_size < 0) {
++rx->errors;
if (rx->errors >= MAX_ERRORS) {
put_byte(uart, CA); put_byte(uart, CA);
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;
} }
packets_received++;
session_begin = 1; 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;
} }
} }
if ((i == pkt_size) && rx->eot > 0) { /* end of stream */
put_byte(uart, ACK);
break; 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;
} }
put_byte(uart, CRC16);
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 * @brief Cal Check sum for YModem Packet
* @param data * @param data

2
lib/ymodem.h

@ -25,7 +25,7 @@ extern "C" {
#define ACK (0x06) /* acknowledge */ #define ACK (0x06) /* acknowledge */
#define NAK (0x15) /* negative acknowledge */ #define NAK (0x15) /* negative acknowledge */
#define CA (0x18) /* two of these in succession aborts transfer */ #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 ABORT1 (0x41) /* 'A' == 0x41, abort by user */
#define ABORT2 (0x61) /* 'a' == 0x61, abort by user */ #define ABORT2 (0x61) /* 'a' == 0x61, abort by user */

19
targets/stm32f429-disc/cmds.c

@ -3,6 +3,7 @@
#include <string.h> #include <string.h>
#include "cmd.h" #include "cmd.h"
#include "efs.h" #include "efs.h"
#include "serial.h"
#include "ymodem.h" #include "ymodem.h"
struct fileinfo { struct fileinfo {
@ -11,7 +12,7 @@ struct fileinfo {
size_t size; 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; struct fileinfo *s = xfer;
@ -24,14 +25,16 @@ static int ry_on_info(void *xfer, const char *name, unsigned int size)
return 0; 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; struct fileinfo *s = xfer;
int len = _len;
s->recv += len; s->recv += len;
if (s->recv > s->size) { if (s->size > 0 && (s->recv > s->size)) {
len -= (s->recv - s->size); len -= (s->recv - s->size);
} }
if (len > 0)
efs_write(s->fh, data, len); efs_write(s->fh, data, len);
return 0; return 0;
} }
@ -55,11 +58,17 @@ static int do_efs(cmd_tbl_t s, int argc, char *argv[])
printf(" rm\tdelete file\n"); printf(" rm\tdelete file\n");
} else if (strcmp(argv[1], "rx") == 0) { } else if (strcmp(argv[1], "rx") == 0) {
memset(&fi, 0, sizeof fi); 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) { if (fi.fh > 0) {
efs_close(fi.fh); 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) { } else if (strcmp(argv[1], "rm") == 0 && argc >= 3) {
efs_remove(argv[2]); efs_remove(argv[2]);
} else if (strcmp(argv[1], "format") == 0) { } else if (strcmp(argv[1], "format") == 0) {

2
targets/stm32f429-disc/main.c

@ -139,7 +139,7 @@ int main()
iis_start_tx(&i2s0); iis_start_tx(&i2s0);
// efs_mount(); efs_mount();
bkp_sram_write(MAGIC_POS, MAGIC_APP); bkp_sram_write(MAGIC_POS, MAGIC_APP);
printf("build: %s (%s)\r\n", __DATE__, __TIME__); printf("build: %s (%s)\r\n", __DATE__, __TIME__);

Loading…
Cancel
Save