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 740