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