|
|
@@ -0,0 +1,347 @@
|
|
|
+#include "ymodem.h"
|
|
|
+#include "ring_buf.h"
|
|
|
+#include "../swapi/subjects/serial/serial.h"
|
|
|
+
|
|
|
+static unsigned short crc16(const unsigned char *buf, unsigned long count)
|
|
|
+{
|
|
|
+ unsigned short crc = 0;
|
|
|
+ while(count--) {
|
|
|
+ crc = crc ^ *buf++ << 8;
|
|
|
+ for(int i = 0; i < 8; i++) {
|
|
|
+ if(crc & 0x8000) crc = crc << 1 ^ 0x1021;
|
|
|
+ else crc = crc << 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return crc;
|
|
|
+}
|
|
|
+
|
|
|
+static const char *u32_to_str(unsigned int val)
|
|
|
+{
|
|
|
+ static char num_str[11]; int pos = 10; num_str[10] = 0;
|
|
|
+
|
|
|
+ if(val == 0) return "0"; // If already zero then just return zero
|
|
|
+
|
|
|
+ while((val != 0) && (pos > 0)) {
|
|
|
+ num_str[--pos] = (val % 10) + '0';
|
|
|
+ val /= 10;
|
|
|
+ }
|
|
|
+
|
|
|
+ return &num_str[pos];
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned long str_to_u32(char* str)
|
|
|
+{
|
|
|
+ const char *s = str; unsigned long acc; int c;
|
|
|
+
|
|
|
+ /* Strip leading spaces if any */
|
|
|
+ do {
|
|
|
+ c = *s++;
|
|
|
+ } while(c == ' ');
|
|
|
+
|
|
|
+ for(acc = 0; (c >= '0') && (c <= '9'); c = *s++) {
|
|
|
+ c -= '0'; acc *= 10; acc += c;
|
|
|
+ }
|
|
|
+
|
|
|
+ return acc;
|
|
|
+}
|
|
|
+
|
|
|
+typedef struct
|
|
|
+{
|
|
|
+ void *h; RingBuf rx_buf;
|
|
|
+ RingBufElement rx_buf_storage[2*(PACKET_1K_SIZE+PACKET_OVERHEAD)];
|
|
|
+} SYmodemUart;
|
|
|
+
|
|
|
+static SYmodemUart s_myUart;
|
|
|
+
|
|
|
+static int uart_getchar(int timeout_ms)
|
|
|
+{
|
|
|
+ RingBufElement val; unsigned long stime, now;
|
|
|
+ if(timeout_ms >= 0) xgettickcount(&stime);
|
|
|
+ while(1) {
|
|
|
+ if(RingBuf_get(&s_myUart.rx_buf, &val)) return (int)val;
|
|
|
+ if(timeout_ms >= 0) {
|
|
|
+ xgettickcount(&now);
|
|
|
+ if((now - stime) >= (unsigned long)timeout_ms) return -1; // timeout
|
|
|
+ }
|
|
|
+ sw_thrd_delay(THRDWAIT_DELAY);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void uart_putchar(int c)
|
|
|
+{
|
|
|
+ unsigned char ch = (unsigned char)c;
|
|
|
+ serial_send_data(s_myUart.h, &ch, 1);
|
|
|
+}
|
|
|
+
|
|
|
+/* Returns 0 on success, 1 on corrupt packet, -1 on error (timeout): */
|
|
|
+static int receive_packet(char *data, int *length)
|
|
|
+{
|
|
|
+ int i, c; unsigned int packet_size;
|
|
|
+ unsigned short received_crc;
|
|
|
+
|
|
|
+ *length = 0;
|
|
|
+
|
|
|
+ c = uart_getchar(PACKET_TIMEOUT);
|
|
|
+ if(c < 0) return -1;
|
|
|
+
|
|
|
+ switch(c) {
|
|
|
+ case SOH:
|
|
|
+ packet_size = PACKET_SIZE;
|
|
|
+ break;
|
|
|
+ case STX:
|
|
|
+ packet_size = PACKET_1K_SIZE;
|
|
|
+ break;
|
|
|
+ case EOT:
|
|
|
+ return 0;
|
|
|
+ case CAN:
|
|
|
+ c = uart_getchar(PACKET_TIMEOUT);
|
|
|
+ if(c == CAN) { *length = -1; return 0; }
|
|
|
+ default:
|
|
|
+ *length = -1;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ *data = (char)c;
|
|
|
+
|
|
|
+ for(i = 1; i < (packet_size + PACKET_OVERHEAD); ++i) {
|
|
|
+ c = uart_getchar(PACKET_TIMEOUT);
|
|
|
+ if(c < 0) return -1;
|
|
|
+ data[i] = (char)c;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff)) return 1;
|
|
|
+
|
|
|
+ received_crc = (data[PACKET_HEADER + packet_size] << 8) | data[PACKET_HEADER + packet_size + 1];
|
|
|
+ if(crc16((unsigned char*)data + PACKET_HEADER, packet_size) != received_crc) return 1;
|
|
|
+
|
|
|
+ *length = packet_size;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int comio_data_recv_proc(unsigned long wParam/*传递打开的串口句柄*/, unsigned long lParam/*保留暂未使用*/)
|
|
|
+{
|
|
|
+ SYmodemUart *pComIO = &s_myUart; void *pSerial = pComIO->h;
|
|
|
+ const unsigned char *pRecvBuf = serial_get_recv_buffer(pSerial); int nRecvBytes = serial_get_recv_buffer_bytes(pSerial);
|
|
|
+ const char *log_prefix = serial_get_log_prefix(pSerial); RingBufElement val = pRecvBuf[nRecvBytes - 1]; // 1字节数据
|
|
|
+ if(!RingBuf_put(&pComIO->rx_buf, val)) {
|
|
|
+ uart_putchar(CAN); uart_putchar(CAN);
|
|
|
+ sw_log_error("%s: sorry,ring buffer full, failed to put!!!", log_prefix);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ serial_clear_recv_buffer(pSerial);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+int ymodem_recv_files(const char *dir)
|
|
|
+{
|
|
|
+ unsigned char packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD];
|
|
|
+ int packet_length, i, file_done, session_done;
|
|
|
+ unsigned int packets_received, errors, first_try, files_num;
|
|
|
+ char file_name[FILE_NAME_LENGTH], file_size[FILE_SIZE_LENGTH], *file_ptr;
|
|
|
+ unsigned char block[PACKET_1K_SIZE]; unsigned int file_size_val = 0; char path[MAX_PATH_CHARS];
|
|
|
+
|
|
|
+ RingBuf_ctor(&s_myUart.rx_buf, s_myUart.rx_buf_storage, sizeof(s_myUart.rx_buf_storage) / sizeof(RingBufElement));
|
|
|
+ s_myUart.h = serial_open(UART_DEVICE_NAME, UART_BAUD_RATE, UART_PARITY_CHECK, \
|
|
|
+ comio_data_recv_proc, NULL, NULL);
|
|
|
+ if(!s_myUart.h) return -1;
|
|
|
+
|
|
|
+ file_name[0] = '\0'; first_try = 1; files_num = 0;
|
|
|
+ for(session_done = 0, errors = 0; serial_recvThrd_isAlive(s_myUart.h); )
|
|
|
+ { // receive files
|
|
|
+ if(!first_try) uart_putchar(CRC);
|
|
|
+ first_try = 0;
|
|
|
+
|
|
|
+ for(packets_received = 0, file_done = 0; serial_recvThrd_isAlive(s_myUart.h); )
|
|
|
+ { // receive packets
|
|
|
+ switch(receive_packet((char *)packet_data, &packet_length))
|
|
|
+ {
|
|
|
+ case 0: // receive success
|
|
|
+ if(errors != 0) errors = 0;
|
|
|
+ switch(packet_length)
|
|
|
+ {
|
|
|
+ case -1: { uart_putchar(ACK); serial_close(s_myUart.h, WAITTHRD_SAFEEXIT_TIMEOUT); return -2; } // abort
|
|
|
+ case 0: { uart_putchar(ACK); file_done = 1; ++files_num; break; } // end of file
|
|
|
+ default: // normal data packet
|
|
|
+ if((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff)) { uart_putchar(NAK); break; } // sequence number error
|
|
|
+ if(packets_received == 0)
|
|
|
+ {
|
|
|
+ if(packet_data[PACKET_HEADER] != 0x00) // filename packet has data
|
|
|
+ {
|
|
|
+ for(file_ptr = (char *)packet_data + PACKET_HEADER, i = 0; *file_ptr && i < FILE_NAME_LENGTH; ) file_name[i++] = *file_ptr++;
|
|
|
+ file_name[i++] = '\0'; // 接收的文件名称
|
|
|
+ for(++file_ptr, i = 0; *file_ptr != ' ' && i < FILE_SIZE_LENGTH; ) file_size[i++] = *file_ptr++;
|
|
|
+ file_size[i++] = '\0'; // 接收的文件大小
|
|
|
+ file_size_val = str_to_u32(file_size);
|
|
|
+
|
|
|
+ uart_putchar(ACK); uart_putchar(CRC);
|
|
|
+
|
|
|
+ sw_log_debug("[%s] <文件: %s, 大小: %u> 开始接收...", UART_MODULE_NAME, file_name, file_size_val);
|
|
|
+ snprintf(path, sizeof(path), "%s/%s", dir, file_name);
|
|
|
+ if(sw_file_exists(path)) sw_file_delete(path);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ { // filename packet is empty(空包文件); end of session
|
|
|
+ uart_putchar(ACK); file_done = 1; session_done = 1; break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ for(i = 0; i < packet_length; i++) { block[i] = packet_data[PACKET_HEADER+i]; }
|
|
|
+ uart_putchar(ACK);
|
|
|
+ sw_log_debug("[%s] <文件: %s, 大小: %u> 收到第%u包数据, 数据包大小: %u", UART_MODULE_NAME, \
|
|
|
+ file_name, file_size_val, packets_received, packet_length);
|
|
|
+
|
|
|
+ if(!sw_dir_exists(dir)) sw_dir_create(dir);
|
|
|
+ if(sw_file_update(path, "ab", (char *)block, packet_length) != packet_length) { uart_putchar(CAN); uart_putchar(CAN); serial_close(s_myUart.h, WAITTHRD_SAFEEXIT_TIMEOUT); return -3; }
|
|
|
+ }
|
|
|
+ ++packets_received;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default: // receive error
|
|
|
+ if(packets_received != 0) { if(++errors >= MAX_ERRORS/*many errors*/) { uart_putchar(CAN); uart_putchar(CAN); serial_close(s_myUart.h, WAITTHRD_SAFEEXIT_TIMEOUT); return -4; } }
|
|
|
+ uart_putchar(CRC);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(file_done) break; // 文件接收完毕
|
|
|
+ }
|
|
|
+
|
|
|
+ if(session_done) break; // 传输会话结束
|
|
|
+ }
|
|
|
+
|
|
|
+ serial_close(s_myUart.h, WAITTHRD_SAFEEXIT_TIMEOUT);
|
|
|
+ return files_num;
|
|
|
+}
|
|
|
+
|
|
|
+static void send_packet(unsigned char *data, int block_no)
|
|
|
+{
|
|
|
+ int count, packet_size; unsigned short crc;
|
|
|
+
|
|
|
+ /* We use a short packet for block 0 - all others are 1K */
|
|
|
+ if(block_no == 0) packet_size = PACKET_SIZE;
|
|
|
+ else packet_size = PACKET_1K_SIZE;
|
|
|
+
|
|
|
+ crc = crc16(data, packet_size);
|
|
|
+ /* 128 byte packets use SOH, 1K use STX */
|
|
|
+ uart_putchar((block_no == 0) ? SOH : STX);
|
|
|
+ uart_putchar(block_no & 0xFF);
|
|
|
+ uart_putchar(~block_no & 0xFF);
|
|
|
+
|
|
|
+ for(count = 0; count < packet_size; count++) uart_putchar(data[count]);
|
|
|
+ uart_putchar((crc >> 8) & 0xFF);
|
|
|
+ uart_putchar(crc & 0xFF);
|
|
|
+}
|
|
|
+
|
|
|
+/* Send block 0 (the filename block). filename might be truncated to fit. */
|
|
|
+static void send_packet0(char *filename, unsigned long size)
|
|
|
+{
|
|
|
+ unsigned long count = 0; unsigned char block[PACKET_SIZE];
|
|
|
+ const char *num;
|
|
|
+
|
|
|
+ if(filename) {
|
|
|
+ while(*filename && (count < PACKET_SIZE-FILE_SIZE_LENGTH-2)) block[count++] = *filename++;
|
|
|
+ block[count++] = 0;
|
|
|
+
|
|
|
+ num = u32_to_str(size);
|
|
|
+ while(*num) block[count++] = *num++;
|
|
|
+ }
|
|
|
+
|
|
|
+ while(count < PACKET_SIZE) block[count++] = 0;
|
|
|
+
|
|
|
+ send_packet(block, 0);
|
|
|
+}
|
|
|
+
|
|
|
+static long send_data_packets(unsigned char *data, unsigned long size)
|
|
|
+{
|
|
|
+ int blockno = 1; unsigned long send_size; int ch, retry = 0;
|
|
|
+ unsigned long total_sent = 0;
|
|
|
+
|
|
|
+ while(size > 0)
|
|
|
+ {
|
|
|
+ if(size > PACKET_1K_SIZE) send_size = PACKET_1K_SIZE;
|
|
|
+ else send_size = size;
|
|
|
+
|
|
|
+ send_packet(data, blockno);
|
|
|
+ ch = uart_getchar(PACKET_TIMEOUT);
|
|
|
+ if(ch == ACK)
|
|
|
+ {
|
|
|
+ blockno++; retry = 0;
|
|
|
+ data += send_size;
|
|
|
+ size -= send_size;
|
|
|
+ total_sent += send_size; // 已发送的字节数
|
|
|
+ }
|
|
|
+ else if(ch == NAK || ch == -1)
|
|
|
+ {
|
|
|
+ retry++;
|
|
|
+ if(retry >= MAX_ERRORS) return -1; // give up after MAX_ERRORS retries
|
|
|
+ }
|
|
|
+ else if(ch == CAN)
|
|
|
+ {
|
|
|
+ uart_putchar(CAN);
|
|
|
+ uart_putchar(CAN);
|
|
|
+ return -2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ retry = 0; do {
|
|
|
+ uart_putchar(EOT);
|
|
|
+ ch = uart_getchar(PACKET_TIMEOUT);
|
|
|
+ } while((ch != ACK) && (ch != -1) && (++retry < MAX_ERRORS));
|
|
|
+
|
|
|
+ if(ch != ACK) return -3;
|
|
|
+
|
|
|
+ // 最后发送一个空包文件, 结束文件接收
|
|
|
+ ch = uart_getchar(PACKET_TIMEOUT);
|
|
|
+ if(ch == CRC)
|
|
|
+ {
|
|
|
+ retry = 0; do {
|
|
|
+ send_packet0(0, 0);
|
|
|
+ ch = uart_getchar(PACKET_TIMEOUT);
|
|
|
+ } while((ch != ACK) && (ch != -1) && (++retry < MAX_ERRORS));
|
|
|
+
|
|
|
+ if(ch != ACK) return -4;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (long)total_sent;
|
|
|
+}
|
|
|
+
|
|
|
+int ymodem_send_file(const char *path)
|
|
|
+{
|
|
|
+ char file_name[FILE_NAME_LENGTH], *file_buf = NULL;
|
|
|
+ int file_size, ch, crc_nak = 1, retry, ret = 0;
|
|
|
+
|
|
|
+ if(!sw_file_exists(path)) return -1;
|
|
|
+ snprintf(file_name, sizeof(file_name), "%s", xGetPathFileName(path));
|
|
|
+ file_size = sw_file_getSize(path);
|
|
|
+ if(file_size <= 0) return -2;
|
|
|
+
|
|
|
+ file_buf = (char*)malloc(file_size);
|
|
|
+ if(!file_buf) return -3;
|
|
|
+
|
|
|
+ if(sw_file_load(path, "rb", file_buf, file_size) != file_size) { ret = -4; goto ret_p; }
|
|
|
+
|
|
|
+ RingBuf_ctor(&s_myUart.rx_buf, s_myUart.rx_buf_storage, sizeof(s_myUart.rx_buf_storage) / sizeof(RingBufElement));
|
|
|
+ s_myUart.h = serial_open(UART_DEVICE_NAME, UART_BAUD_RATE, UART_PARITY_CHECK, \
|
|
|
+ comio_data_recv_proc, NULL, NULL);
|
|
|
+ if(!s_myUart.h) { ret = -5; goto ret_p; }
|
|
|
+
|
|
|
+ retry = 0; do { ch = uart_getchar(PACKET_TIMEOUT); } while(ch != CRC && ++retry < MAX_ERRORS);
|
|
|
+ if(ch != CRC) { uart_putchar(CAN); uart_putchar(CAN); ret = -6; goto ret_p; }
|
|
|
+
|
|
|
+ retry = 0; do
|
|
|
+ {
|
|
|
+ send_packet0(file_name, (unsigned long)file_size);
|
|
|
+ ch = uart_getchar(PACKET_TIMEOUT);
|
|
|
+ if(ch == ACK)
|
|
|
+ {
|
|
|
+ ch = uart_getchar(PACKET_TIMEOUT);
|
|
|
+ if(ch == CRC) { ret = send_data_packets((unsigned char *)file_buf, file_size); if(ret < 0) { ret += -7; } break; }
|
|
|
+ }
|
|
|
+ else if((ch == CRC) && (crc_nak)) { crc_nak = 0; continue; } // 接收到CRC, 重发一次0包
|
|
|
+ else if((ch != NAK) || (crc_nak)) { break; } // 接收到NAK, 需要重发一次0包
|
|
|
+ } while(++retry < MAX_ERRORS);
|
|
|
+
|
|
|
+ret_p:
|
|
|
+ if(file_buf) free(file_buf);
|
|
|
+ if(s_myUart.h) serial_close(s_myUart.h, WAITTHRD_SAFEEXIT_TIMEOUT); s_myUart.h = NULL;
|
|
|
+ return ret;
|
|
|
+}
|