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 //==========================================================================
59 // Assumption - run xyzModem protocol over the console port
61 // Values magic to the protocol
69 #define EOF 0x1A // ^Z for DOS officionados
71 #define USE_YMODEM_LENGTH
73 // Data & state local to the protocol
75 hal_virtual_comm_table_t* __chan;
76 unsigned char pkt[1024], *bufp;
77 unsigned char blk,cblk,crc1,crc2;
78 unsigned char next_blk; // Expected block
79 int len, mode, total_retries;
80 int total_SOH, total_STX, total_CAN;
81 bool crc_mode, at_eof, tx_ack;
82 #ifdef USE_YMODEM_LENGTH
83 unsigned long file_length, read_length;
87 #define xyzModem_CHAR_TIMEOUT 2000 // 2 seconds
88 #define xyzModem_MAX_RETRIES 20
89 #define xyzModem_MAX_RETRIES_WITH_CRC 10
90 #define xyzModem_CAN_COUNT 3 // Wait for 3 CAN before quitting
95 // Note: this debug setup only works if the target platform has two serial ports
96 // available so that the other one (currently only port 1) can be used for debug
100 zm_dprintf(char *fmt, ...)
106 cur_console = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
107 CYGACC_CALL_IF_SET_CONSOLE_COMM(1);
108 diag_vprintf(fmt, args);
109 CYGACC_CALL_IF_SET_CONSOLE_COMM(cur_console);
119 // Note: this debug setup works by storing the strings in a fixed buffer
121 static char *zm_out = (char *)0x00380000;
122 static char *zm_out_start = (char *)0x00380000;
125 zm_dprintf(char *fmt, ...)
131 len = diag_vsprintf(zm_out, fmt, args);
139 char *p = zm_out_start;
140 while (*p) mon_write_char(*p++);
141 zm_out = zm_out_start;
146 zm_dump_buf(void *buf, int len)
148 diag_vdump_buf_with_offset(zm_dprintf, buf, len, 0);
151 static unsigned char zm_buf[2048];
152 static unsigned char *zm_bp;
161 zm_save(unsigned char c)
169 zm_dprintf("Packet at line: %d\n", line);
170 zm_dump_buf(zm_buf, zm_bp-zm_buf);
173 #define ZM_DEBUG(x) x
178 // Wait for the line to go idle
185 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c);
191 xyzModem_get_hdr(void)
195 bool hdr_found = false;
196 int i, can_total, hdr_chars;
197 unsigned short cksum;
200 // Find the start of a header
205 CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
209 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c);
210 ZM_DEBUG(zm_save(c));
217 if (c == STX) xyz.total_STX++;
222 ZM_DEBUG(zm_dump(__LINE__));
223 if (++can_total == xyzModem_CAN_COUNT) {
224 return xyzModem_cancel;
226 // Wait for multiple CAN to avoid early quits
229 // EOT only supported if no noise
230 if (hdr_chars == 1) {
231 CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
232 ZM_DEBUG(zm_dprintf("ACK on EOT #%d\n", __LINE__));
233 ZM_DEBUG(zm_dump(__LINE__));
238 // Ignore, waiting for start of header
242 // Data stream timed out
243 xyzModem_flush(); // Toss any current input
244 ZM_DEBUG(zm_dump(__LINE__));
245 CYGACC_CALL_IF_DELAY_US((cyg_int32)250000);
246 return xyzModem_timeout;
250 // Header found, now read the data
251 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.blk);
252 ZM_DEBUG(zm_save(xyz.blk));
254 ZM_DEBUG(zm_dump(__LINE__));
255 return xyzModem_timeout;
257 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.cblk);
258 ZM_DEBUG(zm_save(xyz.cblk));
260 ZM_DEBUG(zm_dump(__LINE__));
261 return xyzModem_timeout;
263 xyz.len = (c == SOH) ? 128 : 1024;
265 for (i = 0; i < xyz.len; i++) {
266 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c);
267 ZM_DEBUG(zm_save(c));
271 ZM_DEBUG(zm_dump(__LINE__));
272 return xyzModem_timeout;
275 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.crc1);
276 ZM_DEBUG(zm_save(xyz.crc1));
278 ZM_DEBUG(zm_dump(__LINE__));
279 return xyzModem_timeout;
282 res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.crc2);
283 ZM_DEBUG(zm_save(xyz.crc2));
285 ZM_DEBUG(zm_dump(__LINE__));
286 return xyzModem_timeout;
289 ZM_DEBUG(zm_dump(__LINE__));
290 // Validate the message
291 if ((xyz.blk ^ xyz.cblk) != (unsigned char)0xFF) {
292 ZM_DEBUG(zm_dprintf("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk, (xyz.blk ^ xyz.cblk)));
293 ZM_DEBUG(zm_dump_buf(xyz.pkt, xyz.len));
295 return xyzModem_frame;
297 // Verify checksum/CRC
299 cksum = cyg_crc16(xyz.pkt, xyz.len);
300 if (cksum != ((xyz.crc1 << 8) | xyz.crc2)) {
301 ZM_DEBUG(zm_dprintf("CRC error - recvd: %02x%02x, computed: %x\n",
302 xyz.crc1, xyz.crc2, cksum & 0xFFFF));
303 return xyzModem_cksum;
307 for (i = 0; i < xyz.len; i++) {
310 if (xyz.crc1 != (cksum & 0xFF)) {
311 ZM_DEBUG(zm_dprintf("Checksum error - recvd: %x, computed: %x\n", xyz.crc1, cksum & 0xFF));
312 return xyzModem_cksum;
315 // If we get here, the message passes [structural] muster
320 xyzModem_stream_open(connection_info_t *info, int *err)
322 int console_chan, stat=0;
323 int retries = xyzModem_MAX_RETRIES;
324 int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
326 // ZM_DEBUG(zm_out = zm_out_start);
327 #ifdef xyzModem_zmodem
328 if (info->mode == xyzModem_zmodem) {
329 *err = xyzModem_noZmodem;
334 // Set up the I/O channel. Note: this allows for using a different port in the future
335 console_chan = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
336 if (info->chan >= 0) {
337 CYGACC_CALL_IF_SET_CONSOLE_COMM(info->chan);
339 CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan);
341 xyz.__chan = CYGACC_CALL_IF_CONSOLE_PROCS();
342 CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan);
343 CYGACC_COMM_IF_CONTROL(*xyz.__chan, __COMMCTL_SET_TIMEOUT, xyzModem_CHAR_TIMEOUT);
348 xyz.mode = info->mode;
349 xyz.total_retries = 0;
353 #ifdef USE_YMODEM_LENGTH
358 CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
360 if (xyz.mode == xyzModem_xmodem) {
361 // X-modem doesn't have an information header - exit here
366 while (retries-- > 0) {
367 stat = xyzModem_get_hdr();
369 // Y-modem file information header
371 #ifdef USE_YMODEM_LENGTH
375 parse_num((char *)xyz.bufp, &xyz.file_length, NULL, " ");
377 // The rest of the file name data block quietly discarded
384 if (stat == xyzModem_timeout) {
385 if (--crc_retries <= 0) xyz.crc_mode = false;
386 CYGACC_CALL_IF_DELAY_US(5*100000); // Extra delay for startup
387 CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
389 ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
391 if (stat == xyzModem_cancel) {
396 ZM_DEBUG(zm_flush());
401 xyzModem_stream_read(void *buf, int size, int *err)
403 int stat, total, len;
407 stat = xyzModem_cancel;
408 // Try and get 'size' bytes into the buffer
409 while (!xyz.at_eof && (size > 0)) {
411 retries = xyzModem_MAX_RETRIES;
412 while (retries-- > 0) {
413 stat = xyzModem_get_hdr();
415 if (xyz.blk == xyz.next_blk) {
417 ZM_DEBUG(zm_dprintf("ACK block %d (%d)\n", xyz.blk, __LINE__));
418 xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
420 #if defined(xyzModem_zmodem) || defined(USE_YMODEM_LENGTH)
421 if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0) {
425 // Data blocks can be padded with ^Z (EOF) characters
426 // This code tries to detect and remove them
427 if ((xyz.bufp[xyz.len-1] == EOF) &&
428 (xyz.bufp[xyz.len-2] == EOF) &&
429 (xyz.bufp[xyz.len-3] == EOF)) {
430 while (xyz.len && (xyz.bufp[xyz.len-1] == EOF)) {
436 #ifdef USE_YMODEM_LENGTH
437 // See if accumulated length exceeds that of the file.
438 // If so, reduce size (i.e., cut out pad bytes)
439 // Only do this for Y-modem (and Z-modem should it ever
440 // be supported since it can fall back to Y-modem mode).
441 if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length) {
442 xyz.read_length += xyz.len;
443 if (xyz.read_length > xyz.file_length) {
444 xyz.len -= (xyz.read_length - xyz.file_length);
449 } else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF)) {
450 // Just re-ACK this so sender will get on with it
451 CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
452 continue; // Need new header
454 stat = xyzModem_sequence;
457 if (stat == xyzModem_cancel) {
460 if (stat == xyzModem_eof) {
461 CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
462 ZM_DEBUG(zm_dprintf("ACK (%d)\n", __LINE__));
463 if (xyz.mode == xyzModem_ymodem) {
464 CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
466 ZM_DEBUG(zm_dprintf("Reading Final Header\n"));
467 stat = xyzModem_get_hdr();
468 CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
469 ZM_DEBUG(zm_dprintf("FINAL ACK (%d)\n", __LINE__));
474 CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
476 ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
484 // Don't "read" data from the EOF protocol package
487 if (size < len) len = size;
488 memcpy(buf, xyz.bufp, len);
490 buf = (char *)buf + len;
500 xyzModem_stream_close(int *err)
502 diag_printf("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
503 xyz.crc_mode ? "CRC" : "Cksum",
504 xyz.total_SOH, xyz.total_STX, xyz.total_CAN,
506 // ZM_DEBUG(zm_flush());
509 // Need to be able to clean out the input buffer, so have to take the
511 void xyzModem_stream_terminate(bool abort, int (*getc)(void))
516 ZM_DEBUG(zm_dprintf("!!!! TRANSFER ABORT !!!!\n"));
518 case xyzModem_xmodem:
519 case xyzModem_ymodem:
520 // The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal
521 // number of Backspaces is a friendly way to get the other end to abort.
522 CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
523 CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
524 CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
525 CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
526 CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
527 CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
528 CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
529 CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
530 // Now consume the rest of what's waiting on the line.
531 ZM_DEBUG(zm_dprintf("Flushing serial line.\n"));
535 #ifdef xyzModem_zmodem
536 case xyzModem_zmodem:
537 // Might support it some day I suppose.
542 ZM_DEBUG(zm_dprintf("Engaging cleanup mode...\n"));
543 // Consume any trailing crap left in the inbuffer from
544 // previous recieved blocks. Since very few files are an exact multiple
545 // of the transfer block size, there will almost always be some gunk here.
546 // If we don't eat it now, RedBoot will think the user typed it.
547 ZM_DEBUG(zm_dprintf("Trailing gunk:\n"));
548 while ((c = (*getc)()) > -1) ;
549 ZM_DEBUG(zm_dprintf("\n"));
550 // Make a small delay to give terminal programs like minicom
551 // time to get control again after their file transfer program
553 CYGACC_CALL_IF_DELAY_US((cyg_int32)250000);
558 xyzModem_error(int err)
561 case xyzModem_access:
562 return "Can't access file";
563 case xyzModem_noZmodem:
564 return "Sorry, zModem not available yet";
565 case xyzModem_timeout:
568 return "End of file";
569 case xyzModem_cancel:
572 return "Invalid framing";
574 return "CRC/checksum error";
575 case xyzModem_sequence:
576 return "Block sequence error";
578 return "Unknown error";
585 GETC_IO_FUNCS(xyzModem_io, xyzModem_stream_open, xyzModem_stream_close,
586 xyzModem_stream_terminate, xyzModem_stream_read, xyzModem_error);
587 RedBoot_load(xmodem, xyzModem_io, false, false, xyzModem_xmodem);
588 RedBoot_load(ymodem, xyzModem_io, false, false, xyzModem_ymodem);