#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; 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)]; // 最大缓存2包数据 } 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(unsigned 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(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 *send_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!!", send_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, ret; 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; ret = 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(packet_data, &packet_length)) { case 0: // receive success if(errors != 0) errors = 0; switch(packet_length) { case -1: { uart_putchar(ACK); ret = -2; goto ret_p; } // 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); ret = sw_file_update(path, "ab", (char *)block, packet_length); if(ret != packet_length) { uart_putchar(CAN); uart_putchar(CAN); ret = -3; goto ret_p; } } ++packets_received; // 累加已接收包数 } break; default: // receive error if(packets_received != 0) { if(++errors >= MAX_ERRORS/* too many errors */) { uart_putchar(CAN); uart_putchar(CAN); ret = -4; goto ret_p; } } uart_putchar(CRC); } if(file_done) { if(!session_done) sw_log_debug("[%s] 文件: %s 接收完成, 实际接收文件大小: %u字节", UART_MODULE_NAME, file_name, sw_file_getSize(path)); break; } // 文件接收完成 } if(session_done) { ret = files_num; break; } // 传输会话结束 } ret_p: if(s_myUart.h) serial_close(s_myUart.h, WAITTHRD_SAFEEXIT_TIMEOUT); return ret; } static void send_packet(unsigned char *data, int block_no) { int count, packet_size; unsigned short crc; if(block_no == 0) packet_size = PACKET_SIZE; else packet_size = PACKET_1K_SIZE; crc = crc16(data, packet_size); 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 char send_log_prefix[MAX_LINE_CHARS]; static void send_packet0(char *filename, unsigned long size) { unsigned long count = 0; unsigned char block[PACKET_SIZE]; const char *num; const char *str = (filename ? filename : "null"); 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); snprintf(send_log_prefix, sizeof(send_log_prefix), "<文件: %s, 大小: %lu字节>", str, size); sw_log_debug("[%s] %s 开始发送...", UART_MODULE_NAME, send_log_prefix); } 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); sw_log_debug("[%s] %s 已发送第%u包数据, 数据包大小: %lu字节", UART_MODULE_NAME, send_log_prefix, blockno, send_size); 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); sw_log_debug("[%s] %s 文件发送已完成, 实际发送字节数: %lu字节", UART_MODULE_NAME, send_log_prefix, total_sent); 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); sw_log_debug("[%s] %s 已发送空包文件, 等待传输会话结束", UART_MODULE_NAME, send_log_prefix); 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 < 30); // 等待接收CRC, 而后才开始发送文件 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)) { sw_log_error("[%s] %s 接收到错误应答: 0x%02X", UART_MODULE_NAME, send_log_prefix, ch); break; } // 接收到NAK, 需要重发一次0包 } while(++retry < MAX_ERRORS && serial_recvThrd_isAlive(s_myUart.h)); ret_p: if(file_buf) free(file_buf); if(s_myUart.h) serial_close(s_myUart.h, WAITTHRD_SAFEEXIT_TIMEOUT); return ret; }