mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-25 23:06:15 +00:00

Now when loadx and loady commands could be aborted / cancelled by CTRL+C, allow to configure timeout for initial x/y-modem packet via env variable $loadxy_timeout and by default use value from new compile-time config option CONFIG_CMD_LOADXY_TIMEOUT. Value is in seconds and zero value means infinite timeout. Default value is 90s which is the value used before this change for loadx command. Other load commands loadb and loads already waits infinitely. Same behavior for loadx and loady commands can be achieved by setting $loadxy_timeout or CONFIG_CMD_LOADXY_TIMEOUT to 0. Signed-off-by: Pali Rohár <pali@kernel.org>
751 lines
17 KiB
C
751 lines
17 KiB
C
// SPDX-License-Identifier: eCos-2.0
|
|
/*
|
|
*==========================================================================
|
|
*
|
|
* xyzModem.c
|
|
*
|
|
* RedBoot stream handler for xyzModem protocol
|
|
*
|
|
*==========================================================================
|
|
*#####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 <u-boot/crc.h>
|
|
#include <watchdog.h>
|
|
#include <env.h>
|
|
|
|
/* Assumption - run xyzModem protocol over the console port */
|
|
|
|
/* Values magic to the protocol */
|
|
#define SOH 0x01
|
|
#define STX 0x02
|
|
#define ETX 0x03 /* ^C for interrupt */
|
|
#define EOT 0x04
|
|
#define ACK 0x06
|
|
#define BSP 0x08
|
|
#define NAK 0x15
|
|
#define CAN 0x18
|
|
#define EOF 0x1A /* ^Z for DOS officionados */
|
|
|
|
/* 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;
|
|
bool crc_mode, at_eof, tx_ack;
|
|
bool first_xmodem_packet;
|
|
ulong initial_time, timeout;
|
|
unsigned long file_length, read_length;
|
|
} 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 */
|
|
|
|
|
|
typedef int cyg_int32;
|
|
static int
|
|
CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c)
|
|
{
|
|
|
|
ulong now = get_timer(0);
|
|
WATCHDOG_RESET();
|
|
while (!tstc ())
|
|
{
|
|
if (get_timer(now) > xyzModem_CHAR_TIMEOUT)
|
|
break;
|
|
}
|
|
if (tstc ())
|
|
{
|
|
*c = getchar();
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
CYGACC_COMM_IF_PUTC (char x, char y)
|
|
{
|
|
putc (y);
|
|
}
|
|
|
|
/* Validate a hex character */
|
|
__inline__ static bool
|
|
_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 */
|
|
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)) < 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++;
|
|
if (*dp)
|
|
break; /* Found a good delimiter */
|
|
}
|
|
return false; /* Malformatted number */
|
|
}
|
|
}
|
|
*val = result;
|
|
if (es != (char **) 0)
|
|
{
|
|
*es = s;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
#if defined(DEBUG) && !CONFIG_IS_ENABLED(USE_TINY_PRINTF)
|
|
/*
|
|
* Note: this debug setup works by storing the strings in a fixed buffer
|
|
*/
|
|
static char zm_debug_buf[8192];
|
|
static char *zm_out = zm_debug_buf;
|
|
static char *zm_out_start = zm_debug_buf;
|
|
|
|
static int
|
|
zm_dprintf(char *fmt, ...)
|
|
{
|
|
int len;
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
len = diag_vsprintf(zm_out, fmt, args);
|
|
va_end(args);
|
|
zm_out += len;
|
|
return len;
|
|
}
|
|
|
|
static void
|
|
zm_flush (void)
|
|
{
|
|
zm_out = zm_out_start;
|
|
}
|
|
|
|
static void
|
|
zm_dump_buf (void *buf, int len)
|
|
{
|
|
|
|
}
|
|
|
|
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:
|
|
case ETX:
|
|
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 = crc16_ccitt(0, 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;
|
|
}
|
|
|
|
static
|
|
ulong
|
|
xyzModem_get_initial_timeout (void)
|
|
{
|
|
/* timeout is in seconds, non-positive timeout value is infinity */
|
|
#if CONFIG_IS_ENABLED(ENV_SUPPORT)
|
|
const char *timeout_str = env_get("loadxy_timeout");
|
|
if (timeout_str)
|
|
return 1000 * simple_strtol(timeout_str, NULL, 10);
|
|
#endif
|
|
return 1000 * CONFIG_CMD_LOADXY_TIMEOUT;
|
|
}
|
|
|
|
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;
|
|
|
|
/* ZM_DEBUG(zm_out = zm_out_start); */
|
|
#ifdef xyzModem_zmodem
|
|
if (info->mode == xyzModem_zmodem)
|
|
{
|
|
*err = xyzModem_noZmodem;
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
/* TODO: CHECK ! */
|
|
int dummy = 0;
|
|
xyz.__chan = &dummy;
|
|
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;
|
|
xyz.read_length = 0;
|
|
xyz.file_length = 0;
|
|
xyz.first_xmodem_packet = false;
|
|
xyz.initial_time = get_timer(0);
|
|
xyz.timeout = xyzModem_get_initial_timeout();
|
|
|
|
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.first_xmodem_packet = true;
|
|
xyz.next_blk = 1;
|
|
return 0;
|
|
}
|
|
|
|
while (!(xyz.timeout && get_timer(xyz.initial_time) > xyz.timeout))
|
|
{
|
|
if (--retries <= 0)
|
|
{
|
|
retries = xyzModem_MAX_RETRIES;
|
|
crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
|
|
xyz.crc_mode = true;
|
|
}
|
|
stat = xyzModem_get_hdr ();
|
|
if (stat == 0)
|
|
{
|
|
/* Y-modem file information header */
|
|
if (xyz.blk == 0)
|
|
{
|
|
/* skip filename */
|
|
while (*xyz.bufp++);
|
|
/* get the length */
|
|
parse_num ((char *) xyz.bufp, &xyz.file_length, NULL, " ");
|
|
/* 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;
|
|
CYGACC_CALL_IF_DELAY_US (5 * 100000); /* Extra delay for startup */
|
|
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 && xyz.len >= 0 && (size > 0))
|
|
{
|
|
if (xyz.len == 0)
|
|
{
|
|
retries = xyzModem_MAX_RETRIES;
|
|
while (retries-- > 0)
|
|
{
|
|
if (xyz.first_xmodem_packet && xyz.timeout &&
|
|
get_timer(xyz.initial_time) > xyz.timeout)
|
|
{
|
|
*err = xyzModem_timeout;
|
|
xyz.len = -1;
|
|
return total;
|
|
}
|
|
|
|
stat = xyzModem_get_hdr ();
|
|
if (stat == 0)
|
|
{
|
|
if (xyz.mode == xyzModem_xmodem && xyz.first_xmodem_packet)
|
|
xyz.first_xmodem_packet = false;
|
|
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 (xyz.mode == xyzModem_xmodem || xyz.file_length == 0)
|
|
{
|
|
/* 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--;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
}
|
|
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);
|
|
continue; /* Need new header */
|
|
}
|
|
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__));
|
|
}
|
|
else
|
|
stat = 0;
|
|
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 && (!xyz.first_xmodem_packet || stat != xyzModem_timeout))
|
|
{
|
|
*err = stat;
|
|
xyz.len = -1;
|
|
return total;
|
|
}
|
|
}
|
|
/* Don't "read" data from the EOF protocol package */
|
|
if (!xyz.at_eof && xyz.len > 0)
|
|
{
|
|
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)
|
|
{
|
|
ZM_DEBUG (zm_dprintf
|
|
("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 received 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
|
|
*/
|