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