3 * @brief RedBoot stream handler for xyzModem protocol
5 * FileName: commands/xyzModem.c
6 * Originally from u-boot xyzModem.c
9 * 2008 - Nishanth Menon <x0nishan@ti.com>
10 * Modified for sparse and checkpatch.pl compliance
13 *==========================================================================
17 * RedBoot stream handler for xyzModem protocol
19 *==========================================================================
20 *####ECOSGPLCOPYRIGHTBEGIN####
21 * -------------------------------------------
22 * This file is part of eCos, the Embedded Configurable Operating System.
23 * Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
24 * Copyright (C) 2002 Gary Thomas
26 * eCos is free software; you can redistribute it and/or modify it under
27 * the terms of the GNU General Public License as published by the Free
28 * Software Foundation; either version 2 or (at your option) any later version.
30 * eCos is distributed in the hope that it will be useful, but WITHOUT ANY
31 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
32 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
35 * You should have received a copy of the GNU General Public License along
36 * with eCos; if not, write to the Free Software Foundation, Inc.,
37 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
39 * As a special exception, if other files instantiate templates or use macros
40 * or inline functions from this file, or you compile this file and link it
41 * with other works to produce a work based on this file, this file does not
42 * by itself cause the resulting work to be covered by the GNU General Public
43 * License. However the source code for this file must still be made available
44 * in accordance with section (3) of the GNU General Public License.
46 * This exception does not invalidate any other reasons why a work based on
47 * this file might be covered by the GNU General Public License.
49 * Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
50 * at http: *sources.redhat.com/ecos/ecos-license/
51 * -------------------------------------------
52 *####ECOSGPLCOPYRIGHTEND####
53 *==========================================================================
54 *#####DESCRIPTIONBEGIN####
57 * Contributors: gthomas, tsmith, Yoshinori Sato
62 * This code is part of RedBoot (tm).
64 *####DESCRIPTIONEND####
66 *==========================================================================
74 #include <linux/ctype.h>
76 /* Assumption - run xyzModem protocol over the console port */
78 /* Values magic to the protocol */
86 #define EOF 0x1A /* ^Z for DOS officionados */
88 #define USE_YMODEM_LENGTH
90 /* Data & state local to the protocol */
93 hal_virtual_comm_table_t
*__chan
;
97 unsigned char pkt
[1024], *bufp
;
98 unsigned char blk
, cblk
, crc1
, crc2
;
99 unsigned char next_blk
; /* Expected block */
100 int len
, mode
, total_retries
;
101 int total_SOH
, total_STX
, total_CAN
;
102 bool crc_mode
, at_eof
, tx_ack
;
103 #ifdef USE_YMODEM_LENGTH
104 unsigned long file_length
, read_length
;
108 #define xyzModem_CHAR_TIMEOUT 2000 /* 2 seconds */
109 #define xyzModem_MAX_RETRIES 20
110 #define xyzModem_MAX_RETRIES_WITH_CRC 10
111 /* Wait for 3 CAN before quitting */
112 #define xyzModem_CAN_COUNT 3
114 #ifndef REDBOOT /*SB */
115 typedef int cyg_int32
;
116 static int CYGACC_COMM_IF_GETC_TIMEOUT(char chan
, char *c
)
119 unsigned long counter
= 0;
120 while (!tstc() && (counter
< xyzModem_CHAR_TIMEOUT
* 1000 / DELAY
)) {
131 static void CYGACC_COMM_IF_PUTC(char x
, char y
)
133 console_putc(CONSOLE_STDOUT
, y
);
136 /* Validate a hex character */
137 static inline bool _is_hex(char c
)
139 return (((c
>= '0') && (c
<= '9')) ||
140 ((c
>= 'A') && (c
<= 'F')) || ((c
>= 'a') && (c
<= 'f')));
143 /* Convert a single hex nibble */
144 static inline int _from_hex(char c
)
148 if ((c
>= '0') && (c
<= '9'))
150 else if ((c
>= 'a') && (c
<= 'f'))
151 ret
= (c
- 'a' + 0x0a);
152 else if ((c
>= 'A') && (c
<= 'F'))
153 ret
= (c
- 'A' + 0x0A);
158 /* Parse (scan) a number */
159 static bool parse_num(char *s
, unsigned long *val
, char **es
, char *delim
)
164 unsigned long result
= 0;
170 if (first
&& (s
[0] == '0') && (tolower(s
[1]) == 'x')) {
177 digit
= _from_hex(c
);
178 if (_is_hex(c
) && (digit
< radix
)) {
180 #ifdef CYGPKG_HAL_MIPS
181 /* FIXME: tx49 compiler generates 0x2539018 for
182 * MUL which isn't any good. */
184 result
= result
<< 4;
186 result
= 10 * result
;
189 result
= (result
* radix
) + digit
;
192 if (delim
!= (char *)0) {
193 /* See if this character is one of the
196 while (*dp
&& (c
!= *dp
))
199 break; /* Found a good delimiter */
201 return false; /* Malformatted number */
205 if (es
!= (char **)0)
216 * Note: this debug setup only works if the target platform has two serial ports
217 * available so that the other one (currently only port 1) can be used for debug
220 static int zm_dprintf(char *fmt
, ...)
228 CYGACC_CALL_IF_SET_CONSOLE_COMM
229 (CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT
);
230 CYGACC_CALL_IF_SET_CONSOLE_COMM(1);
232 diag_vprintf(fmt
, args
);
234 CYGACC_CALL_IF_SET_CONSOLE_COMM(cur_console
);
238 static void zm_flush(void)
244 * Note: this debug setup works by storing the strings in a fixed buffer
248 static char *zm_out
= (char *)0x00380000;
249 static char *zm_out_start
= (char *)0x00380000;
251 static char zm_buf
[8192];
252 static char *zm_out
= zm_buf
;
253 static char *zm_out_start
= zm_buf
;
256 static int zm_dprintf(char *fmt
, ...)
262 len
= diag_vsprintf(zm_out
, fmt
, args
);
267 static void zm_flush(void)
270 char *p
= zm_out_start
;
272 mon_write_char(*p
++);
274 zm_out
= zm_out_start
;
278 static void zm_dump_buf(void *buf
, int len
)
281 diag_vdump_buf_with_offset(zm_dprintf
, buf
, len
, 0);
287 static unsigned char zm_buf
[2048];
288 static unsigned char *zm_bp
;
290 static void zm_new(void)
295 static void zm_save(unsigned char c
)
300 static void zm_dump(int line
)
302 zm_dprintf("Packet at line: %d\n", line
);
303 zm_dump_buf(zm_buf
, zm_bp
- zm_buf
);
306 #define ZM_DEBUG(x) x
311 /* Wait for the line to go idle */
312 static void xyzModem_flush(void)
317 res
= CYGACC_COMM_IF_GETC_TIMEOUT(*xyz
.__chan
, &c
);
323 static int xyzModem_get_hdr(void)
327 bool hdr_found
= false;
328 int i
, can_total
, hdr_chars
;
329 unsigned short cksum
;
332 /* Find the start of a header */
337 CYGACC_COMM_IF_PUTC(*xyz
.__chan
, ACK
);
341 res
= CYGACC_COMM_IF_GETC_TIMEOUT(*xyz
.__chan
, &c
);
342 ZM_DEBUG(zm_save(c
));
355 ZM_DEBUG(zm_dump(__LINE__
));
356 if (++can_total
== xyzModem_CAN_COUNT
) {
357 return xyzModem_cancel
;
359 /* Wait for multiple CAN to avoid
364 /* EOT only supported if no noise */
365 if (hdr_chars
== 1) {
366 CYGACC_COMM_IF_PUTC(*xyz
.__chan
, ACK
);
370 ZM_DEBUG(zm_dump(__LINE__
));
374 /* Ignore, waiting for start of header */
378 /* Data stream timed out */
379 xyzModem_flush(); /* Toss any current input */
380 ZM_DEBUG(zm_dump(__LINE__
));
381 CYGACC_CALL_IF_DELAY_US((cyg_int32
) 250000);
382 return xyzModem_timeout
;
386 /* Header found, now read the data */
387 res
= CYGACC_COMM_IF_GETC_TIMEOUT(*xyz
.__chan
, (char *)&xyz
.blk
);
388 ZM_DEBUG(zm_save(xyz
.blk
));
390 ZM_DEBUG(zm_dump(__LINE__
));
391 return xyzModem_timeout
;
393 res
= CYGACC_COMM_IF_GETC_TIMEOUT(*xyz
.__chan
, (char *)&xyz
.cblk
);
394 ZM_DEBUG(zm_save(xyz
.cblk
));
396 ZM_DEBUG(zm_dump(__LINE__
));
397 return xyzModem_timeout
;
399 xyz
.len
= (c
== SOH
) ? 128 : 1024;
401 for (i
= 0; i
< xyz
.len
; i
++) {
402 res
= CYGACC_COMM_IF_GETC_TIMEOUT(*xyz
.__chan
, &c
);
403 ZM_DEBUG(zm_save(c
));
407 ZM_DEBUG(zm_dump(__LINE__
));
408 return xyzModem_timeout
;
411 res
= CYGACC_COMM_IF_GETC_TIMEOUT(*xyz
.__chan
, (char *)&xyz
.crc1
);
412 ZM_DEBUG(zm_save(xyz
.crc1
));
414 ZM_DEBUG(zm_dump(__LINE__
));
415 return xyzModem_timeout
;
419 CYGACC_COMM_IF_GETC_TIMEOUT(*xyz
.__chan
, (char *)&xyz
.crc2
);
420 ZM_DEBUG(zm_save(xyz
.crc2
));
422 ZM_DEBUG(zm_dump(__LINE__
));
423 return xyzModem_timeout
;
426 ZM_DEBUG(zm_dump(__LINE__
));
427 /* Validate the message */
428 if ((xyz
.blk
^ xyz
.cblk
) != (unsigned char)0xFF) {
430 ("Framing error - blk: %x/%x/%x\n", xyz
.blk
, xyz
.cblk
,
431 (xyz
.blk
^ xyz
.cblk
)));
432 ZM_DEBUG(zm_dump_buf(xyz
.pkt
, xyz
.len
));
434 return xyzModem_frame
;
436 /* Verify checksum/CRC */
438 cksum
= cyg_crc16(xyz
.pkt
, xyz
.len
);
439 if (cksum
!= ((xyz
.crc1
<< 8) | xyz
.crc2
)) {
441 ("CRC error - recvd: %02x%02x, computed: %x\n",
442 xyz
.crc1
, xyz
.crc2
, cksum
& 0xFFFF));
443 return xyzModem_cksum
;
447 for (i
= 0; i
< xyz
.len
; i
++)
449 if (xyz
.crc1
!= (cksum
& 0xFF)) {
451 ("Checksum error - recvd: %x, computed: %x\n",
452 xyz
.crc1
, cksum
& 0xFF));
453 return xyzModem_cksum
;
456 /* If we get here, the message passes [structural] muster */
460 int xyzModem_stream_open(connection_info_t
*info
, int *err
)
466 int retries
= xyzModem_MAX_RETRIES
;
467 int crc_retries
= xyzModem_MAX_RETRIES_WITH_CRC
;
469 /* ZM_DEBUG(zm_out = zm_out_start); */
470 #ifdef xyzModem_zmodem
471 if (info
->mode
== xyzModem_zmodem
) {
472 *err
= xyzModem_noZmodem
;
478 /* Set up the I/O channel. Note: this allows for using a different
479 * port in the future */
481 CYGACC_CALL_IF_SET_CONSOLE_COMM
482 (CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT
);
484 CYGACC_CALL_IF_SET_CONSOLE_COMM(info
->chan
);
486 CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan
);
488 xyz
.__chan
= CYGACC_CALL_IF_CONSOLE_PROCS();
490 CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan
);
491 CYGACC_COMM_IF_CONTROL(*xyz
.__chan
, __COMMCTL_SET_TIMEOUT
,
492 xyzModem_CHAR_TIMEOUT
);
502 xyz
.mode
= info
->mode
;
503 xyz
.total_retries
= 0;
507 #ifdef USE_YMODEM_LENGTH
512 CYGACC_COMM_IF_PUTC(*xyz
.__chan
, (xyz
.crc_mode
? 'C' : NAK
));
514 if (xyz
.mode
== xyzModem_xmodem
) {
515 /* X-modem doesn't have an information header - exit here */
520 while (retries
-- > 0) {
521 stat
= xyzModem_get_hdr();
523 /* Y-modem file information header */
525 #ifdef USE_YMODEM_LENGTH
527 while (*xyz
.bufp
++) ;
529 parse_num((char *)xyz
.bufp
, &xyz
.file_length
,
532 /* The rest of the file name data block
540 } else if (stat
== xyzModem_timeout
) {
541 if (--crc_retries
<= 0)
542 xyz
.crc_mode
= false;
543 /* Extra delay for startup */
544 CYGACC_CALL_IF_DELAY_US(5 * 100000);
545 CYGACC_COMM_IF_PUTC(*xyz
.__chan
,
546 (xyz
.crc_mode
? 'C' : NAK
));
548 ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__
));
550 if (stat
== xyzModem_cancel
)
554 ZM_DEBUG(zm_flush());
558 int xyzModem_stream_read(char *buf
, int size
, int *err
)
560 int stat
, total
, len
;
564 stat
= xyzModem_cancel
;
565 /* Try and get 'size' bytes into the buffer */
566 while (!xyz
.at_eof
&& (size
> 0)) {
568 retries
= xyzModem_MAX_RETRIES
;
569 while (retries
-- > 0) {
570 stat
= xyzModem_get_hdr();
572 if (xyz
.blk
== xyz
.next_blk
) {
575 ("ACK block %d (%d)\n",
578 (xyz
.next_blk
+ 1) & 0xFF;
580 #if defined(xyzModem_zmodem) || defined(USE_YMODEM_LENGTH)
581 if (xyz
.mode
== xyzModem_xmodem
582 || xyz
.file_length
== 0) {
586 /* WARNING - Leaving formatting aside for code
588 /* Data blocks can be padded with ^Z (EOF) characters */
589 /* This code tries to detect and remove them */
590 if ((xyz
.bufp
[xyz
.len
- 1] == EOF
)
591 && (xyz
.bufp
[xyz
.len
- 2] == EOF
)
592 && (xyz
.bufp
[xyz
.len
- 3] == EOF
)) {
594 (xyz
.bufp
[xyz
.len
- 1] == EOF
))
598 #ifdef USE_YMODEM_LENGTH
599 /* WARNING - Leaving formatting aside for code
602 * See if accumulated length exceeds that of the file.
603 * If so, reduce size (i.e., cut out pad bytes)
604 * Only do this for Y-modem (and Z-modem should it ever
605 * be supported since it can fall back to Y-modem mode).
607 if (xyz
.mode
!= xyzModem_xmodem
608 && 0 != xyz
.file_length
) {
609 xyz
.read_length
+= xyz
.len
;
610 if (xyz
.read_length
> xyz
.file_length
)
611 xyz
.len
-= (xyz
.read_length
-
616 } else if (xyz
.blk
==
617 ((xyz
.next_blk
- 1) &
619 /* Just re-ACK this so sender
620 * will get on with it */
621 CYGACC_COMM_IF_PUTC(*xyz
.__chan
,
623 /* Need new header */
626 stat
= xyzModem_sequence
;
628 if (stat
== xyzModem_cancel
)
630 if (stat
== xyzModem_eof
) {
631 CYGACC_COMM_IF_PUTC(*xyz
.__chan
, ACK
);
633 ("ACK (%d)\n", __LINE__
));
634 if (xyz
.mode
== xyzModem_ymodem
) {
635 CYGACC_COMM_IF_PUTC(*xyz
.__chan
,
642 ("Reading Final Header\n"));
643 stat
= xyzModem_get_hdr();
644 CYGACC_COMM_IF_PUTC(*xyz
.__chan
,
653 CYGACC_COMM_IF_PUTC(*xyz
.__chan
,
654 (xyz
.crc_mode
? 'C' : NAK
));
656 ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__
));
664 /* Don't "read" data from the EOF protocol package */
669 memcpy(buf
, xyz
.bufp
, len
);
680 void xyzModem_stream_close(int *err
)
683 ("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets,"
685 xyz
.crc_mode
? "CRC" : "Cksum", xyz
.total_SOH
, xyz
.total_STX
,
686 xyz
.total_CAN
, xyz
.total_retries
);
687 ZM_DEBUG(zm_flush());
690 /* Need to be able to clean out the input buffer, so have to take the */
692 void xyzModem_stream_terminate(bool abort
, int (*getc
) (void))
697 ZM_DEBUG(zm_dprintf("!!!! TRANSFER ABORT !!!!\n"));
699 case xyzModem_xmodem
:
700 case xyzModem_ymodem
:
701 /* The X/YMODEM Spec seems to suggest that multiple CAN
702 * followed by an equal number of Backspaces is a
703 * friendly way to get the other end to abort. */
704 CYGACC_COMM_IF_PUTC(*xyz
.__chan
, CAN
);
705 CYGACC_COMM_IF_PUTC(*xyz
.__chan
, CAN
);
706 CYGACC_COMM_IF_PUTC(*xyz
.__chan
, CAN
);
707 CYGACC_COMM_IF_PUTC(*xyz
.__chan
, CAN
);
708 CYGACC_COMM_IF_PUTC(*xyz
.__chan
, BSP
);
709 CYGACC_COMM_IF_PUTC(*xyz
.__chan
, BSP
);
710 CYGACC_COMM_IF_PUTC(*xyz
.__chan
, BSP
);
711 CYGACC_COMM_IF_PUTC(*xyz
.__chan
, BSP
);
712 /* Now consume the rest of what's waiting on the line.*/
713 ZM_DEBUG(zm_dprintf("Flushing serial line.\n"));
717 #ifdef xyzModem_zmodem
718 case xyzModem_zmodem
:
719 /* Might support it some day I suppose. */
724 ZM_DEBUG(zm_dprintf("Engaging cleanup mode...\n"));
726 * Consume any trailing crap left in the inbuffer from
727 * previous recieved blocks. Since very few files are an
728 * exact multiple of the transfer block size, there will
729 * almost always be some gunk here.
730 * If we don't eat it now, RedBoot will think the user typed it.
732 ZM_DEBUG(zm_dprintf("Trailing gunk:\n"));
733 while ((c
= (*getc
) ()) > -1) ;
734 ZM_DEBUG(zm_dprintf("\n"));
736 * Make a small delay to give terminal programs like minicom
737 * time to get control again after their file transfer program
740 CYGACC_CALL_IF_DELAY_US((cyg_int32
) 250000);
744 char *xyzModem_error(int err
)
747 case xyzModem_access
:
748 return "Can't access file";
750 case xyzModem_noZmodem
:
751 return "Sorry, zModem not available yet";
753 case xyzModem_timeout
:
757 return "End of file";
759 case xyzModem_cancel
:
763 return "Invalid framing";
766 return "CRC/checksum error";
768 case xyzModem_sequence
:
769 return "Block sequence error";
772 return "Unknown error";
781 GETC_IO_FUNCS(xyzModem_io
, xyzModem_stream_open
, xyzModem_stream_close
,
782 xyzModem_stream_terminate
, xyzModem_stream_read
, xyzModem_error
);
783 RedBoot_load(xmodem
, xyzModem_io
, false, false, xyzModem_xmodem
);
784 RedBoot_load(ymodem
, xyzModem_io
, false, false, xyzModem_ymodem
);