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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [redboot/] [current/] [src/] [xyzModem.c] - Blame information for rev 810

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

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