2 *==========================================================================
6 * RedBoot stream handler for xyzModem protocol
8 *==========================================================================
9 *####ECOSGPLCOPYRIGHTBEGIN####
10 * -------------------------------------------
11 * This file is part of eCos, the Embedded Configurable Operating System.
12 * Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
13 * Copyright (C) 2002 Gary Thomas
15 * eCos is free software; you can redistribute it and/or modify it under
16 * the terms of the GNU General Public License as published by the Free
17 * Software Foundation; either version 2 or (at your option) any later version.
19 * eCos is distributed in the hope that it will be useful, but WITHOUT ANY
20 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 * You should have received a copy of the GNU General Public License along
25 * with eCos; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
28 * As a special exception, if other files instantiate templates or use macros
29 * or inline functions from this file, or you compile this file and link it
30 * with other works to produce a work based on this file, this file does not
31 * by itself cause the resulting work to be covered by the GNU General Public
32 * License. However the source code for this file must still be made available
33 * in accordance with section (3) of the GNU General Public License.
35 * This exception does not invalidate any other reasons why a work based on
36 * this file might be covered by the GNU General Public License.
38 * Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
39 * at http: *sources.redhat.com/ecos/ecos-license/
40 * -------------------------------------------
41 *####ECOSGPLCOPYRIGHTEND####
42 *==========================================================================
43 *#####DESCRIPTIONBEGIN####
46 * Contributors: gthomas, tsmith, Yoshinori Sato
51 * This code is part of RedBoot (tm).
53 *####DESCRIPTIONEND####
55 *==========================================================================
62 /* Assumption - run xyzModem protocol over the console port */
64 /* Values magic to the protocol */
72 #define EOF 0x1A /* ^Z for DOS officionados */
74 #define USE_YMODEM_LENGTH
76 /* Data & state local to the protocol */
79 unsigned char pkt[1024], *bufp;
80 unsigned char blk,cblk,crc1,crc2;
81 unsigned char next_blk; /* Expected block */
82 int len, mode, total_retries;
83 int total_SOH, total_STX, total_CAN;
84 unsigned int crc_mode, at_eof, tx_ack;
85 #ifdef USE_YMODEM_LENGTH
86 unsigned long file_length, read_length;
90 #define xyzModem_CHAR_TIMEOUT 2000 /* 2 seconds */
91 #define xyzModem_MAX_RETRIES 20
92 #define xyzModem_MAX_RETRIES_WITH_CRC 10
93 #define xyzModem_CAN_COUNT 3 /* Wait for 3 CAN before quitting */
95 int comm_if_getc_tout(char *c)
97 unsigned long counter=0;
100 while(!tstc() && (counter < xyzModem_CHAR_TIMEOUT*1000/DELAY)){
113 /* Validate a hex character */
114 inline static unsigned int _is_hex(char c)
116 return(((c >= '0') && (c <= '9')) ||
117 ((c >= 'A') && (c <= 'F')) ||
118 ((c >= 'a') && (c <= 'f')));
121 /* Convert a single hex nibble */
122 inline static int _from_hex(char c)
126 if((c >= '0') && (c <= '9')){
128 } else if((c >= 'a') && (c <= 'f')){
129 ret = (c - 'a' + 0x0a);
130 } else if((c >= 'A') && (c <= 'F')){
131 ret = (c - 'A' + 0x0A);
137 /* Convert a character to lower case */
138 inline static char _tolower(char c)
140 if((c >= 'A') && (c <= 'Z')){
147 /* Parse (scan) a number */
148 unsigned int parse_num(char *s, unsigned long *val, char **es, char *delim)
150 unsigned int first = 1;
153 unsigned long result = 0;
160 if(first && (s[0] == '0') && (_tolower(s[1]) == 'x')){
168 if(_is_hex(c) && ((digit = _from_hex(c)) < radix)){
170 result = (result * radix) + digit;
172 if(delim != (char *)0){
173 /* See if this character is one of the delimiters */
176 while(*dp && (c != *dp))
179 /* Found a good delimiter */
184 /* Malformatted number */
191 if(es != (char **)0){
198 /* Wait for the line to go idle */
199 static void xyzModem_flush(void)
205 res = comm_if_getc_tout(&c);
211 static int xyzModem_get_hdr(void)
215 unsigned int hdr_found = 0;
216 int i, can_total, hdr_chars;
217 unsigned short cksum;
219 /* Find the start of a header */
229 res = comm_if_getc_tout(&c);
246 if(++can_total == xyzModem_CAN_COUNT){
247 return xyzModem_cancel;
249 /* Wait for multiple CAN to avoid early quits */
253 /* EOT only supported if no noise */
259 /* Ignore, waiting for start of header */
263 /* Data stream timed out */
264 xyzModem_flush(); /* Toss any current input */
266 return xyzModem_timeout;
270 /* Header found, now read the data */
271 res = comm_if_getc_tout((char *)&xyz.blk);
273 return xyzModem_timeout;
276 res = comm_if_getc_tout((char *)&xyz.cblk);
278 return xyzModem_timeout;
281 xyz.len = (c == SOH) ? 128 : 1024;
283 for(i = 0; i < xyz.len; i++){
284 res = comm_if_getc_tout(&c);
288 return xyzModem_timeout;
292 res = comm_if_getc_tout((char *)&xyz.crc1);
294 return xyzModem_timeout;
298 res = comm_if_getc_tout((char *)&xyz.crc2);
300 return xyzModem_timeout;
304 /* Validate the message */
305 if((xyz.blk ^ xyz.cblk) != (unsigned char)0xFF){
307 return xyzModem_frame;
310 /* Verify checksum/CRC */
312 cksum = cyg_crc16(xyz.pkt, xyz.len);
313 if(cksum != ((xyz.crc1 << 8) | xyz.crc2)){
314 return xyzModem_cksum;
318 for(i = 0; i < xyz.len; i++){
322 if(xyz.crc1 != (cksum & 0xFF)){
323 return xyzModem_cksum;
327 /* If we get here, the message passes [structural] muster */
331 int xyzModem_stream_open(connection_info_t *info, int *err)
334 int retries = xyzModem_MAX_RETRIES;
335 int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
337 #ifdef xyzModem_zmodem
338 if(info->mode == xyzModem_zmodem){
339 *err = xyzModem_noZmodem;
351 xyz.mode = info->mode;
352 xyz.total_retries = 0;
356 #ifdef USE_YMODEM_LENGTH
361 putc(xyz.crc_mode ? 'C' : NAK);
363 if(xyz.mode == xyzModem_xmodem){
364 /* X-modem doesn't have an information header - exit here */
369 while(retries-- > 0){
370 stat = xyzModem_get_hdr();
372 /* Y-modem file information header */
374 #ifdef USE_YMODEM_LENGTH
379 parse_num((char *)xyz.bufp, &xyz.file_length, NULL, " ");
381 /* The rest of the file name data block quietly discarded */
389 } else if(stat == xyzModem_timeout){
390 if(--crc_retries <= 0)
393 /* Extra delay for startup */
396 putc(xyz.crc_mode ? 'C' : NAK);
400 if(stat == xyzModem_cancel){
410 int xyzModem_stream_read(char *buf, int size, int *err)
412 int stat, total, len;
416 stat = xyzModem_cancel;
418 /* Try and get 'size' bytes into the buffer */
419 while(!xyz.at_eof && (size > 0)){
421 retries = xyzModem_MAX_RETRIES;
423 while(retries-- > 0){
424 stat = xyzModem_get_hdr();
426 if(xyz.blk == xyz.next_blk){
428 xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
430 #if defined(xyzModem_zmodem) || defined(USE_YMODEM_LENGTH)
431 if(xyz.mode == xyzModem_xmodem || xyz.file_length == 0){
435 /* Data blocks can be padded with ^Z (EOF) characters */
436 /* This code tries to detect and remove them */
437 if( (xyz.bufp[xyz.len-1] == EOF) &&
438 (xyz.bufp[xyz.len-2] == EOF) &&
439 (xyz.bufp[xyz.len-3] == EOF) ){
440 while(xyz.len && (xyz.bufp[xyz.len-1] == EOF)){
446 #ifdef USE_YMODEM_LENGTH
448 * See if accumulated length exceeds that of the file.
449 * If so, reduce size (i.e., cut out pad bytes)
450 * Only do this for Y-modem (and Z-modem should it ever
451 * be supported since it can fall back to Y-modem mode).
453 if(xyz.mode != xyzModem_xmodem && 0 != xyz.file_length){
454 xyz.read_length += xyz.len;
455 if(xyz.read_length > xyz.file_length){
456 xyz.len -= (xyz.read_length - xyz.file_length);
461 } else if(xyz.blk == ((xyz.next_blk - 1) & 0xFF)){
462 /* Just re-ACK this so sender will get on with it */
464 continue; /* Need new header */
466 stat = xyzModem_sequence;
470 if(stat == xyzModem_cancel){
474 if(stat == xyzModem_eof){
477 if(xyz.mode == xyzModem_ymodem){
478 putc(xyz.crc_mode ? 'C' : NAK);
480 stat = xyzModem_get_hdr();
489 putc(xyz.crc_mode ? 'C' : NAK);
502 /* Don't "read" data from the EOF protocol package */
508 memcpy(buf, xyz.bufp, len);
520 void xyzModem_stream_close(int *err)
522 printf("\n\nxyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
523 xyz.crc_mode ? "CRC" : "Cksum",
524 xyz.total_SOH, xyz.total_STX, xyz.total_CAN,
528 /* Need to be able to clean out the input buffer, so have to take the */
530 void xyzModem_stream_terminate(unsigned int abort, int (*getc)(void))
536 case xyzModem_xmodem:
537 case xyzModem_ymodem:
538 /* The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal */
539 /* number of Backspaces is a friendly way to get the other end to abort. */
548 /* Now consume the rest of what's waiting on the line. */
552 #ifdef xyzModem_zmodem
553 case xyzModem_zmodem:
554 /* Might support it some day I suppose. */
560 * Consume any trailing crap left in the inbuffer from
561 * previous recieved blocks. Since very few files are an exact multiple
562 * of the transfer block size, there will almost always be some gunk here.
563 * If we don't eat it now, RedBoot will think the user typed it.
566 while((c = (*getc)()) > -1);
568 * Make a small delay to give terminal programs like minicom
569 * time to get control again after their file transfer program
576 char *xyzModem_error(int err)
579 case xyzModem_access:
580 return "can't access file";
582 case xyzModem_noZmodem:
583 return "sorry, zModem not available yet";
585 case xyzModem_timeout:
589 return "end of file";
591 case xyzModem_cancel:
595 return "invalid framing";
598 return "CRC/checksum error";
600 case xyzModem_sequence:
601 return "block sequence error";
604 return "unknown error";