uboot/common/xyzModem.c
<<
>>
Prefs
   1/*
   2 *==========================================================================
   3 *
   4 *      xyzModem.c
   5 *
   6 *      RedBoot stream handler for xyzModem protocol
   7 *
   8 *==========================================================================
   9 *####ECOSGPLCOPYRIGHTBEGIN####
  10 * -------------------------------------------
  11 * This file is part of eCos, the Embedded Configurable Operating System.
  12 * Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
  13 * Copyright (C) 2002 Gary Thomas
  14 *
  15 * eCos is free software; you can redistribute it and/or modify it under
  16 * the terms of the GNU General Public License as published by the Free
  17 * Software Foundation; either version 2 or (at your option) any later version.
  18 *
  19 * eCos is distributed in the hope that it will be useful, but WITHOUT ANY
  20 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
  21 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  22 * for more details.
  23 *
  24 * You should have received a copy of the GNU General Public License along
  25 * with eCos; if not, write to the Free Software Foundation, Inc.,
  26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  27 *
  28 * As a special exception, if other files instantiate templates or use macros
  29 * or inline functions from this file, or you compile this file and link it
  30 * with other works to produce a work based on this file, this file does not
  31 * by itself cause the resulting work to be covered by the GNU General Public
  32 * License. However the source code for this file must still be made available
  33 * in accordance with section (3) of the GNU General Public License.
  34 *
  35 * This exception does not invalidate any other reasons why a work based on
  36 * this file might be covered by the GNU General Public License.
  37 *
  38 * Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
  39 * at http: *sources.redhat.com/ecos/ecos-license/
  40 * -------------------------------------------
  41 *####ECOSGPLCOPYRIGHTEND####
  42 *==========================================================================
  43 *#####DESCRIPTIONBEGIN####
  44 *
  45 * Author(s):    gthomas
  46 * Contributors: gthomas, tsmith, Yoshinori Sato
  47 * Date:         2000-07-14
  48 * Purpose:
  49 * Description:
  50 *
  51 * This code is part of RedBoot (tm).
  52 *
  53 *####DESCRIPTIONEND####
  54 *
  55 *==========================================================================
  56 */
  57#include <common.h>
  58#include <xyzModem.h>
  59#include <stdarg.h>
  60#include <crc.h>
  61
  62/* Assumption - run xyzModem protocol over the console port */
  63
  64/* Values magic to the protocol */
  65#define SOH 0x01
  66#define STX 0x02
  67#define EOT 0x04
  68#define ACK 0x06
  69#define BSP 0x08
  70#define NAK 0x15
  71#define CAN 0x18
  72#define EOF 0x1A                /* ^Z for DOS officionados */
  73
  74#define USE_YMODEM_LENGTH
  75
  76/* Data & state local to the protocol */
  77static struct
  78{
  79#ifdef REDBOOT
  80  hal_virtual_comm_table_t *__chan;
  81#else
  82  int *__chan;
  83#endif
  84  unsigned char pkt[1024], *bufp;
  85  unsigned char blk, cblk, crc1, crc2;
  86  unsigned char next_blk;       /* Expected block */
  87  int len, mode, total_retries;
  88  int total_SOH, total_STX, total_CAN;
  89  bool crc_mode, at_eof, tx_ack;
  90#ifdef USE_YMODEM_LENGTH
  91  unsigned long file_length, read_length;
  92#endif
  93} xyz;
  94
  95#define xyzModem_CHAR_TIMEOUT            2000   /* 2 seconds */
  96#define xyzModem_MAX_RETRIES             20
  97#define xyzModem_MAX_RETRIES_WITH_CRC    10
  98#define xyzModem_CAN_COUNT                3     /* Wait for 3 CAN before quitting */
  99
 100
 101#ifndef REDBOOT                 /*SB */
 102typedef int cyg_int32;
 103int
 104CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c)
 105{
 106#define DELAY 20
 107  unsigned long counter = 0;
 108  while (!tstc () && (counter < xyzModem_CHAR_TIMEOUT * 1000 / DELAY))
 109    {
 110      udelay (DELAY);
 111      counter++;
 112    }
 113  if (tstc ())
 114    {
 115      *c = getc ();
 116      return 1;
 117    }
 118  return 0;
 119}
 120
 121void
 122CYGACC_COMM_IF_PUTC (char x, char y)
 123{
 124  putc (y);
 125}
 126
 127/* Validate a hex character */
 128__inline__ static bool
 129_is_hex (char c)
 130{
 131  return (((c >= '0') && (c <= '9')) ||
 132          ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f')));
 133}
 134
 135/* Convert a single hex nibble */
 136__inline__ static int
 137_from_hex (char c)
 138{
 139  int ret = 0;
 140
 141  if ((c >= '0') && (c <= '9'))
 142    {
 143      ret = (c - '0');
 144    }
 145  else if ((c >= 'a') && (c <= 'f'))
 146    {
 147      ret = (c - 'a' + 0x0a);
 148    }
 149  else if ((c >= 'A') && (c <= 'F'))
 150    {
 151      ret = (c - 'A' + 0x0A);
 152    }
 153  return ret;
 154}
 155
 156/* Convert a character to lower case */
 157__inline__ static char
 158_tolower (char c)
 159{
 160  if ((c >= 'A') && (c <= 'Z'))
 161    {
 162      c = (c - 'A') + 'a';
 163    }
 164  return c;
 165}
 166
 167/* Parse (scan) a number */
 168bool
 169parse_num (char *s, unsigned long *val, char **es, char *delim)
 170{
 171  bool first = true;
 172  int radix = 10;
 173  char c;
 174  unsigned long result = 0;
 175  int digit;
 176
 177  while (*s == ' ')
 178    s++;
 179  while (*s)
 180    {
 181      if (first && (s[0] == '0') && (_tolower (s[1]) == 'x'))
 182        {
 183          radix = 16;
 184          s += 2;
 185        }
 186      first = false;
 187      c = *s++;
 188      if (_is_hex (c) && ((digit = _from_hex (c)) < radix))
 189        {
 190          /* Valid digit */
 191#ifdef CYGPKG_HAL_MIPS
 192          /* FIXME: tx49 compiler generates 0x2539018 for MUL which */
 193          /* isn't any good. */
 194          if (16 == radix)
 195            result = result << 4;
 196          else
 197            result = 10 * result;
 198          result += digit;
 199#else
 200          result = (result * radix) + digit;
 201#endif
 202        }
 203      else
 204        {
 205          if (delim != (char *) 0)
 206            {
 207              /* See if this character is one of the delimiters */
 208              char *dp = delim;
 209              while (*dp && (c != *dp))
 210                dp++;
 211              if (*dp)
 212                break;          /* Found a good delimiter */
 213            }
 214          return false;         /* Malformatted number */
 215        }
 216    }
 217  *val = result;
 218  if (es != (char **) 0)
 219    {
 220      *es = s;
 221    }
 222  return true;
 223}
 224
 225#endif
 226
 227#define USE_SPRINTF
 228#ifdef DEBUG
 229#ifndef USE_SPRINTF
 230/*
 231 * Note: this debug setup only works if the target platform has two serial ports
 232 * available so that the other one (currently only port 1) can be used for debug
 233 * messages.
 234 */
 235static int
 236zm_dprintf (char *fmt, ...)
 237{
 238  int cur_console;
 239  va_list args;
 240
 241  va_start (args, fmt);
 242#ifdef REDBOOT
 243  cur_console =
 244    CYGACC_CALL_IF_SET_CONSOLE_COMM
 245    (CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
 246  CYGACC_CALL_IF_SET_CONSOLE_COMM (1);
 247#endif
 248  diag_vprintf (fmt, args);
 249#ifdef REDBOOT
 250  CYGACC_CALL_IF_SET_CONSOLE_COMM (cur_console);
 251#endif
 252}
 253
 254static void
 255zm_flush (void)
 256{
 257}
 258
 259#else
 260/*
 261 * Note: this debug setup works by storing the strings in a fixed buffer
 262 */
 263#define FINAL
 264#ifdef FINAL
 265static char *zm_out = (char *) 0x00380000;
 266static char *zm_out_start = (char *) 0x00380000;
 267#else
 268static char zm_buf[8192];
 269static char *zm_out = zm_buf;
 270static char *zm_out_start = zm_buf;
 271
 272#endif
 273static int
 274zm_dprintf (char *fmt, ...)
 275{
 276  int len;
 277  va_list args;
 278
 279  va_start (args, fmt);
 280  len = diag_vsprintf (zm_out, fmt, args);
 281  zm_out += len;
 282  return len;
 283}
 284
 285static void
 286zm_flush (void)
 287{
 288#ifdef REDBOOT
 289  char *p = zm_out_start;
 290  while (*p)
 291    mon_write_char (*p++);
 292#endif
 293  zm_out = zm_out_start;
 294}
 295#endif
 296
 297static void
 298zm_dump_buf (void *buf, int len)
 299{
 300#ifdef REDBOOT
 301  diag_vdump_buf_with_offset (zm_dprintf, buf, len, 0);
 302#else
 303
 304#endif
 305}
 306
 307static unsigned char zm_buf[2048];
 308static unsigned char *zm_bp;
 309
 310static void
 311zm_new (void)
 312{
 313  zm_bp = zm_buf;
 314}
 315
 316static void
 317zm_save (unsigned char c)
 318{
 319  *zm_bp++ = c;
 320}
 321
 322static void
 323zm_dump (int line)
 324{
 325  zm_dprintf ("Packet at line: %d\n", line);
 326  zm_dump_buf (zm_buf, zm_bp - zm_buf);
 327}
 328
 329#define ZM_DEBUG(x) x
 330#else
 331#define ZM_DEBUG(x)
 332#endif
 333
 334/* Wait for the line to go idle */
 335static void
 336xyzModem_flush (void)
 337{
 338  int res;
 339  char c;
 340  while (true)
 341    {
 342      res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
 343      if (!res)
 344        return;
 345    }
 346}
 347
 348static int
 349xyzModem_get_hdr (void)
 350{
 351  char c;
 352  int res;
 353  bool hdr_found = false;
 354  int i, can_total, hdr_chars;
 355  unsigned short cksum;
 356
 357  ZM_DEBUG (zm_new ());
 358  /* Find the start of a header */
 359  can_total = 0;
 360  hdr_chars = 0;
 361
 362  if (xyz.tx_ack)
 363    {
 364      CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
 365      xyz.tx_ack = false;
 366    }
 367  while (!hdr_found)
 368    {
 369      res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
 370      ZM_DEBUG (zm_save (c));
 371      if (res)
 372        {
 373          hdr_chars++;
 374          switch (c)
 375            {
 376            case SOH:
 377              xyz.total_SOH++;
 378            case STX:
 379              if (c == STX)
 380                xyz.total_STX++;
 381              hdr_found = true;
 382              break;
 383            case CAN:
 384              xyz.total_CAN++;
 385              ZM_DEBUG (zm_dump (__LINE__));
 386              if (++can_total == xyzModem_CAN_COUNT)
 387                {
 388                  return xyzModem_cancel;
 389                }
 390              else
 391                {
 392                  /* Wait for multiple CAN to avoid early quits */
 393                  break;
 394                }
 395            case EOT:
 396              /* EOT only supported if no noise */
 397              if (hdr_chars == 1)
 398                {
 399                  CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
 400                  ZM_DEBUG (zm_dprintf ("ACK on EOT #%d\n", __LINE__));
 401                  ZM_DEBUG (zm_dump (__LINE__));
 402                  return xyzModem_eof;
 403                }
 404            default:
 405              /* Ignore, waiting for start of header */
 406              ;
 407            }
 408        }
 409      else
 410        {
 411          /* Data stream timed out */
 412          xyzModem_flush ();    /* Toss any current input */
 413          ZM_DEBUG (zm_dump (__LINE__));
 414          CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
 415          return xyzModem_timeout;
 416        }
 417    }
 418
 419  /* Header found, now read the data */
 420  res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.blk);
 421  ZM_DEBUG (zm_save (xyz.blk));
 422  if (!res)
 423    {
 424      ZM_DEBUG (zm_dump (__LINE__));
 425      return xyzModem_timeout;
 426    }
 427  res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.cblk);
 428  ZM_DEBUG (zm_save (xyz.cblk));
 429  if (!res)
 430    {
 431      ZM_DEBUG (zm_dump (__LINE__));
 432      return xyzModem_timeout;
 433    }
 434  xyz.len = (c == SOH) ? 128 : 1024;
 435  xyz.bufp = xyz.pkt;
 436  for (i = 0; i < xyz.len; i++)
 437    {
 438      res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
 439      ZM_DEBUG (zm_save (c));
 440      if (res)
 441        {
 442          xyz.pkt[i] = c;
 443        }
 444      else
 445        {
 446          ZM_DEBUG (zm_dump (__LINE__));
 447          return xyzModem_timeout;
 448        }
 449    }
 450  res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc1);
 451  ZM_DEBUG (zm_save (xyz.crc1));
 452  if (!res)
 453    {
 454      ZM_DEBUG (zm_dump (__LINE__));
 455      return xyzModem_timeout;
 456    }
 457  if (xyz.crc_mode)
 458    {
 459      res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc2);
 460      ZM_DEBUG (zm_save (xyz.crc2));
 461      if (!res)
 462        {
 463          ZM_DEBUG (zm_dump (__LINE__));
 464          return xyzModem_timeout;
 465        }
 466    }
 467  ZM_DEBUG (zm_dump (__LINE__));
 468  /* Validate the message */
 469  if ((xyz.blk ^ xyz.cblk) != (unsigned char) 0xFF)
 470    {
 471      ZM_DEBUG (zm_dprintf
 472                ("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk,
 473                 (xyz.blk ^ xyz.cblk)));
 474      ZM_DEBUG (zm_dump_buf (xyz.pkt, xyz.len));
 475      xyzModem_flush ();
 476      return xyzModem_frame;
 477    }
 478  /* Verify checksum/CRC */
 479  if (xyz.crc_mode)
 480    {
 481      cksum = cyg_crc16 (xyz.pkt, xyz.len);
 482      if (cksum != ((xyz.crc1 << 8) | xyz.crc2))
 483        {
 484          ZM_DEBUG (zm_dprintf ("CRC error - recvd: %02x%02x, computed: %x\n",
 485                                xyz.crc1, xyz.crc2, cksum & 0xFFFF));
 486          return xyzModem_cksum;
 487        }
 488    }
 489  else
 490    {
 491      cksum = 0;
 492      for (i = 0; i < xyz.len; i++)
 493        {
 494          cksum += xyz.pkt[i];
 495        }
 496      if (xyz.crc1 != (cksum & 0xFF))
 497        {
 498          ZM_DEBUG (zm_dprintf
 499                    ("Checksum error - recvd: %x, computed: %x\n", xyz.crc1,
 500                     cksum & 0xFF));
 501          return xyzModem_cksum;
 502        }
 503    }
 504  /* If we get here, the message passes [structural] muster */
 505  return 0;
 506}
 507
 508int
 509xyzModem_stream_open (connection_info_t * info, int *err)
 510{
 511#ifdef REDBOOT
 512  int console_chan;
 513#endif
 514  int stat = 0;
 515  int retries = xyzModem_MAX_RETRIES;
 516  int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
 517
 518/*    ZM_DEBUG(zm_out = zm_out_start); */
 519#ifdef xyzModem_zmodem
 520  if (info->mode == xyzModem_zmodem)
 521    {
 522      *err = xyzModem_noZmodem;
 523      return -1;
 524    }
 525#endif
 526
 527#ifdef REDBOOT
 528  /* Set up the I/O channel.  Note: this allows for using a different port in the future */
 529  console_chan =
 530    CYGACC_CALL_IF_SET_CONSOLE_COMM
 531    (CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
 532  if (info->chan >= 0)
 533    {
 534      CYGACC_CALL_IF_SET_CONSOLE_COMM (info->chan);
 535    }
 536  else
 537    {
 538      CYGACC_CALL_IF_SET_CONSOLE_COMM (console_chan);
 539    }
 540  xyz.__chan = CYGACC_CALL_IF_CONSOLE_PROCS ();
 541
 542  CYGACC_CALL_IF_SET_CONSOLE_COMM (console_chan);
 543  CYGACC_COMM_IF_CONTROL (*xyz.__chan, __COMMCTL_SET_TIMEOUT,
 544                          xyzModem_CHAR_TIMEOUT);
 545#else
 546/* TODO: CHECK ! */
 547  int dummy = 0;
 548  xyz.__chan = &dummy;
 549#endif
 550  xyz.len = 0;
 551  xyz.crc_mode = true;
 552  xyz.at_eof = false;
 553  xyz.tx_ack = false;
 554  xyz.mode = info->mode;
 555  xyz.total_retries = 0;
 556  xyz.total_SOH = 0;
 557  xyz.total_STX = 0;
 558  xyz.total_CAN = 0;
 559#ifdef USE_YMODEM_LENGTH
 560  xyz.read_length = 0;
 561  xyz.file_length = 0;
 562#endif
 563
 564  CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
 565
 566  if (xyz.mode == xyzModem_xmodem)
 567    {
 568      /* X-modem doesn't have an information header - exit here */
 569      xyz.next_blk = 1;
 570      return 0;
 571    }
 572
 573  while (retries-- > 0)
 574    {
 575      stat = xyzModem_get_hdr ();
 576      if (stat == 0)
 577        {
 578          /* Y-modem file information header */
 579          if (xyz.blk == 0)
 580            {
 581#ifdef USE_YMODEM_LENGTH
 582              /* skip filename */
 583              while (*xyz.bufp++);
 584              /* get the length */
 585              parse_num ((char *) xyz.bufp, &xyz.file_length, NULL, " ");
 586#endif
 587              /* The rest of the file name data block quietly discarded */
 588              xyz.tx_ack = true;
 589            }
 590          xyz.next_blk = 1;
 591          xyz.len = 0;
 592          return 0;
 593        }
 594      else if (stat == xyzModem_timeout)
 595        {
 596          if (--crc_retries <= 0)
 597            xyz.crc_mode = false;
 598          CYGACC_CALL_IF_DELAY_US (5 * 100000); /* Extra delay for startup */
 599          CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
 600          xyz.total_retries++;
 601          ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
 602        }
 603      if (stat == xyzModem_cancel)
 604        {
 605          break;
 606        }
 607    }
 608  *err = stat;
 609  ZM_DEBUG (zm_flush ());
 610  return -1;
 611}
 612
 613int
 614xyzModem_stream_read (char *buf, int size, int *err)
 615{
 616  int stat, total, len;
 617  int retries;
 618
 619  total = 0;
 620  stat = xyzModem_cancel;
 621  /* Try and get 'size' bytes into the buffer */
 622  while (!xyz.at_eof && (size > 0))
 623    {
 624      if (xyz.len == 0)
 625        {
 626          retries = xyzModem_MAX_RETRIES;
 627          while (retries-- > 0)
 628            {
 629              stat = xyzModem_get_hdr ();
 630              if (stat == 0)
 631                {
 632                  if (xyz.blk == xyz.next_blk)
 633                    {
 634                      xyz.tx_ack = true;
 635                      ZM_DEBUG (zm_dprintf
 636                                ("ACK block %d (%d)\n", xyz.blk, __LINE__));
 637                      xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
 638
 639#if defined(xyzModem_zmodem) || defined(USE_YMODEM_LENGTH)
 640                      if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0)
 641                        {
 642#else
 643                      if (1)
 644                        {
 645#endif
 646                          /* Data blocks can be padded with ^Z (EOF) characters */
 647                          /* This code tries to detect and remove them */
 648                          if ((xyz.bufp[xyz.len - 1] == EOF) &&
 649                              (xyz.bufp[xyz.len - 2] == EOF) &&
 650                              (xyz.bufp[xyz.len - 3] == EOF))
 651                            {
 652                              while (xyz.len
 653                                     && (xyz.bufp[xyz.len - 1] == EOF))
 654                                {
 655                                  xyz.len--;
 656                                }
 657                            }
 658                        }
 659
 660#ifdef USE_YMODEM_LENGTH
 661                      /*
 662                       * See if accumulated length exceeds that of the file.
 663                       * If so, reduce size (i.e., cut out pad bytes)
 664                       * Only do this for Y-modem (and Z-modem should it ever
 665                       * be supported since it can fall back to Y-modem mode).
 666                       */
 667                      if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length)
 668                        {
 669                          xyz.read_length += xyz.len;
 670                          if (xyz.read_length > xyz.file_length)
 671                            {
 672                              xyz.len -= (xyz.read_length - xyz.file_length);
 673                            }
 674                        }
 675#endif
 676                      break;
 677                    }
 678                  else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF))
 679                    {
 680                      /* Just re-ACK this so sender will get on with it */
 681                      CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
 682                      continue; /* Need new header */
 683                    }
 684                  else
 685                    {
 686                      stat = xyzModem_sequence;
 687                    }
 688                }
 689              if (stat == xyzModem_cancel)
 690                {
 691                  break;
 692                }
 693              if (stat == xyzModem_eof)
 694                {
 695                  CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
 696                  ZM_DEBUG (zm_dprintf ("ACK (%d)\n", __LINE__));
 697                  if (xyz.mode == xyzModem_ymodem)
 698                    {
 699                      CYGACC_COMM_IF_PUTC (*xyz.__chan,
 700                                           (xyz.crc_mode ? 'C' : NAK));
 701                      xyz.total_retries++;
 702                      ZM_DEBUG (zm_dprintf ("Reading Final Header\n"));
 703                      stat = xyzModem_get_hdr ();
 704                      CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
 705                      ZM_DEBUG (zm_dprintf ("FINAL ACK (%d)\n", __LINE__));
 706                    }
 707                  xyz.at_eof = true;
 708                  break;
 709                }
 710              CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
 711              xyz.total_retries++;
 712              ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
 713            }
 714          if (stat < 0)
 715            {
 716              *err = stat;
 717              xyz.len = -1;
 718              return total;
 719            }
 720        }
 721      /* Don't "read" data from the EOF protocol package */
 722      if (!xyz.at_eof)
 723        {
 724          len = xyz.len;
 725          if (size < len)
 726            len = size;
 727          memcpy (buf, xyz.bufp, len);
 728          size -= len;
 729          buf += len;
 730          total += len;
 731          xyz.len -= len;
 732          xyz.bufp += len;
 733        }
 734    }
 735  return total;
 736}
 737
 738void
 739xyzModem_stream_close (int *err)
 740{
 741  diag_printf
 742    ("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
 743     xyz.crc_mode ? "CRC" : "Cksum", xyz.total_SOH, xyz.total_STX,
 744     xyz.total_CAN, xyz.total_retries);
 745  ZM_DEBUG (zm_flush ());
 746}
 747
 748/* Need to be able to clean out the input buffer, so have to take the */
 749/* getc */
 750void
 751xyzModem_stream_terminate (bool abort, int (*getc) (void))
 752{
 753  int c;
 754
 755  if (abort)
 756    {
 757      ZM_DEBUG (zm_dprintf ("!!!! TRANSFER ABORT !!!!\n"));
 758      switch (xyz.mode)
 759        {
 760        case xyzModem_xmodem:
 761        case xyzModem_ymodem:
 762          /* The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal */
 763          /* number of Backspaces is a friendly way to get the other end to abort. */
 764          CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
 765          CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
 766          CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
 767          CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
 768          CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
 769          CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
 770          CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
 771          CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
 772          /* Now consume the rest of what's waiting on the line. */
 773          ZM_DEBUG (zm_dprintf ("Flushing serial line.\n"));
 774          xyzModem_flush ();
 775          xyz.at_eof = true;
 776          break;
 777#ifdef xyzModem_zmodem
 778        case xyzModem_zmodem:
 779          /* Might support it some day I suppose. */
 780#endif
 781          break;
 782        }
 783    }
 784  else
 785    {
 786      ZM_DEBUG (zm_dprintf ("Engaging cleanup mode...\n"));
 787      /*
 788       * Consume any trailing crap left in the inbuffer from
 789       * previous recieved blocks. Since very few files are an exact multiple
 790       * of the transfer block size, there will almost always be some gunk here.
 791       * If we don't eat it now, RedBoot will think the user typed it.
 792       */
 793      ZM_DEBUG (zm_dprintf ("Trailing gunk:\n"));
 794      while ((c = (*getc) ()) > -1);
 795      ZM_DEBUG (zm_dprintf ("\n"));
 796      /*
 797       * Make a small delay to give terminal programs like minicom
 798       * time to get control again after their file transfer program
 799       * exits.
 800       */
 801      CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
 802    }
 803}
 804
 805char *
 806xyzModem_error (int err)
 807{
 808  switch (err)
 809    {
 810    case xyzModem_access:
 811      return "Can't access file";
 812      break;
 813    case xyzModem_noZmodem:
 814      return "Sorry, zModem not available yet";
 815      break;
 816    case xyzModem_timeout:
 817      return "Timed out";
 818      break;
 819    case xyzModem_eof:
 820      return "End of file";
 821      break;
 822    case xyzModem_cancel:
 823      return "Cancelled";
 824      break;
 825    case xyzModem_frame:
 826      return "Invalid framing";
 827      break;
 828    case xyzModem_cksum:
 829      return "CRC/checksum error";
 830      break;
 831    case xyzModem_sequence:
 832      return "Block sequence error";
 833      break;
 834    default:
 835      return "Unknown error";
 836      break;
 837    }
 838}
 839
 840/*
 841 * RedBoot interface
 842 */
 843#if 0                           /* SB */
 844GETC_IO_FUNCS (xyzModem_io, xyzModem_stream_open, xyzModem_stream_close,
 845               xyzModem_stream_terminate, xyzModem_stream_read,
 846               xyzModem_error);
 847RedBoot_load (xmodem, xyzModem_io, false, false, xyzModem_xmodem);
 848RedBoot_load (ymodem, xyzModem_io, false, false, xyzModem_ymodem);
 849#endif
 850