Newer
Older
// 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 <crc.h>
/* Assumption - run xyzModem protocol over the console port */
#define SOH 0x01
#define STX 0x02
#define EOT 0x04
#define ACK 0x06
#define BSP 0x08
#define NAK 0x15
#define CAN 0x18
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;
unsigned long file_length, read_length;
#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;
CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c)
{
ulong now = get_timer(0);
while (!tstc ())
if (get_timer(now) > xyzModem_CHAR_TIMEOUT)
break;
}
if (tstc ())
{
*c = getc ();
return 1;
}
return 0;
CYGACC_COMM_IF_PUTC (char x, char y)
{
putc (y);
__inline__ static bool
return (((c >= '0') && (c <= '9')) ||
((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f')));
__inline__ static int
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;
__inline__ static char
if ((c >= 'A') && (c <= 'Z'))
{
c = (c - 'A') + 'a';
parse_num (char *s, unsigned long *val, char **es, char *delim)
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
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;
}
#ifdef DEBUG
/*
* 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;
va_start (args, fmt);
len = diag_vsprintf (zm_out, fmt, args);
zm_out += len;
return len;
}
static void
}
static void
{
}
static unsigned char zm_buf[2048];
static unsigned char *zm_bp;
static void
}
static void
}
static void
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
int res;
char c;
while (true)
{
res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
if (!res)
return;
}
}
static int
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;
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
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 = 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;
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;
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;
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;
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
while (retries-- > 0)
{
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;
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 (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--;
}
}
}
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
/*
* 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__));
}
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;
}
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))
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:
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.
*/
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.
*/
CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
}
case xyzModem_access:
case xyzModem_noZmodem:
return "Sorry, zModem not available yet";
break;
case xyzModem_timeout:
case xyzModem_eof:
case xyzModem_cancel:
case xyzModem_frame:
case xyzModem_cksum:
case xyzModem_sequence: