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