Merge with git://www.denx.de/git/u-boot.git
[platform/kernel/u-boot.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 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;
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 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
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