1/* ********************************************************************* 2 * Broadcom Common Firmware Environment (CFE) 3 * 4 * "Xmodem" file system File: cfe_xmodem.c 5 * 6 * This "file system" only works with serial devices, and 7 * implements the venerable XMODEM protocol to receive files. 8 * 9 * Author: Mitch Lichtenberg 10 * 11 ********************************************************************* 12 * 13 * Copyright 2000,2001,2002,2003 14 * Broadcom Corporation. All rights reserved. 15 * 16 * This software is furnished under license and may be used and 17 * copied only in accordance with the following terms and 18 * conditions. Subject to these conditions, you may download, 19 * copy, install, use, modify and distribute modified or unmodified 20 * copies of this software in source and/or binary form. No title 21 * or ownership is transferred hereby. 22 * 23 * 1) Any source code used, modified or distributed must reproduce 24 * and retain this copyright notice and list of conditions 25 * as they appear in the source file. 26 * 27 * 2) No right is granted to use any trade name, trademark, or 28 * logo of Broadcom Corporation. The "Broadcom Corporation" 29 * name may not be used to endorse or promote products derived 30 * from this software without the prior written permission of 31 * Broadcom Corporation. 32 * 33 * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR 34 * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED 35 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 36 * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT 37 * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN 38 * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, 39 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 40 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 41 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 42 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 43 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 44 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF 45 * THE POSSIBILITY OF SUCH DAMAGE. 46 ********************************************************************* */ 47 48 49/* ********************************************************************* 50 * WARNING! This is very much a work-in-progress. Beware! 51 ********************************************************************* */ 52 53#include "cfe.h" 54#include "cfe_fileops.h" 55 56/* ********************************************************************* 57 * XMODEM protocol and state machine constants 58 ********************************************************************* */ 59 60/* Buffer is big enough for a 1K packet plus packet # and 2-byte CRC */ 61#define XMODEM_BUFSIZE (1024 + 4) 62 63/* Protocol states */ 64#define XMS_SYNC 0 65#define XMS_SYNCWAIT 1 66#define XMS_RX 2 67#define XMS_DONE 3 68#define XMS_WAIT 4 69#define XMS_ERROR 5 70#define XMS_RETRY 6 71 72#define XMODEM_TIMEOUT (CFE_HZ) /* Timeout for sync */ 73#define XMODEM_RETRIES 16 /* Number of retransmissions */ 74 75 76/* Special XMODEM control characters */ 77#define XMC_SOH 0x01 78#define XMC_STX 0x02 79#define XMC_EOT 0x04 80#define XMC_ACK 0x06 81#define XMC_NAK 0x15 82#define XMC_CAN 0x18 83#define XMC_SYNC 'C' 84 85#define HEADERSIZE 2 /* header is blk # + complement */ 86#define CRCSIZE 2 /* CRC follows the user data */ 87 88/* ********************************************************************* 89 * XMODEM context 90 ********************************************************************* */ 91 92/* 93 * File system context - describes overall file system info, 94 * such as the handle to the underlying device. 95 */ 96 97typedef struct xmodem_fsctx_s { 98 int xmodem_dev; 99 int xmodem_isconsole; 100 int xmodem_refcnt; 101} xmodem_fsctx_t; 102 103/* 104 * File context - describes an open file on the file system. 105 */ 106 107 108typedef struct xmodem_file_s { 109 xmodem_fsctx_t *xmodem_fsctx; 110 int xmodem_fileoffset; 111 int xmodem_blkoffset; 112 int xmodem_eof; 113 int xmodem_error; 114 115 /* XMODEM control variables */ 116 int xmodem_state; 117 uint8_t xmodem_sync; /* char we use for sync */ 118 uint8_t xmodem_buffer[XMODEM_BUFSIZE]; 119 uint8_t xmodem_curblk; 120 int xmodem_tries; 121 int xmodem_blksize; 122 int xmodem_crcmode; 123 cfe_timer_t xmodem_timer; 124} xmodem_file_t; 125 126/* ********************************************************************* 127 * Prototypes 128 ********************************************************************* */ 129 130static int xmodem_fileop_init(void **fsctx,void *devicename); 131static int xmodem_fileop_open(void **ref,void *fsctx,char *filename,int mode); 132static int xmodem_fileop_read(void *ref,hsaddr_t buf,int len); 133static int xmodem_fileop_write(void *ref,hsaddr_t buf,int len); 134static int xmodem_fileop_seek(void *ref,int offset,int how); 135static void xmodem_fileop_close(void *ref); 136static void xmodem_fileop_uninit(void *fsctx); 137 138 139/* ********************************************************************* 140 * XMODEM CRC 141 ********************************************************************* */ 142 143 144static uint16_t calc_crc(uint8_t *buf,int len) 145{ 146 int i; 147 uint16_t crc = 0; 148 149 while (len > 0) { 150 crc = crc ^ (((uint16_t) *buf) << 8); 151 for (i = 0; i < 8; i++) { 152 if (crc & 0x8000) crc = crc << 1 ^ 0x1021; 153 else crc = crc << 1; 154 } 155 len--; 156 buf++; 157 } 158 159 return crc; 160} 161 162 163/* ********************************************************************* 164 * XMODEM protocol 165 ********************************************************************* */ 166 167 168/* ********************************************************************* 169 * xmodem_rxbuf(xmf,chptr,len,timeout) 170 * 171 * Receive 'n' characters from remote host, with a timeout 172 * if we do not get them all. 173 * 174 * Input parameters: 175 * xmf - XMODEM state 176 * chptr - pointer to receive buffer 177 * len - number of characters to receive 178 * timeout - timeout value in CFE ticks 179 * 180 * Return value: 181 * -1: timeout occured 182 * else number of characters received 183 ********************************************************************* */ 184 185static int xmodem_rxbuf(xmodem_file_t *xmf,uint8_t *chptr,int len,int timeout) 186{ 187 cfe_timer_t timer; 188 int savelen = len; 189 int res; 190 191 TIMER_SET(timer,timeout); 192 193 while (!TIMER_EXPIRED(timer)) { 194 195 if (len == 0) break; 196 /* note: we assume cfe_read calls POLL() internally to advance time */ 197 res = cfe_read(xmf->xmodem_fsctx->xmodem_dev,PTR2HSADDR(chptr),len); 198 199 /* nothing received, wait for more */ 200 if (res == 0) { 201 POLL(); 202 continue; 203 } 204 if (res < 0) return -1; /* some sort of device error */ 205 206 /* if we got anything, reset timer */ 207 TIMER_SET(timer,timeout); 208 209 /* try for less next time */ 210 len -= res; 211 chptr += res; 212 } 213 214 if (len != 0) return -1; /* we did not get it all */ 215 return savelen; /* otherwise, we got it all. */ 216 217} 218 219 220/* ********************************************************************* 221 * xmodem_waitidle(xmf) 222 * 223 * Wait for line to become idle-- toss characters until we see 224 * one second of silence. 225 * 226 * Input parameters: 227 * xmf - XMODEM state 228 * 229 * Return value: 230 * nothing 231 ********************************************************************* */ 232 233static void xmodem_waitidle(xmodem_file_t *xmf) 234{ 235 uint8_t b; 236 237 while (xmodem_rxbuf(xmf,&b,1,CFE_HZ) >= 0) ; /* wait till timeout */ 238 239} 240 241 242/* ********************************************************************* 243 * xmodem_send1(xmf,c) 244 * 245 * Send a control character, such as an ACK or NAK 246 * 247 * Input parameters: 248 * xmf - XMODEM state 249 * c - character to send 250 * 251 * Return value: 252 * nothing 253 ********************************************************************* */ 254 255static void xmodem_send1(xmodem_file_t *xmf,uint8_t c) 256{ 257 int dev; 258 259 dev = xmf->xmodem_fsctx->xmodem_dev; 260 261 cfe_write(dev,PTR2HSADDR(&c),1); 262} 263 264 265 266/* ********************************************************************* 267 * xmodem_run(xmf) 268 * 269 * Main XMODEM state machine. We call this periodically to advance 270 * the XMODEM protocol through its states. The xmodem_state field 271 * of the state data structure is monitored for changes, and 272 * while in any of the transfer states this routine gets called 273 * over and over to process data. 274 * 275 * Input parameters: 276 * xmf - XMODEM state 277 * 278 * Return value: 279 * nothing 280 ********************************************************************* */ 281 282static void xmodem_run(xmodem_file_t *xmf) 283{ 284 uint8_t syncbuf[1]; 285 uint16_t crc,pktcrc; 286 287 switch (xmf->xmodem_state) { 288 case XMS_SYNC: 289 xmodem_send1(xmf,xmf->xmodem_sync); 290 xmf->xmodem_state = XMS_SYNCWAIT; 291 TIMER_SET(xmf->xmodem_timer,XMODEM_TIMEOUT); 292 break; 293 294 case XMS_SYNCWAIT: 295 if (xmodem_rxbuf(xmf,syncbuf,1,XMODEM_TIMEOUT) < 0) { 296 xmf->xmodem_tries++; 297 if (xmf->xmodem_tries < XMODEM_RETRIES) { 298 xmf->xmodem_state = XMS_SYNC; 299 break; 300 } 301 else { 302 xmf->xmodem_state = XMS_ERROR; 303 break; 304 } 305 } 306 307 switch (syncbuf[0]) { 308 case XMC_SOH: 309 xmf->xmodem_blksize = 128; 310 xmf->xmodem_state = XMS_RX; 311 xmf->xmodem_tries = 0; 312 break; 313 314 case XMC_STX: 315 xmf->xmodem_blksize = 1024; 316 xmf->xmodem_state = XMS_RX; 317 xmf->xmodem_tries = 0; 318 break; 319 320 case XMC_EOT: 321 xmodem_waitidle(xmf); 322 xmodem_send1(xmf,XMC_ACK); 323 xmf->xmodem_state = XMS_DONE; 324 xmf->xmodem_tries = 0; 325 break; 326 327 case XMC_CAN: 328 /* 329 * If two CAN (Ctrl-X) chars received, go to ERROR state 330 */ 331 if (xmodem_rxbuf(xmf,syncbuf,1,XMODEM_TIMEOUT) < 0) { 332 xmf->xmodem_state = XMS_SYNC; 333 break; 334 } 335 if (syncbuf[0] != XMC_CAN) { 336 xmf->xmodem_state = XMS_SYNC; 337 break; 338 } 339 xmf->xmodem_state = XMS_ERROR; 340 break; 341 342 default: 343 /* Ignore bad sync characters, wait until line is idle and stay in SYNCWAIT */ 344 xmodem_waitidle(xmf); 345 break; 346 } 347 break; 348 349 case XMS_RX: 350 /* 351 * receive block plus 2 chars at the front with blk# and complement and one checksum byte 352 * or two CRC bytes 353 */ 354 355 if (xmodem_rxbuf(xmf,xmf->xmodem_buffer,xmf->xmodem_blksize+(HEADERSIZE+CRCSIZE),XMODEM_TIMEOUT) < 0) { 356 xmf->xmodem_state = XMS_RETRY; 357 break; 358 } 359 360 /* Check the block number */ 361 if (xmf->xmodem_buffer[0] != (uint8_t)(~xmf->xmodem_buffer[1])) { 362 xmf->xmodem_state = XMS_RETRY; 363 break; 364 } 365 366 367 /* Check the checksum or CRC */ 368 369 if (xmf->xmodem_crcmode) { 370 crc = calc_crc(&(xmf->xmodem_buffer[HEADERSIZE]),xmf->xmodem_blksize); 371 pktcrc = (((uint16_t)xmf->xmodem_buffer[HEADERSIZE+xmf->xmodem_blksize]) << 8) | 372 (uint16_t)(xmf->xmodem_buffer[HEADERSIZE+xmf->xmodem_blksize+1]); 373 374 375 if (crc != pktcrc) { 376 xmf->xmodem_state = XMS_RETRY; 377 break; 378 } 379 } 380 else { 381#if 0 382 /* XXX do regular checksum someday */ 383 for (idx = 0; idx < xmf->xmodem_blksize; idx++) { 384 csum += xmf->xmodem_buffer[2+idx]; 385 } 386#endif 387 388 } 389 390 /* 391 * If the other side lost our ack, it might send the same 392 * block again. Just ack it again if we get a repeat block. 393 */ 394 395 if (xmf->xmodem_buffer[0] == (xmf->xmodem_curblk-1)) { 396 xmodem_send1(xmf,XMC_ACK); 397 xmf->xmodem_state = XMS_SYNCWAIT; 398 break; 399 } 400 401 /* 402 * Otherwise, we want exactly the right block in sequence. 403 */ 404 405 if (xmf->xmodem_buffer[0] != xmf->xmodem_curblk) { 406 printf("incorrect block, want %02X got %02X\n", 407 xmf->xmodem_curblk,xmf->xmodem_buffer[0]); 408 xmf->xmodem_state = XMS_RETRY; 409 break; 410 } 411 412 xmf->xmodem_curblk++; 413 414 /* 415 * go to WAIT state until CFE requests the data. We'll exit this 416 * state when CFE has fetched all the data in our buffer, then 417 * we'll send an ACK. 418 */ 419 xmf->xmodem_state = XMS_WAIT; 420 421 break; 422 423 /* DONE, WAIT, and ERROR are all terminal states */ 424 case XMS_DONE: 425 break; 426 427 case XMS_WAIT: 428 break; 429 430 case XMS_ERROR: 431 break; 432 433 case XMS_RETRY: 434 xmodem_waitidle(xmf); 435 xmf->xmodem_tries++; 436 if (xmf->xmodem_tries >= XMODEM_RETRIES) { 437 xmf->xmodem_state = XMS_ERROR; 438 xmodem_send1(xmf,XMC_CAN); 439 xmodem_send1(xmf,XMC_CAN); 440 break; 441 } 442 xmodem_send1(xmf,XMC_NAK); 443 xmf->xmodem_state = XMS_SYNCWAIT; 444 break; 445 } 446} 447 448 449/* ********************************************************************* 450 * xmodem_continue(xmf) 451 * 452 * This routine is called by upper levels when we're done 453 * processing a recieved block and it's time to request another 454 * one. When xmodem_run puts the connection in a WAIT state, 455 * that means it's time for the application to process data. 456 * When the app is done, we come here and the protocol acks the 457 * data and requests more. 458 * 459 * Input parameters: 460 * xmf - XMODEM state 461 * 462 * Return value: 463 * nothing 464 ********************************************************************* */ 465 466static void xmodem_continue(xmodem_file_t *xmf) 467{ 468 xmodem_send1(xmf,XMC_ACK); 469 xmf->xmodem_state = XMS_SYNCWAIT; 470 TIMER_SET(xmf->xmodem_timer,XMODEM_TIMEOUT); 471} 472 473 474/* ********************************************************************* 475 * xmodem_cancel(xmf) 476 * 477 * This routine is called when we want to terminate a recieve 478 * file operation early. Not many XMODEM senders will actually 479 * listen to this, but we try anyway. 480 * 481 * Input parameters: 482 * xmf - XMODEM state 483 * 484 * Return value: 485 * nothing 486 ********************************************************************* */ 487 488static void xmodem_cancel(xmodem_file_t *xmf) 489{ 490 /* wait till line is idle */ 491 xmodem_waitidle(xmf); 492 493 /* send at least two CAN characters to the other side */ 494 xmodem_send1(xmf,XMC_CAN); 495 xmodem_send1(xmf,XMC_CAN); 496 xmodem_send1(xmf,XMC_CAN); 497} 498 499 500/* ********************************************************************* 501 * xmodem_readmore(xmf) 502 * 503 * This routine is called by the filesystem hooks (see below) 504 * to request more data from the other side. It's basically 505 * a helper routine to call xmodem_run and xmodem_continue 506 * at appropriate times. 507 * 508 * Input parameters: 509 * xmf - XMODEM state 510 * 511 * Return value: 512 * 1: at EOF 513 * -1: some error state 514 * 0: still receiving 515 ********************************************************************* */ 516 517static int xmodem_readmore(xmodem_file_t *xmf) 518{ 519 if (xmf->xmodem_eof) return 1; /* already at EOF */ 520 if (xmf->xmodem_error) return -1; /* in error state */ 521 522 /* 523 * If we were waiting before, send an ack to get the next chunk 524 */ 525 526 if (xmf->xmodem_state == XMS_WAIT) { 527 xmodem_continue(xmf); 528 } 529 530 /* 531 * Run protocol engine until we get something. 532 */ 533 534 for (;;) { 535 536 xmodem_run(xmf); 537 538 if (xmf->xmodem_state == XMS_WAIT) { 539 xmf->xmodem_blkoffset = 0; 540 return 0; /* OK */ 541 } 542 543 if (xmf->xmodem_state == XMS_DONE) { 544 xmf->xmodem_blkoffset = 0; 545 xmf->xmodem_blksize = 0; 546 xmf->xmodem_eof = 1; 547 return 1; 548 } 549 550 if (xmf->xmodem_state == XMS_ERROR) { 551 xmf->xmodem_blkoffset = 0; 552 xmf->xmodem_blksize = 0; 553 xmf->xmodem_error = 1; 554 return -1; 555 } 556 } 557 558} 559 560/* ********************************************************************* 561 * RAW fileio dispatch table 562 ********************************************************************* */ 563 564const fileio_dispatch_t xmodem_fileops = { 565 "xmodem", 566 0, 567 xmodem_fileop_init, 568 xmodem_fileop_open, 569 xmodem_fileop_read, 570 xmodem_fileop_write, 571 xmodem_fileop_seek, 572 xmodem_fileop_close, 573 xmodem_fileop_uninit 574}; 575 576static int xmodem_fileop_init(void **newfsctx,void *dev) 577{ 578 xmodem_fsctx_t *fsctx; 579 char *devicename = (char *) dev; 580 581 *newfsctx = NULL; 582 583 fsctx = KMALLOC(sizeof(xmodem_fsctx_t),0); 584 if (!fsctx) { 585 return CFE_ERR_NOMEM; 586 } 587 588 if (strcmp(devicename,console_name) == 0) { 589 fsctx->xmodem_dev = console_handle; 590 fsctx->xmodem_isconsole = TRUE; 591 } 592 else { 593 fsctx->xmodem_dev = cfe_open(devicename); 594 fsctx->xmodem_isconsole = FALSE; 595 } 596 597 fsctx->xmodem_refcnt = 0; 598 599 if (fsctx->xmodem_dev >= 0) { 600 *newfsctx = fsctx; 601 return 0; 602 } 603 604 KFREE(fsctx); 605 606 return CFE_ERR_FILENOTFOUND; 607} 608 609static int xmodem_fileop_open(void **ref,void *fsctx_arg,char *filename,int mode) 610{ 611 xmodem_fsctx_t *fsctx; 612 xmodem_file_t *file; 613 614 if (mode != FILE_MODE_READ) return CFE_ERR_UNSUPPORTED; 615 616 fsctx = (xmodem_fsctx_t *) fsctx_arg; 617 618 file = KMALLOC(sizeof(xmodem_file_t),0); 619 if (!file) { 620 return CFE_ERR_NOMEM; 621 } 622 623 memset(file,0,sizeof(xmodem_file_t)); 624 file->xmodem_state = XMS_SYNC; 625 file->xmodem_fsctx = fsctx; 626 file->xmodem_sync = XMC_SYNC; 627 file->xmodem_curblk = 1; 628 file->xmodem_crcmode = 1; 629 630 fsctx->xmodem_refcnt++; 631 632 xprintf("Ready to receive XMODEM/CRC data. Type two Ctrl-X characters to cancel\n"); 633 634 *ref = file; 635 return 0; 636} 637 638static int xmodem_fileop_read(void *ref,hsaddr_t buf,int len) 639{ 640 xmodem_file_t *xmf = (xmodem_file_t *) ref; 641 int copied = 0; 642 int amtcopy; 643 int res; 644 645 if (xmf->xmodem_error) return CFE_ERR_IOERR; 646 647 if (xmf->xmodem_blksize == 0) { 648 res = xmodem_readmore(xmf); 649 if (res < 0) return CFE_ERR_IOERR; 650 if (res == 1) return 0; /* EOF */ 651 } 652 653 654 while (len) { 655 if (xmf->xmodem_blkoffset >= xmf->xmodem_blksize) break; 656 amtcopy = len; 657 658 if (amtcopy > (xmf->xmodem_blksize-xmf->xmodem_blkoffset)) { 659 amtcopy = (xmf->xmodem_blksize-xmf->xmodem_blkoffset); 660 } 661 662 if (buf) { 663 hs_memcpy_to_hs(buf,&(xmf->xmodem_buffer[xmf->xmodem_blkoffset+HEADERSIZE]),amtcopy); 664 buf += amtcopy; 665 } 666 667 xmf->xmodem_blkoffset += amtcopy; 668 len -= amtcopy; 669 xmf->xmodem_fileoffset += amtcopy; 670 copied += amtcopy; 671 672 if (xmf->xmodem_blkoffset >= xmf->xmodem_blksize) { 673 res = xmodem_readmore(xmf); 674 if (res != 0) break; 675 } 676 } 677 678 return copied; 679 680} 681 682static int xmodem_fileop_write(void *ref,hsaddr_t buf,int len) 683{ 684 return CFE_ERR_UNSUPPORTED; 685} 686 687static int xmodem_fileop_seek(void *ref,int offset,int how) 688{ 689 xmodem_file_t *file = (xmodem_file_t *) ref; 690 int delta; 691 int startloc; 692 int res; 693 694 switch (how) { 695 case FILE_SEEK_BEGINNING: 696 startloc = file->xmodem_fileoffset; 697 break; 698 case FILE_SEEK_CURRENT: 699 startloc = 0; 700 break; 701 default: 702 startloc = 0; 703 break; 704 } 705 706 delta = offset - startloc; 707 if (delta < 0) { 708 /* xprintf("Warning: negative seek on xmodem file attempted\n"); */ 709 return CFE_ERR_UNSUPPORTED; 710 } 711 res = xmodem_fileop_read(ref,NULL,delta); 712 if (res < 0) return res; 713 714 return file->xmodem_fileoffset; 715} 716 717 718static void xmodem_fileop_close(void *ref) 719{ 720 xmodem_file_t *file = (xmodem_file_t *) ref; 721 722 /* 723 * If we were not done receiving a file, send a CANCEL 724 * XXX should we drain the rest of the file out? It appears that 725 * XXX some XMODEM senders do not honor the CAN command, so 726 * XXX both ends will look stuck if you don't need all the file, 727 * XXX as is the case when loading ELF files. 728 */ 729 730 if ((file->xmodem_state != XMS_ERROR) && (file->xmodem_state != XMS_DONE)) { 731 xmodem_cancel(file); 732 } 733 734 file->xmodem_fsctx->xmodem_refcnt--; 735 736 KFREE(file); 737} 738 739static void xmodem_fileop_uninit(void *fsctx_arg) 740{ 741 xmodem_fsctx_t *fsctx = (xmodem_fsctx_t *) fsctx_arg; 742 743 if (fsctx->xmodem_refcnt) { 744 return; 745 } 746 747 if (fsctx->xmodem_isconsole == FALSE) { 748 cfe_close(fsctx->xmodem_dev); 749 } 750 751 KFREE(fsctx); 752} 753