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