xref: /openbmc/u-boot/common/xyzModem.c (revision ee52b188)
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 */
77 static 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 */
102 typedef int cyg_int32;
103 int
104 CYGACC_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 
121 void
122 CYGACC_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 */
168 bool
169 parse_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  */
235 static int
236 zm_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 
254 static void
255 zm_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
265 static char *zm_out = (char *) 0x00380000;
266 static char *zm_out_start = (char *) 0x00380000;
267 #else
268 static char zm_buf[8192];
269 static char *zm_out = zm_buf;
270 static char *zm_out_start = zm_buf;
271 
272 #endif
273 static int
274 zm_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 
285 static void
286 zm_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 
297 static void
298 zm_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 
307 static unsigned char zm_buf[2048];
308 static unsigned char *zm_bp;
309 
310 static void
311 zm_new (void)
312 {
313   zm_bp = zm_buf;
314 }
315 
316 static void
317 zm_save (unsigned char c)
318 {
319   *zm_bp++ = c;
320 }
321 
322 static void
323 zm_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 */
335 static void
336 xyzModem_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 
348 static int
349 xyzModem_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 
508 int
509 xyzModem_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 
613 int
614 xyzModem_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 
738 void
739 xyzModem_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 */
750 void
751 xyzModem_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 received 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 
805 char *
806 xyzModem_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 */
844 GETC_IO_FUNCS (xyzModem_io, xyzModem_stream_open, xyzModem_stream_close,
845 	       xyzModem_stream_terminate, xyzModem_stream_read,
846 	       xyzModem_error);
847 RedBoot_load (xmodem, xyzModem_io, false, false, xyzModem_xmodem);
848 RedBoot_load (ymodem, xyzModem_io, false, false, xyzModem_ymodem);
849 #endif
850