| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- #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包数据
- } SYmodemCom;
- static SYmodemCom s_myCom;
- 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_myCom.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_myCom.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/*保留暂未使用*/)
- {
- SYmodemCom *pComIO = &s_myCom; 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, waits, first_try, files_num;
- char file_name[FILE_NAME_LENGTH], file_size[FILE_SIZE_LENGTH], *file_ptr, path[MAX_PATH_CHARS];
- unsigned char file_data[PACKET_1K_SIZE]; unsigned int file_size1 = 0, file_size2 = 0;
- RingBuf_ctor(&s_myCom.rx_buf, s_myCom.rx_buf_storage, sizeof(s_myCom.rx_buf_storage)/sizeof(RingBufElement));
- s_myCom.h = serial_open(UART_DEVICE_NAME, UART_BAUD_RATE, UART_PARITY_CHECK, \
- comio_data_recv_proc, NULL, NULL);
- if(!s_myCom.h) return -1;
- file_name[0] = '\0'; first_try = 1; ret = files_num = 0;
- for(session_done = 0, errors = 0, waits = 0; serial_recvThrd_isAlive(s_myCom.h); )
- { // 批量的接收文件
- if(!first_try) uart_putchar(CRC);
- first_try = 0;
- for(packets_received = 0, file_done = 0; serial_recvThrd_isAlive(s_myCom.h); )
- { // 接收单个文件
- switch(receive_packet(packet_data, &packet_length))
- {
- case 0: // receive success
- if(errors != 0) errors = 0;
- if(waits != 0) waits = 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_size1 = str_to_u32(file_size); file_size2 = 0;
- uart_putchar(ACK); uart_putchar(CRC);
- sw_log_debug("[%s] <文件: %s, 大小: %u字节> 开始接收...", UART_MODULE_NAME, file_name, file_size1);
- 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++) { file_data[i] = packet_data[PACKET_HEADER+i]; }
- uart_putchar(ACK);
- sw_log_debug("[%s] <文件: %s, 大小: %u字节> 收到第%u包数据, 数据包大小: %u字节", \
- UART_MODULE_NAME, file_name, file_size1, packets_received, packet_length);
- if(!sw_dir_exists(dir)) sw_dir_create(dir);
- if((file_size1-file_size2) < packet_length) packet_length = (file_size1-file_size2); // 最后一包数据可能小于PACKET_1K_SIZE
- ret = sw_file_update(path, "ab", (char *)file_data, packet_length);
- if(ret != packet_length) { uart_putchar(CAN); uart_putchar(CAN); ret = -3; goto ret_p; }
- file_size2 += ret;
- }
- ++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; } }
- else { if(++waits >= MAX_START_WAITS) { uart_putchar(CAN); uart_putchar(CAN); ret = -5; 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_myCom.h) serial_close(s_myCom.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 zero_block[PACKET_SIZE];
- const char *num; const char *str = (filename ? filename : "null");
- if(filename) {
- while(*filename && (count < PACKET_SIZE-FILE_SIZE_LENGTH-2)) zero_block[count++] = *filename++;
- zero_block[count++] = 0;
- num = u32_to_str(size);
- while(*num) zero_block[count++] = *num++;
- }
- while(count < PACKET_SIZE) zero_block[count++] = 0;
- send_packet(zero_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; unsigned char last_block[PACKET_1K_SIZE];
- while(size > 0)
- {
- if(size >= PACKET_1K_SIZE) send_size = PACKET_1K_SIZE;
- else
- { // 最后一包
- memset(last_block, 0x1A, sizeof(last_block));
- memcpy(last_block, data, size);
- send_size = size; data = last_block;
- }
- 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_myCom.rx_buf, s_myCom.rx_buf_storage, sizeof(s_myCom.rx_buf_storage)/sizeof(RingBufElement));
- s_myCom.h = serial_open(UART_DEVICE_NAME, UART_BAUD_RATE, UART_PARITY_CHECK, \
- comio_data_recv_proc, NULL, NULL);
- if(!s_myCom.h) { ret = -5; goto ret_p; }
- retry = 0; do { ch = uart_getchar(PACKET_TIMEOUT); } while(ch != CRC && ++retry < MAX_START_WAITS); // 等待接收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; } goto ret_p; }
- }
- else if((ch == CRC) && (crc_nak)) { crc_nak = 0; continue; } // 发送0包后, 接收到CRC, 重新开始一次发送(加强容错)
- else if((ch != NAK) || (crc_nak)) { sw_log_error("[%s] %s 接收到错误应答: 0x%02X", UART_MODULE_NAME, send_log_prefix, ch); goto ret_p; } // 发送0包后, 接收到NAK, 重新开始发送
- } while(++retry < MAX_ERRORS && serial_recvThrd_isAlive(s_myCom.h));
- ret_p:
- if(file_buf) free(file_buf);
- if(s_myCom.h) serial_close(s_myCom.h, WAITTHRD_SAFEEXIT_TIMEOUT);
- return ret;
- }
|