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