blob: a0c5dfeece505ad07f9973ee7706fb2451ee02b4 [file] [log] [blame]
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02001/*
2 *==========================================================================
3 *
4 * xyzModem.c
5 *
6 * RedBoot stream handler for xyzModem protocol
7 *
8 *==========================================================================
Wolfgang Denke85427f2013-07-08 12:11:35 +02009 * SPDX-License-Identifier: eCos-2.0
Wolfgang Denkcf48eb92006-04-16 10:51:58 +020010 *==========================================================================
11 *#####DESCRIPTIONBEGIN####
12 *
13 * Author(s): gthomas
14 * Contributors: gthomas, tsmith, Yoshinori Sato
15 * Date: 2000-07-14
16 * Purpose:
17 * Description:
18 *
19 * This code is part of RedBoot (tm).
20 *
21 *####DESCRIPTIONEND####
22 *
23 *==========================================================================
24 */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020025#include <common.h>
26#include <xyzModem.h>
27#include <stdarg.h>
28#include <crc.h>
29
Wolfgang Denkcf48eb92006-04-16 10:51:58 +020030/* Assumption - run xyzModem protocol over the console port */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020031
Wolfgang Denkcf48eb92006-04-16 10:51:58 +020032/* Values magic to the protocol */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020033#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
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020040#define EOF 0x1A /* ^Z for DOS officionados */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020041
Wolfgang Denkcf48eb92006-04-16 10:51:58 +020042/* Data & state local to the protocol */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020043static struct
44{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020045 int *__chan;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020046 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;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020052 unsigned long file_length, read_length;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020053} xyz;
54
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020055#define xyzModem_CHAR_TIMEOUT 2000 /* 2 seconds */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020056#define xyzModem_MAX_RETRIES 20
57#define xyzModem_MAX_RETRIES_WITH_CRC 10
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020058#define xyzModem_CAN_COUNT 3 /* Wait for 3 CAN before quitting */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020059
60
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020061typedef int cyg_int32;
Kim Phillips199adb62012-10-29 13:34:32 +000062static int
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020063CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c)
64{
tomas.melin@vaisala.com2c77c0d2016-11-21 10:18:51 +020065
66 ulong now = get_timer(0);
67 while (!tstc ())
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020068 {
tomas.melin@vaisala.com2c77c0d2016-11-21 10:18:51 +020069 if (get_timer(now) > xyzModem_CHAR_TIMEOUT)
70 break;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020071 }
72 if (tstc ())
73 {
74 *c = getc ();
75 return 1;
76 }
77 return 0;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020078}
79
Kim Phillips199adb62012-10-29 13:34:32 +000080static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020081CYGACC_COMM_IF_PUTC (char x, char y)
82{
83 putc (y);
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020084}
85
Wolfgang Denkcf48eb92006-04-16 10:51:58 +020086/* Validate a hex character */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020087__inline__ static bool
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020088_is_hex (char c)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020089{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020090 return (((c >= '0') && (c <= '9')) ||
91 ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f')));
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020092}
93
Wolfgang Denkcf48eb92006-04-16 10:51:58 +020094/* Convert a single hex nibble */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020095__inline__ static int
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020096_from_hex (char c)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020097{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +020098 int ret = 0;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020099
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200100 if ((c >= '0') && (c <= '9'))
101 {
102 ret = (c - '0');
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200103 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200104 else if ((c >= 'a') && (c <= 'f'))
105 {
106 ret = (c - 'a' + 0x0a);
107 }
108 else if ((c >= 'A') && (c <= 'F'))
109 {
110 ret = (c - 'A' + 0x0A);
111 }
112 return ret;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200113}
114
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200115/* Convert a character to lower case */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200116__inline__ static char
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200117_tolower (char c)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200118{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200119 if ((c >= 'A') && (c <= 'Z'))
120 {
121 c = (c - 'A') + 'a';
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200122 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200123 return c;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200124}
125
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200126/* Parse (scan) a number */
Kim Phillips199adb62012-10-29 13:34:32 +0000127static bool
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200128parse_num (char *s, unsigned long *val, char **es, char *delim)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200129{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200130 bool first = true;
131 int radix = 10;
132 char c;
133 unsigned long result = 0;
134 int digit;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200135
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200136 while (*s == ' ')
137 s++;
138 while (*s)
139 {
140 if (first && (s[0] == '0') && (_tolower (s[1]) == 'x'))
141 {
142 radix = 16;
143 s += 2;
144 }
145 first = false;
146 c = *s++;
147 if (_is_hex (c) && ((digit = _from_hex (c)) < radix))
148 {
149 /* Valid digit */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200150 result = (result * radix) + digit;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200151 }
152 else
153 {
154 if (delim != (char *) 0)
155 {
156 /* See if this character is one of the delimiters */
157 char *dp = delim;
158 while (*dp && (c != *dp))
159 dp++;
160 if (*dp)
161 break; /* Found a good delimiter */
162 }
163 return false; /* Malformatted number */
164 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200165 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200166 *val = result;
167 if (es != (char **) 0)
168 {
169 *es = s;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200170 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200171 return true;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200172}
173
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200174
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200175#ifdef DEBUG
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200176/*
177 * Note: this debug setup works by storing the strings in a fixed buffer
178 */
Alexandru Gagniucb09ece02017-04-04 10:42:31 -0700179static char zm_debug_buf[8192];
180static char *zm_out = zm_debug_buf;
181static char *zm_out_start = zm_debug_buf;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200182
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200183static int
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200184zm_dprintf (char *fmt, ...)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200185{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200186 int len;
187 va_list args;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200188
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200189 va_start (args, fmt);
190 len = diag_vsprintf (zm_out, fmt, args);
191 zm_out += len;
192 return len;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200193}
194
195static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200196zm_flush (void)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200197{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200198 zm_out = zm_out_start;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200199}
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200200
201static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200202zm_dump_buf (void *buf, int len)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200203{
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200204
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200205}
206
207static unsigned char zm_buf[2048];
208static unsigned char *zm_bp;
209
210static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200211zm_new (void)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200212{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200213 zm_bp = zm_buf;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200214}
215
216static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200217zm_save (unsigned char c)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200218{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200219 *zm_bp++ = c;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200220}
221
222static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200223zm_dump (int line)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200224{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200225 zm_dprintf ("Packet at line: %d\n", line);
226 zm_dump_buf (zm_buf, zm_bp - zm_buf);
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200227}
228
229#define ZM_DEBUG(x) x
230#else
231#define ZM_DEBUG(x)
232#endif
233
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200234/* Wait for the line to go idle */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200235static void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200236xyzModem_flush (void)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200237{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200238 int res;
239 char c;
240 while (true)
241 {
242 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
243 if (!res)
244 return;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200245 }
246}
247
248static int
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200249xyzModem_get_hdr (void)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200250{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200251 char c;
252 int res;
253 bool hdr_found = false;
254 int i, can_total, hdr_chars;
255 unsigned short cksum;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200256
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200257 ZM_DEBUG (zm_new ());
258 /* Find the start of a header */
259 can_total = 0;
260 hdr_chars = 0;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200261
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200262 if (xyz.tx_ack)
263 {
264 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
265 xyz.tx_ack = false;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200266 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200267 while (!hdr_found)
268 {
269 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
270 ZM_DEBUG (zm_save (c));
271 if (res)
272 {
273 hdr_chars++;
274 switch (c)
275 {
276 case SOH:
277 xyz.total_SOH++;
278 case STX:
279 if (c == STX)
280 xyz.total_STX++;
281 hdr_found = true;
282 break;
283 case CAN:
284 xyz.total_CAN++;
285 ZM_DEBUG (zm_dump (__LINE__));
286 if (++can_total == xyzModem_CAN_COUNT)
287 {
288 return xyzModem_cancel;
289 }
290 else
291 {
292 /* Wait for multiple CAN to avoid early quits */
293 break;
294 }
295 case EOT:
296 /* EOT only supported if no noise */
297 if (hdr_chars == 1)
298 {
299 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
300 ZM_DEBUG (zm_dprintf ("ACK on EOT #%d\n", __LINE__));
301 ZM_DEBUG (zm_dump (__LINE__));
302 return xyzModem_eof;
303 }
304 default:
305 /* Ignore, waiting for start of header */
306 ;
307 }
308 }
309 else
310 {
311 /* Data stream timed out */
312 xyzModem_flush (); /* Toss any current input */
313 ZM_DEBUG (zm_dump (__LINE__));
314 CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
315 return xyzModem_timeout;
316 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200317 }
318
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200319 /* Header found, now read the data */
320 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.blk);
321 ZM_DEBUG (zm_save (xyz.blk));
322 if (!res)
323 {
324 ZM_DEBUG (zm_dump (__LINE__));
325 return xyzModem_timeout;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200326 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200327 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.cblk);
328 ZM_DEBUG (zm_save (xyz.cblk));
329 if (!res)
330 {
331 ZM_DEBUG (zm_dump (__LINE__));
332 return xyzModem_timeout;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200333 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200334 xyz.len = (c == SOH) ? 128 : 1024;
335 xyz.bufp = xyz.pkt;
336 for (i = 0; i < xyz.len; i++)
337 {
338 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
339 ZM_DEBUG (zm_save (c));
340 if (res)
341 {
342 xyz.pkt[i] = c;
343 }
344 else
345 {
346 ZM_DEBUG (zm_dump (__LINE__));
347 return xyzModem_timeout;
348 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200349 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200350 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc1);
351 ZM_DEBUG (zm_save (xyz.crc1));
352 if (!res)
353 {
354 ZM_DEBUG (zm_dump (__LINE__));
355 return xyzModem_timeout;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200356 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200357 if (xyz.crc_mode)
358 {
359 res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc2);
360 ZM_DEBUG (zm_save (xyz.crc2));
361 if (!res)
362 {
363 ZM_DEBUG (zm_dump (__LINE__));
364 return xyzModem_timeout;
365 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200366 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200367 ZM_DEBUG (zm_dump (__LINE__));
368 /* Validate the message */
369 if ((xyz.blk ^ xyz.cblk) != (unsigned char) 0xFF)
370 {
371 ZM_DEBUG (zm_dprintf
372 ("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk,
373 (xyz.blk ^ xyz.cblk)));
374 ZM_DEBUG (zm_dump_buf (xyz.pkt, xyz.len));
375 xyzModem_flush ();
376 return xyzModem_frame;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200377 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200378 /* Verify checksum/CRC */
379 if (xyz.crc_mode)
380 {
Stefan Roeseecb57f62016-03-03 09:34:12 +0100381 cksum = crc16_ccitt(0, xyz.pkt, xyz.len);
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200382 if (cksum != ((xyz.crc1 << 8) | xyz.crc2))
383 {
384 ZM_DEBUG (zm_dprintf ("CRC error - recvd: %02x%02x, computed: %x\n",
385 xyz.crc1, xyz.crc2, cksum & 0xFFFF));
386 return xyzModem_cksum;
387 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200388 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200389 else
390 {
391 cksum = 0;
392 for (i = 0; i < xyz.len; i++)
393 {
394 cksum += xyz.pkt[i];
395 }
396 if (xyz.crc1 != (cksum & 0xFF))
397 {
398 ZM_DEBUG (zm_dprintf
399 ("Checksum error - recvd: %x, computed: %x\n", xyz.crc1,
400 cksum & 0xFF));
401 return xyzModem_cksum;
402 }
403 }
404 /* If we get here, the message passes [structural] muster */
405 return 0;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200406}
407
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200408int
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200409xyzModem_stream_open (connection_info_t * info, int *err)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200410{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200411 int stat = 0;
412 int retries = xyzModem_MAX_RETRIES;
413 int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200414
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200415/* ZM_DEBUG(zm_out = zm_out_start); */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200416#ifdef xyzModem_zmodem
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200417 if (info->mode == xyzModem_zmodem)
418 {
419 *err = xyzModem_noZmodem;
420 return -1;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200421 }
422#endif
423
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200424/* TODO: CHECK ! */
Kim Phillips2a2ed842009-06-15 11:50:40 -0500425 int dummy = 0;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200426 xyz.__chan = &dummy;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200427 xyz.len = 0;
428 xyz.crc_mode = true;
429 xyz.at_eof = false;
430 xyz.tx_ack = false;
431 xyz.mode = info->mode;
432 xyz.total_retries = 0;
433 xyz.total_SOH = 0;
434 xyz.total_STX = 0;
435 xyz.total_CAN = 0;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200436 xyz.read_length = 0;
437 xyz.file_length = 0;
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200438
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200439 CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200440
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200441 if (xyz.mode == xyzModem_xmodem)
442 {
443 /* X-modem doesn't have an information header - exit here */
444 xyz.next_blk = 1;
445 return 0;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200446 }
447
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200448 while (retries-- > 0)
449 {
450 stat = xyzModem_get_hdr ();
451 if (stat == 0)
452 {
453 /* Y-modem file information header */
454 if (xyz.blk == 0)
455 {
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200456 /* skip filename */
457 while (*xyz.bufp++);
458 /* get the length */
459 parse_num ((char *) xyz.bufp, &xyz.file_length, NULL, " ");
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200460 /* The rest of the file name data block quietly discarded */
461 xyz.tx_ack = true;
462 }
463 xyz.next_blk = 1;
464 xyz.len = 0;
465 return 0;
466 }
467 else if (stat == xyzModem_timeout)
468 {
469 if (--crc_retries <= 0)
470 xyz.crc_mode = false;
471 CYGACC_CALL_IF_DELAY_US (5 * 100000); /* Extra delay for startup */
472 CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
473 xyz.total_retries++;
474 ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
475 }
476 if (stat == xyzModem_cancel)
477 {
478 break;
479 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200480 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200481 *err = stat;
482 ZM_DEBUG (zm_flush ());
483 return -1;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200484}
485
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200486int
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200487xyzModem_stream_read (char *buf, int size, int *err)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200488{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200489 int stat, total, len;
490 int retries;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200491
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200492 total = 0;
493 stat = xyzModem_cancel;
494 /* Try and get 'size' bytes into the buffer */
495 while (!xyz.at_eof && (size > 0))
496 {
497 if (xyz.len == 0)
498 {
499 retries = xyzModem_MAX_RETRIES;
500 while (retries-- > 0)
501 {
502 stat = xyzModem_get_hdr ();
503 if (stat == 0)
504 {
505 if (xyz.blk == xyz.next_blk)
506 {
507 xyz.tx_ack = true;
508 ZM_DEBUG (zm_dprintf
509 ("ACK block %d (%d)\n", xyz.blk, __LINE__));
510 xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200511
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200512 if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0)
513 {
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200514 /* Data blocks can be padded with ^Z (EOF) characters */
515 /* This code tries to detect and remove them */
516 if ((xyz.bufp[xyz.len - 1] == EOF) &&
517 (xyz.bufp[xyz.len - 2] == EOF) &&
518 (xyz.bufp[xyz.len - 3] == EOF))
519 {
520 while (xyz.len
521 && (xyz.bufp[xyz.len - 1] == EOF))
522 {
523 xyz.len--;
524 }
525 }
526 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200527
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200528 /*
529 * See if accumulated length exceeds that of the file.
530 * If so, reduce size (i.e., cut out pad bytes)
531 * Only do this for Y-modem (and Z-modem should it ever
532 * be supported since it can fall back to Y-modem mode).
533 */
534 if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length)
535 {
536 xyz.read_length += xyz.len;
537 if (xyz.read_length > xyz.file_length)
538 {
539 xyz.len -= (xyz.read_length - xyz.file_length);
540 }
541 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200542 break;
543 }
544 else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF))
545 {
546 /* Just re-ACK this so sender will get on with it */
547 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
548 continue; /* Need new header */
549 }
550 else
551 {
552 stat = xyzModem_sequence;
553 }
554 }
555 if (stat == xyzModem_cancel)
556 {
557 break;
558 }
559 if (stat == xyzModem_eof)
560 {
561 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
562 ZM_DEBUG (zm_dprintf ("ACK (%d)\n", __LINE__));
563 if (xyz.mode == xyzModem_ymodem)
564 {
565 CYGACC_COMM_IF_PUTC (*xyz.__chan,
566 (xyz.crc_mode ? 'C' : NAK));
567 xyz.total_retries++;
568 ZM_DEBUG (zm_dprintf ("Reading Final Header\n"));
569 stat = xyzModem_get_hdr ();
570 CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
571 ZM_DEBUG (zm_dprintf ("FINAL ACK (%d)\n", __LINE__));
572 }
573 xyz.at_eof = true;
574 break;
575 }
576 CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
577 xyz.total_retries++;
578 ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
579 }
580 if (stat < 0)
581 {
582 *err = stat;
583 xyz.len = -1;
584 return total;
585 }
586 }
587 /* Don't "read" data from the EOF protocol package */
588 if (!xyz.at_eof)
589 {
590 len = xyz.len;
591 if (size < len)
592 len = size;
593 memcpy (buf, xyz.bufp, len);
594 size -= len;
595 buf += len;
596 total += len;
597 xyz.len -= len;
598 xyz.bufp += len;
599 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200600 }
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200601 return total;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200602}
603
604void
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200605xyzModem_stream_close (int *err)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200606{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200607 diag_printf
608 ("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
609 xyz.crc_mode ? "CRC" : "Cksum", xyz.total_SOH, xyz.total_STX,
610 xyz.total_CAN, xyz.total_retries);
611 ZM_DEBUG (zm_flush ());
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200612}
613
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200614/* Need to be able to clean out the input buffer, so have to take the */
615/* getc */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200616void
617xyzModem_stream_terminate (bool abort, int (*getc) (void))
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200618{
619 int c;
620
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200621 if (abort)
622 {
623 ZM_DEBUG (zm_dprintf ("!!!! TRANSFER ABORT !!!!\n"));
624 switch (xyz.mode)
625 {
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200626 case xyzModem_xmodem:
627 case xyzModem_ymodem:
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200628 /* The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal */
629 /* number of Backspaces is a friendly way to get the other end to abort. */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200630 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, CAN);
634 CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
635 CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
636 CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
637 CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200638 /* Now consume the rest of what's waiting on the line. */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200639 ZM_DEBUG (zm_dprintf ("Flushing serial line.\n"));
640 xyzModem_flush ();
641 xyz.at_eof = true;
642 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200643#ifdef xyzModem_zmodem
644 case xyzModem_zmodem:
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200645 /* Might support it some day I suppose. */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200646#endif
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200647 break;
648 }
649 }
650 else
651 {
652 ZM_DEBUG (zm_dprintf ("Engaging cleanup mode...\n"));
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200653 /*
654 * Consume any trailing crap left in the inbuffer from
Mike Williams16263082011-07-22 04:01:30 +0000655 * previous received blocks. Since very few files are an exact multiple
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200656 * of the transfer block size, there will almost always be some gunk here.
657 * If we don't eat it now, RedBoot will think the user typed it.
658 */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200659 ZM_DEBUG (zm_dprintf ("Trailing gunk:\n"));
Jeroen Hofsteee153b132014-06-11 01:04:42 +0200660 while ((c = (*getc) ()) > -1)
661 ;
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200662 ZM_DEBUG (zm_dprintf ("\n"));
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200663 /*
664 * Make a small delay to give terminal programs like minicom
665 * time to get control again after their file transfer program
666 * exits.
667 */
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200668 CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
669 }
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200670}
671
672char *
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200673xyzModem_error (int err)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200674{
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200675 switch (err)
676 {
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200677 case xyzModem_access:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200678 return "Can't access file";
679 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200680 case xyzModem_noZmodem:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200681 return "Sorry, zModem not available yet";
682 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200683 case xyzModem_timeout:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200684 return "Timed out";
685 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200686 case xyzModem_eof:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200687 return "End of file";
688 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200689 case xyzModem_cancel:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200690 return "Cancelled";
691 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200692 case xyzModem_frame:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200693 return "Invalid framing";
694 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200695 case xyzModem_cksum:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200696 return "CRC/checksum error";
697 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200698 case xyzModem_sequence:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200699 return "Block sequence error";
700 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200701 default:
Wolfgang Denk7d0432c2006-08-31 16:46:53 +0200702 return "Unknown error";
703 break;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200704 }
705}
706
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200707/*
708 * RedBoot interface
709 */