/******************************************************************************************** * File: xmodem.c * Version: 1.0.0 * Copyright: 2011 (c) Guo Wenxue * Description: Xmodem protocal used to download the second stage bootloader(launcher) * ChangeLog: 1, Release initial version on "Tue Jul 12 16:43:18 CST 2011" * *******************************************************************************************/ #include "common.h" #define XMODEM_SOH 0x01 /*Start Of Header, standard Xmodem */ #define XMODEM_STX 0x02 /*1K-Modem start Xmodem */ #define XMODEM_EOT 0x04 /*End Of Transmission */ #define XMODEM_ACK 0x06 #define XMODEM_NAK 0x15 #define XMODEM_CAN 0x18 /*Stop transmission */ #define XMODEM_EOF 0x1a /*CTRL+Z EOF */ #define XMODEM_BLOCK_SIZE 128 static int xmodem_wait(void) { long cnt = 0; while (!serial_tstc()) { if (++cnt >= 20000000) { cnt = 0; serial_putc(XMODEM_NAK); } } return 0; } static int xmodem_read_block(unsigned char block_number, char *buf) { unsigned char c, block_num, check_sum; int i; block_num = serial_getc(); if (block_num != block_number) return -1; block_num ^= serial_getc(); if (block_num != 0xff) return -1; check_sum = 0; for (i = 0; i < XMODEM_BLOCK_SIZE; i++) { c = serial_getc(); *(buf++) = c; check_sum += c; } check_sum ^= serial_getc(); if (check_sum) return -1; return i; } long xmodem_recv(char *buf) { int r, receiving = 0; long size = 0; unsigned char c, block_number = 1; while (1) { if (!receiving) xmodem_wait(); c = serial_getc(); switch (c) { case XMODEM_EOT: serial_putc(XMODEM_ACK); /*Remove the CMPEOF */ long cnt = 0; if ((buf[-1] == XMODEM_EOF) && (buf[-2] == XMODEM_EOF) && (buf[-3] == XMODEM_EOF)) { while (size && buf[-cnt - 1] == XMODEM_EOF) cnt++; } size -= cnt; goto RECV_OK; case XMODEM_CAN: return -1; case XMODEM_SOH: receiving++; r = xmodem_read_block(block_number, buf); if (r < 0) { serial_putc(XMODEM_NAK); } else { block_number++; size += r; buf += r; serial_putc(XMODEM_ACK); } break; default: if (receiving) return -1; } } RECV_OK: return size; }