]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/xyzModem.c
TX51 pre-release
[karo-tx-redboot.git] / packages / redboot / v2_0 / src / xyzModem.c
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 USE_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                                 }
226                                 // Wait for multiple CAN to avoid early quits
227                                 break;
228                         case EOT:
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__));
234                                         return xyzModem_eof;
235                                 }
236                                 break;
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=0;
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((char *)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
400 int
401 xyzModem_stream_read(void *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
420 #if defined(xyzModem_zmodem) || defined(USE_YMODEM_LENGTH)
421                                                 if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0) {
422 #else
423                                                 if (1) {
424 #endif
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)) {
431                                                                         xyz.len--;
432                                                                 }
433                                                         }
434                                                 }
435
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);
445                                                         }
446                                                 }
447 #endif
448                                                 break;
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
453                                         } else {
454                                                 stat = xyzModem_sequence;
455                                         }
456                                 }
457                                 if (stat == xyzModem_cancel) {
458                                         break;
459                                 }
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));
465                                                 xyz.total_retries++;
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__));
470                                         }
471                                         xyz.at_eof = true;
472                                         break;
473                                 }
474                                 CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
475                                 xyz.total_retries++;
476                                 ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
477                         }
478                         if (stat < 0) {
479                                 *err = stat;
480                                 xyz.len = -1;
481                                 return total;
482                         }
483                 }
484                 // Don't "read" data from the EOF protocol package
485                 if (!xyz.at_eof) {
486                         len = xyz.len;
487                         if (size < len) len = size;
488                         memcpy(buf, xyz.bufp, len);
489                         size -= len;
490                         buf = (char *)buf + len;
491                         total += len;
492                         xyz.len -= len;
493                         xyz.bufp += len;
494                 }
495         }
496         return total;
497 }
498
499 void
500 xyzModem_stream_close(int *err)
501 {
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,
505                                 xyz.total_retries);
506 //    ZM_DEBUG(zm_flush());
507 }
508
509 // Need to be able to clean out the input buffer, so have to take the
510 // getc
511 void xyzModem_stream_terminate(bool abort, int (*getc)(void))
512 {
513         int c;
514
515         if (abort) {
516                 ZM_DEBUG(zm_dprintf("!!!! TRANSFER ABORT !!!!\n"));
517                 switch (xyz.mode) {
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"));
532                         xyzModem_flush();
533                         xyz.at_eof = true;
534                         break;
535 #ifdef xyzModem_zmodem
536                 case xyzModem_zmodem:
537                         // Might support it some day I suppose.
538                         break;
539 #endif
540                 }
541         } else {
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
552                 // exits.
553                 CYGACC_CALL_IF_DELAY_US((cyg_int32)250000);
554         }
555 }
556
557 char *
558 xyzModem_error(int err)
559 {
560         switch (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:
566                 return "Timed out";
567         case xyzModem_eof:
568                 return "End of file";
569         case xyzModem_cancel:
570                 return "Cancelled";
571         case xyzModem_frame:
572                 return "Invalid framing";
573         case xyzModem_cksum:
574                 return "CRC/checksum error";
575         case xyzModem_sequence:
576                 return "Block sequence error";
577         default:
578                 return "Unknown error";
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);