cmd_net.o \
cmd_nvedit.o \
cmd_itest.o \
+ cmd_load.o \
command.o \
console.o \
devices.o \
flash.o \
lists.o \
env_flash.o \
- hush.o
+ hush.o \
+ xyzModem.o \
+ crc16.o \
+ s_record.o
OBJS = $(AOBJS) $(COBJS)
--- /dev/null
+/*
+ * (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 <common.h>
+#include <command.h>
+#include <s_record.h>
+#include <net.h>
+#include <exports.h>
+#include <xyzModem.h>
+
+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)*/
--- /dev/null
+/*
+ *==========================================================================
+ *
+ * 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;
+}
--- /dev/null
+/*
+ * (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 <common.h>
+#include <s_record.h>
+
+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);
+}
--- /dev/null
+/*
+ *==========================================================================
+ *
+ * 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 <common.h>
+#include <xyzModem.h>
+#include <stdarg.h>
+#include <crc.h>
+
+/* 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;
+ }
+}
--- /dev/null
+/*
+ * (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<type><length><address><data....><checksum>
+ * S4<length><address><name>,<checksum>
+ *
+ * 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);
--- /dev/null
+/*
+ *==========================================================================
+ *
+ * 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_ */