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