source: SVN/cambria/redboot/packages/redboot/current/src/xyzModem.c @ 1

Last change on this file since 1 was 1, checked in by Tim Harvey, 2 years ago

restored latest version of files from server backup

Signed-off-by: Tim Harvey <tharvey@…>

File size: 18.2 KB
Line 
1//==========================================================================
2//
3//      xyzModem.c
4//
5//      RedBoot stream handler for xyzModem protocol
6//
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
13//
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.
17//
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
21// for more details.
22//
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.
26//
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.
33//
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.
36//
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####
43//
44// Author(s):    gthomas
45// Contributors: gthomas, tsmith, Yoshinori Sato
46// Date:         2000-07-14
47// Purpose:     
48// Description: 
49//             
50// This code is part of RedBoot (tm).
51//
52//####DESCRIPTIONEND####
53//
54//==========================================================================
55
56#include <redboot.h>
57#include <xyzModem.h>
58
59// Assumption - run xyzModem protocol over the console port
60
61// Values magic to the protocol
62#define SOH 0x01
63#define STX 0x02
64#define EOT 0x04
65#define ACK 0x06
66#define BSP 0x08
67#define NAK 0x15
68#define CAN 0x18
69#define EOF 0x1A  // ^Z for DOS officionados
70
71#define nUSE_YMODEM_LENGTH
72
73// Data & state local to the protocol
74static struct {
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;
84#endif
85} xyz;
86
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
91
92#ifdef DEBUG
93#ifndef USE_SPRINTF
94//
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
97// messages.
98//
99static int
100zm_dprintf(char *fmt, ...)
101{
102    int cur_console;
103    va_list args;
104
105    va_start(args, 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);
110}
111
112static void
113zm_flush(void)
114{
115}
116
117#else
118//
119// Note: this debug setup works by storing the strings in a fixed buffer
120//
121static char *zm_out = (char *)0x00380000;
122static char *zm_out_start = (char *)0x00380000;
123
124static int
125zm_dprintf(char *fmt, ...)
126{
127    int len;
128    va_list args;
129
130    va_start(args, fmt);
131    len = diag_vsprintf(zm_out, fmt, args);
132    zm_out += len;
133    return len;
134}
135
136static void
137zm_flush(void)
138{
139    char *p = zm_out_start;
140    while (*p) mon_write_char(*p++);
141    zm_out = zm_out_start;
142}
143#endif
144
145static void
146zm_dump_buf(void *buf, int len)
147{
148    diag_vdump_buf_with_offset(zm_dprintf, buf, len, 0);
149}
150
151static unsigned char zm_buf[2048];
152static unsigned char *zm_bp;
153
154static void
155zm_new(void)
156{
157    zm_bp = zm_buf;
158}
159
160static void
161zm_save(unsigned char c)
162{
163    *zm_bp++ = c;
164}
165
166static void
167zm_dump(int line)
168{
169    zm_dprintf("Packet at line: %d\n", line);
170    zm_dump_buf(zm_buf, zm_bp-zm_buf);
171}
172
173#define ZM_DEBUG(x) x
174#else
175#define ZM_DEBUG(x)
176#endif
177
178// Wait for the line to go idle
179static void
180xyzModem_flush(void)
181{
182    int res;
183    char c;
184    while (true) {
185        res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c);
186        if (!res) return;
187    }
188}
189
190static int
191xyzModem_get_hdr(void)
192{
193    char c;
194    int res;
195    bool hdr_found = false;
196    int i, can_total, hdr_chars;
197    unsigned short cksum;
198
199    ZM_DEBUG(zm_new());
200    // Find the start of a header
201    can_total = 0;
202    hdr_chars = 0;
203
204    if (xyz.tx_ack) {
205        CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
206        xyz.tx_ack = false;
207    }
208    while (!hdr_found) {
209        res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c);
210        ZM_DEBUG(zm_save(c));
211        if (res) {
212            hdr_chars++;
213            switch (c) {
214            case SOH:
215                xyz.total_SOH++;
216            case STX:
217                if (c == STX) xyz.total_STX++;
218                hdr_found = true;
219                break;
220            case CAN:
221                xyz.total_CAN++;
222                ZM_DEBUG(zm_dump(__LINE__));
223                if (++can_total == xyzModem_CAN_COUNT) {
224                    return xyzModem_cancel;
225                } else {
226                    // Wait for multiple CAN to avoid early quits
227                    break;
228                }
229            case EOT:
230                // EOT only supported if no noise
231                if (hdr_chars == 1) {
232                    CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
233                    ZM_DEBUG(zm_dprintf("ACK on EOT #%d\n", __LINE__));
234                    ZM_DEBUG(zm_dump(__LINE__));
235                    return xyzModem_eof;
236                }
237            default:
238                // Ignore, waiting for start of header
239                ;
240            }
241        } else {
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;
247        }
248    }
249
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));
253    if (!res) {
254        ZM_DEBUG(zm_dump(__LINE__));
255        return xyzModem_timeout;
256    }
257    res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.cblk);
258    ZM_DEBUG(zm_save(xyz.cblk));
259    if (!res) {
260        ZM_DEBUG(zm_dump(__LINE__));
261        return xyzModem_timeout;
262    }
263    xyz.len = (c == SOH) ? 128 : 1024;
264    xyz.bufp = xyz.pkt;
265    for (i = 0;  i < xyz.len;  i++) {
266        res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c);
267        ZM_DEBUG(zm_save(c));
268        if (res) {
269            xyz.pkt[i] = c;
270        } else {
271            ZM_DEBUG(zm_dump(__LINE__));
272            return xyzModem_timeout;
273        }
274    }
275    res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.crc1);
276    ZM_DEBUG(zm_save(xyz.crc1));
277    if (!res) {
278        ZM_DEBUG(zm_dump(__LINE__));
279        return xyzModem_timeout;
280    }
281    if (xyz.crc_mode) {
282        res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.crc2);
283        ZM_DEBUG(zm_save(xyz.crc2));
284        if (!res) {
285            ZM_DEBUG(zm_dump(__LINE__));
286            return xyzModem_timeout;
287        }
288    }
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));
294        xyzModem_flush();
295        return xyzModem_frame;
296    }
297    // Verify checksum/CRC
298    if (xyz.crc_mode) {
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;
304        }
305    } else {
306        cksum = 0;
307        for (i = 0;  i < xyz.len;  i++) {
308            cksum += xyz.pkt[i];
309        }
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;
313        }
314    }
315    // If we get here, the message passes [structural] muster
316    return 0;
317}
318
319int 
320xyzModem_stream_open(connection_info_t *info, int *err)
321{
322    int console_chan, stat;
323    int retries = xyzModem_MAX_RETRIES;
324    int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
325
326//    ZM_DEBUG(zm_out = zm_out_start);
327#ifdef xyzModem_zmodem
328    if (info->mode == xyzModem_zmodem) {
329        *err = xyzModem_noZmodem;
330        return -1;
331    }
332#endif
333
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);
338    } else {
339        CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan);
340    }
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);
344    xyz.len = 0;
345    xyz.crc_mode = true;
346    xyz.at_eof = false;
347    xyz.tx_ack = false;
348    xyz.mode = info->mode;
349    xyz.total_retries = 0;
350    xyz.total_SOH = 0;
351    xyz.total_STX = 0;
352    xyz.total_CAN = 0;
353#ifdef USE_YMODEM_LENGTH
354    xyz.read_length = 0;
355    xyz.file_length = 0;
356#endif
357   
358    CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
359
360    if (xyz.mode == xyzModem_xmodem) {
361            // X-modem doesn't have an information header - exit here
362            xyz.next_blk = 1;
363            return 0;
364    }
365
366    while (retries-- > 0) {
367        stat = xyzModem_get_hdr();
368        if (stat == 0) {
369            // Y-modem file information header
370            if (xyz.blk == 0) {
371#ifdef USE_YMODEM_LENGTH
372                // skip filename
373                while (*xyz.bufp++);
374                // get the length
375                parse_num(xyz.bufp, &xyz.file_length, NULL, " ");
376#endif
377                // The rest of the file name data block quietly discarded
378                xyz.tx_ack = true;
379            }
380            xyz.next_blk = 1;
381            xyz.len = 0;
382            return 0;
383        } else 
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));
388            xyz.total_retries++;
389            ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
390        }
391        if (stat == xyzModem_cancel) {
392            break;
393        }
394    }
395    *err = stat;
396    ZM_DEBUG(zm_flush());
397    return -1;
398}
399
400int 
401xyzModem_stream_read(char *buf, int size, int *err)
402{
403    int stat, total, len;
404    int retries;
405
406    total = 0;
407    stat = xyzModem_cancel;
408    // Try and get 'size' bytes into the buffer
409    while (!xyz.at_eof && (size > 0)) {
410        if (xyz.len == 0) {
411            retries = xyzModem_MAX_RETRIES;
412            while (retries-- > 0) {
413                stat = xyzModem_get_hdr();
414                if (stat == 0) {
415                    if (xyz.blk == xyz.next_blk) {
416                        xyz.tx_ack = true;
417                        ZM_DEBUG(zm_dprintf("ACK block %d (%d)\n", xyz.blk, __LINE__));
418                        xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
419                        // Data blocks can be padded with ^Z (EOF) characters
420                        // This code tries to detect and remove them
421#ifdef xyzModem_zmodem
422                        if (xyz.mode != xyzModem_zmodem) {
423#else
424                        if (1) {
425#endif
426                            if ((xyz.bufp[xyz.len-1] == EOF) &&
427                                (xyz.bufp[xyz.len-2] == EOF) &&
428                                (xyz.bufp[xyz.len-3] == EOF)) {
429                                while (xyz.len && (xyz.bufp[xyz.len-1] == EOF)) {
430                                    xyz.len--;
431                                }
432                            }
433                        }
434
435#ifdef USE_YMODEM_LENGTH
436                        // See if accumulated length exceeds that of the file.
437                        // If so, reduce size (i.e., cut out pad bytes)
438                        // Only do this for Y-modem (and Z-modem should it ever
439                        // be supported since it can fall back to Y-modem mode).
440                        if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length) {
441                            xyz.read_length += xyz.len;
442                            if (xyz.read_length > xyz.file_length) {
443                                xyz.len -= (xyz.read_length - xyz.file_length);
444                            }
445                        }
446#endif
447                        break;
448                    } else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF)) {
449                        // Just re-ACK this so sender will get on with it
450                        CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
451                        continue;  // Need new header
452                    } else {
453                        stat = xyzModem_sequence;
454                    }
455                }
456                if (stat == xyzModem_cancel) {
457                    break;
458                }
459                if (stat == xyzModem_eof) {
460                    CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
461                    ZM_DEBUG(zm_dprintf("ACK (%d)\n", __LINE__));
462                    if (xyz.mode == xyzModem_ymodem) {
463                        CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
464                        xyz.total_retries++;
465                        ZM_DEBUG(zm_dprintf("Reading Final Header\n"));
466                        stat = xyzModem_get_hdr();                       
467                        CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
468                        ZM_DEBUG(zm_dprintf("FINAL ACK (%d)\n", __LINE__));
469                    }
470                    xyz.at_eof = true;
471                    break;
472                }
473                CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
474                xyz.total_retries++;
475                ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
476            }
477            if (stat < 0) {
478                *err = stat;
479                xyz.len = -1;
480                return total;
481            }
482        }
483        // Don't "read" data from the EOF protocol package
484        if (!xyz.at_eof) {
485            len = xyz.len;
486            if (size < len) len = size;
487            memcpy(buf, xyz.bufp, len);
488            size -= len;
489            buf += len;
490            total += len;
491            xyz.len -= len;
492            xyz.bufp += len;
493        }
494    }
495    return total;
496}
497
498void
499xyzModem_stream_close(int *err)
500{
501    diag_printf("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n", 
502                xyz.crc_mode ? "CRC" : "Cksum",
503                xyz.total_SOH, xyz.total_STX, xyz.total_CAN,
504                xyz.total_retries);
505//    ZM_DEBUG(zm_flush());
506}
507
508// Need to be able to clean out the input buffer, so have to take the
509// getc
510void xyzModem_stream_terminate(bool abort, int (*getc)(void))
511{
512  int c;
513
514  if (abort) {
515      ZM_DEBUG(zm_dprintf("!!!! TRANSFER ABORT !!!!\n"));
516      switch (xyz.mode) {
517        case xyzModem_xmodem:
518        case xyzModem_ymodem:
519          // The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal
520          // number of Backspaces is a friendly way to get the other end to abort.
521          CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
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,BSP);
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          // Now consume the rest of what's waiting on the line.
530          ZM_DEBUG(zm_dprintf("Flushing serial line.\n"));
531          xyzModem_flush();
532          xyz.at_eof = true;
533        break;
534#ifdef xyzModem_zmodem
535        case xyzModem_zmodem:
536          // Might support it some day I suppose.
537#endif
538        break;
539      }
540  } else {
541      ZM_DEBUG(zm_dprintf("Engaging cleanup mode...\n"));
542      // Consume any trailing crap left in the inbuffer from
543      // previous recieved blocks. Since very few files are an exact multiple
544      // of the transfer block size, there will almost always be some gunk here.
545      // If we don't eat it now, RedBoot will think the user typed it.
546      ZM_DEBUG(zm_dprintf("Trailing gunk:\n"));
547      while ((c = (*getc)()) > -1) ;
548      ZM_DEBUG(zm_dprintf("\n"));
549      // Make a small delay to give terminal programs like minicom
550      // time to get control again after their file transfer program
551      // exits.
552      CYGACC_CALL_IF_DELAY_US((cyg_int32)250000);
553  }
554}
555
556char *
557xyzModem_error(int err)
558{
559    switch (err) {
560    case xyzModem_access:
561        return "Can't access file";
562        break;
563    case xyzModem_noZmodem:
564        return "Sorry, zModem not available yet";
565        break;
566    case xyzModem_timeout:
567        return "Timed out";
568        break;
569    case xyzModem_eof:
570        return "End of file";
571        break;
572    case xyzModem_cancel:
573        return "Cancelled";
574        break;
575    case xyzModem_frame:
576        return "Invalid framing";
577        break;
578    case xyzModem_cksum:
579        return "CRC/checksum error";
580        break;
581    case xyzModem_sequence:
582        return "Block sequence error";
583        break;
584    default:
585        return "Unknown error";
586        break;
587    }
588}
589
590//
591// RedBoot interface
592//
593GETC_IO_FUNCS(xyzModem_io, xyzModem_stream_open, xyzModem_stream_close,
594              xyzModem_stream_terminate, xyzModem_stream_read, xyzModem_error);
595RedBoot_load(xmodem, xyzModem_io, false, false, xyzModem_xmodem);
596RedBoot_load(ymodem, xyzModem_io, false, false, xyzModem_ymodem);
Note: See TracBrowser for help on using the repository browser.