Restore support for loadb, loads and saves commands. Still needs some tests and fixes.
authorPiotr Dymacz <pepe2k@gmail.com>
Wed, 29 Apr 2015 14:00:32 +0000 (16:00 +0200)
committerPiotr Dymacz <pepe2k@gmail.com>
Wed, 29 Apr 2015 14:00:32 +0000 (16:00 +0200)
u-boot/common/Makefile
u-boot/common/cmd_load.c [new file with mode: 0755]
u-boot/common/crc16.c [new file with mode: 0755]
u-boot/common/s_record.c [new file with mode: 0755]
u-boot/common/xyzModem.c [new file with mode: 0755]
u-boot/include/s_record.h [new file with mode: 0755]
u-boot/include/xyzModem.h [new file with mode: 0755]

index aea6c089126d0050841330d3a6b88a6f4919d7ee..9fdb706df80b7aa600066afe0b6f68819c1174d7 100644 (file)
@@ -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 (executable)
index 0000000..3d9172a
--- /dev/null
@@ -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 <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)*/
diff --git a/u-boot/common/crc16.c b/u-boot/common/crc16.c
new file mode 100755 (executable)
index 0000000..f4c680d
--- /dev/null
@@ -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 (executable)
index 0000000..dcc4305
--- /dev/null
@@ -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 <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);
+}
diff --git a/u-boot/common/xyzModem.c b/u-boot/common/xyzModem.c
new file mode 100755 (executable)
index 0000000..26df26e
--- /dev/null
@@ -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 <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;
+       }
+}
diff --git a/u-boot/include/s_record.h b/u-boot/include/s_record.h
new file mode 100755 (executable)
index 0000000..07806d5
--- /dev/null
@@ -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<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);
diff --git a/u-boot/include/xyzModem.h b/u-boot/include/xyzModem.h
new file mode 100755 (executable)
index 0000000..ca295ce
--- /dev/null
@@ -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_ */