blob: a126e32ed35e61b50a350074cc6ffe1f3276000e [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: eCos-2.0
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02002/*
3 *==========================================================================
4 *
5 * xyzModem.c
6 *
7 * RedBoot stream handler for xyzModem protocol
8 *
9 *==========================================================================
Wolfgang Denkcf48eb92006-04-16 10:51:58 +020010 *#####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 */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020024#include <common.h>
25#include <xyzModem.h>
26#include <stdarg.h>
27#include <crc.h>
28
Wolfgang Denkcf48eb92006-04-16 10:51:58 +020029/* Assumption - run xyzModem protocol over the console port */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020030
Wolfgang Denkcf48eb92006-04-16 10:51:58 +020031/* Values magic to the protocol */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020032#define SOH 0x01
33#define STX 0x02
34#define EOT 0x04
35#define ACK 0x06
36#define BSP 0x08
37#define NAK 0x15
38#define CAN 0x18
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020039#define EOF 0x1A /* ^Z for DOS officionados */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020040
Wolfgang Denkcf48eb92006-04-16 10:51:58 +020041/* Data & state local to the protocol */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020042static struct
43{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020044 int *__chan;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020045 unsigned char pkt[1024], *bufp;
46 unsigned char blk, cblk, crc1, crc2;
47 unsigned char next_blk; /* Expected block */
48 int len, mode, total_retries;
49 int total_SOH, total_STX, total_CAN;
50 bool crc_mode, at_eof, tx_ack;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020051 unsigned long file_length, read_length;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020052} xyz;
53
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020054#define xyzModem_CHAR_TIMEOUT 2000 /* 2 seconds */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020055#define xyzModem_MAX_RETRIES 20
56#define xyzModem_MAX_RETRIES_WITH_CRC 10
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020057#define xyzModem_CAN_COUNT 3 /* Wait for 3 CAN before quitting */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020058
59
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020060typedef int cyg_int32;
Kim Phillips199adb62012-10-29 13:34:32 +000061static int
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020062CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c)
63{
tomas.melin@vaisala.com2c77c0d2016-11-21 10:18:51 +020064
65 ulong now = get_timer(0);
66 while (!tstc ())
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020067 {
tomas.melin@vaisala.com2c77c0d2016-11-21 10:18:51 +020068 if (get_timer(now) > xyzModem_CHAR_TIMEOUT)
69 break;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020070 }
71 if (tstc ())
72 {
73 *c = getc ();
74 return 1;
75 }
76 return 0;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020077}
78
Kim Phillips199adb62012-10-29 13:34:32 +000079static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020080CYGACC_COMM_IF_PUTC (char x, char y)
81{
82 putc (y);
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020083}
84
Wolfgang Denkcf48eb92006-04-16 10:51:58 +020085/* Validate a hex character */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020086__inline__ static bool
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020087_is_hex (char c)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020088{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020089 return (((c >= '0') && (c <= '9')) ||
90 ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f')));
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020091}
92
Wolfgang Denkcf48eb92006-04-16 10:51:58 +020093/* Convert a single hex nibble */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020094__inline__ static int
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020095_from_hex (char c)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020096{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020097 int ret = 0;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020098
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020099 if ((c >= '0') && (c <= '9'))
100 {
101 ret = (c - '0');
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200102 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200103 else if ((c >= 'a') && (c <= 'f'))
104 {
105 ret = (c - 'a' + 0x0a);
106 }
107 else if ((c >= 'A') && (c <= 'F'))
108 {
109 ret = (c - 'A' + 0x0A);
110 }
111 return ret;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200112}
113
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200114/* Convert a character to lower case */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200115__inline__ static char
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200116_tolower (char c)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200117{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200118 if ((c >= 'A') && (c <= 'Z'))
119 {
120 c = (c - 'A') + 'a';
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200121 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200122 return c;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200123}
124
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200125/* Parse (scan) a number */
Kim Phillips199adb62012-10-29 13:34:32 +0000126static bool
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200127parse_num (char *s, unsigned long *val, char **es, char *delim)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200128{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200129 bool first = true;
130 int radix = 10;
131 char c;
132 unsigned long result = 0;
133 int digit;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200134
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200135 while (*s == ' ')
136 s++;
137 while (*s)
138 {
139 if (first && (s[0] == '0') && (_tolower (s[1]) == 'x'))
140 {
141 radix = 16;
142 s += 2;
143 }
144 first = false;
145 c = *s++;
146 if (_is_hex (c) && ((digit = _from_hex (c)) < radix))
147 {
148 /* Valid digit */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200149 result = (result * radix) + digit;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200150 }
151 else
152 {
153 if (delim != (char *) 0)
154 {
155 /* See if this character is one of the delimiters */
156 char *dp = delim;
157 while (*dp && (c != *dp))
158 dp++;
159 if (*dp)
160 break; /* Found a good delimiter */
161 }
162 return false; /* Malformatted number */
163 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200164 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200165 *val = result;
166 if (es != (char **) 0)
167 {
168 *es = s;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200169 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200170 return true;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200171}
172
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200173
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200174#ifdef DEBUG
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200175/*
176 * Note: this debug setup works by storing the strings in a fixed buffer
177 */
Alexandru Gagniucb09ece02017-04-04 10:42:31 -0700178static char zm_debug_buf[8192];
179static char *zm_out = zm_debug_buf;
180static char *zm_out_start = zm_debug_buf;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200181
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200182static int
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200183zm_dprintf (char *fmt, ...)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200184{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200185 int len;
186 va_list args;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200187
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200188 va_start (args, fmt);
189 len = diag_vsprintf (zm_out, fmt, args);
190 zm_out += len;
191 return len;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200192}
193
194static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200195zm_flush (void)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200196{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200197 zm_out = zm_out_start;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200198}
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200199
200static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200201zm_dump_buf (void *buf, int len)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200202{
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200203
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200204}
205
206static unsigned char zm_buf[2048];
207static unsigned char *zm_bp;
208
209static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200210zm_new (void)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200211{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200212 zm_bp = zm_buf;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200213}
214
215static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200216zm_save (unsigned char c)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200217{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200218 *zm_bp++ = c;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200219}
220
221static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200222zm_dump (int line)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200223{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200224 zm_dprintf ("Packet at line: %d\n", line);
225 zm_dump_buf (zm_buf, zm_bp - zm_buf);
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200226}
227
228#define ZM_DEBUG(x) x
229#else
230#define ZM_DEBUG(x)
231#endif
232
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200233/* Wait for the line to go idle */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200234static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200235xyzModem_flush (void)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200236{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200237 int res;
238 char c;
239 while (true)
240 {
241 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
242 if (!res)
243 return;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200244 }
245}
246
247static int
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200248xyzModem_get_hdr (void)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200249{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200250 char c;
251 int res;
252 bool hdr_found = false;
253 int i, can_total, hdr_chars;
254 unsigned short cksum;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200255
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200256 ZM_DEBUG (zm_new ());
257 /* Find the start of a header */
258 can_total = 0;
259 hdr_chars = 0;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200260
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200261 if (xyz.tx_ack)
262 {
263 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
264 xyz.tx_ack = false;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200265 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200266 while (!hdr_found)
267 {
268 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
269 ZM_DEBUG (zm_save (c));
270 if (res)
271 {
272 hdr_chars++;
273 switch (c)
274 {
275 case SOH:
276 xyz.total_SOH++;
277 case STX:
278 if (c == STX)
279 xyz.total_STX++;
280 hdr_found = true;
281 break;
282 case CAN:
283 xyz.total_CAN++;
284 ZM_DEBUG (zm_dump (__LINE__));
285 if (++can_total == xyzModem_CAN_COUNT)
286 {
287 return xyzModem_cancel;
288 }
289 else
290 {
291 /* Wait for multiple CAN to avoid early quits */
292 break;
293 }
294 case EOT:
295 /* EOT only supported if no noise */
296 if (hdr_chars == 1)
297 {
298 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
299 ZM_DEBUG (zm_dprintf ("ACK on EOT #%d\n", __LINE__));
300 ZM_DEBUG (zm_dump (__LINE__));
301 return xyzModem_eof;
302 }
303 default:
304 /* Ignore, waiting for start of header */
305 ;
306 }
307 }
308 else
309 {
310 /* Data stream timed out */
311 xyzModem_flush (); /* Toss any current input */
312 ZM_DEBUG (zm_dump (__LINE__));
313 CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
314 return xyzModem_timeout;
315 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200316 }
317
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200318 /* Header found, now read the data */
319 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.blk);
320 ZM_DEBUG (zm_save (xyz.blk));
321 if (!res)
322 {
323 ZM_DEBUG (zm_dump (__LINE__));
324 return xyzModem_timeout;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200325 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200326 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.cblk);
327 ZM_DEBUG (zm_save (xyz.cblk));
328 if (!res)
329 {
330 ZM_DEBUG (zm_dump (__LINE__));
331 return xyzModem_timeout;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200332 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200333 xyz.len = (c == SOH) ? 128 : 1024;
334 xyz.bufp = xyz.pkt;
335 for (i = 0; i < xyz.len; i++)
336 {
337 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
338 ZM_DEBUG (zm_save (c));
339 if (res)
340 {
341 xyz.pkt[i] = c;
342 }
343 else
344 {
345 ZM_DEBUG (zm_dump (__LINE__));
346 return xyzModem_timeout;
347 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200348 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200349 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc1);
350 ZM_DEBUG (zm_save (xyz.crc1));
351 if (!res)
352 {
353 ZM_DEBUG (zm_dump (__LINE__));
354 return xyzModem_timeout;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200355 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200356 if (xyz.crc_mode)
357 {
358 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc2);
359 ZM_DEBUG (zm_save (xyz.crc2));
360 if (!res)
361 {
362 ZM_DEBUG (zm_dump (__LINE__));
363 return xyzModem_timeout;
364 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200365 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200366 ZM_DEBUG (zm_dump (__LINE__));
367 /* Validate the message */
368 if ((xyz.blk ^ xyz.cblk) != (unsigned char) 0xFF)
369 {
370 ZM_DEBUG (zm_dprintf
371 ("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk,
372 (xyz.blk ^ xyz.cblk)));
373 ZM_DEBUG (zm_dump_buf (xyz.pkt, xyz.len));
374 xyzModem_flush ();
375 return xyzModem_frame;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200376 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200377 /* Verify checksum/CRC */
378 if (xyz.crc_mode)
379 {
Stefan Roeseecb57f62016-03-03 09:34:12 +0100380 cksum = crc16_ccitt(0, xyz.pkt, xyz.len);
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200381 if (cksum != ((xyz.crc1 << 8) | xyz.crc2))
382 {
383 ZM_DEBUG (zm_dprintf ("CRC error - recvd: %02x%02x, computed: %x\n",
384 xyz.crc1, xyz.crc2, cksum & 0xFFFF));
385 return xyzModem_cksum;
386 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200387 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200388 else
389 {
390 cksum = 0;
391 for (i = 0; i < xyz.len; i++)
392 {
393 cksum += xyz.pkt[i];
394 }
395 if (xyz.crc1 != (cksum & 0xFF))
396 {
397 ZM_DEBUG (zm_dprintf
398 ("Checksum error - recvd: %x, computed: %x\n", xyz.crc1,
399 cksum & 0xFF));
400 return xyzModem_cksum;
401 }
402 }
403 /* If we get here, the message passes [structural] muster */
404 return 0;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200405}
406
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200407int
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200408xyzModem_stream_open (connection_info_t * info, int *err)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200409{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200410 int stat = 0;
411 int retries = xyzModem_MAX_RETRIES;
412 int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200413
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200414/* ZM_DEBUG(zm_out = zm_out_start); */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200415#ifdef xyzModem_zmodem
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200416 if (info->mode == xyzModem_zmodem)
417 {
418 *err = xyzModem_noZmodem;
419 return -1;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200420 }
421#endif
422
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200423/* TODO: CHECK ! */
Kim Phillips2a2ed842009-06-15 11:50:40 -0500424 int dummy = 0;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200425 xyz.__chan = &dummy;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200426 xyz.len = 0;
427 xyz.crc_mode = true;
428 xyz.at_eof = false;
429 xyz.tx_ack = false;
430 xyz.mode = info->mode;
431 xyz.total_retries = 0;
432 xyz.total_SOH = 0;
433 xyz.total_STX = 0;
434 xyz.total_CAN = 0;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200435 xyz.read_length = 0;
436 xyz.file_length = 0;
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200437
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200438 CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200439
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200440 if (xyz.mode == xyzModem_xmodem)
441 {
442 /* X-modem doesn't have an information header - exit here */
443 xyz.next_blk = 1;
444 return 0;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200445 }
446
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200447 while (retries-- > 0)
448 {
449 stat = xyzModem_get_hdr ();
450 if (stat == 0)
451 {
452 /* Y-modem file information header */
453 if (xyz.blk == 0)
454 {
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200455 /* skip filename */
456 while (*xyz.bufp++);
457 /* get the length */
458 parse_num ((char *) xyz.bufp, &xyz.file_length, NULL, " ");
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200459 /* The rest of the file name data block quietly discarded */
460 xyz.tx_ack = true;
461 }
462 xyz.next_blk = 1;
463 xyz.len = 0;
464 return 0;
465 }
466 else if (stat == xyzModem_timeout)
467 {
468 if (--crc_retries <= 0)
469 xyz.crc_mode = false;
470 CYGACC_CALL_IF_DELAY_US (5 * 100000); /* Extra delay for startup */
471 CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
472 xyz.total_retries++;
473 ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
474 }
475 if (stat == xyzModem_cancel)
476 {
477 break;
478 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200479 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200480 *err = stat;
481 ZM_DEBUG (zm_flush ());
482 return -1;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200483}
484
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200485int
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200486xyzModem_stream_read (char *buf, int size, int *err)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200487{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200488 int stat, total, len;
489 int retries;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200490
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200491 total = 0;
492 stat = xyzModem_cancel;
493 /* Try and get 'size' bytes into the buffer */
494 while (!xyz.at_eof && (size > 0))
495 {
496 if (xyz.len == 0)
497 {
498 retries = xyzModem_MAX_RETRIES;
499 while (retries-- > 0)
500 {
501 stat = xyzModem_get_hdr ();
502 if (stat == 0)
503 {
504 if (xyz.blk == xyz.next_blk)
505 {
506 xyz.tx_ack = true;
507 ZM_DEBUG (zm_dprintf
508 ("ACK block %d (%d)\n", xyz.blk, __LINE__));
509 xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200510
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200511 if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0)
512 {
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200513 /* Data blocks can be padded with ^Z (EOF) characters */
514 /* This code tries to detect and remove them */
515 if ((xyz.bufp[xyz.len - 1] == EOF) &&
516 (xyz.bufp[xyz.len - 2] == EOF) &&
517 (xyz.bufp[xyz.len - 3] == EOF))
518 {
519 while (xyz.len
520 && (xyz.bufp[xyz.len - 1] == EOF))
521 {
522 xyz.len--;
523 }
524 }
525 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200526
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200527 /*
528 * See if accumulated length exceeds that of the file.
529 * If so, reduce size (i.e., cut out pad bytes)
530 * Only do this for Y-modem (and Z-modem should it ever
531 * be supported since it can fall back to Y-modem mode).
532 */
533 if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length)
534 {
535 xyz.read_length += xyz.len;
536 if (xyz.read_length > xyz.file_length)
537 {
538 xyz.len -= (xyz.read_length - xyz.file_length);
539 }
540 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200541 break;
542 }
543 else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF))
544 {
545 /* Just re-ACK this so sender will get on with it */
546 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
547 continue; /* Need new header */
548 }
549 else
550 {
551 stat = xyzModem_sequence;
552 }
553 }
554 if (stat == xyzModem_cancel)
555 {
556 break;
557 }
558 if (stat == xyzModem_eof)
559 {
560 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
561 ZM_DEBUG (zm_dprintf ("ACK (%d)\n", __LINE__));
562 if (xyz.mode == xyzModem_ymodem)
563 {
564 CYGACC_COMM_IF_PUTC (*xyz.__chan,
565 (xyz.crc_mode ? 'C' : NAK));
566 xyz.total_retries++;
567 ZM_DEBUG (zm_dprintf ("Reading Final Header\n"));
568 stat = xyzModem_get_hdr ();
569 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
570 ZM_DEBUG (zm_dprintf ("FINAL ACK (%d)\n", __LINE__));
571 }
572 xyz.at_eof = true;
573 break;
574 }
575 CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
576 xyz.total_retries++;
577 ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
578 }
579 if (stat < 0)
580 {
581 *err = stat;
582 xyz.len = -1;
583 return total;
584 }
585 }
586 /* Don't "read" data from the EOF protocol package */
587 if (!xyz.at_eof)
588 {
589 len = xyz.len;
590 if (size < len)
591 len = size;
592 memcpy (buf, xyz.bufp, len);
593 size -= len;
594 buf += len;
595 total += len;
596 xyz.len -= len;
597 xyz.bufp += len;
598 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200599 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200600 return total;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200601}
602
603void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200604xyzModem_stream_close (int *err)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200605{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200606 diag_printf
607 ("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
608 xyz.crc_mode ? "CRC" : "Cksum", xyz.total_SOH, xyz.total_STX,
609 xyz.total_CAN, xyz.total_retries);
610 ZM_DEBUG (zm_flush ());
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200611}
612
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200613/* Need to be able to clean out the input buffer, so have to take the */
614/* getc */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200615void
616xyzModem_stream_terminate (bool abort, int (*getc) (void))
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200617{
618 int c;
619
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200620 if (abort)
621 {
622 ZM_DEBUG (zm_dprintf ("!!!! TRANSFER ABORT !!!!\n"));
623 switch (xyz.mode)
624 {
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200625 case xyzModem_xmodem:
626 case xyzModem_ymodem:
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200627 /* The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal */
628 /* number of Backspaces is a friendly way to get the other end to abort. */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200629 CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
630 CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
631 CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
632 CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
633 CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
634 CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
635 CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
636 CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200637 /* Now consume the rest of what's waiting on the line. */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200638 ZM_DEBUG (zm_dprintf ("Flushing serial line.\n"));
639 xyzModem_flush ();
640 xyz.at_eof = true;
641 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200642#ifdef xyzModem_zmodem
643 case xyzModem_zmodem:
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200644 /* Might support it some day I suppose. */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200645#endif
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200646 break;
647 }
648 }
649 else
650 {
651 ZM_DEBUG (zm_dprintf ("Engaging cleanup mode...\n"));
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200652 /*
653 * Consume any trailing crap left in the inbuffer from
Mike Williams16263082011-07-22 04:01:30 +0000654 * previous received blocks. Since very few files are an exact multiple
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200655 * of the transfer block size, there will almost always be some gunk here.
656 * If we don't eat it now, RedBoot will think the user typed it.
657 */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200658 ZM_DEBUG (zm_dprintf ("Trailing gunk:\n"));
Jeroen Hofsteee153b132014-06-11 01:04:42 +0200659 while ((c = (*getc) ()) > -1)
660 ;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200661 ZM_DEBUG (zm_dprintf ("\n"));
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200662 /*
663 * Make a small delay to give terminal programs like minicom
664 * time to get control again after their file transfer program
665 * exits.
666 */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200667 CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
668 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200669}
670
671char *
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200672xyzModem_error (int err)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200673{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200674 switch (err)
675 {
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200676 case xyzModem_access:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200677 return "Can't access file";
678 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200679 case xyzModem_noZmodem:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200680 return "Sorry, zModem not available yet";
681 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200682 case xyzModem_timeout:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200683 return "Timed out";
684 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200685 case xyzModem_eof:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200686 return "End of file";
687 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200688 case xyzModem_cancel:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200689 return "Cancelled";
690 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200691 case xyzModem_frame:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200692 return "Invalid framing";
693 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200694 case xyzModem_cksum:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200695 return "CRC/checksum error";
696 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200697 case xyzModem_sequence:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200698 return "Block sequence error";
699 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200700 default:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200701 return "Unknown error";
702 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200703 }
704}
705
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200706/*
707 * RedBoot interface
708 */