From b1337f7b95d7e77bf771e187b44e3e3b32123170 Mon Sep 17 00:00:00 2001 From: Piotr Dymacz Date: Wed, 29 Apr 2015 16:00:32 +0200 Subject: [PATCH] Restore support for loadb, loads and saves commands. Still needs some tests and fixes. --- u-boot/common/Makefile | 6 +- u-boot/common/cmd_load.c | 1149 +++++++++++++++++++++++++++++++++++++ u-boot/common/crc16.c | 108 ++++ u-boot/common/s_record.c | 212 +++++++ u-boot/common/xyzModem.c | 607 ++++++++++++++++++++ u-boot/include/s_record.h | 114 ++++ u-boot/include/xyzModem.h | 90 +++ 7 files changed, 2285 insertions(+), 1 deletion(-) create mode 100755 u-boot/common/cmd_load.c create mode 100755 u-boot/common/crc16.c create mode 100755 u-boot/common/s_record.c create mode 100755 u-boot/common/xyzModem.c create mode 100755 u-boot/include/s_record.h create mode 100755 u-boot/include/xyzModem.h diff --git a/u-boot/common/Makefile b/u-boot/common/Makefile index aea6c08..9fdb706 100644 --- a/u-boot/common/Makefile +++ b/u-boot/common/Makefile @@ -36,6 +36,7 @@ COBJS = main.o \ cmd_net.o \ cmd_nvedit.o \ cmd_itest.o \ + cmd_load.o \ command.o \ console.o \ devices.o \ @@ -47,7 +48,10 @@ COBJS = main.o \ flash.o \ lists.o \ env_flash.o \ - hush.o + hush.o \ + xyzModem.o \ + crc16.o \ + s_record.o OBJS = $(AOBJS) $(COBJS) diff --git a/u-boot/common/cmd_load.c b/u-boot/common/cmd_load.c new file mode 100755 index 0000000..3d9172a --- /dev/null +++ b/u-boot/common/cmd_load.c @@ -0,0 +1,1149 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * Serial up- and download support + */ +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#if (CONFIG_COMMANDS & CFG_CMD_LOADB) +static ulong load_serial_ymodem(ulong offset); +#endif + +#if (CONFIG_COMMANDS & CFG_CMD_LOADS) +static ulong load_serial(ulong offset); +static int read_record(char *buf, ulong len); + +#if (CONFIG_COMMANDS & CFG_CMD_SAVES) +static int save_serial(ulong offset, ulong size); +static int write_record(char *buf); +# endif /* CFG_CMD_SAVES */ + +static int do_echo = 1; +#endif /* CFG_CMD_LOADS */ + +#if (CONFIG_COMMANDS & CFG_CMD_LOADB) || defined(CFG_LOADS_BAUD_CHANGE) +static const unsigned long baudrate_table[] = CFG_BAUDRATE_TABLE; +#define N_BAUDRATES (sizeof(baudrate_table) / sizeof(baudrate_table[0])) + +static void switch_baudrate(int baudrate, int back) +{ + printf("Switch%s baudrate to %d bps and press ENTER...\n", back > 0 ? " back" : "", baudrate); + udelay(50000); + + gd->baudrate = baudrate; + + serial_setbrg(); + + udelay(50000); + + for(;;){ + if(getc() == '\r'){ + break; + } + } +} +#endif + +/* -------------------------------------------------------------------- */ + +#if (CONFIG_COMMANDS & CFG_CMD_LOADS) +int do_load_serial(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + ulong offset = 0; + ulong addr; + int i; + char *env_echo; + int rcode = 0; +#ifdef CFG_LOADS_BAUD_CHANGE + int load_baudrate, current_baudrate; + + load_baudrate = current_baudrate = gd->baudrate; +#endif + + if(((env_echo = getenv("loads_echo")) != NULL) && (*env_echo == '1')){ + do_echo = 1; + } else { + do_echo = 0; + } + +#ifdef CFG_LOADS_BAUD_CHANGE + if(argc >= 2){ + offset = simple_strtoul(argv[1], NULL, 16); + } + + if(argc == 3){ + load_baudrate = (int)simple_strtoul(argv[2], NULL, 10); + + /* default to current baudrate */ + if(load_baudrate == 0) + load_baudrate = current_baudrate; + } + + if(load_baudrate != current_baudrate){ + for(i = 0; i < N_BAUDRATES; ++i){ + if(load_baudrate == baudrate_table[i]){ + break; + } + } + + if(i == N_BAUDRATES){ + printf("## Error: baudrate %d bps is not supported, will use current: %d bps\n", + load_baudrate, current_baudrate); + } else { + switch_baudrate(load_baudrate, 0); + } + } +#else /* ! CFG_LOADS_BAUD_CHANGE */ + if(argc == 2){ + offset = simple_strtoul(argv[1], NULL, 16); + } +#endif /* CFG_LOADS_BAUD_CHANGE */ + + printf("Ready for S-Record download...\n"); + + addr = load_serial(offset); + + /* + * Gather any trailing characters (for instance, the ^D which + * is sent by 'cu' after sending a file), and give the + * box some time (100 * 1 ms) + */ + for(i=0; i<100; ++i){ + if(tstc()){ + (void)getc(); + } + + udelay(1000); + } + + if(addr == ~0){ + printf("## Error: S-Record download aborted!\n"); + rcode = 1; + } else { + printf("Start address = 0x%08lX\n", addr); + load_addr = addr; + } + +#ifdef CFG_LOADS_BAUD_CHANGE + if(load_baudrate != current_baudrate){ + switch_baudrate(current_baudrate, 1); + } +#endif + + return rcode; +} + +static ulong load_serial(ulong offset) +{ + char record[SREC_MAXRECLEN + 1]; /* buffer for one S-Record */ + char binbuf[SREC_MAXBINLEN]; /* buffer for binary data */ + int binlen; /* no. of data bytes in S-Rec. */ + int type; /* return code for record type */ + ulong addr; /* load address from S-Record */ + ulong size; /* number of bytes transferred */ + char buf[32]; + ulong store_addr; + ulong start_addr = ~0; + ulong end_addr = 0; + int line_count = 0; + + while(read_record(record, SREC_MAXRECLEN + 1) >= 0){ + type = srec_decode(record, &binlen, &addr, binbuf); + + if(type < 0){ + return(~0); /* Invalid S-Record */ + } + + switch(type){ + case SREC_DATA2: + case SREC_DATA3: + case SREC_DATA4: + store_addr = addr + offset; +#ifndef CFG_NO_FLASH + if(addr2info(store_addr)){ + int rc = flash_write((char *)binbuf,store_addr,binlen); + + if(rc != 0){ + flash_perror(rc); + return(~0); + } + } else +#endif + { + memcpy((char *)(store_addr), binbuf, binlen); + } + + if((store_addr) < start_addr) + start_addr = store_addr; + + if((store_addr + binlen - 1) > end_addr) + end_addr = store_addr + binlen - 1; + break; + case SREC_END2: + case SREC_END3: + case SREC_END4: + udelay(10000); + + size = end_addr - start_addr + 1; + printf("\n" + "First load address = 0x%08lX\n" + "Last load address = 0x%08lX\n" + "Total size = 0x%08lX = %ld bytes\n", + start_addr, end_addr, size, size); + + flush_cache(start_addr, size); + + sprintf(buf, "%lX", size); + setenv("filesize", buf); + + return(addr); + case SREC_START: + break; + default: + break; + } + + /* print a '.' every 100 lines */ + if(!do_echo){ + if((++line_count % 100) == 0) + putc('.'); + } + } + + /* Download aborted */ + return(~0); +} + +static int read_record(char *buf, ulong len) +{ + char *p; + char c; + + /* always leave room for terminating '\0' byte */ + --len; + + for(p=buf; p < buf+len; ++p){ + /* read character */ + c = getc(); + + /* ... and echo it */ + if(do_echo) + putc(c); + + switch(c){ + case '\r': + case '\n': + *p = '\0'; + return(p - buf); + case '\0': + /* ^C - Control C */ + case 0x03: + return(-1); + default: + *p = c; + } + + /* Check for the console hangup (if any different from serial) */ + if(gd->jt[XF_getc] != getc) { + if(ctrlc()){ + return(-1); + } + } + } + + /* line too long - truncate */ + *p = '\0'; + return(p - buf); +} + +#if (CONFIG_COMMANDS & CFG_CMD_SAVES) +int do_save_serial(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + ulong offset = 0; + ulong size = 0; +#ifdef CFG_LOADS_BAUD_CHANGE + int save_baudrate, current_baudrate, i; + + save_baudrate = current_baudrate = gd->baudrate; +#endif + + if(argc >= 2){ + offset = simple_strtoul(argv[1], NULL, 16); + } +#ifdef CFG_LOADS_BAUD_CHANGE + if(argc >= 3){ + size = simple_strtoul(argv[2], NULL, 16); + } + + if(argc == 4){ + save_baudrate = (int)simple_strtoul(argv[3], NULL, 10); + + /* default to current baudrate */ + if(save_baudrate == 0) + save_baudrate = current_baudrate; + } + + if(save_baudrate != current_baudrate){ + for(i = 0; i < N_BAUDRATES; ++i){ + if(save_baudrate == baudrate_table[i]){ + break; + } + } + + if(i == N_BAUDRATES){ + printf("## Error: baudrate %d bps is not supported, will use current: %d bps\n", + save_baudrate, current_baudrate); + } else { + switch_baudrate(save_baudrate, 0); + } + } +#else /* ! CFG_LOADS_BAUD_CHANGE */ + if(argc == 3){ + size = simple_strtoul(argv[2], NULL, 16); + } +#endif /* CFG_LOADS_BAUD_CHANGE */ + + printf("Ready for S-Record upload, press ENTER to proceed...\n"); + + for(;;){ + if(getc() == '\r') + break; + } + + if(save_serial(offset, size)){ + printf("## Error: S-Record upload aborted!\n"); + } else { + printf("S-Record upload complete!\n"); + } + +#ifdef CFG_LOADS_BAUD_CHANGE + if(save_baudrate != current_baudrate){ + switch_baudrate(current_baudrate, 1); + } +#endif + + return 0; +} + +#define SREC3_START "S0030000FC\n" +#define SREC3_FORMAT "S3%02X%08lX%s%02X\n" +#define SREC3_END "S70500000000FA\n" +#define SREC_BYTES_PER_RECORD 16 + +static int save_serial(ulong address, ulong count) +{ + int i, c, reclen, checksum, length; + char *hex = "0123456789ABCDEF"; + char record[2*SREC_BYTES_PER_RECORD+16]; /* buffer for one S-Record */ + char data[2*SREC_BYTES_PER_RECORD+1]; /* buffer for hex data */ + + reclen = 0; + checksum = 0; + + /* write the header */ + if(write_record(SREC3_START)) + return(-1); + + do { + /* collect hex data in the buffer */ + if(count){ + /* get one byte */ + c = *(volatile uchar*)(address + reclen); + + /* accumulate checksum */ + checksum += c; + + data[2*reclen] = hex[(c>>4)&0x0f]; + data[2*reclen+1] = hex[c & 0x0f]; + data[2*reclen+2] = '\0'; + ++reclen; + --count; + } + + if(reclen == SREC_BYTES_PER_RECORD || count == 0){ + /* enough data collected for one record: dump it */ + /* build & write a data record: */ + if(reclen){ + /* address + data + checksum */ + length = 4 + reclen + 1; + + /* accumulate length bytes into checksum */ + for(i = 0; i < 2; i++) + checksum += (length >> (8*i)) & 0xff; + + /* accumulate address bytes into checksum */ + for(i = 0; i < 4; i++) + checksum += (address >> (8*i)) & 0xff; + + /* make proper checksum byte: */ + checksum = ~checksum & 0xff; + + /* output one record: */ + sprintf(record, SREC3_FORMAT, length, address, data, checksum); + + if(write_record(record)) + return(-1); + } + + /* increment address */ + address += reclen; + checksum = 0; + reclen = 0; + } + } while(count); + + /* write the final record */ + if(write_record(SREC3_END)) + return(-1); + + return(0); +} + +static int write_record(char *buf) +{ + char c; + + while((c = *buf++)) + putc(c); + + /* Check for the console hangup (if any different from serial) */ + if(ctrlc()){ + return(-1); + } + + return(0); +} +#endif /* CFG_CMD_SAVES */ +#endif /* CFG_CMD_LOADS */ + +/* loadb command (load binary) included */ +#if (CONFIG_COMMANDS & CFG_CMD_LOADB) + +#define XON_CHAR 17 +#define XOFF_CHAR 19 +#define START_CHAR 0x01 +#define ETX_CHAR 0x03 +#define END_CHAR 0x0D +#define SPACE 0x20 +#define K_ESCAPE 0x23 +#define SEND_TYPE 'S' +#define DATA_TYPE 'D' +#define ACK_TYPE 'Y' +#define NACK_TYPE 'N' +#define BREAK_TYPE 'B' +#define tochar(x) ((char) (((x) + SPACE) & 0xff)) +#define untochar(x) ((int) (((x) - SPACE) & 0xff)) + +extern int os_data_count; +extern int os_data_header[8]; + +static void set_kerm_bin_mode(unsigned long *); +static int k_recv(void); +static int load_serial_bin(ulong offset); + +char his_eol; /* character he needs at end of packet */ +int his_pad_count; /* number of pad chars he needs */ +char his_pad_char; /* pad chars he needs */ +char his_quote; /* quote chars he'll use */ + +int do_load_serial_bin(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + ulong offset = 0; + int load_baudrate, current_baudrate; + int rcode = 0, size_dl = 0; + int i; + char *s, buf[32];; + + /* pre-set offset from CFG_LOAD_ADDR */ + offset = CFG_LOAD_ADDR; + + /* pre-set offset from $loadaddr */ + if((s = getenv("loadaddr")) != NULL){ + offset = simple_strtoul(s, NULL, 16); + } + + load_baudrate = current_baudrate = gd->baudrate; + + if(argc >= 2){ + offset = simple_strtoul(argv[1], NULL, 16); + } + + if(argc == 3){ + load_baudrate = (int)simple_strtoul(argv[2], NULL, 10); + + /* default to current baudrate */ + if(load_baudrate == 0) + load_baudrate = current_baudrate; + } + + if(load_baudrate != current_baudrate){ + for(i = 0; i < N_BAUDRATES; ++i){ + if(load_baudrate == baudrate_table[i]){ + break; + } + } + + if(i == N_BAUDRATES){ + printf("## Error: baudrate %d bps is not supported, will use current: %d bps\n", + load_baudrate, current_baudrate); + } else { + switch_baudrate(load_baudrate, 0); + } + } + + if(strcmp(argv[0],"loady") == 0){ + printf("Ready for binary (ymodem) download to 0x%08lX at %d bps...\n", + offset, load_baudrate); + + size_dl = load_serial_ymodem(offset); + } else { + printf("Ready for binary (kermit) download to 0x%08lX at %d bps...\n", + offset, load_baudrate); + + size_dl = load_serial_bin(offset); + } + + if(size_dl > 0){ + printf("\nTransfer complete!\n"); + printf("Total downloaded size: 0x%08lX (%d bytes)\n", size_dl, size_dl); + printf(" Data start address: 0x%08lX\n\n", offset); + + flush_cache(offset, size_dl); + + sprintf(buf, "%X", size_dl); + setenv("filesize", buf); + } else { + printf("\n## Error: downloaded data size is zero!\n"); + rcode = 1; + } + + if(load_baudrate != current_baudrate){ + switch_baudrate(current_baudrate, 1); + } + + return rcode; +} + +static int load_serial_bin(ulong offset) +{ + int size, i; + + set_kerm_bin_mode((ulong *) offset); + size = k_recv(); + + /* + * Gather any trailing characters (for instance, the ^D which + * is sent by 'cu' after sending a file), and give the + * box some time (100 * 1 ms) + */ + for(i = 0; i < 100; ++i) { + if(tstc()){ + (void)getc(); + } + + udelay(1000); + } + + return size; +} + +void send_pad(void) +{ + int count = his_pad_count; + + while(count-- > 0) + putc(his_pad_char); +} + +/* converts escaped kermit char to binary char */ +char ktrans(char in) +{ + if((in & 0x60) == 0x40){ + return(char)(in & ~0x40); + } else if((in & 0x7f) == 0x3f){ + return(char)(in | 0x40); + } else { + return in; + } +} + +int chk1(char *buffer) +{ + int total = 0; + + while(*buffer){ + total += *buffer++; + } + + return (int)((total + ((total >> 6) & 0x03)) & 0x3f); +} + +void s1_sendpacket(char *packet) +{ + send_pad(); + + while(*packet){ + putc(*packet++); + } +} + +static char a_b[24]; +void send_ack(int n) +{ + a_b[0] = START_CHAR; + a_b[1] = tochar(3); + a_b[2] = tochar(n); + a_b[3] = ACK_TYPE; + a_b[4] = '\0'; + a_b[4] = tochar(chk1(&a_b[1])); + a_b[5] = his_eol; + a_b[6] = '\0'; + + s1_sendpacket(a_b); +} + +void send_nack(int n) +{ + a_b[0] = START_CHAR; + a_b[1] = tochar(3); + a_b[2] = tochar(n); + a_b[3] = NACK_TYPE; + a_b[4] = '\0'; + a_b[4] = tochar(chk1(&a_b[1])); + a_b[5] = his_eol; + a_b[6] = '\0'; + + s1_sendpacket(a_b); +} + + +/* os_data_* takes an OS Open image and puts it into memory, and + puts the boot header in an array named os_data_header + if image is binary, no header is stored in os_data_header. +*/ +void (*os_data_init)(void); +void (*os_data_char)(char new_char); +static int os_data_state, os_data_state_saved; +int os_data_count; +static int os_data_count_saved; +static char *os_data_addr, *os_data_addr_saved; +static char *bin_start_address; +int os_data_header[8]; + +static void bin_data_init(void) +{ + os_data_state = 0; + os_data_count = 0; + os_data_addr = bin_start_address; +} + +static void os_data_save(void) +{ + os_data_state_saved = os_data_state; + os_data_count_saved = os_data_count; + os_data_addr_saved = os_data_addr; +} +static void os_data_restore(void) +{ + os_data_state = os_data_state_saved; + os_data_count = os_data_count_saved; + os_data_addr = os_data_addr_saved; +} + +static void bin_data_char(char new_char) +{ + switch(os_data_state){ + /* data */ + case 0: + *os_data_addr++ = new_char; + --os_data_count; + break; + } +} + +static void set_kerm_bin_mode(unsigned long *addr) +{ + bin_start_address = (char *) addr; + os_data_init = bin_data_init; + os_data_char = bin_data_char; +} + +/* k_data_* simply handles the kermit escape translations */ +static int k_data_escape, k_data_escape_saved; +void k_data_init(void) +{ + k_data_escape = 0; + os_data_init(); +} + +void k_data_save(void) +{ + k_data_escape_saved = k_data_escape; + os_data_save(); +} + +void k_data_restore(void) +{ + k_data_escape = k_data_escape_saved; + os_data_restore(); +} + +void k_data_char(char new_char) +{ + if(k_data_escape){ + /* last char was escape - translate this character */ + os_data_char(ktrans(new_char)); + k_data_escape = 0; + } else { + if(new_char == his_quote){ + /* this char is escape - remember */ + k_data_escape = 1; + } else { + /* otherwise send this char as-is */ + os_data_char(new_char); + } + } +} + +#define SEND_DATA_SIZE 20 +char send_parms[SEND_DATA_SIZE]; +char *send_ptr; + +/* handle_send_packet interprits the protocol info and builds and + sends an appropriate ack for what we can do */ +void handle_send_packet(int n) +{ + int length = 3; + int bytes; + + /* initialize some protocol parameters */ + his_eol = END_CHAR; /* default end of line character */ + his_pad_count = 0; + his_pad_char = '\0'; + his_quote = K_ESCAPE; + + /* ignore last character if it filled the buffer */ + if(send_ptr == &send_parms[SEND_DATA_SIZE - 1]) + --send_ptr; + + /* how many bytes we'll process */ + bytes = send_ptr - send_parms; + + do { + if(bytes-- <= 0) + break; + + /* handle MAXL - max length */ + /* ignore what he says - most I'll take (here) is 94 */ + a_b[++length] = tochar(94); + + if(bytes-- <= 0) + break; + + /* handle TIME - time you should wait for my packets */ + /* ignore what he says - don't wait for my ack longer than 1 second */ + a_b[++length] = tochar(1); + + if(bytes-- <= 0) + break; + + /* handle NPAD - number of pad chars I need */ + /* remember what he says - I need none */ + his_pad_count = untochar(send_parms[2]); + a_b[++length] = tochar(0); + + if(bytes-- <= 0) + break; + + /* handle PADC - pad chars I need */ + /* remember what he says - I need none */ + his_pad_char = ktrans(send_parms[3]); + a_b[++length] = 0x40; /* He should ignore this */ + + if(bytes-- <= 0) + break; + + /* handle EOL - end of line he needs */ + /* remember what he says - I need CR */ + his_eol = untochar(send_parms[4]); + a_b[++length] = tochar(END_CHAR); + + if(bytes-- <= 0) + break; + + /* handle QCTL - quote control char he'll use */ + /* remember what he says - I'll use '#' */ + his_quote = send_parms[5]; + a_b[++length] = '#'; + + if(bytes-- <= 0) + break; + + /* handle QBIN - 8-th bit prefixing */ + /* ignore what he says - I refuse */ + a_b[++length] = 'N'; + + if(bytes-- <= 0) + break; + + /* handle CHKT - the clock check type */ + /* ignore what he says - I do type 1 (for now) */ + a_b[++length] = '1'; + + if(bytes-- <= 0) + break; + + /* handle REPT - the repeat prefix */ + /* ignore what he says - I refuse (for now) */ + a_b[++length] = 'N'; + + if(bytes-- <= 0) + break; + + /* handle CAPAS - the capabilities mask */ + /* ignore what he says - I only do long packets - I don't do windows */ + a_b[++length] = tochar(2); /* only long packets */ + a_b[++length] = tochar(0); /* no windows */ + a_b[++length] = tochar(94); /* large packet msb */ + a_b[++length] = tochar(94); /* large packet lsb */ + } while(0); + + a_b[0] = START_CHAR; + a_b[1] = tochar(length); + a_b[2] = tochar(n); + a_b[3] = ACK_TYPE; + a_b[++length] = '\0'; + a_b[length] = tochar(chk1(&a_b[1])); + a_b[++length] = his_eol; + a_b[++length] = '\0'; + + s1_sendpacket(a_b); +} + +/* k_recv receives a OS Open image file over kermit line */ +static int k_recv(void) +{ + char new_char; + char k_state, k_state_saved; + int sum; + int done; + int length; + int n, last_n; + int z = 0; + int len_lo, len_hi; + + /* initialize some protocol parameters */ + his_eol = END_CHAR; /* default end of line character */ + his_pad_count = 0; + his_pad_char = '\0'; + his_quote = K_ESCAPE; + + /* initialize the k_recv and k_data state machine */ + done = 0; + k_state = 0; + + k_data_init(); + k_state_saved = k_state; + + k_data_save(); + + /* just to get rid of a warning */ + n = 0; + last_n = -1; + + /* expect this "type" sequence (but don't check): + S: send initiate + F: file header + D: data (multiple) + Z: end of file + B: break transmission + */ + + /* enter main loop */ + while(!done){ + /* set the send packet pointer to begining of send packet parms */ + send_ptr = send_parms; + + /* With each packet, start summing the bytes starting with the length. + Save the current sequence number. + Note the type of the packet. + If a character less than SPACE (0x20) is received - error. + */ + + /* get a packet */ + /* wait for the starting character or ^C */ + for(;;){ + switch(getc()){ + /* start packet */ + case START_CHAR: + goto START; + /* ^C waiting for packet */ + case ETX_CHAR: + return(0); + default: + ; + } + } + +START: + /* get length of packet */ + sum = 0; + new_char = getc(); + if((new_char & 0xE0) == 0) + goto packet_error; + + sum += new_char & 0xff; + length = untochar(new_char); + + /* get sequence number */ + new_char = getc(); + if((new_char & 0xE0) == 0) + goto packet_error; + + sum += new_char & 0xff; + n = untochar(new_char); + --length; + + /* NEW CODE - check sequence numbers for retried packets */ + /* Note - this new code assumes that the sequence number is correctly + * received. Handling an invalid sequence number adds another layer + * of complexity that may not be needed - yet! At this time, I'm hoping + * that I don't need to buffer the incoming data packets and can write + * the data into memory in real time. + */ + if(n == last_n){ + /* same sequence number, restore the previous state */ + k_state = k_state_saved; + k_data_restore(); + } else { + /* new sequence number, checkpoint the download */ + last_n = n; + k_state_saved = k_state; + k_data_save(); + } + /* END NEW CODE */ + + /* get packet type */ + new_char = getc(); + if((new_char & 0xE0) == 0) + goto packet_error; + + sum += new_char & 0xff; + k_state = new_char; + --length; + + /* check for extended length */ + if(length == -2){ + /* (length byte was 0, decremented twice) */ + /* get the two length bytes */ + new_char = getc(); + if((new_char & 0xE0) == 0) + goto packet_error; + + sum += new_char & 0xff; + len_hi = untochar(new_char); + new_char = getc(); + if((new_char & 0xE0) == 0) + goto packet_error; + + sum += new_char & 0xff; + len_lo = untochar(new_char); + length = len_hi * 95 + len_lo; + /* check header checksum */ + new_char = getc(); + if((new_char & 0xE0) == 0) + goto packet_error; + + if(new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f)) + goto packet_error; + + sum += new_char & 0xff; +/* --length; */ /* new length includes only data and block check to come */ + } + + /* bring in rest of packet */ + while(length > 1){ + new_char = getc(); + if((new_char & 0xE0) == 0) + goto packet_error; + + sum += new_char & 0xff; + --length; + + if(k_state == DATA_TYPE){ + /* pass on the data if this is a data packet */ + k_data_char(new_char); + } else if(k_state == SEND_TYPE){ + /* save send pack in buffer as is */ + *send_ptr++ = new_char; + /* if too much data, back off the pointer */ + if(send_ptr >= &send_parms[SEND_DATA_SIZE]) + --send_ptr; + } + } + + /* get and validate checksum character */ + new_char = getc(); + if((new_char & 0xE0) == 0) + goto packet_error; + + if(new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f)) + goto packet_error; + + /* get END_CHAR */ + new_char = getc(); + if(new_char != END_CHAR) { +packet_error: + /* restore state machines */ + k_state = k_state_saved; + k_data_restore(); + + /* send a negative acknowledge packet in */ + send_nack(n); + } else if(k_state == SEND_TYPE){ + /* crack the protocol parms, build an appropriate ack packet */ + handle_send_packet(n); + } else { + /* send simple acknowledge packet in */ + send_ack(n); + /* quit if end of transmission */ + if(k_state == BREAK_TYPE) + done = 1; + } + + ++z; + } + + return((ulong)os_data_addr - (ulong)bin_start_address); +} + +static int getcxmodem(void) +{ + if(tstc()) + return(getc()); + + return -1; +} + +static ulong load_serial_ymodem(ulong offset) +{ + int size; + int err; + int res; + connection_info_t info; + char ymodemBuf[1024]; + ulong store_addr = ~0; + ulong addr = 0; + + size = 0; + info.mode = xyzModem_ymodem; + + res = xyzModem_stream_open(&info, &err); + + if(!res){ + while((res = xyzModem_stream_read(ymodemBuf, 1024, &err)) > 0){ + store_addr = addr + offset; + size += res; + addr += res; +#ifndef CFG_NO_FLASH + if(addr2info(store_addr)){ + int rc; + + rc = flash_write((char *) ymodemBuf, store_addr, res); + if(rc != 0){ + flash_perror(rc); + return(~0); + } + } else +#endif + { + memcpy((char *)(store_addr), ymodemBuf, res); + } + + } + } else { + printf("\n## Error: %s\n", xyzModem_error(err)); + } + + xyzModem_stream_close(&err); + xyzModem_stream_terminate(0, &getcxmodem); + + return size; +} + +#endif /* CFG_CMD_LOADB */ + +/* -------------------------------------------------------------------- */ + +#if (CONFIG_COMMANDS & CFG_CMD_LOADS) + +#ifdef CFG_LOADS_BAUD_CHANGE +U_BOOT_CMD(loads, 3, 0, do_load_serial, "load S-Record file over serial line\n", + "[off] [baud]\n" + "\t- load S-Record file over serial line with offset 'off' and baudrate 'baud'\n" +); +#else /* ! CFG_LOADS_BAUD_CHANGE */ +U_BOOT_CMD(loads, 2, 0, do_load_serial, "load S-Record file over serial line\n", + "[off]\n" + "\t- load S-Record file over serial line with offset 'off'\n" +); +#endif /* #ifdef CFG_LOADS_BAUD_CHANGE */ + +/* + * SAVES always requires LOADS support, but not vice versa + */ +#if (CONFIG_COMMANDS & CFG_CMD_SAVES) +#ifdef CFG_LOADS_BAUD_CHANGE +U_BOOT_CMD(saves, 4, 0, do_save_serial, "save S-Record file over serial line\n", + "[off] [size] [baud]\n" + "\t- save S-Record file over serial line with offset 'off', size 'size' and baudrate 'baud'\n" +); +#else /* ! CFG_LOADS_BAUD_CHANGE */ +U_BOOT_CMD(saves, 3, 0, do_save_serial, "save S-Record file over serial line\n", + "[off] [size]\n" + "\t- save S-Record file over serial line with offset 'off' and size 'size'\n" +); +#endif /* #ifdef CFG_LOADS_BAUD_CHANGE */ +#endif /* #if (CONFIG_COMMANDS & CFG_CMD_SAVES) */ + +#endif /* #if (CONFIG_COMMANDS & CFG_CMD_LOADS) */ + +#if (CONFIG_COMMANDS & CFG_CMD_LOADB) +U_BOOT_CMD(loadb, 3, 0, do_load_serial_bin, "load binary file over serial line (kermit mode)\n", + "[off] [baud]\n" + "\t- load binary file over serial line with offset 'off' and baudrate 'baud'\n" +); + +U_BOOT_CMD(loady, 3, 0, do_load_serial_bin, "load binary file over serial line (ymodem mode)\n", + "[off] [baud]\n" + "\t- load binary file over serial line with offset 'off' and baudrate 'baud'\n" +); +#endif /* #if (CONFIG_COMMANDS & CFG_CMD_LOADB)*/ diff --git a/u-boot/common/crc16.c b/u-boot/common/crc16.c new file mode 100755 index 0000000..f4c680d --- /dev/null +++ b/u-boot/common/crc16.c @@ -0,0 +1,108 @@ +/* + *========================================================================== + * + * crc16.c + * + * 16 bit CRC with polynomial x^16+x^12+x^5+1 + * + *========================================================================== + *####ECOSGPLCOPYRIGHTBEGIN#### + * ------------------------------------------- + * This file is part of eCos, the Embedded Configurable Operating System. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + * Copyright (C) 2002 Gary Thomas + * + * eCos is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 or (at your option) any later version. + * + * eCos is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with eCos; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * As a special exception, if other files instantiate templates or use macros + * or inline functions from this file, or you compile this file and link it + * with other works to produce a work based on this file, this file does not + * by itself cause the resulting work to be covered by the GNU General Public + * License. However the source code for this file must still be made available + * in accordance with section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + * + * Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. + * at http: *sources.redhat.com/ecos/ecos-license/ + * ------------------------------------------- + *####ECOSGPLCOPYRIGHTEND#### + *========================================================================== + *#####DESCRIPTIONBEGIN#### + * + * Author(s): gthomas + * Contributors: gthomas,asl + * Date: 2001-01-31 + * Purpose: + * Description: + * + * This code is part of eCos (tm). + * + *####DESCRIPTIONEND#### + * + *========================================================================== + */ + +#include "crc.h" + +/* Table of CRC constants - implements x^16+x^12+x^5+1 */ +static const uint16_t crc16_tab[] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, +}; + +uint16_t cyg_crc16(unsigned char *buf, int len) +{ + int i; + uint16_t cksum; + + cksum = 0; + + for(i = 0; i < len; i++){ + cksum = crc16_tab[((cksum>>8) ^ *buf++) & 0xFF] ^ (cksum << 8); + } + + return cksum; +} diff --git a/u-boot/common/s_record.c b/u-boot/common/s_record.c new file mode 100755 index 0000000..dcc4305 --- /dev/null +++ b/u-boot/common/s_record.c @@ -0,0 +1,212 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include + +static int hex1_bin(char c); +static int hex2_bin(char *s); + +int srec_decode(char *input, int *count, ulong *addr, char *data) +{ + int i; + int v; /* conversion buffer */ + int srec_type; /* S-Record type */ + unsigned char chksum; /* buffer for checksum */ + + chksum = 0; + + /* skip anything before 'S', and the 'S' itself. + * Return error if not found + */ + + for(; *input; ++input){ + /* skip 'S' */ + if(*input == 'S'){ + ++input; + break; + } + } + + /* no more data? */ + if(*input == '\0'){ + return(SREC_EMPTY); + } + + /* record type */ + v = *input++; + + if((*count = hex2_bin(input)) < 0){ + return(SREC_E_NOSREC); + } + + chksum += *count; + input += 2; + + /* record type */ + switch(v){ + /* start record */ + case '0': + srec_type = SREC_START; /* 2 byte addr field */ + *count -= 3; /* - checksum and addr */ + break; + case '1': + srec_type = SREC_DATA2; /* 2 byte addr field */ + *count -= 3; /* - checksum and addr */ + break; + case '2': + srec_type = SREC_DATA3; /* 3 byte addr field */ + *count -= 4; /* - checksum and addr */ + break; + /* data record with a */ + case '3': + srec_type = SREC_DATA4; /* 4 byte addr field */ + *count -= 5; /* - checksum and addr */ + break; + /* count record, addr field contains */ + case '5': + srec_type = SREC_COUNT; /* a 2 byte record counter */ + *count = 0; /* no data */ + break; + /* end record with a */ + case '7': + srec_type = SREC_END4; /* 4 byte addr field */ + *count -= 5; /* - checksum and addr */ + break; + /* end record with a */ + case '8': + srec_type = SREC_END3; /* 3 byte addr field */ + *count -= 4; /* - checksum and addr */ + break; + /* end record with a */ + case '9': + srec_type = SREC_END2; /* 2 byte addr field */ + *count -= 3; /* - checksum and addr */ + break; + default: + return(SREC_E_BADTYPE); + } + + /* read address field */ + *addr = 0; + + switch(v){ + /* 4 byte addr field */ + case '3': + case '7': + if((v = hex2_bin(input)) < 0){ + return(SREC_E_NOSREC); + } + + *addr += v; + chksum += v; + input += 2; + /* FALL THRU */ + /* 3 byte addr field */ + case '2': + case '8': + if((v = hex2_bin(input)) < 0){ + return(SREC_E_NOSREC); + } + + *addr <<= 8; + *addr += v; + chksum += v; + input += 2; + /* FALL THRU */ + /* 2 byte addr field */ + case '0': + case '1': + case '5': + case '9': + if((v = hex2_bin(input)) < 0){ + return(SREC_E_NOSREC); + } + + *addr <<= 8; + *addr += v; + chksum += v; + input += 2; + + if((v = hex2_bin(input)) < 0){ + return(SREC_E_NOSREC); + } + + *addr <<= 8; + *addr += v; + chksum += v; + input += 2; + break; + default: + return(SREC_E_BADTYPE); + } + + /* convert data and calculate checksum */ + for(i=0; i < *count; ++i){ + if((v = hex2_bin(input)) < 0){ + return(SREC_E_NOSREC); + } + + data[i] = v; + chksum += v; + input += 2; + } + + /* read anc check checksum */ + if((v = hex2_bin(input)) < 0){ + return(SREC_E_NOSREC); + } + + if((unsigned char)v != (unsigned char)~chksum){ + return(SREC_E_BADCHKS); + } + + return(srec_type); +} + +static int hex1_bin(char c) +{ + if(c >= '0' && c <= '9') + return(c - '0'); + + if(c >= 'a' && c <= 'f') + return(c + 10 - 'a'); + + if(c >= 'A' && c <= 'F') + return(c + 10 - 'A'); + + return(-1); +} + +static int hex2_bin(char *s) +{ + int i, j; + + if((i = hex1_bin(*s++)) < 0) + return(-1); + + if((j = hex1_bin(*s)) < 0) + return(-1); + + return((i<<4) + j); +} diff --git a/u-boot/common/xyzModem.c b/u-boot/common/xyzModem.c new file mode 100755 index 0000000..26df26e --- /dev/null +++ b/u-boot/common/xyzModem.c @@ -0,0 +1,607 @@ +/* + *========================================================================== + * + * xyzModem.c + * + * RedBoot stream handler for xyzModem protocol + * + *========================================================================== + *####ECOSGPLCOPYRIGHTBEGIN#### + * ------------------------------------------- + * This file is part of eCos, the Embedded Configurable Operating System. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + * Copyright (C) 2002 Gary Thomas + * + * eCos is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 or (at your option) any later version. + * + * eCos is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with eCos; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * As a special exception, if other files instantiate templates or use macros + * or inline functions from this file, or you compile this file and link it + * with other works to produce a work based on this file, this file does not + * by itself cause the resulting work to be covered by the GNU General Public + * License. However the source code for this file must still be made available + * in accordance with section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + * + * Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. + * at http: *sources.redhat.com/ecos/ecos-license/ + * ------------------------------------------- + *####ECOSGPLCOPYRIGHTEND#### + *========================================================================== + *#####DESCRIPTIONBEGIN#### + * + * Author(s): gthomas + * Contributors: gthomas, tsmith, Yoshinori Sato + * Date: 2000-07-14 + * Purpose: + * Description: + * + * This code is part of RedBoot (tm). + * + *####DESCRIPTIONEND#### + * + *========================================================================== + */ +#include +#include +#include +#include + +/* Assumption - run xyzModem protocol over the console port */ + +/* Values magic to the protocol */ +#define SOH 0x01 +#define STX 0x02 +#define EOT 0x04 +#define ACK 0x06 +#define BSP 0x08 +#define NAK 0x15 +#define CAN 0x18 +#define EOF 0x1A /* ^Z for DOS officionados */ + +#define USE_YMODEM_LENGTH + +/* Data & state local to the protocol */ +static struct { + int *__chan; + unsigned char pkt[1024], *bufp; + unsigned char blk,cblk,crc1,crc2; + unsigned char next_blk; /* Expected block */ + int len, mode, total_retries; + int total_SOH, total_STX, total_CAN; + unsigned int crc_mode, at_eof, tx_ack; +#ifdef USE_YMODEM_LENGTH + unsigned long file_length, read_length; +#endif +} xyz; + +#define xyzModem_CHAR_TIMEOUT 2000 /* 2 seconds */ +#define xyzModem_MAX_RETRIES 20 +#define xyzModem_MAX_RETRIES_WITH_CRC 10 +#define xyzModem_CAN_COUNT 3 /* Wait for 3 CAN before quitting */ + +int comm_if_getc_tout(char *c) +{ + unsigned long counter=0; + #define DELAY 20 + + while(!tstc() && (counter < xyzModem_CHAR_TIMEOUT*1000/DELAY)){ + udelay(DELAY); + counter++; + } + + if(tstc()){ + *c=getc(); + return 1; + } + + return 0; +} + +/* Validate a hex character */ +inline static unsigned int _is_hex(char c) +{ + return(((c >= '0') && (c <= '9')) || + ((c >= 'A') && (c <= 'F')) || + ((c >= 'a') && (c <= 'f'))); +} + +/* Convert a single hex nibble */ +inline static int _from_hex(char c) +{ + int ret = 0; + + if((c >= '0') && (c <= '9')){ + ret = (c - '0'); + } else if((c >= 'a') && (c <= 'f')){ + ret = (c - 'a' + 0x0a); + } else if((c >= 'A') && (c <= 'F')){ + ret = (c - 'A' + 0x0A); + } + + return ret; +} + +/* Convert a character to lower case */ +inline static char _tolower(char c) +{ + if((c >= 'A') && (c <= 'Z')){ + c = (c - 'A') + 'a'; + } + + return c; +} + +/* Parse (scan) a number */ +unsigned int parse_num(char *s, unsigned long *val, char **es, char *delim) +{ + unsigned int first = 1; + int radix = 10; + char c; + unsigned long result = 0; + int digit; + + while(*s == ' ') + s++; + + while(*s){ + if(first && (s[0] == '0') && (_tolower(s[1]) == 'x')){ + radix = 16; + s += 2; + } + + first = 0; + c = *s++; + + if(_is_hex(c) && ((digit = _from_hex(c)) < radix)){ + /* Valid digit */ + result = (result * radix) + digit; + } else { + if(delim != (char *)0){ + /* See if this character is one of the delimiters */ + char *dp = delim; + + while(*dp && (c != *dp)) + dp++; + + /* Found a good delimiter */ + if(*dp) + break; + } + + /* Malformatted number */ + return 0; + } + } + + *val = result; + + if(es != (char **)0){ + *es = s; + } + + return 1; +} + +/* Wait for the line to go idle */ +static void xyzModem_flush(void) +{ + int res; + char c; + + while(1){ + res = comm_if_getc_tout(&c); + if(!res) + return; + } +} + +static int xyzModem_get_hdr(void) +{ + char c; + int res; + unsigned int hdr_found = 0; + int i, can_total, hdr_chars; + unsigned short cksum; + + /* Find the start of a header */ + can_total = 0; + hdr_chars = 0; + + if(xyz.tx_ack){ + putc(ACK); + xyz.tx_ack = 0; + } + + while(!hdr_found){ + res = comm_if_getc_tout(&c); + + if(res){ + hdr_chars++; + + switch(c){ + case SOH: + xyz.total_SOH++; + case STX: + if(c == STX) + xyz.total_STX++; + + hdr_found = 1; + break; + case CAN: + xyz.total_CAN++; + + if(++can_total == xyzModem_CAN_COUNT){ + return xyzModem_cancel; + } else { + /* Wait for multiple CAN to avoid early quits */ + break; + } + case EOT: + /* EOT only supported if no noise */ + if(hdr_chars == 1){ + putc(ACK); + return xyzModem_eof; + } + default: + /* Ignore, waiting for start of header */ + ; + } + } else { + /* Data stream timed out */ + xyzModem_flush(); /* Toss any current input */ + udelay(250000); + return xyzModem_timeout; + } + } + + /* Header found, now read the data */ + res = comm_if_getc_tout((char *)&xyz.blk); + if(!res){ + return xyzModem_timeout; + } + + res = comm_if_getc_tout((char *)&xyz.cblk); + if(!res){ + return xyzModem_timeout; + } + + xyz.len = (c == SOH) ? 128 : 1024; + xyz.bufp = xyz.pkt; + for(i = 0; i < xyz.len; i++){ + res = comm_if_getc_tout(&c); + if(res){ + xyz.pkt[i] = c; + } else { + return xyzModem_timeout; + } + } + + res = comm_if_getc_tout((char *)&xyz.crc1); + if(!res){ + return xyzModem_timeout; + } + + if(xyz.crc_mode){ + res = comm_if_getc_tout((char *)&xyz.crc2); + if(!res){ + return xyzModem_timeout; + } + } + + /* Validate the message */ + if((xyz.blk ^ xyz.cblk) != (unsigned char)0xFF){ + xyzModem_flush(); + return xyzModem_frame; + } + + /* Verify checksum/CRC */ + if(xyz.crc_mode){ + cksum = cyg_crc16(xyz.pkt, xyz.len); + if(cksum != ((xyz.crc1 << 8) | xyz.crc2)){ + return xyzModem_cksum; + } + } else { + cksum = 0; + for(i = 0; i < xyz.len; i++){ + cksum += xyz.pkt[i]; + } + + if(xyz.crc1 != (cksum & 0xFF)){ + return xyzModem_cksum; + } + } + + /* If we get here, the message passes [structural] muster */ + return 0; +} + +int xyzModem_stream_open(connection_info_t *info, int *err) +{ + int stat = 0; + int retries = xyzModem_MAX_RETRIES; + int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC; + +#ifdef xyzModem_zmodem + if(info->mode == xyzModem_zmodem){ + *err = xyzModem_noZmodem; + return -1; + } +#endif + +/* TODO: CHECK ! */ + int dummy; + xyz.__chan=&dummy; + xyz.len = 0; + xyz.crc_mode = 1; + xyz.at_eof = 0; + xyz.tx_ack = 0; + xyz.mode = info->mode; + xyz.total_retries = 0; + xyz.total_SOH = 0; + xyz.total_STX = 0; + xyz.total_CAN = 0; +#ifdef USE_YMODEM_LENGTH + xyz.read_length = 0; + xyz.file_length = 0; +#endif + + putc(xyz.crc_mode ? 'C' : NAK); + + if(xyz.mode == xyzModem_xmodem){ + /* X-modem doesn't have an information header - exit here */ + xyz.next_blk = 1; + return 0; + } + + while(retries-- > 0){ + stat = xyzModem_get_hdr(); + if(stat == 0){ + /* Y-modem file information header */ + if(xyz.blk == 0){ +#ifdef USE_YMODEM_LENGTH + /* skip filename */ + while(*xyz.bufp++); + + /* get the length */ + parse_num((char *)xyz.bufp, &xyz.file_length, NULL, " "); +#endif + /* The rest of the file name data block quietly discarded */ + xyz.tx_ack = 1; + } + + xyz.next_blk = 1; + xyz.len = 0; + + return 0; + } else if(stat == xyzModem_timeout){ + if(--crc_retries <= 0) + xyz.crc_mode = 0; + + /* Extra delay for startup */ + udelay(5*100000); + + putc(xyz.crc_mode ? 'C' : NAK); + xyz.total_retries++; + } + + if(stat == xyzModem_cancel){ + break; + } + } + + *err = stat; + + return -1; +} + +int xyzModem_stream_read(char *buf, int size, int *err) +{ + int stat, total, len; + int retries; + + total = 0; + stat = xyzModem_cancel; + + /* Try and get 'size' bytes into the buffer */ + while(!xyz.at_eof && (size > 0)){ + if(xyz.len == 0){ + retries = xyzModem_MAX_RETRIES; + + while(retries-- > 0){ + stat = xyzModem_get_hdr(); + if(stat == 0){ + if(xyz.blk == xyz.next_blk){ + xyz.tx_ack = 1; + xyz.next_blk = (xyz.next_blk + 1) & 0xFF; + +#if defined(xyzModem_zmodem) || defined(USE_YMODEM_LENGTH) + if(xyz.mode == xyzModem_xmodem || xyz.file_length == 0){ +#else + if(1){ +#endif + /* Data blocks can be padded with ^Z (EOF) characters */ + /* This code tries to detect and remove them */ + if( (xyz.bufp[xyz.len-1] == EOF) && + (xyz.bufp[xyz.len-2] == EOF) && + (xyz.bufp[xyz.len-3] == EOF) ){ + while(xyz.len && (xyz.bufp[xyz.len-1] == EOF)){ + xyz.len--; + } + } + } + +#ifdef USE_YMODEM_LENGTH + /* + * See if accumulated length exceeds that of the file. + * If so, reduce size (i.e., cut out pad bytes) + * Only do this for Y-modem (and Z-modem should it ever + * be supported since it can fall back to Y-modem mode). + */ + if(xyz.mode != xyzModem_xmodem && 0 != xyz.file_length){ + xyz.read_length += xyz.len; + if(xyz.read_length > xyz.file_length){ + xyz.len -= (xyz.read_length - xyz.file_length); + } + } +#endif + break; + } else if(xyz.blk == ((xyz.next_blk - 1) & 0xFF)){ + /* Just re-ACK this so sender will get on with it */ + putc(ACK); + continue; /* Need new header */ + } else { + stat = xyzModem_sequence; + } + } + + if(stat == xyzModem_cancel){ + break; + } + + if(stat == xyzModem_eof){ + putc(ACK); + + if(xyz.mode == xyzModem_ymodem){ + putc(xyz.crc_mode ? 'C' : NAK); + xyz.total_retries++; + stat = xyzModem_get_hdr(); + putc(ACK); + } + + xyz.at_eof = 1; + + break; + } + + putc(xyz.crc_mode ? 'C' : NAK); + xyz.total_retries++; + } + + if(stat < 0){ + *err = stat; + xyz.len = -1; + + return total; + } + + } + + /* Don't "read" data from the EOF protocol package */ + if(!xyz.at_eof){ + len = xyz.len; + if(size < len) + len = size; + + memcpy(buf, xyz.bufp, len); + size -= len; + buf += len; + total += len; + xyz.len -= len; + xyz.bufp += len; + } + } + + return total; +} + +void xyzModem_stream_close(int *err) +{ + printf("\n\nxyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n", + xyz.crc_mode ? "CRC" : "Cksum", + xyz.total_SOH, xyz.total_STX, xyz.total_CAN, + xyz.total_retries); +} + +/* Need to be able to clean out the input buffer, so have to take the */ +/* getc */ +void xyzModem_stream_terminate(unsigned int abort, int (*getc)(void)) +{ + int c; + + if(abort){ + switch(xyz.mode){ + case xyzModem_xmodem: + case xyzModem_ymodem: + /* The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal */ + /* number of Backspaces is a friendly way to get the other end to abort. */ + putc(CAN); + putc(CAN); + putc(CAN); + putc(CAN); + putc(BSP); + putc(BSP); + putc(BSP); + putc(BSP); + /* Now consume the rest of what's waiting on the line. */ + xyzModem_flush(); + xyz.at_eof = 1; + break; +#ifdef xyzModem_zmodem + case xyzModem_zmodem: + /* Might support it some day I suppose. */ +#endif + break; + } + } else { + /* + * Consume any trailing crap left in the inbuffer from + * previous recieved blocks. Since very few files are an exact multiple + * of the transfer block size, there will almost always be some gunk here. + * If we don't eat it now, RedBoot will think the user typed it. + */ + + while((c = (*getc)()) > -1); + /* + * Make a small delay to give terminal programs like minicom + * time to get control again after their file transfer program + * exits. + */ + udelay(250000); + } +} + +char *xyzModem_error(int err) +{ + switch (err) { + case xyzModem_access: + return "can't access file"; + break; + case xyzModem_noZmodem: + return "sorry, zModem not available yet"; + break; + case xyzModem_timeout: + return "timed out"; + break; + case xyzModem_eof: + return "end of file"; + break; + case xyzModem_cancel: + return "cancelled"; + break; + case xyzModem_frame: + return "invalid framing"; + break; + case xyzModem_cksum: + return "CRC/checksum error"; + break; + case xyzModem_sequence: + return "block sequence error"; + break; + default: + return "unknown error"; + break; + } +} diff --git a/u-boot/include/s_record.h b/u-boot/include/s_record.h new file mode 100755 index 0000000..07806d5 --- /dev/null +++ b/u-boot/include/s_record.h @@ -0,0 +1,114 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/*-------------------------------------------------------------------------- + * + * Motorola S-Record Format: + * + * Motorola S-Records are an industry-standard format for + * transmitting binary files to target systems and PROM + * programmers. LSI Logic have extended this standard to include + * an S4-record containing an address and a symbol. + * + * The extended S-record standard is as follows: + * + * S
+ * S4
, + * + * Where: + * + * type + * is the record type. Where: + * + * 0 starting record (optional) + * 1 data record with 16-bit address + * 2 data record with 24-bit address + * 3 data record with 32-bit address + * 4 symbol record (LSI extension) + * 5 number of data records in preceeding block + * 6 unused + * 7 ending record for S3 records + * 8 ending record for S2 records + * 9 ending record for S1 records + * + * length + * is two hex characters. This defines the length of the + * record in bytes (not characters). It includes the address + * field, the data field, and the checksum field. + * + * address + * is 4, 6, or 8 characters. Corresponding to a 16-, 24-, or + * 32-bit address. The address field for S4 records is + * always 32 bits. + * + * data + * + * Are the data bytes. Each pair of hex characters represent + * one byte in memory. + * + * name + * Is the symbol name. The symbol is terminated by a ','. + * + * checksum + * Is the one's complement of the 8-bit checksum. + * + * Example + * + * S0030000FC + * . + * . + * S325000004403C0880018D08DD900000000011000026000000003C0880012508DC50C50000B401 + * S32500000460C50100B8C50200BCC50300C0C50400C4C50500C8C50600CCC50700D0C50800D4FA + * S32500000480C50900D8C50A00DCC50B00E0C50C00E4C50D00E8C50E00ECC50F00F0C51000F49A + * S325000004A0C51100F8C51200FCC5130100C5140104C5150108C516010CC5170110C518011434 + * . + * . + * S70500000000FA + * + * The S0 record starts the file. The S3 records contain the + * data. The S7 record contains the entry address and terminates + * the download. + * + *-------------------------------------------------------------------------- + */ + +#define SREC_START 0 /* Start Record (module name) */ +#define SREC_DATA2 1 /* Data Record with 2 byte address */ +#define SREC_DATA3 2 /* Data Record with 3 byte address */ +#define SREC_DATA4 3 /* Data Record with 4 byte address */ +#define SREC_COUNT 5 /* Count Record (previously transmitted) */ +#define SREC_END4 7 /* End Record with 4 byte start address */ +#define SREC_END3 8 /* End Record with 3 byte start address */ +#define SREC_END2 9 /* End Record with 2 byte start address */ +#define SREC_EMPTY 10 /* Empty Record without any data */ + +#define SREC_REC_OK SREC_EMPTY /* last code without error condition */ + +#define SREC_E_BADTYPE -1 /* no valid S-Record */ +#define SREC_E_NOSREC -2 /* line format differs from s-record */ +#define SREC_E_BADCHKS -3 /* checksum error in an s-record line */ + +#define SREC_MAXRECLEN (512 + 4) /* max ASCII record length */ +#define SREC_MAXBINLEN 255 /* resulting binary length */ + +int srec_decode (char *input, int *count, ulong *addr, char *data); diff --git a/u-boot/include/xyzModem.h b/u-boot/include/xyzModem.h new file mode 100755 index 0000000..ca295ce --- /dev/null +++ b/u-boot/include/xyzModem.h @@ -0,0 +1,90 @@ +/* + *========================================================================== + * + * xyzModem.h + * + * RedBoot stream handler for xyzModem protocol + * + *========================================================================== + *####ECOSGPLCOPYRIGHTBEGIN#### + * ------------------------------------------- + * This file is part of eCos, the Embedded Configurable Operating System. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + * Copyright (C) 2002 Gary Thomas + * + * eCos is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 or (at your option) any later version. + * + * eCos is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with eCos; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * As a special exception, if other files instantiate templates or use macros + * or inline functions from this file, or you compile this file and link it + * with other works to produce a work based on this file, this file does not + * by itself cause the resulting work to be covered by the GNU General Public + * License. However the source code for this file must still be made available + * in accordance with section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + * + * Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. + * at http: *sources.redhat.com/ecos/ecos-license/ + * ------------------------------------------- + *####ECOSGPLCOPYRIGHTEND#### + *========================================================================== + *#####DESCRIPTIONBEGIN#### + * + * Author(s): gthomas + * Contributors: gthomas + * Date: 2000-07-14 + * Purpose: + * Description: + * + * This code is part of RedBoot (tm). + * + *####DESCRIPTIONEND#### + * + *========================================================================== + */ + +#ifndef _XYZMODEM_H_ +#define _XYZMODEM_H_ + +#define xyzModem_xmodem 1 +#define xyzModem_ymodem 2 +/* Don't define this until the protocol support is in place */ +/*#define xyzModem_zmodem 3 */ + +#define xyzModem_access -1 +#define xyzModem_noZmodem -2 +#define xyzModem_timeout -3 +#define xyzModem_eof -4 +#define xyzModem_cancel -5 +#define xyzModem_frame -6 +#define xyzModem_cksum -7 +#define xyzModem_sequence -8 + +#define xyzModem_close 1 +#define xyzModem_abort 2 + +typedef struct { + char *filename; + int mode; + int chan; +} connection_info_t; + +int xyzModem_stream_open(connection_info_t *info, int *err); +void xyzModem_stream_close(int *err); +void xyzModem_stream_terminate(unsigned int method, int (*getc)(void)); +int xyzModem_stream_read(char *buf, int size, int *err); +char *xyzModem_error(int err); + +#endif /* _XYZMODEM_H_ */ -- 2.25.1