OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [ecos-2.0/] [packages/] [redboot/] [v2_0/] [src/] [xyzModem.c] - Blame information for rev 1773

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1254 phoenix
//==========================================================================
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
74
static 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
//
99
static int
100
zm_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
 
112
static void
113
zm_flush(void)
114
{
115
}
116
 
117
#else
118
//
119
// Note: this debug setup works by storing the strings in a fixed buffer
120
//
121
static char *zm_out = (char *)0x00380000;
122
static char *zm_out_start = (char *)0x00380000;
123
 
124
static int
125
zm_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
 
136
static void
137
zm_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
 
145
static void
146
zm_dump_buf(void *buf, int len)
147
{
148
    diag_vdump_buf_with_offset(zm_dprintf, buf, len, 0);
149
}
150
 
151
static unsigned char zm_buf[2048];
152
static unsigned char *zm_bp;
153
 
154
static void
155
zm_new(void)
156
{
157
    zm_bp = zm_buf;
158
}
159
 
160
static void
161
zm_save(unsigned char c)
162
{
163
    *zm_bp++ = c;
164
}
165
 
166
static void
167
zm_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
179
static void
180
xyzModem_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
 
190
static int
191
xyzModem_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
 
319
int
320
xyzModem_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
    while (retries-- > 0) {
359
        stat = xyzModem_get_hdr();
360
        if (stat == 0) {
361
            // Y-modem file information header
362
            if (xyz.blk == 0) {
363
#ifdef USE_YMODEM_LENGTH
364
                // skip filename
365
                while (*xyz.bufp++);
366
                // get the length
367
                parse_num(xyz.bufp, &xyz.file_length, NULL, " ");
368
#endif
369
                // The rest of the file name data block quietly discarded
370
                xyz.tx_ack = true;
371
            }
372
            xyz.next_blk = 1;
373
            xyz.len = 0;
374
            return 0;
375
        } else
376
        if (stat == xyzModem_timeout) {
377
            if (--crc_retries <= 0) xyz.crc_mode = false;
378
            CYGACC_CALL_IF_DELAY_US(5*100000);   // Extra delay for startup
379
            CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
380
            xyz.total_retries++;
381
            ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
382
        }
383
        if (stat == xyzModem_cancel) {
384
            break;
385
        }
386
    }
387
    *err = stat;
388
    ZM_DEBUG(zm_flush());
389
    return -1;
390
}
391
 
392
int
393
xyzModem_stream_read(char *buf, int size, int *err)
394
{
395
    int stat, total, len;
396
    int retries;
397
 
398
    total = 0;
399
    stat = xyzModem_cancel;
400
    // Try and get 'size' bytes into the buffer
401
    while (!xyz.at_eof && (size > 0)) {
402
        if (xyz.len == 0) {
403
            retries = xyzModem_MAX_RETRIES;
404
            while (retries-- > 0) {
405
                stat = xyzModem_get_hdr();
406
                if (stat == 0) {
407
                    if (xyz.blk == xyz.next_blk) {
408
                        xyz.tx_ack = true;
409
                        ZM_DEBUG(zm_dprintf("ACK block %d (%d)\n", xyz.blk, __LINE__));
410
                        xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
411
                        // Data blocks can be padded with ^Z (EOF) characters
412
                        // This code tries to detect and remove them
413
#ifdef xyzModem_zmodem
414
                        if (xyz.mode != xyzModem_zmodem) {
415
#else
416
                        if (1) {
417
#endif
418
                            if ((xyz.bufp[xyz.len-1] == EOF) &&
419
                                (xyz.bufp[xyz.len-2] == EOF) &&
420
                                (xyz.bufp[xyz.len-3] == EOF)) {
421
                                while (xyz.len && (xyz.bufp[xyz.len-1] == EOF)) {
422
                                    xyz.len--;
423
                                }
424
                            }
425
                        }
426
 
427
#ifdef USE_YMODEM_LENGTH
428
                        // See if accumulated length exceeds that of the file.
429
                        // If so, reduce size (i.e., cut out pad bytes)
430
                        // Only do this for Y-modem (and Z-modem should it ever
431
                        // be supported since it can fall back to Y-modem mode).
432
                        if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length) {
433
                            xyz.read_length += xyz.len;
434
                            if (xyz.read_length > xyz.file_length) {
435
                                xyz.len -= (xyz.read_length - xyz.file_length);
436
                            }
437
                        }
438
#endif
439
                        break;
440
                    } else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF)) {
441
                        // Just re-ACK this so sender will get on with it
442
                        CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
443
                        continue;  // Need new header
444
                    } else {
445
                        stat = xyzModem_sequence;
446
                    }
447
                }
448
                if (stat == xyzModem_cancel) {
449
                    break;
450
                }
451
                if (stat == xyzModem_eof) {
452
                    CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
453
                    ZM_DEBUG(zm_dprintf("ACK (%d)\n", __LINE__));
454
                    if (xyz.mode == xyzModem_ymodem) {
455
                        CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
456
                        xyz.total_retries++;
457
                        ZM_DEBUG(zm_dprintf("Reading Final Header\n"));
458
                        stat = xyzModem_get_hdr();
459
                        CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
460
                        ZM_DEBUG(zm_dprintf("FINAL ACK (%d)\n", __LINE__));
461
                    }
462
                    xyz.at_eof = true;
463
                    break;
464
                }
465
                CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
466
                xyz.total_retries++;
467
                ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
468
            }
469
            if (stat < 0) {
470
                *err = stat;
471
                xyz.len = -1;
472
                return total;
473
            }
474
        }
475
        // Don't "read" data from the EOF protocol package
476
        if (!xyz.at_eof) {
477
            len = xyz.len;
478
            if (size < len) len = size;
479
            memcpy(buf, xyz.bufp, len);
480
            size -= len;
481
            buf += len;
482
            total += len;
483
            xyz.len -= len;
484
            xyz.bufp += len;
485
        }
486
    }
487
    return total;
488
}
489
 
490
void
491
xyzModem_stream_close(int *err)
492
{
493
    diag_printf("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
494
                xyz.crc_mode ? "CRC" : "Cksum",
495
                xyz.total_SOH, xyz.total_STX, xyz.total_CAN,
496
                xyz.total_retries);
497
//    ZM_DEBUG(zm_flush());
498
}
499
 
500
// Need to be able to clean out the input buffer, so have to take the
501
// getc
502
void xyzModem_stream_terminate(bool abort, int (*getc)(void))
503
{
504
  int c;
505
 
506
  if (abort) {
507
      ZM_DEBUG(zm_dprintf("!!!! TRANSFER ABORT !!!!\n"));
508
      switch (xyz.mode) {
509
        case xyzModem_xmodem:
510
        case xyzModem_ymodem:
511
          // The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal
512
          // number of Backspaces is a friendly way to get the other end to abort.
513
          CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
514
          CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
515
          CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
516
          CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
517
          CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
518
          CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
519
          CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
520
          CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
521
          // Now consume the rest of what's waiting on the line.
522
          ZM_DEBUG(zm_dprintf("Flushing serial line.\n"));
523
          xyzModem_flush();
524
          xyz.at_eof = true;
525
        break;
526
#ifdef xyzModem_zmodem
527
        case xyzModem_zmodem:
528
          // Might support it some day I suppose.
529
#endif
530
        break;
531
      }
532
  } else {
533
      ZM_DEBUG(zm_dprintf("Engaging cleanup mode...\n"));
534
      // Consume any trailing crap left in the inbuffer from
535
      // previous recieved blocks. Since very few files are an exact multiple
536
      // of the transfer block size, there will almost always be some gunk here.
537
      // If we don't eat it now, RedBoot will think the user typed it.
538
      ZM_DEBUG(zm_dprintf("Trailing gunk:\n"));
539
      while ((c = (*getc)()) > -1) ;
540
      ZM_DEBUG(zm_dprintf("\n"));
541
      // Make a small delay to give terminal programs like minicom
542
      // time to get control again after their file transfer program
543
      // exits.
544
      CYGACC_CALL_IF_DELAY_US((cyg_int32)250000);
545
  }
546
}
547
 
548
char *
549
xyzModem_error(int err)
550
{
551
    switch (err) {
552
    case xyzModem_access:
553
        return "Can't access file";
554
        break;
555
    case xyzModem_noZmodem:
556
        return "Sorry, zModem not available yet";
557
        break;
558
    case xyzModem_timeout:
559
        return "Timed out";
560
        break;
561
    case xyzModem_eof:
562
        return "End of file";
563
        break;
564
    case xyzModem_cancel:
565
        return "Cancelled";
566
        break;
567
    case xyzModem_frame:
568
        return "Invalid framing";
569
        break;
570
    case xyzModem_cksum:
571
        return "CRC/checksum error";
572
        break;
573
    case xyzModem_sequence:
574
        return "Block sequence error";
575
        break;
576
    default:
577
        return "Unknown error";
578
        break;
579
    }
580
}
581
 
582
//
583
// RedBoot interface
584
//
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);

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.