]>
Commit | Line | Data |
---|---|---|
f2841d37 MK |
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 | #include <common.h> | |
56 | #include <xyzModem.h> | |
57 | #include <stdarg.h> | |
58 | #include <crc.h> | |
59 | ||
60 | // Assumption - run xyzModem protocol over the console port | |
61 | ||
62 | // Values magic to the protocol | |
63 | #define SOH 0x01 | |
64 | #define STX 0x02 | |
65 | #define EOT 0x04 | |
66 | #define ACK 0x06 | |
67 | #define BSP 0x08 | |
68 | #define NAK 0x15 | |
69 | #define CAN 0x18 | |
70 | #define EOF 0x1A // ^Z for DOS officionados | |
71 | ||
72 | #define USE_YMODEM_LENGTH | |
73 | ||
74 | // Data & state local to the protocol | |
75 | static struct { | |
76 | #ifdef REDBOOT | |
77 | hal_virtual_comm_table_t* __chan; | |
78 | #else | |
79 | int *__chan; | |
80 | #endif | |
81 | unsigned char pkt[1024], *bufp; | |
82 | unsigned char blk,cblk,crc1,crc2; | |
83 | unsigned char next_blk; // Expected block | |
84 | int len, mode, total_retries; | |
85 | int total_SOH, total_STX, total_CAN; | |
86 | bool crc_mode, at_eof, tx_ack; | |
87 | #ifdef USE_YMODEM_LENGTH | |
88 | unsigned long file_length, read_length; | |
89 | #endif | |
90 | } xyz; | |
91 | ||
92 | #define xyzModem_CHAR_TIMEOUT 2000 // 2 seconds | |
93 | #define xyzModem_MAX_RETRIES 20 | |
94 | #define xyzModem_MAX_RETRIES_WITH_CRC 10 | |
95 | #define xyzModem_CAN_COUNT 3 // Wait for 3 CAN before quitting | |
96 | ||
97 | ||
98 | #ifndef REDBOOT //SB | |
99 | typedef int cyg_int32; | |
100 | int CYGACC_COMM_IF_GETC_TIMEOUT (char chan,char *c) { | |
101 | #define DELAY 20 | |
102 | unsigned long counter=0; | |
103 | while (!tstc() && (counter < xyzModem_CHAR_TIMEOUT*1000/DELAY)) { | |
104 | udelay(DELAY); | |
105 | counter++; | |
106 | } | |
107 | if (tstc()) { | |
108 | *c=getc(); | |
109 | return 1; | |
110 | } | |
111 | return 0; | |
112 | } | |
113 | ||
114 | void CYGACC_COMM_IF_PUTC(char x,char y) { | |
115 | putc(y); | |
116 | } | |
117 | ||
118 | // Validate a hex character | |
119 | __inline__ static bool | |
120 | _is_hex(char c) | |
121 | { | |
122 | return (((c >= '0') && (c <= '9')) || | |
123 | ((c >= 'A') && (c <= 'F')) || | |
124 | ((c >= 'a') && (c <= 'f'))); | |
125 | } | |
126 | ||
127 | // Convert a single hex nibble | |
128 | __inline__ static int | |
129 | _from_hex(char c) | |
130 | { | |
131 | int ret = 0; | |
132 | ||
133 | if ((c >= '0') && (c <= '9')) { | |
134 | ret = (c - '0'); | |
135 | } else if ((c >= 'a') && (c <= 'f')) { | |
136 | ret = (c - 'a' + 0x0a); | |
137 | } else if ((c >= 'A') && (c <= 'F')) { | |
138 | ret = (c - 'A' + 0x0A); | |
139 | } | |
140 | return ret; | |
141 | } | |
142 | ||
143 | // Convert a character to lower case | |
144 | __inline__ static char | |
145 | _tolower(char c) | |
146 | { | |
147 | if ((c >= 'A') && (c <= 'Z')) { | |
148 | c = (c - 'A') + 'a'; | |
149 | } | |
150 | return c; | |
151 | } | |
152 | ||
153 | ||
154 | ||
155 | // | |
156 | // Parse (scan) a number | |
157 | // | |
158 | bool | |
159 | parse_num(char *s, unsigned long *val, char **es, char *delim) | |
160 | { | |
161 | bool first = true; | |
162 | int radix = 10; | |
163 | char c; | |
164 | unsigned long result = 0; | |
165 | int digit; | |
166 | ||
167 | while (*s == ' ') s++; | |
168 | while (*s) { | |
169 | if (first && (s[0] == '0') && (_tolower(s[1]) == 'x')) { | |
170 | radix = 16; | |
171 | s += 2; | |
172 | } | |
173 | first = false; | |
174 | c = *s++; | |
175 | if (_is_hex(c) && ((digit = _from_hex(c)) < radix)) { | |
176 | // Valid digit | |
177 | #ifdef CYGPKG_HAL_MIPS | |
178 | // FIXME: tx49 compiler generates 0x2539018 for MUL which | |
179 | // isn't any good. | |
180 | if (16 == radix) | |
181 | result = result << 4; | |
182 | else | |
183 | result = 10 * result; | |
184 | result += digit; | |
185 | #else | |
186 | result = (result * radix) + digit; | |
187 | #endif | |
188 | } else { | |
189 | if (delim != (char *)0) { | |
190 | // See if this character is one of the delimiters | |
191 | char *dp = delim; | |
192 | while (*dp && (c != *dp)) dp++; | |
193 | if (*dp) break; // Found a good delimiter | |
194 | } | |
195 | return false; // Malformatted number | |
196 | } | |
197 | } | |
198 | *val = result; | |
199 | if (es != (char **)0) { | |
200 | *es = s; | |
201 | } | |
202 | return true; | |
203 | } | |
204 | ||
205 | #endif | |
206 | ||
207 | #define USE_SPRINTF | |
208 | #ifdef DEBUG | |
209 | #ifndef USE_SPRINTF | |
210 | // | |
211 | // Note: this debug setup only works if the target platform has two serial ports | |
212 | // available so that the other one (currently only port 1) can be used for debug | |
213 | // messages. | |
214 | // | |
215 | static int | |
216 | zm_dprintf(char *fmt, ...) | |
217 | { | |
218 | int cur_console; | |
219 | va_list args; | |
220 | ||
221 | va_start(args, fmt); | |
222 | #ifdef REDBOOT | |
223 | cur_console = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); | |
224 | CYGACC_CALL_IF_SET_CONSOLE_COMM(1); | |
225 | #endif | |
226 | diag_vprintf(fmt, args); | |
227 | #ifdef REDBOOT | |
228 | CYGACC_CALL_IF_SET_CONSOLE_COMM(cur_console); | |
229 | #endif | |
230 | } | |
231 | ||
232 | static void | |
233 | zm_flush(void) | |
234 | { | |
235 | } | |
236 | ||
237 | #else | |
238 | // | |
239 | // Note: this debug setup works by storing the strings in a fixed buffer | |
240 | // | |
241 | #define FINAL | |
242 | #ifdef FINAL | |
243 | static char *zm_out = (char *)0x00380000; | |
244 | static char *zm_out_start = (char *)0x00380000; | |
245 | #else | |
246 | static char zm_buf[8192]; | |
247 | static char *zm_out=zm_buf; | |
248 | static char *zm_out_start = zm_buf; | |
249 | ||
250 | #endif | |
251 | static int | |
252 | zm_dprintf(char *fmt, ...) | |
253 | { | |
254 | int len; | |
255 | va_list args; | |
256 | ||
257 | va_start(args, fmt); | |
258 | len = diag_vsprintf(zm_out, fmt, args); | |
259 | zm_out += len; | |
260 | return len; | |
261 | } | |
262 | ||
263 | static void | |
264 | zm_flush(void) | |
265 | { | |
266 | char *p = zm_out_start; | |
267 | #ifdef REDBOOT | |
268 | while (*p) mon_write_char(*p++); | |
269 | #endif | |
270 | zm_out = zm_out_start; | |
271 | } | |
272 | #endif | |
273 | ||
274 | static void | |
275 | zm_dump_buf(void *buf, int len) | |
276 | { | |
277 | #ifdef REDBOOT | |
278 | diag_vdump_buf_with_offset(zm_dprintf, buf, len, 0); | |
279 | #else | |
280 | ||
281 | #endif | |
282 | } | |
283 | ||
284 | static unsigned char zm_buf[2048]; | |
285 | static unsigned char *zm_bp; | |
286 | ||
287 | static void | |
288 | zm_new(void) | |
289 | { | |
290 | zm_bp = zm_buf; | |
291 | } | |
292 | ||
293 | static void | |
294 | zm_save(unsigned char c) | |
295 | { | |
296 | *zm_bp++ = c; | |
297 | } | |
298 | ||
299 | static void | |
300 | zm_dump(int line) | |
301 | { | |
302 | zm_dprintf("Packet at line: %d\n", line); | |
303 | zm_dump_buf(zm_buf, zm_bp-zm_buf); | |
304 | } | |
305 | ||
306 | #define ZM_DEBUG(x) x | |
307 | #else | |
308 | #define ZM_DEBUG(x) | |
309 | #endif | |
310 | ||
311 | // Wait for the line to go idle | |
312 | static void | |
313 | xyzModem_flush(void) | |
314 | { | |
315 | int res; | |
316 | char c; | |
317 | while (true) { | |
318 | res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c); | |
319 | if (!res) return; | |
320 | } | |
321 | } | |
322 | ||
323 | static int | |
324 | xyzModem_get_hdr(void) | |
325 | { | |
326 | char c; | |
327 | int res; | |
328 | bool hdr_found = false; | |
329 | int i, can_total, hdr_chars; | |
330 | unsigned short cksum; | |
331 | ||
332 | ZM_DEBUG(zm_new()); | |
333 | // Find the start of a header | |
334 | can_total = 0; | |
335 | hdr_chars = 0; | |
336 | ||
337 | if (xyz.tx_ack) { | |
338 | CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK); | |
339 | xyz.tx_ack = false; | |
340 | } | |
341 | while (!hdr_found) { | |
342 | res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c); | |
343 | ZM_DEBUG(zm_save(c)); | |
344 | if (res) { | |
345 | hdr_chars++; | |
346 | switch (c) { | |
347 | case SOH: | |
348 | xyz.total_SOH++; | |
349 | case STX: | |
350 | if (c == STX) xyz.total_STX++; | |
351 | hdr_found = true; | |
352 | break; | |
353 | case CAN: | |
354 | xyz.total_CAN++; | |
355 | ZM_DEBUG(zm_dump(__LINE__)); | |
356 | if (++can_total == xyzModem_CAN_COUNT) { | |
357 | return xyzModem_cancel; | |
358 | } else { | |
359 | // Wait for multiple CAN to avoid early quits | |
360 | break; | |
361 | } | |
362 | case EOT: | |
363 | // EOT only supported if no noise | |
364 | if (hdr_chars == 1) { | |
365 | CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK); | |
366 | ZM_DEBUG(zm_dprintf("ACK on EOT #%d\n", __LINE__)); | |
367 | ZM_DEBUG(zm_dump(__LINE__)); | |
368 | return xyzModem_eof; | |
369 | } | |
370 | default: | |
371 | // Ignore, waiting for start of header | |
372 | ; | |
373 | } | |
374 | } else { | |
375 | // Data stream timed out | |
376 | xyzModem_flush(); // Toss any current input | |
377 | ZM_DEBUG(zm_dump(__LINE__)); | |
378 | CYGACC_CALL_IF_DELAY_US((cyg_int32)250000); | |
379 | return xyzModem_timeout; | |
380 | } | |
381 | } | |
382 | ||
383 | // Header found, now read the data | |
384 | res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.blk); | |
385 | ZM_DEBUG(zm_save(xyz.blk)); | |
386 | if (!res) { | |
387 | ZM_DEBUG(zm_dump(__LINE__)); | |
388 | return xyzModem_timeout; | |
389 | } | |
390 | res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.cblk); | |
391 | ZM_DEBUG(zm_save(xyz.cblk)); | |
392 | if (!res) { | |
393 | ZM_DEBUG(zm_dump(__LINE__)); | |
394 | return xyzModem_timeout; | |
395 | } | |
396 | xyz.len = (c == SOH) ? 128 : 1024; | |
397 | xyz.bufp = xyz.pkt; | |
398 | for (i = 0; i < xyz.len; i++) { | |
399 | res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c); | |
400 | ZM_DEBUG(zm_save(c)); | |
401 | if (res) { | |
402 | xyz.pkt[i] = c; | |
403 | } else { | |
404 | ZM_DEBUG(zm_dump(__LINE__)); | |
405 | return xyzModem_timeout; | |
406 | } | |
407 | } | |
408 | res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.crc1); | |
409 | ZM_DEBUG(zm_save(xyz.crc1)); | |
410 | if (!res) { | |
411 | ZM_DEBUG(zm_dump(__LINE__)); | |
412 | return xyzModem_timeout; | |
413 | } | |
414 | if (xyz.crc_mode) { | |
415 | res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.crc2); | |
416 | ZM_DEBUG(zm_save(xyz.crc2)); | |
417 | if (!res) { | |
418 | ZM_DEBUG(zm_dump(__LINE__)); | |
419 | return xyzModem_timeout; | |
420 | } | |
421 | } | |
422 | ZM_DEBUG(zm_dump(__LINE__)); | |
423 | // Validate the message | |
424 | if ((xyz.blk ^ xyz.cblk) != (unsigned char)0xFF) { | |
425 | ZM_DEBUG(zm_dprintf("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk, (xyz.blk ^ xyz.cblk))); | |
426 | ZM_DEBUG(zm_dump_buf(xyz.pkt, xyz.len)); | |
427 | xyzModem_flush(); | |
428 | return xyzModem_frame; | |
429 | } | |
430 | // Verify checksum/CRC | |
431 | if (xyz.crc_mode) { | |
432 | cksum = cyg_crc16(xyz.pkt, xyz.len); | |
433 | if (cksum != ((xyz.crc1 << 8) | xyz.crc2)) { | |
434 | ZM_DEBUG(zm_dprintf("CRC error - recvd: %02x%02x, computed: %x\n", | |
435 | xyz.crc1, xyz.crc2, cksum & 0xFFFF)); | |
436 | return xyzModem_cksum; | |
437 | } | |
438 | } else { | |
439 | cksum = 0; | |
440 | for (i = 0; i < xyz.len; i++) { | |
441 | cksum += xyz.pkt[i]; | |
442 | } | |
443 | if (xyz.crc1 != (cksum & 0xFF)) { | |
444 | ZM_DEBUG(zm_dprintf("Checksum error - recvd: %x, computed: %x\n", xyz.crc1, cksum & 0xFF)); | |
445 | return xyzModem_cksum; | |
446 | } | |
447 | } | |
448 | // If we get here, the message passes [structural] muster | |
449 | return 0; | |
450 | } | |
451 | ||
452 | int | |
453 | xyzModem_stream_open(connection_info_t *info, int *err) | |
454 | { | |
455 | int console_chan, stat=0; | |
456 | int retries = xyzModem_MAX_RETRIES; | |
457 | int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC; | |
458 | ||
459 | // ZM_DEBUG(zm_out = zm_out_start); | |
460 | #ifdef xyzModem_zmodem | |
461 | if (info->mode == xyzModem_zmodem) { | |
462 | *err = xyzModem_noZmodem; | |
463 | return -1; | |
464 | } | |
465 | #endif | |
466 | ||
467 | #ifdef REDBOOT | |
468 | // Set up the I/O channel. Note: this allows for using a different port in the future | |
469 | console_chan = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); | |
470 | if (info->chan >= 0) { | |
471 | CYGACC_CALL_IF_SET_CONSOLE_COMM(info->chan); | |
472 | } else { | |
473 | CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan); | |
474 | } | |
475 | xyz.__chan = CYGACC_CALL_IF_CONSOLE_PROCS(); | |
476 | ||
477 | CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan); | |
478 | CYGACC_COMM_IF_CONTROL(*xyz.__chan, __COMMCTL_SET_TIMEOUT, xyzModem_CHAR_TIMEOUT); | |
479 | #else | |
480 | // TODO: CHECK ! | |
481 | int dummy; | |
482 | xyz.__chan=&dummy; | |
483 | #endif | |
484 | xyz.len = 0; | |
485 | xyz.crc_mode = true; | |
486 | xyz.at_eof = false; | |
487 | xyz.tx_ack = false; | |
488 | xyz.mode = info->mode; | |
489 | xyz.total_retries = 0; | |
490 | xyz.total_SOH = 0; | |
491 | xyz.total_STX = 0; | |
492 | xyz.total_CAN = 0; | |
493 | #ifdef USE_YMODEM_LENGTH | |
494 | xyz.read_length = 0; | |
495 | xyz.file_length = 0; | |
496 | #endif | |
497 | ||
498 | CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK)); | |
499 | ||
500 | if (xyz.mode == xyzModem_xmodem) { | |
501 | // X-modem doesn't have an information header - exit here | |
502 | xyz.next_blk = 1; | |
503 | return 0; | |
504 | } | |
505 | ||
506 | while (retries-- > 0) { | |
507 | stat = xyzModem_get_hdr(); | |
508 | if (stat == 0) { | |
509 | // Y-modem file information header | |
510 | if (xyz.blk == 0) { | |
511 | #ifdef USE_YMODEM_LENGTH | |
512 | // skip filename | |
513 | while (*xyz.bufp++); | |
514 | // get the length | |
515 | parse_num(xyz.bufp, &xyz.file_length, NULL, " "); | |
516 | #endif | |
517 | // The rest of the file name data block quietly discarded | |
518 | xyz.tx_ack = true; | |
519 | } | |
520 | xyz.next_blk = 1; | |
521 | xyz.len = 0; | |
522 | return 0; | |
523 | } else | |
524 | if (stat == xyzModem_timeout) { | |
525 | if (--crc_retries <= 0) xyz.crc_mode = false; | |
526 | CYGACC_CALL_IF_DELAY_US(5*100000); // Extra delay for startup | |
527 | CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK)); | |
528 | xyz.total_retries++; | |
529 | ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__)); | |
530 | } | |
531 | if (stat == xyzModem_cancel) { | |
532 | break; | |
533 | } | |
534 | } | |
535 | *err = stat; | |
536 | ZM_DEBUG(zm_flush()); | |
537 | return -1; | |
538 | } | |
539 | ||
540 | int | |
541 | xyzModem_stream_read(char *buf, int size, int *err) | |
542 | { | |
543 | int stat, total, len; | |
544 | int retries; | |
545 | ||
546 | total = 0; | |
547 | stat = xyzModem_cancel; | |
548 | // Try and get 'size' bytes into the buffer | |
549 | while (!xyz.at_eof && (size > 0)) { | |
550 | if (xyz.len == 0) { | |
551 | retries = xyzModem_MAX_RETRIES; | |
552 | while (retries-- > 0) { | |
553 | stat = xyzModem_get_hdr(); | |
554 | if (stat == 0) { | |
555 | if (xyz.blk == xyz.next_blk) { | |
556 | xyz.tx_ack = true; | |
557 | ZM_DEBUG(zm_dprintf("ACK block %d (%d)\n", xyz.blk, __LINE__)); | |
558 | xyz.next_blk = (xyz.next_blk + 1) & 0xFF; | |
559 | ||
560 | #if defined(xyzModem_zmodem) || defined(USE_YMODEM_LENGTH) | |
561 | if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0) { | |
562 | #else | |
563 | if (1) { | |
564 | #endif | |
565 | // Data blocks can be padded with ^Z (EOF) characters | |
566 | // This code tries to detect and remove them | |
567 | if ((xyz.bufp[xyz.len-1] == EOF) && | |
568 | (xyz.bufp[xyz.len-2] == EOF) && | |
569 | (xyz.bufp[xyz.len-3] == EOF)) { | |
570 | while (xyz.len && (xyz.bufp[xyz.len-1] == EOF)) { | |
571 | xyz.len--; | |
572 | } | |
573 | } | |
574 | } | |
575 | ||
576 | #ifdef USE_YMODEM_LENGTH | |
577 | // See if accumulated length exceeds that of the file. | |
578 | // If so, reduce size (i.e., cut out pad bytes) | |
579 | // Only do this for Y-modem (and Z-modem should it ever | |
580 | // be supported since it can fall back to Y-modem mode). | |
581 | if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length) { | |
582 | xyz.read_length += xyz.len; | |
583 | if (xyz.read_length > xyz.file_length) { | |
584 | xyz.len -= (xyz.read_length - xyz.file_length); | |
585 | } | |
586 | } | |
587 | #endif | |
588 | break; | |
589 | } else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF)) { | |
590 | // Just re-ACK this so sender will get on with it | |
591 | CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK); | |
592 | continue; // Need new header | |
593 | } else { | |
594 | stat = xyzModem_sequence; | |
595 | } | |
596 | } | |
597 | if (stat == xyzModem_cancel) { | |
598 | break; | |
599 | } | |
600 | if (stat == xyzModem_eof) { | |
601 | CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK); | |
602 | ZM_DEBUG(zm_dprintf("ACK (%d)\n", __LINE__)); | |
603 | if (xyz.mode == xyzModem_ymodem) { | |
604 | CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK)); | |
605 | xyz.total_retries++; | |
606 | ZM_DEBUG(zm_dprintf("Reading Final Header\n")); | |
607 | stat = xyzModem_get_hdr(); | |
608 | CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK); | |
609 | ZM_DEBUG(zm_dprintf("FINAL ACK (%d)\n", __LINE__)); | |
610 | } | |
611 | xyz.at_eof = true; | |
612 | break; | |
613 | } | |
614 | CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK)); | |
615 | xyz.total_retries++; | |
616 | ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__)); | |
617 | } | |
618 | if (stat < 0) { | |
619 | *err = stat; | |
620 | xyz.len = -1; | |
621 | return total; | |
622 | } | |
623 | } | |
624 | // Don't "read" data from the EOF protocol package | |
625 | if (!xyz.at_eof) { | |
626 | len = xyz.len; | |
627 | if (size < len) len = size; | |
628 | memcpy(buf, xyz.bufp, len); | |
629 | size -= len; | |
630 | buf += len; | |
631 | total += len; | |
632 | xyz.len -= len; | |
633 | xyz.bufp += len; | |
634 | } | |
635 | } | |
636 | return total; | |
637 | } | |
638 | ||
639 | void | |
640 | xyzModem_stream_close(int *err) | |
641 | { | |
642 | diag_printf("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n", | |
643 | xyz.crc_mode ? "CRC" : "Cksum", | |
644 | xyz.total_SOH, xyz.total_STX, xyz.total_CAN, | |
645 | xyz.total_retries); | |
646 | ZM_DEBUG(zm_flush()); | |
647 | } | |
648 | ||
649 | // Need to be able to clean out the input buffer, so have to take the | |
650 | // getc | |
651 | void xyzModem_stream_terminate(bool abort, int (*getc)(void)) | |
652 | { | |
653 | int c; | |
654 | ||
655 | if (abort) { | |
656 | ZM_DEBUG(zm_dprintf("!!!! TRANSFER ABORT !!!!\n")); | |
657 | switch (xyz.mode) { | |
658 | case xyzModem_xmodem: | |
659 | case xyzModem_ymodem: | |
660 | // The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal | |
661 | // number of Backspaces is a friendly way to get the other end to abort. | |
662 | CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN); | |
663 | CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN); | |
664 | CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN); | |
665 | CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN); | |
666 | CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP); | |
667 | CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP); | |
668 | CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP); | |
669 | CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP); | |
670 | // Now consume the rest of what's waiting on the line. | |
671 | ZM_DEBUG(zm_dprintf("Flushing serial line.\n")); | |
672 | xyzModem_flush(); | |
673 | xyz.at_eof = true; | |
674 | break; | |
675 | #ifdef xyzModem_zmodem | |
676 | case xyzModem_zmodem: | |
677 | // Might support it some day I suppose. | |
678 | #endif | |
679 | break; | |
680 | } | |
681 | } else { | |
682 | ZM_DEBUG(zm_dprintf("Engaging cleanup mode...\n")); | |
683 | // Consume any trailing crap left in the inbuffer from | |
684 | // previous recieved blocks. Since very few files are an exact multiple | |
685 | // of the transfer block size, there will almost always be some gunk here. | |
686 | // If we don't eat it now, RedBoot will think the user typed it. | |
687 | ZM_DEBUG(zm_dprintf("Trailing gunk:\n")); | |
688 | while ((c = (*getc)()) > -1) ; | |
689 | ZM_DEBUG(zm_dprintf("\n")); | |
690 | // Make a small delay to give terminal programs like minicom | |
691 | // time to get control again after their file transfer program | |
692 | // exits. | |
693 | CYGACC_CALL_IF_DELAY_US((cyg_int32)250000); | |
694 | } | |
695 | } | |
696 | ||
697 | char * | |
698 | xyzModem_error(int err) | |
699 | { | |
700 | switch (err) { | |
701 | case xyzModem_access: | |
702 | return "Can't access file"; | |
703 | break; | |
704 | case xyzModem_noZmodem: | |
705 | return "Sorry, zModem not available yet"; | |
706 | break; | |
707 | case xyzModem_timeout: | |
708 | return "Timed out"; | |
709 | break; | |
710 | case xyzModem_eof: | |
711 | return "End of file"; | |
712 | break; | |
713 | case xyzModem_cancel: | |
714 | return "Cancelled"; | |
715 | break; | |
716 | case xyzModem_frame: | |
717 | return "Invalid framing"; | |
718 | break; | |
719 | case xyzModem_cksum: | |
720 | return "CRC/checksum error"; | |
721 | break; | |
722 | case xyzModem_sequence: | |
723 | return "Block sequence error"; | |
724 | break; | |
725 | default: | |
726 | return "Unknown error"; | |
727 | break; | |
728 | } | |
729 | } | |
730 | ||
731 | // | |
732 | // RedBoot interface | |
733 | // | |
734 | #if 0 // SB | |
735 | GETC_IO_FUNCS(xyzModem_io, xyzModem_stream_open, xyzModem_stream_close, | |
736 | xyzModem_stream_terminate, xyzModem_stream_read, xyzModem_error); | |
737 | RedBoot_load(xmodem, xyzModem_io, false, false, xyzModem_xmodem); | |
738 | RedBoot_load(ymodem, xyzModem_io, false, false, xyzModem_ymodem); | |
739 | #endif |