#include "ymodem.h" #include #include #include #define YMODEM_SEND_MAX_PACKET_SIZE PACKET_1K_SIZE // #define YMODEM_SEND_MAX_PACKET_SIZE PACKET_SIZE #define YMODEM_DEBUG 1 #if YMODEM_DEBUG #define ym_printf(...) printf(__VA_ARGS__) #else #define ym_printf(...) #endif /*YMODEM_DEBUG*/ /* http://www.ccsinfo.com/forum/viewtopic.php?t=24977 */ unsigned short crc16(const unsigned char *buf, unsigned long count) { unsigned short crc = 0; int i; while(count--) { crc = crc ^ *buf++ << 8; for (i = 0; i < 8; i++) { if (crc & 0x8000) { crc = crc << 1 ^ 0x1021; } else { crc = crc << 1; } } } return crc; } 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; } static const char *u32_to_str(unsigned int val) { /* Maximum number of decimal digits in u32 is 10 */ static char num_str[11]; int pos = 10; num_str[10] = 0; if (val == 0) { /* If already zero then just return zero */ return "0"; } while ((val != 0) && (pos > 0)) { num_str[--pos] = (val % 10) + '0'; val /= 10; } return &num_str[pos]; } static int ymodem_show_packet(unsigned char *packet, unsigned int packet_size) { #if YMODEM_DEBUG switch (packet[0]) { case YMODEM_CODE_SOH: ym_printf("SOH "); break; case YMODEM_CODE_STX: ym_printf("STX "); break; case YMODEM_CODE_EOT: ym_printf("EOT "); break; case YMODEM_CODE_ACK: ym_printf("ACK "); break; case YMODEM_CODE_NAK: ym_printf("NAK "); break; case YMODEM_CODE_CAN: ym_printf("CAN "); break; default: break; } if (packet_size) { ym_printf("%02X %02X Data[%d] CRC CRC\r\n", packet[1], packet[2], packet_size); } else { ym_printf("\r\n"); } #endif return 0; } static int ymodem_putc_and_show(struct ymodem *ymodem, unsigned char ch) { if (ch == YMODEM_CODE_EOT) { ym_printf("EOT\r\n"); return ymodem->putchar(ch); } #if YMODEM_DEBUG ym_printf("\t\t\t\t"); switch (ch) { case YMODEM_CODE_CRC: ym_printf("%d ",ch); ym_printf("C\r\n"); break; case YMODEM_CODE_CAN: ym_printf("%d ",ch); ym_printf("CAN\r\n"); break; case YMODEM_CODE_NAK: ym_printf("%d ",ch); ym_printf("NAK\r\n"); break; case YMODEM_CODE_ACK: ym_printf("%d ",ch); ym_printf("ACK\r\n"); break; default: ym_printf("%d ",ch); break; } #endif /*YMODEM_DEBUG*/ return ymodem->putchar(ch); } static int ymodem_getc_and_show(struct ymodem *ymodem) { int ch; ch = ymodem->getchar(); #if YMODEM_DEBUG ym_printf("\t\t\t\t"); switch (ch) { case YMODEM_CODE_CRC: ym_printf("0x%X ",ch); ym_printf("C\r\n"); break; case YMODEM_CODE_CAN: ym_printf("0x%X ",ch); ym_printf("CAN\r\n"); break; case YMODEM_CODE_NAK: ym_printf("0x%X ",ch); ym_printf("NAK\r\n"); break; case YMODEM_CODE_ACK: ym_printf("0x%X ",ch); ym_printf("ACK\r\n"); break; default: ym_printf("0x%X ",ch); ym_printf("CAN\r\n"); break; } #endif /*YMODEM_DEBUG*/ return ch; } static int receive_packet(struct ymodem *ymodem, unsigned char *data, unsigned long *length) { int i, c; unsigned long packet_size; *length = 0; c = ymodem->getchar(); if (c < 0) return -1; *data = (unsigned char)c; switch(c) { case YMODEM_CODE_SOH: packet_size = PACKET_SIZE; break; case YMODEM_CODE_STX: packet_size = PACKET_1K_SIZE; break; case YMODEM_CODE_EOT: packet_size = 0; goto OUT; case YMODEM_CODE_CAN: c = ymodem->getchar(); if (c == YMODEM_CODE_CAN) { return 0; } default: /* This case could be the result of corruption on the first octet * of the packet, but it's more likely that it's the user banging * on the terminal trying to abort a transfer. Technically, the * former case deserves a NAK, but for now we'll just treat this * as an abort case. */ return -1; } for(i = 1; i < (packet_size + PACKET_OVERHEAD); ++i) { c = ymodem->getchar(); if (c < 0) { return -1; } data[i] = (unsigned char)c; } /* Just a sanity check on the sequence number/complement value. * Caller should check for in-order arrival. */ if (data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff)) { return 1; } if (crc16(data + PACKET_HEADER, packet_size + PACKET_TRAILER) != 0) { return 1; } *length = packet_size; OUT: ymodem_show_packet(data, packet_size); return 0; } static int ymodem_handshake(struct ymodem *ymodem) { int timeout = YMODEM_TIMEOUT; int ret = -1; unsigned long packet_size = 0; unsigned char packet[PACKET_OVERHEAD+PACKET_1K_SIZE] = {0}; char *file_size; ymodem->stage = YMODEM_STAGE_ESTABLISHING; while (timeout--) { ymodem_putc_and_show(ymodem, YMODEM_CODE_CRC); //send 'C' ret = receive_packet(ymodem, packet, &packet_size); if (ret == 0 && packet_size >= PACKET_SIZE) { strcpy(ymodem->file_name, (const char *)&packet[PACKET_HEADER]); file_size = (char *)&packet[PACKET_HEADER + strlen(ymodem->file_name) + 1]; ymodem->file_size = str_to_u32(file_size); ym_printf("file name : %s, size : %ld\r\n", ymodem->file_name, ymodem->file_size); if (ymodem->begin_packet_cb) { if (ymodem->begin_packet_cb(&packet[PACKET_HEADER], packet_size) != 0) return -1; } ymodem_putc_and_show(ymodem, YMODEM_CODE_ACK); ymodem_putc_and_show(ymodem, YMODEM_CODE_CRC); break; } else if (ret == -1) { //timeout } else if (ret == 1) { //check failed } } if (timeout <= 0) return -1; return 0; } int ymodem_receive_file_data(struct ymodem *ymodem) { unsigned char packet[PACKET_OVERHEAD+PACKET_1K_SIZE]; unsigned long packet_size = 0; int i, ret = -1; int retry = 0; ymodem->stage = YMODEM_STAGE_TRANSMITTING; while (1) { ret = receive_packet(ymodem, packet, &packet_size); if (ret == 0) { if (packet_size) { if (ymodem->data_packet_cb && ymodem->data_packet_cb(&packet[PACKET_HEADER], packet_size) != 0) { return -1; } ymodem_putc_and_show(ymodem, YMODEM_CODE_ACK); } else if (packet[0] == YMODEM_CODE_CAN) { /* the spec require multiple CAN */ for (i = 0; i < YMODEM_END_SESSION_SEND_CAN_NUM; i++) { ymodem_putc_and_show(ymodem, YMODEM_CODE_CAN); } } else if (packet[0] == YMODEM_CODE_EOT) { //EOT return 0; } } else { if (retry++ > MAX_ERRORS) { break; } else { ym_printf("retry!\r\n"); } } } return -1; } static int ymodem_recv_finsh(struct ymodem *ymodem) { int ch, ret; unsigned char packet[PACKET_OVERHEAD+PACKET_1K_SIZE]; unsigned long packet_size = 0; ymodem->stage = YMODEM_STAGE_FINISHING; /* we already got one EOT in the caller. invoke the callback if there is * one. */ ymodem_putc_and_show(ymodem, YMODEM_CODE_NAK); ch = ymodem->getchar(); if (ch != YMODEM_CODE_EOT) return -1; ymodem_show_packet((unsigned char*)&ch, 0); ymodem_putc_and_show(ymodem, YMODEM_CODE_ACK); ymodem_putc_and_show(ymodem, YMODEM_CODE_CRC); ret = receive_packet(ymodem, packet, &packet_size); if (ret != 0) return -1; if (packet_size != PACKET_SIZE) return -1; ymodem_putc_and_show(ymodem, YMODEM_CODE_ACK); if (ymodem->end_packet_cb) ymodem->end_packet_cb(&packet[PACKET_HEADER], packet_size); if (packet[3] != 0x0) { //还有文件要接收 if (ymodem->begin_packet_cb) ymodem->begin_packet_cb(&packet[PACKET_HEADER], packet_size); ymodem_putc_and_show(ymodem, YMODEM_CODE_CRC); return 0; } ymodem->stage = YMODEM_STAGE_FINISHED; return 0; } /* Figure 3. YMODEM Batch Transmission Session (1 file) SENDER RECEIVER "sb foo.*" "sending in batch mode etc." C (command:rb) SOH 00 FF foo.c NUL[123] CRC CRC ACK C SOH 01 FE Data[128] CRC CRC ACK SOH 02 FC Data[128] CRC CRC ACK SOH 03 FB Data[100] CPMEOF[28] CRC CRC ACK EOT NAK EOT ACK C SOH 00 FF NUL[128] CRC CRC ACK */ /* Figure 4. YMODEM Batch Transmission Session (2 files) SENDER RECEIVER "sb foo.c baz.c" "sending in batch mode etc." C (command:rb) SOH 00 FF foo.c NUL[123] CRC CRC ACK C SOH 01 FE Data[128] CRC CRC ACK SOH 02 FC Data[128] CRC CRC ACK SOH 03 FB Data[100] CPMEOF[28] CRC CRC ACK EOT NAK EOT ACK C SOH 00 FF baz.c NUL[123] CRC CRC ACK C SOH 01 FB Data[100] CPMEOF[28] CRC CRC ACK EOT NAK EOT ACK C SOH 00 FF NUL[128] CRC CRC ACK */ int ymodem_receive(struct ymodem *ymodem) { int ret = -1; if (NULL == ymodem || NULL == ymodem->getchar || NULL == ymodem->putchar) return -1; ymodem->stage = YMODEM_STAGE_NONE; //handshake ret = ymodem_handshake(ymodem); if (ret != 0) return ret; while (1) { ret = ymodem_receive_file_data(ymodem); if (ret != 0) return ret; ret = ymodem_recv_finsh(ymodem); if (ret != 0) return ret; if (ymodem->stage == YMODEM_STAGE_FINISHED) break; } return 0; } static void send_packet(struct ymodem *ymodem, unsigned char *data, int block_no, int packet_size) { int count, crc; unsigned char packet[3]; crc = crc16(data, packet_size); /* 128 byte packets use SOH, 1K use STX */ packet[0] = (packet_size==PACKET_SIZE)?YMODEM_CODE_SOH:YMODEM_CODE_STX; ymodem->putchar(packet[0]); packet[1] = (block_no & 0xFF); ymodem->putchar(packet[1]); packet[2] = (~block_no & 0xFF); ymodem->putchar(packet[2]); for (count=0; countputchar(data[count]); ymodem->putchar((crc >> 8) & 0xFF); ymodem->putchar(crc & 0xFF); ymodem_show_packet(packet, packet_size); } /* Send block 0 (the filename block). filename might be truncated to fit. */ static void send_packet0(struct ymodem *ymodem, 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++; } ym_printf("file name: %s, size: %d\r\n", block, size); } while (count < PACKET_SIZE) { block[count++] = 0; } send_packet(ymodem, block, 0, PACKET_SIZE); if (ymodem->begin_packet_cb) ymodem->begin_packet_cb(block, PACKET_SIZE); } static void send_data_packets(struct ymodem *ymodem) { int blockno = 1; unsigned long i, size, send_size; int ch; unsigned char packet_data[PACKET_1K_SIZE]; unsigned char *data = ymodem->data; int retry = 0; size = ymodem->file_size; while (size > 0) { if (size > PACKET_SIZE) { send_size = PACKET_1K_SIZE; } else { send_size = PACKET_SIZE; } if (send_size > YMODEM_SEND_MAX_PACKET_SIZE) send_size = YMODEM_SEND_MAX_PACKET_SIZE; if (size < send_size) { memcpy(packet_data, data, size); //需要填充0x1A for (i = size; i < send_size; i++) packet_data[i] = 0x1A; } else { memcpy(packet_data, data, send_size); } send_packet(ymodem, packet_data, blockno, send_size); if (ymodem->data_packet_cb) ymodem->data_packet_cb(packet_data, send_size); ch = ymodem_getc_and_show(ymodem); if (ch == YMODEM_CODE_ACK) { retry = 0; blockno++; data += send_size; if (size < send_size) size = 0; else size -= send_size; } else { if (ch == YMODEM_CODE_NAK) { if (retry++ > MAX_ERRORS) return; } if((ch == YMODEM_CODE_CAN) || (ch == -1)) { return; } } } do { ymodem_putc_and_show(ymodem, YMODEM_CODE_EOT); ch = ymodem_getc_and_show(ymodem); } while((ch != YMODEM_CODE_ACK) && (ch != -1)); /* Send last data packet */ if (ch == YMODEM_CODE_ACK) { ch = ymodem_getc_and_show(ymodem); if (ch == YMODEM_CODE_CRC) { do { send_packet0(ymodem, 0, 0); ch = ymodem_getc_and_show(ymodem); } while((ch != YMODEM_CODE_ACK) && (ch != -1)); } } if (ymodem->end_packet_cb) ymodem->end_packet_cb(NULL, 0); } /* * 为了简单,需要一次性将文件内容复制到ymodem->data中, * 大多数情况都是下位机发送,不会遇到内存问题. * */ int ymodem_send(struct ymodem *ymodem) { int ch, crc_nak = 1; int retry = 0; put_start_char(); printf("put start_char\n"); usleep(300000); if (flush_serial_input() == 0) { printf("Serial input buffer flushed.\n"); } do { ch = ymodem_getc_and_show(ymodem); } while (retry++ < YMODEM_TIMEOUT && ch != YMODEM_CODE_CRC); if (ch == YMODEM_CODE_CRC) { while (1) { send_packet0(ymodem, ymodem->file_name, ymodem->file_size); ch = ymodem_getc_and_show(ymodem); if (ch == YMODEM_CODE_ACK) { ch = ymodem_getc_and_show(ymodem); if (ch == YMODEM_CODE_CRC) { send_data_packets(ymodem); return 0; } // } else if ((ch == YMODEM_CODE_CRC) && (crc_nak)) { } else if ((ch == YMODEM_CODE_CRC) && (crc_nak)) { crc_nak = 0; continue; // } else if ((ch != YMODEM_CODE_NAK) || (crc_nak)) { } else if ((ch != YMODEM_CODE_NAK)) { break; } } } else { ymodem_putc_and_show(ymodem, YMODEM_CODE_CAN); ymodem_putc_and_show(ymodem, YMODEM_CODE_CAN); } return -1; }