diff --git a/commands/Kconfig b/commands/Kconfig index e0e05ad..08db1dd 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -123,11 +123,16 @@ menu "memory " config CMD_LOADB - depends on BROKEN select CRC16 tristate prompt "loadb" +config CMD_LOADY + select CRC16 + depends on EXPERIMENTAL + bool + prompt "loady" + config CMD_LOADS depends on BROKEN tristate diff --git a/commands/Makefile b/commands/Makefile index 1214fa1..157c8cd 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_CMD_BOOTM) += bootm.o obj-$(CONFIG_CMD_LOADB) += loadb.o xyzModem.o +obj-$(CONFIG_CMD_LOADY) += loadb.o xyzModem.o obj-$(CONFIG_CMD_LOADS) += loads.o obj-$(CONFIG_CMD_ECHO) += echo.o obj-$(CONFIG_CMD_MEMORY) += mem.o diff --git a/commands/loadb.c b/commands/loadb.c index a6f3604..d81afce 100644 --- a/commands/loadb.c +++ b/commands/loadb.c @@ -1,3 +1,12 @@ +/** + * @file + * @brief LoadB and LoadY support. + * + * Provides loadb (over Kermit) and LoadY(over Y modem) support to download + * images. + * + * FileName: commands/loadb.c + */ /* * (C) Copyright 2000-2004 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. @@ -26,291 +35,187 @@ */ #include #include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include -DECLARE_GLOBAL_DATA_PTR; - -static ulong load_serial_ymodem (ulong offset); - -#define XON_CHAR 17 -#define XOFF_CHAR 19 -#define START_CHAR 0x01 +#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)) +#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)) +#define DEF_FILE "/dev/mem" -extern int os_data_count; -extern int os_data_header[8]; +static int ofd; /* output file descriptor */ -static void set_kerm_bin_mode(unsigned long *); -static int k_recv(void); -static ulong load_serial_bin (ulong offset); +#ifdef CONFIG_CMD_LOADB +static char his_eol; /* character he needs at end of packet */ +static int his_pad_count; /* number of pad chars he needs */ +static char his_pad_char; /* pad chars he needs */ +static char his_quote; /* quote chars he'll use */ - -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 argc, char *argv[]) -{ - ulong offset = 0; - ulong addr; - int load_baudrate, current_baudrate; - int rcode = 0; - char *s; - - /* 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) { - printf ("## Switch baudrate to %d bps and press ENTER ...\n", - load_baudrate); - udelay(50000); - gd->baudrate = load_baudrate; - serial_setbrg (); - udelay(50000); - for (;;) { - if (getc() == '\r') - break; - } - } - - if (strcmp(argv[0],"loady")==0) { - printf ("## Ready for binary (ymodem) download " - "to 0x%08lX at %d bps...\n", - offset, - load_baudrate); - - addr = load_serial_ymodem (offset); - - } else { - - printf ("## Ready for binary (kermit) download " - "to 0x%08lX at %d bps...\n", - offset, - load_baudrate); - addr = load_serial_bin (offset); - - if (addr == ~0) { - load_addr = 0; - printf ("## Binary (kermit) download aborted\n"); - rcode = 1; - } else { - printf ("## Start Addr = 0x%08lX\n", addr); - load_addr = addr; - } - } - if (load_baudrate != current_baudrate) { - printf ("## Switch baudrate to %d bps and press ESC ...\n", - current_baudrate); - udelay (50000); - gd->baudrate = current_baudrate; - serial_setbrg (); - udelay (50000); - for (;;) { - if (getc() == 0x1B) /* ESC */ - break; - } - } - - return rcode; -} - - -static ulong load_serial_bin (ulong offset) -{ - int size, i; - char buf[32]; - - 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); - } - - flush_cache (offset, size); - - printf("## Total Size = 0x%08x = %d Bytes\n", size, size); - sprintf(buf, "%X", size); - setenv("filesize", buf); - - return offset; -} - -void send_pad (void) +static void send_pad(void) { int count = his_pad_count; while (count-- > 0) - putc (his_pad_char); + console_putc(CONSOLE_STDOUT, his_pad_char); } /* converts escaped kermit char to binary char */ -char ktrans (char in) +static char ktrans(char in) { if ((in & 0x60) == 0x40) { - return (char) (in & ~0x40); + return (char)(in & ~0x40); } else if ((in & 0x7f) == 0x3f) { - return (char) (in | 0x40); + return (char)(in | 0x40); } else return in; } -int chk1 (char *buffer) +static int chk1(char *buffer) { int total = 0; while (*buffer) { total += *buffer++; } - return (int) ((total + ((total >> 6) & 0x03)) & 0x3f); + return (int)((total + ((total >> 6) & 0x03)) & 0x3f); } -void s1_sendpacket (char *packet) +static void s1_sendpacket(char *packet) { - send_pad (); + send_pad(); while (*packet) { - putc (*packet++); + console_putc(CONSOLE_STDOUT, *packet++); } } static char a_b[24]; -void send_ack (int n) +static void send_ack(int n) { a_b[0] = START_CHAR; - a_b[1] = tochar (3); - a_b[2] = tochar (n); + 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[4] = tochar(chk1(&a_b[1])); a_b[5] = his_eol; a_b[6] = '\0'; - s1_sendpacket (a_b); + s1_sendpacket(a_b); } -void send_nack (int n) +static void send_nack(int n) { a_b[0] = START_CHAR; - a_b[1] = tochar (3); - a_b[2] = tochar (n); + 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[4] = tochar(chk1(&a_b[1])); a_b[5] = his_eol; a_b[6] = '\0'; - s1_sendpacket (a_b); + s1_sendpacket(a_b); } +/* os_data_* data buffer handling before flushing the data to the o/p device + * we will only dump valid packets to the device. + */ +static int os_data_count, os_old_count, os_data_total; +static char *os_data_addr, *os_old_buffer; -/* 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) +static void os_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) { - case 0: /* data */ - *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; + os_old_count = 0; + os_data_addr = NULL; + os_old_buffer = NULL; } +static int os_data_alloc(int length) +{ + /* If already allocated, free the memory + */ + if (os_data_addr != NULL) { + os_data_addr -= os_data_count; + free(os_data_addr); + } + os_data_count = 0; + os_data_addr = (char *)malloc(length); + if (os_data_addr == NULL) + return -1; + return 0; +} + +static int os_data_save(void) +{ + int ret = 0; + /* if there was an old data, flush it */ + if (os_old_buffer) { + flush_cache((ulong) os_old_buffer, os_old_count); + ret = write(ofd, os_old_buffer, os_old_count); + if (ret < 0) + return ret; + os_data_total += os_old_count; + free(os_old_buffer); + } + os_old_count = os_data_count; + os_old_buffer = os_data_addr - os_data_count; + os_data_count = 0; + os_data_addr = NULL; + return 0; +} + +static void os_data_dump(void) +{ + if (os_old_buffer) + free(os_old_buffer); + if (os_data_addr) { + os_data_addr -= os_data_count; + free(os_data_addr); + } + os_data_init(); +} +static void os_data_char(char new_char) +{ + *os_data_addr++ = new_char; + os_data_count++; +} /* k_data_* simply handles the kermit escape translations */ static int k_data_escape, k_data_escape_saved; -void k_data_init (void) +static void k_data_init(void) { k_data_escape = 0; - os_data_init (); } -void k_data_save (void) +static void k_data_save(void) { k_data_escape_saved = k_data_escape; - os_data_save (); } -void k_data_restore (void) +static void k_data_restore(void) { k_data_escape = k_data_escape_saved; - os_data_restore (); } -void k_data_char (char new_char) +static void k_data_char(char new_char) { if (k_data_escape) { /* last char was escape - translate this character */ - os_data_char (ktrans (new_char)); + os_data_char(ktrans(new_char)); k_data_escape = 0; } else { if (new_char == his_quote) { @@ -318,24 +223,30 @@ k_data_escape = 1; } else { /* otherwise send this char as-is */ - os_data_char (new_char); + os_data_char(new_char); } } } #define SEND_DATA_SIZE 20 -char send_parms[SEND_DATA_SIZE]; -char *send_ptr; +static char send_parms[SEND_DATA_SIZE]; +static 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) +/** + * @brief interprets the protocol info and builds and + * sends an appropriate ack for what we can do + * + * @param n sequence + * + * @return void + */ +static 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_eol = END_CHAR; /* default end of line character */ his_pad_count = 0; his_pad_char = '\0'; his_quote = K_ESCAPE; @@ -349,30 +260,30 @@ break; /* handle MAXL - max length */ /* ignore what he says - most I'll take (here) is 94 */ - a_b[++length] = tochar (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); + 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); + 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]); + 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); + his_eol = untochar(send_parms[4]); + a_b[++length] = tochar(END_CHAR); if (bytes-- <= 0) break; /* handle QCTL - quote control char he'll use */ @@ -398,25 +309,29 @@ 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 */ + 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[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] = tochar(chk1(&a_b[1])); a_b[++length] = his_eol; a_b[++length] = '\0'; - s1_sendpacket (a_b); + s1_sendpacket(a_b); } -/* k_recv receives a OS Open image file over kermit line */ -static int k_recv (void) +/** + * @brief receives a OS Open image file over kermit line + * + * @return bytes read + */ +static int k_recv(void) { char new_char; char k_state, k_state_saved; @@ -428,7 +343,7 @@ int len_lo, len_hi; /* initialize some protocol parameters */ - his_eol = END_CHAR; /* default end of line character */ + his_eol = END_CHAR; /* default end of line character */ his_pad_count = 0; his_pad_char = '\0'; his_quote = K_ESCAPE; @@ -436,10 +351,12 @@ /* initialize the k_recv and k_data state machine */ done = 0; k_state = 0; - k_data_init (); + k_data_init(); k_state_saved = k_state; - k_data_save (); - n = 0; /* just to get rid of a warning */ + k_data_save(); + os_data_init(); + os_data_total = 0; + n = 0; /* just to get rid of a warning */ last_n = -1; /* expect this "type" sequence (but don't check): @@ -461,14 +378,15 @@ If a character less than SPACE (0x20) is received - error. */ - /* get a packet */ /* wait for the starting character or ^C */ for (;;) { - switch (getc ()) { + switch (getc()) { case START_CHAR: /* start packet */ goto START; - case ETX_CHAR: /* ^C waiting for packet */ + case ETX_CHAR: /* ^C waiting for packet */ + /* Save last success buffer and quit */ + (void)os_data_save(); return (0); default: ; @@ -477,17 +395,17 @@ START: /* get length of packet */ sum = 0; - new_char = getc (); + new_char = getc(); if ((new_char & 0xE0) == 0) goto packet_error; sum += new_char & 0xff; - length = untochar (new_char); + length = untochar(new_char); /* get sequence number */ - new_char = getc (); + new_char = getc(); if ((new_char & 0xE0) == 0) goto packet_error; sum += new_char & 0xff; - n = untochar (new_char); + n = untochar(new_char); --length; /* NEW CODE - check sequence numbers for retried packets */ @@ -500,17 +418,20 @@ if (n == last_n) { /* same sequence number, restore the previous state */ k_state = k_state_saved; - k_data_restore (); + k_data_restore(); + /* Dump any previously allocated data including previous + * buffer host wishes to retry.. */ + os_data_dump(); } else { /* new sequence number, checkpoint the download */ last_n = n; k_state_saved = k_state; - k_data_save (); + k_data_save(); } /* END NEW CODE */ /* get packet type */ - new_char = getc (); + new_char = getc(); if ((new_char & 0xE0) == 0) goto packet_error; sum += new_char & 0xff; @@ -520,36 +441,45 @@ if (length == -2) { /* (length byte was 0, decremented twice) */ /* get the two length bytes */ - new_char = getc (); + new_char = getc(); if ((new_char & 0xE0) == 0) goto packet_error; sum += new_char & 0xff; - len_hi = untochar (new_char); - new_char = getc (); + 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); + len_lo = untochar(new_char); length = len_hi * 95 + len_lo; /* check header checksum */ - new_char = getc (); + new_char = getc(); if ((new_char & 0xE0) == 0) goto packet_error; - if (new_char != tochar ((sum + ((sum >> 6) & 0x03)) & 0x3f)) + 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 */ + /* --length; + * new length includes only data and block check to come + */ } - /* bring in rest of packet */ + /* Try to allocate data chunk */ + if (length > 1) { + if (os_data_alloc(length) < 0) { + /* too large.. NACK the data back */ + goto packet_error; + } + } while (length > 1) { - new_char = getc (); + 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); + k_data_char(new_char); } else if (k_state == SEND_TYPE) { /* save send pack in buffer as is */ *send_ptr++ = new_char; @@ -559,46 +489,96 @@ } } /* get and validate checksum character */ - new_char = getc (); + new_char = getc(); if ((new_char & 0xE0) == 0) goto packet_error; - if (new_char != tochar ((sum + ((sum >> 6) & 0x03)) & 0x3f)) + if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f)) goto packet_error; /* get END_CHAR */ - new_char = getc (); + new_char = getc(); if (new_char != END_CHAR) { - packet_error: +packet_error: /* restore state machines */ k_state = k_state_saved; - k_data_restore (); + k_data_restore(); /* send a negative acknowledge packet in */ - send_nack (n); + send_nack(n); } else if (k_state == SEND_TYPE) { /* crack the protocol parms, build an appropriate ack packet */ - handle_send_packet (n); + handle_send_packet(n); } else { /* send simple acknowledge packet in */ - send_ack (n); + send_ack(n); + /* Flush old buffer, + * Store current buffer as old buffer */ + os_data_save(); /* quit if end of transmission */ - if (k_state == BREAK_TYPE) + if (k_state == BREAK_TYPE) { + /* Flush remaining buffer */ + os_data_save(); done = 1; + } } ++z; } - return ((ulong) os_data_addr - (ulong) bin_start_address); + return ((ulong) os_data_total); } -static int getcxmodem(void) { +/** + * @brief loadb Support over kermit protocol + * + * @return download size + */ +static ulong load_serial_bin(void) +{ + int size, i; + char buf[32]; + + 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); + } + printf("## Total Size = 0x%08x = %d Bytes\n", size, size); + sprintf(buf, "%X", size); + setenv("filesize", buf); + + return size; +} + +#endif /* CONFIG_CMD_LOADB */ + +#ifdef CONFIG_CMD_LOADY +/** + * @brief getcxmodem + * + * @return if character avaiable, return the same, else return -1 + */ +static int getcxmodem(void) +{ if (tstc()) return (getc()); return -1; } -static ulong load_serial_ymodem (ulong offset) + +/** + * @brief LoadY over ymodem protocol + * + * @return download size + */ +static ulong load_serial_ymodem(void) { int size; char buf[32]; int err; - int res; + int res, wr; connection_info_t info; char ymodemBuf[1024]; ulong store_addr = ~0; @@ -606,46 +586,191 @@ size = 0; info.mode = xyzModem_ymodem; - res = xyzModem_stream_open (&info, &err); + res = xyzModem_stream_open(&info, &err); if (!res) { - - while ((res = - xyzModem_stream_read (ymodemBuf, 1024, &err)) > 0) { - store_addr = addr + offset; + while ((res = xyzModem_stream_read(ymodemBuf, 1024, &err)) > + 0) { size += res; addr += res; - memcpy ((char *) (store_addr), ymodemBuf, res); + flush_cache((ulong) yModemBuf, res); + wr = write(ofd, ymodemBuf, res); + if (res != wr) { + perror("ymodem"); + break; + } } } else { - printf ("%s\n", xyzModem_error (err)); + printf("%s\n", xyzModem_error(err)); } - xyzModem_stream_close (&err); - xyzModem_stream_terminate (false, &getcxmodem); + xyzModem_stream_close(&err); + xyzModem_stream_terminate(false, &getcxmodem); + printf("## Total Size = 0x%08x = %d Bytes\n", size, size); + sprintf(buf, "%X", size); + setenv("filesize", buf); - flush_cache (offset, size); + return res; +} +#endif - printf ("## Total Size = 0x%08x = %d Bytes\n", size, size); - sprintf (buf, "%X", size); - setenv ("filesize", buf); - - return offset; +/** + * @brief returns current used console device + * + * @return console device which is registered with CONSOLE_STDIN and + * CONSOLE_STDOUT + */ +static struct console_device *get_current_console(void) +{ + struct console_device *cdev; + /* + * Assumption to have BOTH CONSOLE_STDIN AND STDOUT in the + * same output console + */ + for_each_console(cdev) { + if ((cdev->f_active & (CONSOLE_STDIN | CONSOLE_STDOUT))) + return cdev; + } + return NULL; } -U_BOOT_CMD( - loadb, 3, 0, do_load_serial_bin, - "loadb - load binary file over serial line (kermit mode)\n", - "[ off ] [ baud ]\n" - " - load binary file over serial line" - " with offset 'off' and baudrate 'baud'\n" -); +/** + * @brief provide the loadb(Kermit) or loadY mode support + * + * @param cmdtp + * @param argc + * @param argv + * + * @return success or failure + */ +static int do_load_serial_bin(cmd_tbl_t *cmdtp, int argc, char *argv[]) +{ + ulong offset = 0; + ulong addr; + int load_baudrate = 0, current_baudrate; + int rcode = 0; + int opt; + char *output_file = NULL; + struct console_device *cdev = NULL; -U_BOOT_CMD( - loady, 3, 0, do_load_serial_bin, - "loady - load binary file over serial line (ymodem mode)\n", - "[ off ] [ baud ]\n" - " - load binary file over serial line" - " with offset 'off' and baudrate 'baud'\n" -); + getopt_reset(); + + while ((opt = getopt(argc, argv, "d:b:o:")) > 0) { + switch (opt) { + case 'd': + output_file = optarg; + break; + case 'b': + load_baudrate = (int)simple_strtoul(optarg, NULL, 10); + break; + case 'o': + offset = (int)simple_strtoul(optarg, NULL, 10); + break; + default: + perror(argv[0]); + return 1; + } + } + + cdev = get_current_console(); + if (NULL == cdev) { + printf("%s:No console device with STDIN and STDOUT\n", argv[0]); + return -ENODEV; + } + current_baudrate = simple_strtoul(cdev->baudrate_string, NULL, 10); + + /* Load Defaults */ + if (load_baudrate == 0) + load_baudrate = current_baudrate; + if (NULL == output_file) + output_file = DEF_FILE; + + /* File should exist */ + ofd = open(output_file, O_WRONLY); + if (ofd < 0) { + perror(argv[0]); + return 3; + } + /* Seek to the right offset */ + if (offset) { + int seek = lseek(ofd, offset, SEEK_SET); + if (seek != offset) { + close(ofd); + ofd = 0; + perror(argv[0]); + return 4; + } + } + + if (load_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ENTER ...\n", + load_baudrate); + udelay(50000); + cdev->setbrg(cdev, load_baudrate); + udelay(50000); + for (;;) { + if (getc() == '\r') + break; + } + } +#ifdef CONFIG_CMD_LOADY + if (strcmp(argv[0], "loady") == 0) { + printf("## Ready for binary (ymodem) download " + "to 0x%08lX offset on %s device at %d bps...\n", offset, + output_file, load_baudrate); + addr = load_serial_ymodem(); + } +#endif +#ifdef CONFIG_CMD_LOADB + if (strcmp(argv[0], "loadb") == 0) { + + printf("## Ready for binary (kermit) download " + "to 0x%08lX offset on %s device at %d bps...\n", offset, + output_file, load_baudrate); + addr = load_serial_bin(); + if (addr == 0) { + printf("## Binary (kermit) download aborted\n"); + rcode = 1; + } + } +#endif + if (load_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ESC ...\n", + current_baudrate); + udelay(50000); + cdev->setbrg(cdev, current_baudrate); + udelay(50000); + for (;;) { + if (getc() == 0x1B) /* ESC */ + break; + } + } + + close(ofd); + ofd = 0; + return rcode; +} + +static const __maybe_unused char cmd_loadb_help[] = + "[OPTIONS]\n" + " -d device - which device to download - defaults to " DEF_FILE "\n" + " -o offset - what offset to download - defaults to 0\n" + " -b baud - baudrate at which to download - defaults to " + "console baudrate\n"; +#ifdef CONFIG_CMD_LOADB +U_BOOT_CMD_START(loadb) + .maxargs = 7, + .cmd = do_load_serial_bin, + .usage = "Load binary file over serial line (kermit mode)", + U_BOOT_CMD_HELP(cmd_loadb_help) +U_BOOT_CMD_END +#endif +#ifdef CONFIG_CMD_LOADY +U_BOOT_CMD_START(loady) + .maxargs = 7, + .cmd = do_load_serial_bin, + .usage = "Load binary file over serial line (ymodem mode)", + U_BOOT_CMD_HELP(cmd_loadb_help) +U_BOOT_CMD_END +#endif diff --git a/commands/xyzModem.c b/commands/xyzModem.c new file mode 100644 index 0000000..d4e4bfa --- /dev/null +++ b/commands/xyzModem.c @@ -0,0 +1,785 @@ +/** + * @file + * @brief RedBoot stream handler for xyzModem protocol + * + * FileName: commands/xyzModem.c + * Originally from U-Boot V1 xyzModem.c + */ +/* + * 2008 - Nishanth Menon + * Modified for sparse and checkpatch.pl compliance + */ +/* + *========================================================================== + * + * xyzModem.c + * + * RedBoot stream handler for xyzModem protocol + * + *========================================================================== + *####ECOSGPLCOPYRIGHTBEGIN#### + * ------------------------------------------- + * This file is part of eCos, the Embedded Configurable Operating System. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + * Copyright (C) 2002 Gary Thomas + * + * eCos is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 or (at your option) any later version. + * + * eCos is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with eCos; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * As a special exception, if other files instantiate templates or use macros + * or inline functions from this file, or you compile this file and link it + * with other works to produce a work based on this file, this file does not + * by itself cause the resulting work to be covered by the GNU General Public + * License. However the source code for this file must still be made available + * in accordance with section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + * + * Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. + * at http: *sources.redhat.com/ecos/ecos-license/ + * ------------------------------------------- + *####ECOSGPLCOPYRIGHTEND#### + *========================================================================== + *#####DESCRIPTIONBEGIN#### + * + * Author(s): gthomas + * Contributors: gthomas, tsmith, Yoshinori Sato + * Date: 2000-07-14 + * Purpose: + * Description: + * + * This code is part of RedBoot (tm). + * + *####DESCRIPTIONEND#### + * + *========================================================================== + */ +#include +#include +#include +#include +#include +#include +#include + +/* Assumption - run xyzModem protocol over the console port */ + +/* Values magic to the protocol */ +#define SOH 0x01 +#define STX 0x02 +#define EOT 0x04 +#define ACK 0x06 +#define BSP 0x08 +#define NAK 0x15 +#define CAN 0x18 +#define EOF 0x1A /* ^Z for DOS officionados */ + +#define USE_YMODEM_LENGTH + +/* Data & state local to the protocol */ +static struct { +#ifdef REDBOOT + hal_virtual_comm_table_t *__chan; +#else + int *__chan; +#endif + 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; + bool 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 +/* Wait for 3 CAN before quitting */ +#define xyzModem_CAN_COUNT 3 + +#ifndef REDBOOT /*SB */ +typedef int cyg_int32; +static int CYGACC_COMM_IF_GETC_TIMEOUT(char chan, char *c) +{ +#define DELAY 20 + unsigned long counter = 0; + while (!tstc() && (counter < xyzModem_CHAR_TIMEOUT * 1000 / DELAY)) { + udelay(DELAY); + counter++; + } + if (tstc()) { + *c = getc(); + return 1; + } + return 0; +} + +static void CYGACC_COMM_IF_PUTC(char x, char y) +{ + console_putc(CONSOLE_STDOUT, y); +} + +/* Validate a hex character */ +static inline bool _is_hex(char c) +{ + return (((c >= '0') && (c <= '9')) || + ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f'))); +} + +/* Convert a single hex nibble */ +static inline 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; +} + +/* Parse (scan) a number */ +static bool parse_num(char *s, unsigned long *val, char **es, char *delim) +{ + bool first = true; + 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 = false; + c = *s++; + if (_is_hex(c)) + digit = _from_hex(c); + if (_is_hex(c) && (digit < radix)) { + /* Valid digit */ +#ifdef CYGPKG_HAL_MIPS + /* FIXME: tx49 compiler generates 0x2539018 for + * MUL which isn't any good. */ + if (16 == radix) + result = result << 4; + else + result = 10 * result; + result += digit; +#else + result = (result * radix) + digit; +#endif + } else { + if (delim != (char *)0) { + /* See if this character is one of the + * delimiters */ + char *dp = delim; + while (*dp && (c != *dp)) + dp++; + if (*dp) + break; /* Found a good delimiter */ + } + return false; /* Malformatted number */ + } + } + *val = result; + if (es != (char **)0) + *es = s; + return true; +} + +#endif + +#define USE_SPRINTF +#ifdef DEBUG +#ifndef USE_SPRINTF +/* + * Note: this debug setup only works if the target platform has two serial ports + * available so that the other one (currently only port 1) can be used for debug + * messages. + */ +static int zm_dprintf(char *fmt, ...) +{ + int cur_console; + va_list args; + + va_start(args, fmt); +#ifdef REDBOOT + cur_console = + CYGACC_CALL_IF_SET_CONSOLE_COMM + (CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); + CYGACC_CALL_IF_SET_CONSOLE_COMM(1); +#endif + diag_vprintf(fmt, args); +#ifdef REDBOOT + CYGACC_CALL_IF_SET_CONSOLE_COMM(cur_console); +#endif +} + +static void zm_flush(void) +{ +} + +#else +/* + * Note: this debug setup works by storing the strings in a fixed buffer + */ +#define FINAL +#ifdef FINAL +static char *zm_out = (char *)0x00380000; +static char *zm_out_start = (char *)0x00380000; +#else +static char zm_buf[8192]; +static char *zm_out = zm_buf; +static char *zm_out_start = zm_buf; + +#endif +static int zm_dprintf(char *fmt, ...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = diag_vsprintf(zm_out, fmt, args); + zm_out += len; + return len; +} + +static void zm_flush(void) +{ +#ifdef REDBOOT + char *p = zm_out_start; + while (*p) + mon_write_char(*p++); +#endif + zm_out = zm_out_start; +} +#endif + +static void zm_dump_buf(void *buf, int len) +{ +#ifdef REDBOOT + diag_vdump_buf_with_offset(zm_dprintf, buf, len, 0); +#else + +#endif +} + +static unsigned char zm_buf[2048]; +static unsigned char *zm_bp; + +static void zm_new(void) +{ + zm_bp = zm_buf; +} + +static void zm_save(unsigned char c) +{ + *zm_bp++ = c; +} + +static void zm_dump(int line) +{ + zm_dprintf("Packet at line: %d\n", line); + zm_dump_buf(zm_buf, zm_bp - zm_buf); +} + +#define ZM_DEBUG(x) x +#else +#define ZM_DEBUG(x) +#endif + +/* Wait for the line to go idle */ +static void xyzModem_flush(void) +{ + int res; + char c; + while (true) { + res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c); + if (!res) + return; + } +} + +static int xyzModem_get_hdr(void) +{ + char c; + int res; + bool hdr_found = false; + int i, can_total, hdr_chars; + unsigned short cksum; + + ZM_DEBUG(zm_new()); + /* Find the start of a header */ + can_total = 0; + hdr_chars = 0; + + if (xyz.tx_ack) { + CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK); + xyz.tx_ack = false; + } + while (!hdr_found) { + res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c); + ZM_DEBUG(zm_save(c)); + if (res) { + hdr_chars++; + switch (c) { + case SOH: + xyz.total_SOH++; + case STX: + if (c == STX) + xyz.total_STX++; + hdr_found = true; + break; + case CAN: + xyz.total_CAN++; + ZM_DEBUG(zm_dump(__LINE__)); + 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) { + CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK); + ZM_DEBUG(zm_dprintf + ("ACK on EOT #%d\n", + __LINE__)); + ZM_DEBUG(zm_dump(__LINE__)); + return xyzModem_eof; + } + default: + /* Ignore, waiting for start of header */ + ; + } + } else { + /* Data stream timed out */ + xyzModem_flush(); /* Toss any current input */ + ZM_DEBUG(zm_dump(__LINE__)); + CYGACC_CALL_IF_DELAY_US((cyg_int32) 250000); + return xyzModem_timeout; + } + } + + /* Header found, now read the data */ + res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, (char *)&xyz.blk); + ZM_DEBUG(zm_save(xyz.blk)); + if (!res) { + ZM_DEBUG(zm_dump(__LINE__)); + return xyzModem_timeout; + } + res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, (char *)&xyz.cblk); + ZM_DEBUG(zm_save(xyz.cblk)); + if (!res) { + ZM_DEBUG(zm_dump(__LINE__)); + return xyzModem_timeout; + } + xyz.len = (c == SOH) ? 128 : 1024; + xyz.bufp = xyz.pkt; + for (i = 0; i < xyz.len; i++) { + res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c); + ZM_DEBUG(zm_save(c)); + if (res) { + xyz.pkt[i] = c; + } else { + ZM_DEBUG(zm_dump(__LINE__)); + return xyzModem_timeout; + } + } + res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, (char *)&xyz.crc1); + ZM_DEBUG(zm_save(xyz.crc1)); + if (!res) { + ZM_DEBUG(zm_dump(__LINE__)); + return xyzModem_timeout; + } + if (xyz.crc_mode) { + res = + CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, (char *)&xyz.crc2); + ZM_DEBUG(zm_save(xyz.crc2)); + if (!res) { + ZM_DEBUG(zm_dump(__LINE__)); + return xyzModem_timeout; + } + } + ZM_DEBUG(zm_dump(__LINE__)); + /* Validate the message */ + if ((xyz.blk ^ xyz.cblk) != (unsigned char)0xFF) { + ZM_DEBUG(zm_dprintf + ("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk, + (xyz.blk ^ xyz.cblk))); + ZM_DEBUG(zm_dump_buf(xyz.pkt, xyz.len)); + 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)) { + ZM_DEBUG(zm_dprintf + ("CRC error - recvd: %02x%02x, computed: %x\n", + xyz.crc1, xyz.crc2, cksum & 0xFFFF)); + return xyzModem_cksum; + } + } else { + cksum = 0; + for (i = 0; i < xyz.len; i++) + cksum += xyz.pkt[i]; + if (xyz.crc1 != (cksum & 0xFF)) { + ZM_DEBUG(zm_dprintf + ("Checksum error - recvd: %x, computed: %x\n", + 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) +{ +#ifdef REDBOOT + int console_chan; +#endif + int stat = 0; + int retries = xyzModem_MAX_RETRIES; + int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC; + +/* ZM_DEBUG(zm_out = zm_out_start); */ +#ifdef xyzModem_zmodem + if (info->mode == xyzModem_zmodem) { + *err = xyzModem_noZmodem; + return -1; + } +#endif + +#ifdef REDBOOT + /* Set up the I/O channel. Note: this allows for using a different + * port in the future */ + console_chan = + CYGACC_CALL_IF_SET_CONSOLE_COMM + (CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); + if (info->chan >= 0) + CYGACC_CALL_IF_SET_CONSOLE_COMM(info->chan); + else + CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan); + + xyz.__chan = CYGACC_CALL_IF_CONSOLE_PROCS(); + + CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan); + CYGACC_COMM_IF_CONTROL(*xyz.__chan, __COMMCTL_SET_TIMEOUT, + xyzModem_CHAR_TIMEOUT); +#else +/* TODO: CHECK ! */ + int dummy; + xyz.__chan = &dummy; +#endif + xyz.len = 0; + xyz.crc_mode = true; + xyz.at_eof = false; + xyz.tx_ack = false; + 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 + + CYGACC_COMM_IF_PUTC(*xyz.__chan, (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 = true; + } + xyz.next_blk = 1; + xyz.len = 0; + return 0; + } else if (stat == xyzModem_timeout) { + if (--crc_retries <= 0) + xyz.crc_mode = false; + /* Extra delay for startup */ + CYGACC_CALL_IF_DELAY_US(5 * 100000); + CYGACC_COMM_IF_PUTC(*xyz.__chan, + (xyz.crc_mode ? 'C' : NAK)); + xyz.total_retries++; + ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__)); + } + if (stat == xyzModem_cancel) + break; + } + *err = stat; + ZM_DEBUG(zm_flush()); + 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 = true; + ZM_DEBUG(zm_dprintf + ("ACK block %d (%d)\n", + xyz.blk, __LINE__)); + 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 + /* WARNING - Leaving formatting aside for code + * clarity */ + /* 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 + /* WARNING - Leaving formatting aside for code + * clarity */ + /* + * 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 */ + CYGACC_COMM_IF_PUTC(*xyz.__chan, + ACK); + /* Need new header */ + continue; + } else + stat = xyzModem_sequence; + } + if (stat == xyzModem_cancel) + break; + if (stat == xyzModem_eof) { + CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK); + ZM_DEBUG(zm_dprintf + ("ACK (%d)\n", __LINE__)); + if (xyz.mode == xyzModem_ymodem) { + CYGACC_COMM_IF_PUTC(*xyz.__chan, + (xyz. + crc_mode ? + 'C' : + NAK)); + xyz.total_retries++; + ZM_DEBUG(zm_dprintf + ("Reading Final Header\n")); + stat = xyzModem_get_hdr(); + CYGACC_COMM_IF_PUTC(*xyz.__chan, + ACK); + ZM_DEBUG(zm_dprintf + ("FINAL ACK (%d)\n", + __LINE__)); + } + xyz.at_eof = true; + break; + } + CYGACC_COMM_IF_PUTC(*xyz.__chan, + (xyz.crc_mode ? 'C' : NAK)); + xyz.total_retries++; + ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__)); + } + 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) +{ + diag_printf + ("xyzModem - %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); + ZM_DEBUG(zm_flush()); +} + +/* Need to be able to clean out the input buffer, so have to take the */ +/* getc */ +void xyzModem_stream_terminate(bool abort, int (*getc) (void)) +{ + int c; + + if (abort) { + ZM_DEBUG(zm_dprintf("!!!! TRANSFER ABORT !!!!\n")); + 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. */ + CYGACC_COMM_IF_PUTC(*xyz.__chan, CAN); + CYGACC_COMM_IF_PUTC(*xyz.__chan, CAN); + CYGACC_COMM_IF_PUTC(*xyz.__chan, CAN); + CYGACC_COMM_IF_PUTC(*xyz.__chan, CAN); + CYGACC_COMM_IF_PUTC(*xyz.__chan, BSP); + CYGACC_COMM_IF_PUTC(*xyz.__chan, BSP); + CYGACC_COMM_IF_PUTC(*xyz.__chan, BSP); + CYGACC_COMM_IF_PUTC(*xyz.__chan, BSP); + /* Now consume the rest of what's waiting on the line.*/ + ZM_DEBUG(zm_dprintf("Flushing serial line.\n")); + xyzModem_flush(); + xyz.at_eof = true; + break; +#ifdef xyzModem_zmodem + case xyzModem_zmodem: + /* Might support it some day I suppose. */ +#endif + break; + } + } else { + ZM_DEBUG(zm_dprintf("Engaging cleanup mode...\n")); + /* + * 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. + */ + ZM_DEBUG(zm_dprintf("Trailing gunk:\n")); + while ((c = (*getc) ()) > -1) ; + ZM_DEBUG(zm_dprintf("\n")); + /* + * Make a small delay to give terminal programs like minicom + * time to get control again after their file transfer program + * exits. + */ + CYGACC_CALL_IF_DELAY_US((cyg_int32) 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; + } +} + +/* + * RedBoot interface + */ +#if 0 /* SB */ +GETC_IO_FUNCS(xyzModem_io, xyzModem_stream_open, xyzModem_stream_close, + xyzModem_stream_terminate, xyzModem_stream_read, xyzModem_error); +RedBoot_load(xmodem, xyzModem_io, false, false, xyzModem_xmodem); +RedBoot_load(ymodem, xyzModem_io, false, false, xyzModem_ymodem); +#endif diff --git a/lib/Makefile b/lib/Makefile index b15d2ee..00e78ba 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_BZLIB) += bzlib.o bzlib_crctable.o bzlib_decompress.o bzlib_huffman.o bzlib_randtable.o obj-$(CONFIG_ZLIB) += zlib.o gunzip.o obj-$(CONFIG_CRC32) += crc32.o +obj-$(CONFIG_CRC16) += crc16.o obj-$(CONFIG_CMDLINE_EDITING) += readline.o obj-$(CONFIG_SIMPLE_READLINE) += readline_simple.o obj-$(CONFIG_GLOB) += fnmatch.o