blob: 09f74a1f0c7786dd4f24bf4ca398455f2aa283f9 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: eCos-2.0
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02002/*
3 *==========================================================================
4 *
5 * xyzModem.c
6 *
7 * RedBoot stream handler for xyzModem protocol
8 *
9 *==========================================================================
Wolfgang Denkebd3deb2006-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 Klotzbuecher387f5412006-03-30 13:40:55 +020024#include <xyzModem.h>
25#include <stdarg.h>
Tom Rinidec7ea02024-05-20 13:35:03 -060026#include <time.h>
Philipp Tomsich36b26d12018-11-25 19:22:18 +010027#include <u-boot/crc.h>
Lokesh Vutlaf2091382019-01-08 19:28:35 +053028#include <watchdog.h>
Pali Rohár5dc80cc2022-08-27 16:37:55 +020029#include <env.h>
Tom Rinidec7ea02024-05-20 13:35:03 -060030#include <vsprintf.h>
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020031
Wolfgang Denkebd3deb2006-04-16 10:51:58 +020032/* Assumption - run xyzModem protocol over the console port */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020033
Wolfgang Denkebd3deb2006-04-16 10:51:58 +020034/* Values magic to the protocol */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020035#define SOH 0x01
36#define STX 0x02
Pali Rohár647ae2c2021-08-03 16:28:44 +020037#define ETX 0x03 /* ^C for interrupt */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020038#define EOT 0x04
39#define ACK 0x06
40#define BSP 0x08
41#define NAK 0x15
42#define CAN 0x18
Wolfgang Denk95d11692006-08-31 16:46:53 +020043#define EOF 0x1A /* ^Z for DOS officionados */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020044
Wolfgang Denkebd3deb2006-04-16 10:51:58 +020045/* Data & state local to the protocol */
Wolfgang Denk95d11692006-08-31 16:46:53 +020046static struct
47{
Wolfgang Denk95d11692006-08-31 16:46:53 +020048 int *__chan;
Wolfgang Denk95d11692006-08-31 16:46:53 +020049 unsigned char pkt[1024], *bufp;
50 unsigned char blk, cblk, crc1, crc2;
51 unsigned char next_blk; /* Expected block */
52 int len, mode, total_retries;
53 int total_SOH, total_STX, total_CAN;
54 bool crc_mode, at_eof, tx_ack;
Pali Rohár5dc80cc2022-08-27 16:37:55 +020055 bool first_xmodem_packet;
56 ulong initial_time, timeout;
Wolfgang Denk95d11692006-08-31 16:46:53 +020057 unsigned long file_length, read_length;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020058} xyz;
59
Wolfgang Denk95d11692006-08-31 16:46:53 +020060#define xyzModem_CHAR_TIMEOUT 2000 /* 2 seconds */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020061#define xyzModem_MAX_RETRIES 20
62#define xyzModem_MAX_RETRIES_WITH_CRC 10
Wolfgang Denk95d11692006-08-31 16:46:53 +020063#define xyzModem_CAN_COUNT 3 /* Wait for 3 CAN before quitting */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020064
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020065typedef int cyg_int32;
Kim Phillipsb052b602012-10-29 13:34:32 +000066static int
Wolfgang Denk95d11692006-08-31 16:46:53 +020067CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c)
68{
tomas.melin@vaisala.comd5681a52016-11-21 10:18:51 +020069
70 ulong now = get_timer(0);
Stefan Roese80877fa2022-09-02 14:10:46 +020071 schedule();
tomas.melin@vaisala.comd5681a52016-11-21 10:18:51 +020072 while (!tstc ())
Wolfgang Denk95d11692006-08-31 16:46:53 +020073 {
tomas.melin@vaisala.comd5681a52016-11-21 10:18:51 +020074 if (get_timer(now) > xyzModem_CHAR_TIMEOUT)
75 break;
Wolfgang Denk95d11692006-08-31 16:46:53 +020076 }
77 if (tstc ())
78 {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +020079 *c = getchar();
Wolfgang Denk95d11692006-08-31 16:46:53 +020080 return 1;
81 }
82 return 0;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020083}
84
Kim Phillipsb052b602012-10-29 13:34:32 +000085static void
Wolfgang Denk95d11692006-08-31 16:46:53 +020086CYGACC_COMM_IF_PUTC (char x, char y)
87{
88 putc (y);
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020089}
90
Wolfgang Denkebd3deb2006-04-16 10:51:58 +020091/* Validate a hex character */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020092__inline__ static bool
Wolfgang Denk95d11692006-08-31 16:46:53 +020093_is_hex (char c)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020094{
Wolfgang Denk95d11692006-08-31 16:46:53 +020095 return (((c >= '0') && (c <= '9')) ||
96 ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f')));
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020097}
98
Wolfgang Denkebd3deb2006-04-16 10:51:58 +020099/* Convert a single hex nibble */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200100__inline__ static int
Wolfgang Denk95d11692006-08-31 16:46:53 +0200101_from_hex (char c)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200102{
Wolfgang Denk95d11692006-08-31 16:46:53 +0200103 int ret = 0;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200104
Wolfgang Denk95d11692006-08-31 16:46:53 +0200105 if ((c >= '0') && (c <= '9'))
106 {
107 ret = (c - '0');
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200108 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200109 else if ((c >= 'a') && (c <= 'f'))
110 {
111 ret = (c - 'a' + 0x0a);
112 }
113 else if ((c >= 'A') && (c <= 'F'))
114 {
115 ret = (c - 'A' + 0x0A);
116 }
117 return ret;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200118}
119
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200120/* Convert a character to lower case */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200121__inline__ static char
Wolfgang Denk95d11692006-08-31 16:46:53 +0200122_tolower (char c)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200123{
Wolfgang Denk95d11692006-08-31 16:46:53 +0200124 if ((c >= 'A') && (c <= 'Z'))
125 {
126 c = (c - 'A') + 'a';
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200127 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200128 return c;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200129}
130
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200131/* Parse (scan) a number */
Kim Phillipsb052b602012-10-29 13:34:32 +0000132static bool
Wolfgang Denk95d11692006-08-31 16:46:53 +0200133parse_num (char *s, unsigned long *val, char **es, char *delim)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200134{
Wolfgang Denk95d11692006-08-31 16:46:53 +0200135 bool first = true;
136 int radix = 10;
137 char c;
138 unsigned long result = 0;
139 int digit;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200140
Wolfgang Denk95d11692006-08-31 16:46:53 +0200141 while (*s == ' ')
142 s++;
143 while (*s)
144 {
145 if (first && (s[0] == '0') && (_tolower (s[1]) == 'x'))
146 {
147 radix = 16;
148 s += 2;
149 }
150 first = false;
151 c = *s++;
152 if (_is_hex (c) && ((digit = _from_hex (c)) < radix))
153 {
154 /* Valid digit */
Wolfgang Denk95d11692006-08-31 16:46:53 +0200155 result = (result * radix) + digit;
Wolfgang Denk95d11692006-08-31 16:46:53 +0200156 }
157 else
158 {
159 if (delim != (char *) 0)
160 {
161 /* See if this character is one of the delimiters */
162 char *dp = delim;
163 while (*dp && (c != *dp))
164 dp++;
165 if (*dp)
166 break; /* Found a good delimiter */
167 }
168 return false; /* Malformatted number */
169 }
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200170 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200171 *val = result;
172 if (es != (char **) 0)
173 {
174 *es = s;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200175 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200176 return true;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200177}
178
Simon Glass7611ac62019-09-25 08:56:27 -0600179#if defined(DEBUG) && !CONFIG_IS_ENABLED(USE_TINY_PRINTF)
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200180/*
181 * Note: this debug setup works by storing the strings in a fixed buffer
182 */
Alexandru Gagniuc39671012017-04-04 10:42:31 -0700183static char zm_debug_buf[8192];
184static char *zm_out = zm_debug_buf;
185static char *zm_out_start = zm_debug_buf;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200186
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200187static int
Heinrich Schuchardtb65e1a22018-05-07 21:59:34 +0200188zm_dprintf(char *fmt, ...)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200189{
Heinrich Schuchardtb65e1a22018-05-07 21:59:34 +0200190 int len;
191 va_list args;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200192
Heinrich Schuchardtb65e1a22018-05-07 21:59:34 +0200193 va_start(args, fmt);
194 len = diag_vsprintf(zm_out, fmt, args);
195 va_end(args);
196 zm_out += len;
197 return len;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200198}
199
200static void
Wolfgang Denk95d11692006-08-31 16:46:53 +0200201zm_flush (void)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200202{
Wolfgang Denk95d11692006-08-31 16:46:53 +0200203 zm_out = zm_out_start;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200204}
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200205
206static void
Wolfgang Denk95d11692006-08-31 16:46:53 +0200207zm_dump_buf (void *buf, int len)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200208{
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200209
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200210}
211
212static unsigned char zm_buf[2048];
213static unsigned char *zm_bp;
214
215static void
Wolfgang Denk95d11692006-08-31 16:46:53 +0200216zm_new (void)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200217{
Wolfgang Denk95d11692006-08-31 16:46:53 +0200218 zm_bp = zm_buf;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200219}
220
221static void
Wolfgang Denk95d11692006-08-31 16:46:53 +0200222zm_save (unsigned char c)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200223{
Wolfgang Denk95d11692006-08-31 16:46:53 +0200224 *zm_bp++ = c;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200225}
226
227static void
Wolfgang Denk95d11692006-08-31 16:46:53 +0200228zm_dump (int line)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200229{
Wolfgang Denk95d11692006-08-31 16:46:53 +0200230 zm_dprintf ("Packet at line: %d\n", line);
231 zm_dump_buf (zm_buf, zm_bp - zm_buf);
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200232}
233
234#define ZM_DEBUG(x) x
235#else
236#define ZM_DEBUG(x)
237#endif
238
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200239/* Wait for the line to go idle */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200240static void
Wolfgang Denk95d11692006-08-31 16:46:53 +0200241xyzModem_flush (void)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200242{
Wolfgang Denk95d11692006-08-31 16:46:53 +0200243 int res;
244 char c;
245 while (true)
246 {
247 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
248 if (!res)
249 return;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200250 }
251}
252
253static int
Wolfgang Denk95d11692006-08-31 16:46:53 +0200254xyzModem_get_hdr (void)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200255{
Wolfgang Denk95d11692006-08-31 16:46:53 +0200256 char c;
257 int res;
258 bool hdr_found = false;
259 int i, can_total, hdr_chars;
260 unsigned short cksum;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200261
Wolfgang Denk95d11692006-08-31 16:46:53 +0200262 ZM_DEBUG (zm_new ());
263 /* Find the start of a header */
264 can_total = 0;
265 hdr_chars = 0;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200266
Wolfgang Denk95d11692006-08-31 16:46:53 +0200267 if (xyz.tx_ack)
268 {
269 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
270 xyz.tx_ack = false;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200271 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200272 while (!hdr_found)
273 {
274 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
275 ZM_DEBUG (zm_save (c));
276 if (res)
277 {
278 hdr_chars++;
279 switch (c)
280 {
281 case SOH:
282 xyz.total_SOH++;
283 case STX:
284 if (c == STX)
285 xyz.total_STX++;
286 hdr_found = true;
287 break;
288 case CAN:
Pali Rohár647ae2c2021-08-03 16:28:44 +0200289 case ETX:
Wolfgang Denk95d11692006-08-31 16:46:53 +0200290 xyz.total_CAN++;
291 ZM_DEBUG (zm_dump (__LINE__));
292 if (++can_total == xyzModem_CAN_COUNT)
293 {
294 return xyzModem_cancel;
295 }
296 else
297 {
298 /* Wait for multiple CAN to avoid early quits */
299 break;
300 }
301 case EOT:
302 /* EOT only supported if no noise */
303 if (hdr_chars == 1)
304 {
305 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
306 ZM_DEBUG (zm_dprintf ("ACK on EOT #%d\n", __LINE__));
307 ZM_DEBUG (zm_dump (__LINE__));
308 return xyzModem_eof;
309 }
310 default:
311 /* Ignore, waiting for start of header */
312 ;
313 }
314 }
315 else
316 {
317 /* Data stream timed out */
318 xyzModem_flush (); /* Toss any current input */
319 ZM_DEBUG (zm_dump (__LINE__));
320 CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
321 return xyzModem_timeout;
322 }
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200323 }
324
Wolfgang Denk95d11692006-08-31 16:46:53 +0200325 /* Header found, now read the data */
326 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.blk);
327 ZM_DEBUG (zm_save (xyz.blk));
328 if (!res)
329 {
330 ZM_DEBUG (zm_dump (__LINE__));
331 return xyzModem_timeout;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200332 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200333 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.cblk);
334 ZM_DEBUG (zm_save (xyz.cblk));
335 if (!res)
336 {
337 ZM_DEBUG (zm_dump (__LINE__));
338 return xyzModem_timeout;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200339 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200340 xyz.len = (c == SOH) ? 128 : 1024;
341 xyz.bufp = xyz.pkt;
342 for (i = 0; i < xyz.len; i++)
343 {
344 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
345 ZM_DEBUG (zm_save (c));
346 if (res)
347 {
348 xyz.pkt[i] = c;
349 }
350 else
351 {
352 ZM_DEBUG (zm_dump (__LINE__));
353 return xyzModem_timeout;
354 }
355 }
356 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc1);
357 ZM_DEBUG (zm_save (xyz.crc1));
358 if (!res)
359 {
360 ZM_DEBUG (zm_dump (__LINE__));
361 return xyzModem_timeout;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200362 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200363 if (xyz.crc_mode)
364 {
365 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc2);
366 ZM_DEBUG (zm_save (xyz.crc2));
367 if (!res)
368 {
369 ZM_DEBUG (zm_dump (__LINE__));
370 return xyzModem_timeout;
371 }
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200372 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200373 ZM_DEBUG (zm_dump (__LINE__));
374 /* Validate the message */
375 if ((xyz.blk ^ xyz.cblk) != (unsigned char) 0xFF)
376 {
377 ZM_DEBUG (zm_dprintf
378 ("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk,
379 (xyz.blk ^ xyz.cblk)));
380 ZM_DEBUG (zm_dump_buf (xyz.pkt, xyz.len));
381 xyzModem_flush ();
382 return xyzModem_frame;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200383 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200384 /* Verify checksum/CRC */
385 if (xyz.crc_mode)
386 {
Stefan Roese084ff1e2016-03-03 09:34:12 +0100387 cksum = crc16_ccitt(0, xyz.pkt, xyz.len);
Wolfgang Denk95d11692006-08-31 16:46:53 +0200388 if (cksum != ((xyz.crc1 << 8) | xyz.crc2))
389 {
390 ZM_DEBUG (zm_dprintf ("CRC error - recvd: %02x%02x, computed: %x\n",
391 xyz.crc1, xyz.crc2, cksum & 0xFFFF));
392 return xyzModem_cksum;
393 }
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200394 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200395 else
396 {
397 cksum = 0;
398 for (i = 0; i < xyz.len; i++)
399 {
400 cksum += xyz.pkt[i];
401 }
402 if (xyz.crc1 != (cksum & 0xFF))
403 {
404 ZM_DEBUG (zm_dprintf
405 ("Checksum error - recvd: %x, computed: %x\n", xyz.crc1,
406 cksum & 0xFF));
407 return xyzModem_cksum;
408 }
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200409 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200410 /* If we get here, the message passes [structural] muster */
411 return 0;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200412}
413
Pali Rohár5dc80cc2022-08-27 16:37:55 +0200414static
415ulong
416xyzModem_get_initial_timeout (void)
417{
418 /* timeout is in seconds, non-positive timeout value is infinity */
419#if CONFIG_IS_ENABLED(ENV_SUPPORT)
420 const char *timeout_str = env_get("loadxy_timeout");
421 if (timeout_str)
422 return 1000 * simple_strtol(timeout_str, NULL, 10);
423#endif
424 return 1000 * CONFIG_CMD_LOADXY_TIMEOUT;
425}
426
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200427int
Wolfgang Denk95d11692006-08-31 16:46:53 +0200428xyzModem_stream_open (connection_info_t * info, int *err)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200429{
Wolfgang Denk95d11692006-08-31 16:46:53 +0200430 int stat = 0;
431 int retries = xyzModem_MAX_RETRIES;
432 int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200433
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200434/* ZM_DEBUG(zm_out = zm_out_start); */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200435#ifdef xyzModem_zmodem
Wolfgang Denk95d11692006-08-31 16:46:53 +0200436 if (info->mode == xyzModem_zmodem)
437 {
438 *err = xyzModem_noZmodem;
439 return -1;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200440 }
441#endif
442
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200443/* TODO: CHECK ! */
Kim Phillips66b07262009-06-15 11:50:40 -0500444 int dummy = 0;
Wolfgang Denk95d11692006-08-31 16:46:53 +0200445 xyz.__chan = &dummy;
Wolfgang Denk95d11692006-08-31 16:46:53 +0200446 xyz.len = 0;
447 xyz.crc_mode = true;
448 xyz.at_eof = false;
449 xyz.tx_ack = false;
450 xyz.mode = info->mode;
451 xyz.total_retries = 0;
452 xyz.total_SOH = 0;
453 xyz.total_STX = 0;
454 xyz.total_CAN = 0;
Wolfgang Denk95d11692006-08-31 16:46:53 +0200455 xyz.read_length = 0;
456 xyz.file_length = 0;
Pali Rohár5dc80cc2022-08-27 16:37:55 +0200457 xyz.first_xmodem_packet = false;
458 xyz.initial_time = get_timer(0);
459 xyz.timeout = xyzModem_get_initial_timeout();
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200460
Wolfgang Denk95d11692006-08-31 16:46:53 +0200461 CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200462
Wolfgang Denk95d11692006-08-31 16:46:53 +0200463 if (xyz.mode == xyzModem_xmodem)
464 {
465 /* X-modem doesn't have an information header - exit here */
Pali Rohár5dc80cc2022-08-27 16:37:55 +0200466 xyz.first_xmodem_packet = true;
Wolfgang Denk95d11692006-08-31 16:46:53 +0200467 xyz.next_blk = 1;
468 return 0;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200469 }
470
Pali Rohár5dc80cc2022-08-27 16:37:55 +0200471 while (!(xyz.timeout && get_timer(xyz.initial_time) > xyz.timeout))
Wolfgang Denk95d11692006-08-31 16:46:53 +0200472 {
Pali Rohár5dc80cc2022-08-27 16:37:55 +0200473 if (--retries <= 0)
474 {
475 retries = xyzModem_MAX_RETRIES;
476 crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
477 xyz.crc_mode = true;
478 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200479 stat = xyzModem_get_hdr ();
480 if (stat == 0)
481 {
482 /* Y-modem file information header */
483 if (xyz.blk == 0)
484 {
Wolfgang Denk95d11692006-08-31 16:46:53 +0200485 /* skip filename */
486 while (*xyz.bufp++);
487 /* get the length */
488 parse_num ((char *) xyz.bufp, &xyz.file_length, NULL, " ");
Wolfgang Denk95d11692006-08-31 16:46:53 +0200489 /* The rest of the file name data block quietly discarded */
490 xyz.tx_ack = true;
491 }
492 xyz.next_blk = 1;
493 xyz.len = 0;
494 return 0;
495 }
496 else if (stat == xyzModem_timeout)
497 {
498 if (--crc_retries <= 0)
499 xyz.crc_mode = false;
500 CYGACC_CALL_IF_DELAY_US (5 * 100000); /* Extra delay for startup */
501 CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
502 xyz.total_retries++;
503 ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
504 }
505 if (stat == xyzModem_cancel)
506 {
507 break;
508 }
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200509 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200510 *err = stat;
511 ZM_DEBUG (zm_flush ());
512 return -1;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200513}
514
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200515int
Wolfgang Denk95d11692006-08-31 16:46:53 +0200516xyzModem_stream_read (char *buf, int size, int *err)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200517{
Wolfgang Denk95d11692006-08-31 16:46:53 +0200518 int stat, total, len;
519 int retries;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200520
Wolfgang Denk95d11692006-08-31 16:46:53 +0200521 total = 0;
522 stat = xyzModem_cancel;
523 /* Try and get 'size' bytes into the buffer */
Pali Rohár094cfbb2021-08-03 16:28:38 +0200524 while (!xyz.at_eof && xyz.len >= 0 && (size > 0))
Wolfgang Denk95d11692006-08-31 16:46:53 +0200525 {
526 if (xyz.len == 0)
527 {
528 retries = xyzModem_MAX_RETRIES;
529 while (retries-- > 0)
530 {
Pali Rohár5dc80cc2022-08-27 16:37:55 +0200531 if (xyz.first_xmodem_packet && xyz.timeout &&
532 get_timer(xyz.initial_time) > xyz.timeout)
533 {
534 *err = xyzModem_timeout;
535 xyz.len = -1;
536 return total;
537 }
538
Wolfgang Denk95d11692006-08-31 16:46:53 +0200539 stat = xyzModem_get_hdr ();
540 if (stat == 0)
541 {
Pali Rohár5dc80cc2022-08-27 16:37:55 +0200542 if (xyz.mode == xyzModem_xmodem && xyz.first_xmodem_packet)
543 xyz.first_xmodem_packet = false;
Wolfgang Denk95d11692006-08-31 16:46:53 +0200544 if (xyz.blk == xyz.next_blk)
545 {
546 xyz.tx_ack = true;
547 ZM_DEBUG (zm_dprintf
548 ("ACK block %d (%d)\n", xyz.blk, __LINE__));
549 xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200550
Wolfgang Denk95d11692006-08-31 16:46:53 +0200551 if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0)
552 {
Wolfgang Denk95d11692006-08-31 16:46:53 +0200553 /* Data blocks can be padded with ^Z (EOF) characters */
554 /* This code tries to detect and remove them */
555 if ((xyz.bufp[xyz.len - 1] == EOF) &&
556 (xyz.bufp[xyz.len - 2] == EOF) &&
557 (xyz.bufp[xyz.len - 3] == EOF))
558 {
559 while (xyz.len
560 && (xyz.bufp[xyz.len - 1] == EOF))
561 {
562 xyz.len--;
563 }
564 }
565 }
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200566
Wolfgang Denk95d11692006-08-31 16:46:53 +0200567 /*
568 * See if accumulated length exceeds that of the file.
569 * If so, reduce size (i.e., cut out pad bytes)
570 * Only do this for Y-modem (and Z-modem should it ever
571 * be supported since it can fall back to Y-modem mode).
572 */
573 if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length)
574 {
575 xyz.read_length += xyz.len;
576 if (xyz.read_length > xyz.file_length)
577 {
578 xyz.len -= (xyz.read_length - xyz.file_length);
579 }
580 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200581 break;
582 }
583 else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF))
584 {
585 /* Just re-ACK this so sender will get on with it */
586 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
587 continue; /* Need new header */
588 }
589 else
590 {
591 stat = xyzModem_sequence;
592 }
593 }
594 if (stat == xyzModem_cancel)
595 {
596 break;
597 }
598 if (stat == xyzModem_eof)
599 {
600 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
601 ZM_DEBUG (zm_dprintf ("ACK (%d)\n", __LINE__));
602 if (xyz.mode == xyzModem_ymodem)
603 {
604 CYGACC_COMM_IF_PUTC (*xyz.__chan,
605 (xyz.crc_mode ? 'C' : NAK));
606 xyz.total_retries++;
607 ZM_DEBUG (zm_dprintf ("Reading Final Header\n"));
608 stat = xyzModem_get_hdr ();
609 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
610 ZM_DEBUG (zm_dprintf ("FINAL ACK (%d)\n", __LINE__));
611 }
Pali Rohárc2d20c72021-08-03 16:28:39 +0200612 else
613 stat = 0;
Wolfgang Denk95d11692006-08-31 16:46:53 +0200614 xyz.at_eof = true;
615 break;
616 }
617 CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
618 xyz.total_retries++;
619 ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
620 }
Pali Rohár5dc80cc2022-08-27 16:37:55 +0200621 if (stat < 0 && (!xyz.first_xmodem_packet || stat != xyzModem_timeout))
Wolfgang Denk95d11692006-08-31 16:46:53 +0200622 {
623 *err = stat;
624 xyz.len = -1;
625 return total;
626 }
627 }
628 /* Don't "read" data from the EOF protocol package */
Pali Rohár094cfbb2021-08-03 16:28:38 +0200629 if (!xyz.at_eof && xyz.len > 0)
Wolfgang Denk95d11692006-08-31 16:46:53 +0200630 {
631 len = xyz.len;
632 if (size < len)
633 len = size;
634 memcpy (buf, xyz.bufp, len);
635 size -= len;
636 buf += len;
637 total += len;
638 xyz.len -= len;
639 xyz.bufp += len;
640 }
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200641 }
Wolfgang Denk95d11692006-08-31 16:46:53 +0200642 return total;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200643}
644
645void
Wolfgang Denk95d11692006-08-31 16:46:53 +0200646xyzModem_stream_close (int *err)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200647{
Pali Rohár81f04912021-08-03 16:28:40 +0200648 ZM_DEBUG (zm_dprintf
Wolfgang Denk95d11692006-08-31 16:46:53 +0200649 ("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
650 xyz.crc_mode ? "CRC" : "Cksum", xyz.total_SOH, xyz.total_STX,
Pali Rohár81f04912021-08-03 16:28:40 +0200651 xyz.total_CAN, xyz.total_retries));
Wolfgang Denk95d11692006-08-31 16:46:53 +0200652 ZM_DEBUG (zm_flush ());
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200653}
654
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200655/* Need to be able to clean out the input buffer, so have to take the */
656/* getc */
Wolfgang Denk95d11692006-08-31 16:46:53 +0200657void
658xyzModem_stream_terminate (bool abort, int (*getc) (void))
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200659{
660 int c;
661
Wolfgang Denk95d11692006-08-31 16:46:53 +0200662 if (abort)
663 {
664 ZM_DEBUG (zm_dprintf ("!!!! TRANSFER ABORT !!!!\n"));
665 switch (xyz.mode)
666 {
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200667 case xyzModem_xmodem:
668 case xyzModem_ymodem:
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200669 /* The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal */
670 /* number of Backspaces is a friendly way to get the other end to abort. */
Wolfgang Denk95d11692006-08-31 16:46:53 +0200671 CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
672 CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
673 CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
674 CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
675 CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
676 CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
677 CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
678 CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200679 /* Now consume the rest of what's waiting on the line. */
Wolfgang Denk95d11692006-08-31 16:46:53 +0200680 ZM_DEBUG (zm_dprintf ("Flushing serial line.\n"));
681 xyzModem_flush ();
682 xyz.at_eof = true;
683 break;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200684#ifdef xyzModem_zmodem
685 case xyzModem_zmodem:
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200686 /* Might support it some day I suppose. */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200687#endif
Wolfgang Denk95d11692006-08-31 16:46:53 +0200688 break;
689 }
690 }
691 else
692 {
693 ZM_DEBUG (zm_dprintf ("Engaging cleanup mode...\n"));
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200694 /*
695 * Consume any trailing crap left in the inbuffer from
Mike Williamsbf895ad2011-07-22 04:01:30 +0000696 * previous received blocks. Since very few files are an exact multiple
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200697 * of the transfer block size, there will almost always be some gunk here.
698 * If we don't eat it now, RedBoot will think the user typed it.
699 */
Wolfgang Denk95d11692006-08-31 16:46:53 +0200700 ZM_DEBUG (zm_dprintf ("Trailing gunk:\n"));
Jeroen Hofstee419b6a42014-06-11 01:04:42 +0200701 while ((c = (*getc) ()) > -1)
702 ;
Wolfgang Denk95d11692006-08-31 16:46:53 +0200703 ZM_DEBUG (zm_dprintf ("\n"));
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200704 /*
705 * Make a small delay to give terminal programs like minicom
706 * time to get control again after their file transfer program
707 * exits.
708 */
Wolfgang Denk95d11692006-08-31 16:46:53 +0200709 CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
710 }
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200711}
712
713char *
Wolfgang Denk95d11692006-08-31 16:46:53 +0200714xyzModem_error (int err)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200715{
Wolfgang Denk95d11692006-08-31 16:46:53 +0200716 switch (err)
717 {
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200718 case xyzModem_access:
Wolfgang Denk95d11692006-08-31 16:46:53 +0200719 return "Can't access file";
720 break;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200721 case xyzModem_noZmodem:
Wolfgang Denk95d11692006-08-31 16:46:53 +0200722 return "Sorry, zModem not available yet";
723 break;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200724 case xyzModem_timeout:
Wolfgang Denk95d11692006-08-31 16:46:53 +0200725 return "Timed out";
726 break;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200727 case xyzModem_eof:
Wolfgang Denk95d11692006-08-31 16:46:53 +0200728 return "End of file";
729 break;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200730 case xyzModem_cancel:
Wolfgang Denk95d11692006-08-31 16:46:53 +0200731 return "Cancelled";
732 break;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200733 case xyzModem_frame:
Wolfgang Denk95d11692006-08-31 16:46:53 +0200734 return "Invalid framing";
735 break;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200736 case xyzModem_cksum:
Wolfgang Denk95d11692006-08-31 16:46:53 +0200737 return "CRC/checksum error";
738 break;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200739 case xyzModem_sequence:
Wolfgang Denk95d11692006-08-31 16:46:53 +0200740 return "Block sequence error";
741 break;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200742 default:
Wolfgang Denk95d11692006-08-31 16:46:53 +0200743 return "Unknown error";
744 break;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200745 }
746}
747
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200748/*
749 * RedBoot interface
750 */