1 //==========================================================================
5 // RedBoot stream handler for xyzModem protocol
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
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.
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
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.
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.
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.
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####
45 // Contributors: gthomas, tsmith, Yoshinori Sato
50 // This code is part of RedBoot (tm).
52 //####DESCRIPTIONEND####
54 //==========================================================================
60 // Assumption - run xyzModem protocol over the console port
62 // Values magic to the protocol
70 #define EOF 0x1A // ^Z for DOS officionados
72 #define USE_YMODEM_LENGTH
74 // Data & state local to the protocol
77 hal_virtual_comm_table_t* __chan;
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;
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
99 typedef int cyg_int32;
100 int CYGACC_COMM_IF_GETC_TIMEOUT (char chan,char *c) {
102 unsigned long counter=0;
103 while (!tstc() && (counter < xyzModem_CHAR_TIMEOUT*1000/DELAY)) {
114 void CYGACC_COMM_IF_PUTC(char x,char y) {
118 // Validate a hex character
119 __inline__ static bool
122 return (((c >= '0') && (c <= '9')) ||
123 ((c >= 'A') && (c <= 'F')) ||
124 ((c >= 'a') && (c <= 'f')));
127 // Convert a single hex nibble
128 __inline__ static int
133 if ((c >= '0') && (c <= '9')) {
135 } else if ((c >= 'a') && (c <= 'f')) {
136 ret = (c - 'a' + 0x0a);
137 } else if ((c >= 'A') && (c <= 'F')) {
138 ret = (c - 'A' + 0x0A);
143 // Convert a character to lower case
144 __inline__ static char
147 if ((c >= 'A') && (c <= 'Z')) {
156 // Parse (scan) a number
159 parse_num(char *s, unsigned long *val, char **es, char *delim)
164 unsigned long result = 0;
167 while (*s == ' ') s++;
169 if (first && (s[0] == '0') && (_tolower(s[1]) == 'x')) {
175 if (_is_hex(c) && ((digit = _from_hex(c)) < radix)) {
177 #ifdef CYGPKG_HAL_MIPS
178 // FIXME: tx49 compiler generates 0x2539018 for MUL which
181 result = result << 4;
183 result = 10 * result;
186 result = (result * radix) + digit;
189 if (delim != (char *)0) {
190 // See if this character is one of the delimiters
192 while (*dp && (c != *dp)) dp++;
193 if (*dp) break; // Found a good delimiter
195 return false; // Malformatted number
199 if (es != (char **)0) {
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
216 zm_dprintf(char *fmt, ...)
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);
226 diag_vprintf(fmt, args);
228 CYGACC_CALL_IF_SET_CONSOLE_COMM(cur_console);
239 // Note: this debug setup works by storing the strings in a fixed buffer
243 static char *zm_out = (char *)0x00380000;
244 static char *zm_out_start = (char *)0x00380000;
246 static char zm_buf[8192];
247 static char *zm_out=zm_buf;
248 static char *zm_out_start = zm_buf;
252 zm_dprintf(char *fmt, ...)
258 len = diag_vsprintf(zm_out, fmt, args);
266 char *p = zm_out_start;
268 while (*p) mon_write_char(*p++);
270 zm_out = zm_out_start;
275 zm_dump_buf(void *buf, int len)
278 diag_vdump_buf_with_offset(zm_dprintf, buf, len, 0);
284 static unsigned char zm_buf[2048];
285 static unsigned char *zm_bp;
294 zm_save(unsigned char c)
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
318 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c);
324 xyzModem_get_hdr(void)
328 bool hdr_found = false;
329 int i, can_total, hdr_chars;
330 unsigned short cksum;
333 // Find the start of a header
338 CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
342 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c);
343 ZM_DEBUG(zm_save(c));
350 if (c == STX) xyz.total_STX++;
355 ZM_DEBUG(zm_dump(__LINE__));
356 if (++can_total == xyzModem_CAN_COUNT) {
357 return xyzModem_cancel;
359 // Wait for multiple CAN to avoid early quits
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__));
371 // Ignore, waiting for start of header
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;
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));
387 ZM_DEBUG(zm_dump(__LINE__));
388 return xyzModem_timeout;
390 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.cblk);
391 ZM_DEBUG(zm_save(xyz.cblk));
393 ZM_DEBUG(zm_dump(__LINE__));
394 return xyzModem_timeout;
396 xyz.len = (c == SOH) ? 128 : 1024;
398 for (i = 0; i < xyz.len; i++) {
399 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c);
400 ZM_DEBUG(zm_save(c));
404 ZM_DEBUG(zm_dump(__LINE__));
405 return xyzModem_timeout;
408 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.crc1);
409 ZM_DEBUG(zm_save(xyz.crc1));
411 ZM_DEBUG(zm_dump(__LINE__));
412 return xyzModem_timeout;
415 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.crc2);
416 ZM_DEBUG(zm_save(xyz.crc2));
418 ZM_DEBUG(zm_dump(__LINE__));
419 return xyzModem_timeout;
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));
428 return xyzModem_frame;
430 // Verify checksum/CRC
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;
440 for (i = 0; i < xyz.len; i++) {
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;
448 // If we get here, the message passes [structural] muster
453 xyzModem_stream_open(connection_info_t *info, int *err)
455 int console_chan, stat=0;
456 int retries = xyzModem_MAX_RETRIES;
457 int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
459 // ZM_DEBUG(zm_out = zm_out_start);
460 #ifdef xyzModem_zmodem
461 if (info->mode == xyzModem_zmodem) {
462 *err = xyzModem_noZmodem;
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);
473 CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan);
475 xyz.__chan = CYGACC_CALL_IF_CONSOLE_PROCS();
477 CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan);
478 CYGACC_COMM_IF_CONTROL(*xyz.__chan, __COMMCTL_SET_TIMEOUT, xyzModem_CHAR_TIMEOUT);
488 xyz.mode = info->mode;
489 xyz.total_retries = 0;
493 #ifdef USE_YMODEM_LENGTH
498 CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
500 if (xyz.mode == xyzModem_xmodem) {
501 // X-modem doesn't have an information header - exit here
506 while (retries-- > 0) {
507 stat = xyzModem_get_hdr();
509 // Y-modem file information header
511 #ifdef USE_YMODEM_LENGTH
515 parse_num(xyz.bufp, &xyz.file_length, NULL, " ");
517 // The rest of the file name data block quietly discarded
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));
529 ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
531 if (stat == xyzModem_cancel) {
536 ZM_DEBUG(zm_flush());
541 xyzModem_stream_read(char *buf, int size, int *err)
543 int stat, total, len;
547 stat = xyzModem_cancel;
548 // Try and get 'size' bytes into the buffer
549 while (!xyz.at_eof && (size > 0)) {
551 retries = xyzModem_MAX_RETRIES;
552 while (retries-- > 0) {
553 stat = xyzModem_get_hdr();
555 if (xyz.blk == xyz.next_blk) {
557 ZM_DEBUG(zm_dprintf("ACK block %d (%d)\n", xyz.blk, __LINE__));
558 xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
560 #if defined(xyzModem_zmodem) || defined(USE_YMODEM_LENGTH)
561 if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0) {
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)) {
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);
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
594 stat = xyzModem_sequence;
597 if (stat == xyzModem_cancel) {
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));
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__));
614 CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
616 ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
624 // Don't "read" data from the EOF protocol package
627 if (size < len) len = size;
628 memcpy(buf, xyz.bufp, len);
640 xyzModem_stream_close(int *err)
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,
646 ZM_DEBUG(zm_flush());
649 // Need to be able to clean out the input buffer, so have to take the
651 void xyzModem_stream_terminate(bool abort, int (*getc)(void))
656 ZM_DEBUG(zm_dprintf("!!!! TRANSFER ABORT !!!!\n"));
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"));
675 #ifdef xyzModem_zmodem
676 case xyzModem_zmodem:
677 // Might support it some day I suppose.
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
693 CYGACC_CALL_IF_DELAY_US((cyg_int32)250000);
698 xyzModem_error(int err)
701 case xyzModem_access:
702 return "Can't access file";
704 case xyzModem_noZmodem:
705 return "Sorry, zModem not available yet";
707 case xyzModem_timeout:
711 return "End of file";
713 case xyzModem_cancel:
717 return "Invalid framing";
720 return "CRC/checksum error";
722 case xyzModem_sequence:
723 return "Block sequence error";
726 return "Unknown error";
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);