]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - common/xyzModem.c
TX6 Release 2013-04-22
[karo-tx-uboot.git] / common / xyzModem.c
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 aficionados */
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 static 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 static 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 static 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 __attribute__((unused));
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   return 0;
253 }
254
255 static inline void
256 zm_flush (void)
257 {
258 }
259
260 #else
261 /*
262  * Note: this debug setup works by storing the strings in a fixed buffer
263  */
264 #define FINAL
265 #ifdef FINAL
266 static char *zm_out = (char *) 0x00380000;
267 static char *zm_out_start = (char *) 0x00380000;
268 #else
269 static char zm_buf[8192];
270 static char *zm_out = zm_buf;
271 static char *zm_out_start = zm_buf;
272
273 #endif
274 static int
275 zm_dprintf (char *fmt, ...)
276 {
277   int len;
278   va_list args;
279
280   va_start (args, fmt);
281   len = diag_vsprintf (zm_out, fmt, args);
282   zm_out += len;
283   return len;
284 }
285
286 static inline void
287 zm_flush (void)
288 {
289 #ifdef REDBOOT
290   char *p = zm_out_start;
291   while (*p)
292     mon_write_char (*p++);
293 #endif
294   zm_out = zm_out_start;
295 }
296 #endif
297
298 static void
299 zm_dump_buf (void *buf, int len)
300 {
301 #ifdef REDBOOT
302   diag_vdump_buf_with_offset (zm_dprintf, buf, len, 0);
303 #else
304
305 #endif
306 }
307
308 static unsigned char zm_buf[2048];
309 static unsigned char *zm_bp;
310
311 static inline void
312 zm_new (void)
313 {
314   zm_bp = zm_buf;
315 }
316
317 static inline void
318 zm_save (unsigned char c)
319 {
320   *zm_bp++ = c;
321 }
322
323 static inline void
324 zm_dump (int line)
325 {
326   zm_dprintf ("Packet at line: %d\n", line);
327   zm_dump_buf (zm_buf, zm_bp - zm_buf);
328 }
329
330 #define ZM_DEBUG(x) x
331 #else
332 #define ZM_DEBUG(x)
333 #endif
334
335 /* Wait for the line to go idle */
336 static void
337 xyzModem_flush (void)
338 {
339   int res;
340   char c;
341   while (true)
342     {
343       res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
344       if (!res)
345         return;
346     }
347 }
348
349 static int
350 xyzModem_get_hdr (void)
351 {
352   char c;
353   int res;
354   bool hdr_found = false;
355   int i, can_total, hdr_chars;
356   unsigned short cksum;
357
358   ZM_DEBUG (zm_new ());
359   /* Find the start of a header */
360   can_total = 0;
361   hdr_chars = 0;
362
363   if (xyz.tx_ack)
364     {
365       CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
366       xyz.tx_ack = false;
367     }
368   while (!hdr_found)
369     {
370       res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
371       ZM_DEBUG (zm_save (c));
372       if (res)
373         {
374           hdr_chars++;
375           switch (c)
376             {
377             case SOH:
378               xyz.total_SOH++;
379             case STX:
380               if (c == STX)
381                 xyz.total_STX++;
382               hdr_found = true;
383               break;
384             case CAN:
385               xyz.total_CAN++;
386               ZM_DEBUG (zm_dump (__LINE__));
387               if (++can_total == xyzModem_CAN_COUNT)
388                 {
389                   return xyzModem_cancel;
390                 }
391               else
392                 {
393                   /* Wait for multiple CAN to avoid early quits */
394                   break;
395                 }
396             case EOT:
397               /* EOT only supported if no noise */
398               if (hdr_chars == 1)
399                 {
400                   CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
401                   ZM_DEBUG (zm_dprintf ("ACK on EOT #%d\n", __LINE__));
402                   ZM_DEBUG (zm_dump (__LINE__));
403                   return xyzModem_eof;
404                 }
405             default:
406               /* Ignore, waiting for start of header */
407               ;
408             }
409         }
410       else
411         {
412           /* Data stream timed out */
413           xyzModem_flush ();    /* Toss any current input */
414           ZM_DEBUG (zm_dump (__LINE__));
415           CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
416           return xyzModem_timeout;
417         }
418     }
419
420   /* Header found, now read the data */
421   res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.blk);
422   ZM_DEBUG (zm_save (xyz.blk));
423   if (!res)
424     {
425       ZM_DEBUG (zm_dump (__LINE__));
426       return xyzModem_timeout;
427     }
428   res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.cblk);
429   ZM_DEBUG (zm_save (xyz.cblk));
430   if (!res)
431     {
432       ZM_DEBUG (zm_dump (__LINE__));
433       return xyzModem_timeout;
434     }
435   xyz.len = (c == SOH) ? 128 : 1024;
436   xyz.bufp = xyz.pkt;
437   for (i = 0; i < xyz.len; i++)
438     {
439       res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
440       ZM_DEBUG (zm_save (c));
441       if (res)
442         {
443           xyz.pkt[i] = c;
444         }
445       else
446         {
447           ZM_DEBUG (zm_dump (__LINE__));
448           return xyzModem_timeout;
449         }
450     }
451   res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc1);
452   ZM_DEBUG (zm_save (xyz.crc1));
453   if (!res)
454     {
455       ZM_DEBUG (zm_dump (__LINE__));
456       return xyzModem_timeout;
457     }
458   if (xyz.crc_mode)
459     {
460       res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc2);
461       ZM_DEBUG (zm_save (xyz.crc2));
462       if (!res)
463         {
464           ZM_DEBUG (zm_dump (__LINE__));
465           return xyzModem_timeout;
466         }
467     }
468   ZM_DEBUG (zm_dump (__LINE__));
469   /* Validate the message */
470   if ((xyz.blk ^ xyz.cblk) != (unsigned char) 0xFF)
471     {
472       ZM_DEBUG (zm_dprintf
473                 ("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk,
474                  (xyz.blk ^ xyz.cblk)));
475       ZM_DEBUG (zm_dump_buf (xyz.pkt, xyz.len));
476       xyzModem_flush ();
477       return xyzModem_frame;
478     }
479   /* Verify checksum/CRC */
480   if (xyz.crc_mode)
481     {
482       cksum = cyg_crc16 (xyz.pkt, xyz.len);
483       if (cksum != ((xyz.crc1 << 8) | xyz.crc2))
484         {
485           ZM_DEBUG (zm_dprintf ("CRC error - recvd: %02x%02x, computed: %x\n",
486                                 xyz.crc1, xyz.crc2, cksum & 0xFFFF));
487           return xyzModem_cksum;
488         }
489     }
490   else
491     {
492       cksum = 0;
493       for (i = 0; i < xyz.len; i++)
494         {
495           cksum += xyz.pkt[i];
496         }
497       if (xyz.crc1 != (cksum & 0xFF))
498         {
499           ZM_DEBUG (zm_dprintf
500                     ("Checksum error - recvd: %x, computed: %x\n", xyz.crc1,
501                      cksum & 0xFF));
502           return xyzModem_cksum;
503         }
504     }
505   /* If we get here, the message passes [structural] muster */
506   return 0;
507 }
508
509 int
510 xyzModem_stream_open (connection_info_t * info, int *err)
511 {
512 #ifdef REDBOOT
513   int console_chan;
514 #endif
515   int stat = 0;
516   int retries = xyzModem_MAX_RETRIES;
517   int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
518
519 /*    ZM_DEBUG(zm_out = zm_out_start); */
520 #ifdef xyzModem_zmodem
521   if (info->mode == xyzModem_zmodem)
522     {
523       *err = xyzModem_noZmodem;
524       return -1;
525     }
526 #endif
527
528 #ifdef REDBOOT
529   /* Set up the I/O channel.  Note: this allows for using a different port in the future */
530   console_chan =
531     CYGACC_CALL_IF_SET_CONSOLE_COMM
532     (CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
533   if (info->chan >= 0)
534     {
535       CYGACC_CALL_IF_SET_CONSOLE_COMM (info->chan);
536     }
537   else
538     {
539       CYGACC_CALL_IF_SET_CONSOLE_COMM (console_chan);
540     }
541   xyz.__chan = CYGACC_CALL_IF_CONSOLE_PROCS ();
542
543   CYGACC_CALL_IF_SET_CONSOLE_COMM (console_chan);
544   CYGACC_COMM_IF_CONTROL (*xyz.__chan, __COMMCTL_SET_TIMEOUT,
545                           xyzModem_CHAR_TIMEOUT);
546 #else
547 /* TODO: CHECK ! */
548   int dummy = 0;
549   xyz.__chan = &dummy;
550 #endif
551   xyz.len = 0;
552   xyz.crc_mode = true;
553   xyz.at_eof = false;
554   xyz.tx_ack = false;
555   xyz.mode = info->mode;
556   xyz.total_retries = 0;
557   xyz.total_SOH = 0;
558   xyz.total_STX = 0;
559   xyz.total_CAN = 0;
560 #ifdef USE_YMODEM_LENGTH
561   xyz.read_length = 0;
562   xyz.file_length = 0;
563 #endif
564
565   CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
566
567   if (xyz.mode == xyzModem_xmodem)
568     {
569       /* X-modem doesn't have an information header - exit here */
570       xyz.next_blk = 1;
571       return 0;
572     }
573
574   while (retries-- > 0)
575     {
576       stat = xyzModem_get_hdr ();
577       if (stat == 0)
578         {
579           /* Y-modem file information header */
580           if (xyz.blk == 0)
581             {
582 #ifdef USE_YMODEM_LENGTH
583               /* skip filename */
584               while (*xyz.bufp++);
585               /* get the length */
586               parse_num ((char *) xyz.bufp, &xyz.file_length, NULL, " ");
587 #endif
588               /* The rest of the file name data block quietly discarded */
589               xyz.tx_ack = true;
590             }
591           xyz.next_blk = 1;
592           xyz.len = 0;
593           return 0;
594         }
595       else if (stat == xyzModem_timeout)
596         {
597           if (--crc_retries <= 0)
598             xyz.crc_mode = false;
599           CYGACC_CALL_IF_DELAY_US (5 * 100000); /* Extra delay for startup */
600           CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
601           xyz.total_retries++;
602           ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
603         }
604       if (stat == xyzModem_cancel)
605         {
606           break;
607         }
608     }
609   *err = stat;
610   ZM_DEBUG (zm_flush ());
611   return -1;
612 }
613
614 int
615 xyzModem_stream_read (char *buf, int size, int *err)
616 {
617   int stat, total, len;
618   int retries;
619
620   total = 0;
621   stat = xyzModem_cancel;
622   /* Try and get 'size' bytes into the buffer */
623   while (!xyz.at_eof && (size > 0))
624     {
625       if (xyz.len == 0)
626         {
627           retries = xyzModem_MAX_RETRIES;
628           while (retries-- > 0)
629             {
630               stat = xyzModem_get_hdr ();
631               if (stat == 0)
632                 {
633                   if (xyz.blk == xyz.next_blk)
634                     {
635                       xyz.tx_ack = true;
636                       ZM_DEBUG (zm_dprintf
637                                 ("ACK block %d (%d)\n", xyz.blk, __LINE__));
638                       xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
639
640 #if defined(xyzModem_zmodem) || defined(USE_YMODEM_LENGTH)
641                       if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0)
642                         {
643 #else
644                       if (1)
645                         {
646 #endif
647                           /* Data blocks can be padded with ^Z (EOF) characters */
648                           /* This code tries to detect and remove them */
649                           if ((xyz.bufp[xyz.len - 1] == EOF) &&
650                               (xyz.bufp[xyz.len - 2] == EOF) &&
651                               (xyz.bufp[xyz.len - 3] == EOF))
652                             {
653                               while (xyz.len
654                                      && (xyz.bufp[xyz.len - 1] == EOF))
655                                 {
656                                   xyz.len--;
657                                 }
658                             }
659                         }
660
661 #ifdef USE_YMODEM_LENGTH
662                       /*
663                        * See if accumulated length exceeds that of the file.
664                        * If so, reduce size (i.e., cut out pad bytes)
665                        * Only do this for Y-modem (and Z-modem should it ever
666                        * be supported since it can fall back to Y-modem mode).
667                        */
668                       if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length)
669                         {
670                           xyz.read_length += xyz.len;
671                           if (xyz.read_length > xyz.file_length)
672                             {
673                               xyz.len -= (xyz.read_length - xyz.file_length);
674                             }
675                         }
676 #endif
677                       break;
678                     }
679                   else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF))
680                     {
681                       /* Just re-ACK this so sender will get on with it */
682                       CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
683                       continue; /* Need new header */
684                     }
685                   else
686                     {
687                       stat = xyzModem_sequence;
688                     }
689                 }
690               if (stat == xyzModem_cancel)
691                 {
692                   break;
693                 }
694               if (stat == xyzModem_eof)
695                 {
696                   CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
697                   ZM_DEBUG (zm_dprintf ("ACK (%d)\n", __LINE__));
698                   if (xyz.mode == xyzModem_ymodem)
699                     {
700                       CYGACC_COMM_IF_PUTC (*xyz.__chan,
701                                            (xyz.crc_mode ? 'C' : NAK));
702                       xyz.total_retries++;
703                       ZM_DEBUG (zm_dprintf ("Reading Final Header\n"));
704                       stat = xyzModem_get_hdr ();
705                       CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
706                       ZM_DEBUG (zm_dprintf ("FINAL ACK (%d)\n", __LINE__));
707                     }
708                   xyz.at_eof = true;
709                   break;
710                 }
711               CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
712               xyz.total_retries++;
713               ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
714             }
715           if (stat < 0)
716             {
717               *err = stat;
718               xyz.len = -1;
719               return total;
720             }
721         }
722       /* Don't "read" data from the EOF protocol package */
723       if (!xyz.at_eof)
724         {
725           len = xyz.len;
726           if (size < len)
727             len = size;
728           memcpy (buf, xyz.bufp, len);
729           size -= len;
730           buf += len;
731           total += len;
732           xyz.len -= len;
733           xyz.bufp += len;
734         }
735     }
736   return total;
737 }
738
739 void
740 xyzModem_stream_close (int *err)
741 {
742   diag_printf
743     ("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
744      xyz.crc_mode ? "CRC" : "Cksum", xyz.total_SOH, xyz.total_STX,
745      xyz.total_CAN, xyz.total_retries);
746   ZM_DEBUG (zm_flush ());
747 }
748
749 /* Need to be able to clean out the input buffer, so have to take the */
750 /* getc */
751 void
752 xyzModem_stream_terminate (bool abort, int (*getc) (void))
753 {
754   int c;
755
756   if (abort)
757     {
758       ZM_DEBUG (zm_dprintf ("!!!! TRANSFER ABORT !!!!\n"));
759       switch (xyz.mode)
760         {
761         case xyzModem_xmodem:
762         case xyzModem_ymodem:
763           /* The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal */
764           /* number of Backspaces is a friendly way to get the other end to abort. */
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, CAN);
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           CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
773           /* Now consume the rest of what's waiting on the line. */
774           ZM_DEBUG (zm_dprintf ("Flushing serial line.\n"));
775           xyzModem_flush ();
776           xyz.at_eof = true;
777           break;
778 #ifdef xyzModem_zmodem
779         case xyzModem_zmodem:
780           /* Might support it some day I suppose. */
781 #endif
782           break;
783         }
784     }
785   else
786     {
787       ZM_DEBUG (zm_dprintf ("Engaging cleanup mode...\n"));
788       /*
789        * Consume any trailing crap left in the inbuffer from
790        * previous received blocks. Since very few files are an exact multiple
791        * of the transfer block size, there will almost always be some gunk here.
792        * If we don't eat it now, RedBoot will think the user typed it.
793        */
794       ZM_DEBUG (zm_dprintf ("Trailing gunk:\n"));
795       while ((c = (*getc) ()) > -1);
796       ZM_DEBUG (zm_dprintf ("\n"));
797       /*
798        * Make a small delay to give terminal programs like minicom
799        * time to get control again after their file transfer program
800        * exits.
801        */
802       CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
803     }
804 }
805
806 char *
807 xyzModem_error (int err)
808 {
809   switch (err)
810     {
811     case xyzModem_access:
812       return "Can't access file";
813       break;
814     case xyzModem_noZmodem:
815       return "Sorry, zModem not available yet";
816       break;
817     case xyzModem_timeout:
818       return "Timed out";
819       break;
820     case xyzModem_eof:
821       return "End of file";
822       break;
823     case xyzModem_cancel:
824       return "Cancelled";
825       break;
826     case xyzModem_frame:
827       return "Invalid framing";
828       break;
829     case xyzModem_cksum:
830       return "CRC/checksum error";
831       break;
832     case xyzModem_sequence:
833       return "Block sequence error";
834       break;
835     default:
836       return "Unknown error";
837       break;
838     }
839 }
840
841 /*
842  * RedBoot interface
843  */
844 #if 0                           /* SB */
845 GETC_IO_FUNCS (xyzModem_io, xyzModem_stream_open, xyzModem_stream_close,
846                xyzModem_stream_terminate, xyzModem_stream_read,
847                xyzModem_error);
848 RedBoot_load (xmodem, xyzModem_io, false, false, xyzModem_xmodem);
849 RedBoot_load (ymodem, xyzModem_io, false, false, xyzModem_ymodem);
850 #endif