183d290c5STom Rini // SPDX-License-Identifier: eCos-2.0
2cf48eb9aSWolfgang Denk /*
3cf48eb9aSWolfgang Denk *==========================================================================
4cf48eb9aSWolfgang Denk *
5cf48eb9aSWolfgang Denk * xyzModem.c
6cf48eb9aSWolfgang Denk *
7cf48eb9aSWolfgang Denk * RedBoot stream handler for xyzModem protocol
8cf48eb9aSWolfgang Denk *
9cf48eb9aSWolfgang Denk *==========================================================================
10cf48eb9aSWolfgang Denk *#####DESCRIPTIONBEGIN####
11cf48eb9aSWolfgang Denk *
12cf48eb9aSWolfgang Denk * Author(s): gthomas
13cf48eb9aSWolfgang Denk * Contributors: gthomas, tsmith, Yoshinori Sato
14cf48eb9aSWolfgang Denk * Date: 2000-07-14
15cf48eb9aSWolfgang Denk * Purpose:
16cf48eb9aSWolfgang Denk * Description:
17cf48eb9aSWolfgang Denk *
18cf48eb9aSWolfgang Denk * This code is part of RedBoot (tm).
19cf48eb9aSWolfgang Denk *
20cf48eb9aSWolfgang Denk *####DESCRIPTIONEND####
21cf48eb9aSWolfgang Denk *
22cf48eb9aSWolfgang Denk *==========================================================================
23cf48eb9aSWolfgang Denk */
24f2841d37SMarkus Klotzbuecher #include <common.h>
25f2841d37SMarkus Klotzbuecher #include <xyzModem.h>
26f2841d37SMarkus Klotzbuecher #include <stdarg.h>
27a740ee91SPhilipp Tomsich #include <u-boot/crc.h>
28*a4773c55SLokesh Vutla #include <watchdog.h>
29f2841d37SMarkus Klotzbuecher
30cf48eb9aSWolfgang Denk /* Assumption - run xyzModem protocol over the console port */
31f2841d37SMarkus Klotzbuecher
32cf48eb9aSWolfgang Denk /* Values magic to the protocol */
33f2841d37SMarkus Klotzbuecher #define SOH 0x01
34f2841d37SMarkus Klotzbuecher #define STX 0x02
35f2841d37SMarkus Klotzbuecher #define EOT 0x04
36f2841d37SMarkus Klotzbuecher #define ACK 0x06
37f2841d37SMarkus Klotzbuecher #define BSP 0x08
38f2841d37SMarkus Klotzbuecher #define NAK 0x15
39f2841d37SMarkus Klotzbuecher #define CAN 0x18
40cf48eb9aSWolfgang Denk #define EOF 0x1A /* ^Z for DOS officionados */
41f2841d37SMarkus Klotzbuecher
42cf48eb9aSWolfgang Denk /* Data & state local to the protocol */
437d0432c9SWolfgang Denk static struct
447d0432c9SWolfgang Denk {
45f2841d37SMarkus Klotzbuecher int *__chan;
46f2841d37SMarkus Klotzbuecher unsigned char pkt[1024], *bufp;
47f2841d37SMarkus Klotzbuecher unsigned char blk, cblk, crc1, crc2;
48cf48eb9aSWolfgang Denk unsigned char next_blk; /* Expected block */
49f2841d37SMarkus Klotzbuecher int len, mode, total_retries;
50f2841d37SMarkus Klotzbuecher int total_SOH, total_STX, total_CAN;
51f2841d37SMarkus Klotzbuecher bool crc_mode, at_eof, tx_ack;
52f2841d37SMarkus Klotzbuecher unsigned long file_length, read_length;
53f2841d37SMarkus Klotzbuecher } xyz;
54f2841d37SMarkus Klotzbuecher
55cf48eb9aSWolfgang Denk #define xyzModem_CHAR_TIMEOUT 2000 /* 2 seconds */
56f2841d37SMarkus Klotzbuecher #define xyzModem_MAX_RETRIES 20
57f2841d37SMarkus Klotzbuecher #define xyzModem_MAX_RETRIES_WITH_CRC 10
58cf48eb9aSWolfgang Denk #define xyzModem_CAN_COUNT 3 /* Wait for 3 CAN before quitting */
59f2841d37SMarkus Klotzbuecher
60f2841d37SMarkus Klotzbuecher
61f2841d37SMarkus Klotzbuecher typedef int cyg_int32;
62199adb60SKim Phillips static int
CYGACC_COMM_IF_GETC_TIMEOUT(char chan,char * c)637d0432c9SWolfgang Denk CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c)
647d0432c9SWolfgang Denk {
652c77c0d6Stomas.melin@vaisala.com
662c77c0d6Stomas.melin@vaisala.com ulong now = get_timer(0);
67*a4773c55SLokesh Vutla WATCHDOG_RESET();
682c77c0d6Stomas.melin@vaisala.com while (!tstc ())
697d0432c9SWolfgang Denk {
702c77c0d6Stomas.melin@vaisala.com if (get_timer(now) > xyzModem_CHAR_TIMEOUT)
712c77c0d6Stomas.melin@vaisala.com break;
72f2841d37SMarkus Klotzbuecher }
737d0432c9SWolfgang Denk if (tstc ())
747d0432c9SWolfgang Denk {
75f2841d37SMarkus Klotzbuecher *c = getc ();
76f2841d37SMarkus Klotzbuecher return 1;
77f2841d37SMarkus Klotzbuecher }
78f2841d37SMarkus Klotzbuecher return 0;
79f2841d37SMarkus Klotzbuecher }
80f2841d37SMarkus Klotzbuecher
81199adb60SKim Phillips static void
CYGACC_COMM_IF_PUTC(char x,char y)827d0432c9SWolfgang Denk CYGACC_COMM_IF_PUTC (char x, char y)
837d0432c9SWolfgang Denk {
84f2841d37SMarkus Klotzbuecher putc (y);
85f2841d37SMarkus Klotzbuecher }
86f2841d37SMarkus Klotzbuecher
87cf48eb9aSWolfgang Denk /* Validate a hex character */
88f2841d37SMarkus Klotzbuecher __inline__ static bool
_is_hex(char c)89f2841d37SMarkus Klotzbuecher _is_hex (char c)
90f2841d37SMarkus Klotzbuecher {
91f2841d37SMarkus Klotzbuecher return (((c >= '0') && (c <= '9')) ||
927d0432c9SWolfgang Denk ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f')));
93f2841d37SMarkus Klotzbuecher }
94f2841d37SMarkus Klotzbuecher
95cf48eb9aSWolfgang Denk /* Convert a single hex nibble */
96f2841d37SMarkus Klotzbuecher __inline__ static int
_from_hex(char c)97f2841d37SMarkus Klotzbuecher _from_hex (char c)
98f2841d37SMarkus Klotzbuecher {
99f2841d37SMarkus Klotzbuecher int ret = 0;
100f2841d37SMarkus Klotzbuecher
1017d0432c9SWolfgang Denk if ((c >= '0') && (c <= '9'))
1027d0432c9SWolfgang Denk {
103f2841d37SMarkus Klotzbuecher ret = (c - '0');
1047d0432c9SWolfgang Denk }
1057d0432c9SWolfgang Denk else if ((c >= 'a') && (c <= 'f'))
1067d0432c9SWolfgang Denk {
107f2841d37SMarkus Klotzbuecher ret = (c - 'a' + 0x0a);
1087d0432c9SWolfgang Denk }
1097d0432c9SWolfgang Denk else if ((c >= 'A') && (c <= 'F'))
1107d0432c9SWolfgang Denk {
111f2841d37SMarkus Klotzbuecher ret = (c - 'A' + 0x0A);
112f2841d37SMarkus Klotzbuecher }
113f2841d37SMarkus Klotzbuecher return ret;
114f2841d37SMarkus Klotzbuecher }
115f2841d37SMarkus Klotzbuecher
116cf48eb9aSWolfgang Denk /* Convert a character to lower case */
117f2841d37SMarkus Klotzbuecher __inline__ static char
_tolower(char c)118f2841d37SMarkus Klotzbuecher _tolower (char c)
119f2841d37SMarkus Klotzbuecher {
1207d0432c9SWolfgang Denk if ((c >= 'A') && (c <= 'Z'))
1217d0432c9SWolfgang Denk {
122f2841d37SMarkus Klotzbuecher c = (c - 'A') + 'a';
123f2841d37SMarkus Klotzbuecher }
124f2841d37SMarkus Klotzbuecher return c;
125f2841d37SMarkus Klotzbuecher }
126f2841d37SMarkus Klotzbuecher
127cf48eb9aSWolfgang Denk /* Parse (scan) a number */
128199adb60SKim Phillips static bool
parse_num(char * s,unsigned long * val,char ** es,char * delim)129f2841d37SMarkus Klotzbuecher parse_num (char *s, unsigned long *val, char **es, char *delim)
130f2841d37SMarkus Klotzbuecher {
131f2841d37SMarkus Klotzbuecher bool first = true;
132f2841d37SMarkus Klotzbuecher int radix = 10;
133f2841d37SMarkus Klotzbuecher char c;
134f2841d37SMarkus Klotzbuecher unsigned long result = 0;
135f2841d37SMarkus Klotzbuecher int digit;
136f2841d37SMarkus Klotzbuecher
1377d0432c9SWolfgang Denk while (*s == ' ')
1387d0432c9SWolfgang Denk s++;
1397d0432c9SWolfgang Denk while (*s)
1407d0432c9SWolfgang Denk {
1417d0432c9SWolfgang Denk if (first && (s[0] == '0') && (_tolower (s[1]) == 'x'))
1427d0432c9SWolfgang Denk {
143f2841d37SMarkus Klotzbuecher radix = 16;
144f2841d37SMarkus Klotzbuecher s += 2;
145f2841d37SMarkus Klotzbuecher }
146f2841d37SMarkus Klotzbuecher first = false;
147f2841d37SMarkus Klotzbuecher c = *s++;
1487d0432c9SWolfgang Denk if (_is_hex (c) && ((digit = _from_hex (c)) < radix))
1497d0432c9SWolfgang Denk {
150cf48eb9aSWolfgang Denk /* Valid digit */
151f2841d37SMarkus Klotzbuecher result = (result * radix) + digit;
1527d0432c9SWolfgang Denk }
1537d0432c9SWolfgang Denk else
1547d0432c9SWolfgang Denk {
1557d0432c9SWolfgang Denk if (delim != (char *) 0)
1567d0432c9SWolfgang Denk {
157cf48eb9aSWolfgang Denk /* See if this character is one of the delimiters */
158f2841d37SMarkus Klotzbuecher char *dp = delim;
1597d0432c9SWolfgang Denk while (*dp && (c != *dp))
1607d0432c9SWolfgang Denk dp++;
1617d0432c9SWolfgang Denk if (*dp)
1627d0432c9SWolfgang Denk break; /* Found a good delimiter */
163f2841d37SMarkus Klotzbuecher }
164cf48eb9aSWolfgang Denk return false; /* Malformatted number */
165f2841d37SMarkus Klotzbuecher }
166f2841d37SMarkus Klotzbuecher }
167f2841d37SMarkus Klotzbuecher *val = result;
1687d0432c9SWolfgang Denk if (es != (char **) 0)
1697d0432c9SWolfgang Denk {
170f2841d37SMarkus Klotzbuecher *es = s;
171f2841d37SMarkus Klotzbuecher }
172f2841d37SMarkus Klotzbuecher return true;
173f2841d37SMarkus Klotzbuecher }
174f2841d37SMarkus Klotzbuecher
175f2841d37SMarkus Klotzbuecher
1768140816eSAlex Kiernan #if defined(DEBUG) && !defined(CONFIG_USE_TINY_PRINTF)
177cf48eb9aSWolfgang Denk /*
178cf48eb9aSWolfgang Denk * Note: this debug setup works by storing the strings in a fixed buffer
179cf48eb9aSWolfgang Denk */
180b09ece08SAlexandru Gagniuc static char zm_debug_buf[8192];
181b09ece08SAlexandru Gagniuc static char *zm_out = zm_debug_buf;
182b09ece08SAlexandru Gagniuc static char *zm_out_start = zm_debug_buf;
183f2841d37SMarkus Klotzbuecher
184f2841d37SMarkus Klotzbuecher static int
zm_dprintf(char * fmt,...)185f2841d37SMarkus Klotzbuecher zm_dprintf(char *fmt, ...)
186f2841d37SMarkus Klotzbuecher {
187f2841d37SMarkus Klotzbuecher int len;
188f2841d37SMarkus Klotzbuecher va_list args;
189f2841d37SMarkus Klotzbuecher
190f2841d37SMarkus Klotzbuecher va_start(args, fmt);
191f2841d37SMarkus Klotzbuecher len = diag_vsprintf(zm_out, fmt, args);
19223c64898SHeinrich Schuchardt va_end(args);
193f2841d37SMarkus Klotzbuecher zm_out += len;
194f2841d37SMarkus Klotzbuecher return len;
195f2841d37SMarkus Klotzbuecher }
196f2841d37SMarkus Klotzbuecher
197f2841d37SMarkus Klotzbuecher static void
zm_flush(void)198f2841d37SMarkus Klotzbuecher zm_flush (void)
199f2841d37SMarkus Klotzbuecher {
200f2841d37SMarkus Klotzbuecher zm_out = zm_out_start;
201f2841d37SMarkus Klotzbuecher }
202f2841d37SMarkus Klotzbuecher
203f2841d37SMarkus Klotzbuecher static void
zm_dump_buf(void * buf,int len)204f2841d37SMarkus Klotzbuecher zm_dump_buf (void *buf, int len)
205f2841d37SMarkus Klotzbuecher {
206f2841d37SMarkus Klotzbuecher
207f2841d37SMarkus Klotzbuecher }
208f2841d37SMarkus Klotzbuecher
209f2841d37SMarkus Klotzbuecher static unsigned char zm_buf[2048];
210f2841d37SMarkus Klotzbuecher static unsigned char *zm_bp;
211f2841d37SMarkus Klotzbuecher
212f2841d37SMarkus Klotzbuecher static void
zm_new(void)213f2841d37SMarkus Klotzbuecher zm_new (void)
214f2841d37SMarkus Klotzbuecher {
215f2841d37SMarkus Klotzbuecher zm_bp = zm_buf;
216f2841d37SMarkus Klotzbuecher }
217f2841d37SMarkus Klotzbuecher
218f2841d37SMarkus Klotzbuecher static void
zm_save(unsigned char c)219f2841d37SMarkus Klotzbuecher zm_save (unsigned char c)
220f2841d37SMarkus Klotzbuecher {
221f2841d37SMarkus Klotzbuecher *zm_bp++ = c;
222f2841d37SMarkus Klotzbuecher }
223f2841d37SMarkus Klotzbuecher
224f2841d37SMarkus Klotzbuecher static void
zm_dump(int line)225f2841d37SMarkus Klotzbuecher zm_dump (int line)
226f2841d37SMarkus Klotzbuecher {
227f2841d37SMarkus Klotzbuecher zm_dprintf ("Packet at line: %d\n", line);
228f2841d37SMarkus Klotzbuecher zm_dump_buf (zm_buf, zm_bp - zm_buf);
229f2841d37SMarkus Klotzbuecher }
230f2841d37SMarkus Klotzbuecher
231f2841d37SMarkus Klotzbuecher #define ZM_DEBUG(x) x
232f2841d37SMarkus Klotzbuecher #else
233f2841d37SMarkus Klotzbuecher #define ZM_DEBUG(x)
234f2841d37SMarkus Klotzbuecher #endif
235f2841d37SMarkus Klotzbuecher
236cf48eb9aSWolfgang Denk /* Wait for the line to go idle */
237f2841d37SMarkus Klotzbuecher static void
xyzModem_flush(void)238f2841d37SMarkus Klotzbuecher xyzModem_flush (void)
239f2841d37SMarkus Klotzbuecher {
240f2841d37SMarkus Klotzbuecher int res;
241f2841d37SMarkus Klotzbuecher char c;
2427d0432c9SWolfgang Denk while (true)
2437d0432c9SWolfgang Denk {
244f2841d37SMarkus Klotzbuecher res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
2457d0432c9SWolfgang Denk if (!res)
2467d0432c9SWolfgang Denk return;
247f2841d37SMarkus Klotzbuecher }
248f2841d37SMarkus Klotzbuecher }
249f2841d37SMarkus Klotzbuecher
250f2841d37SMarkus Klotzbuecher static int
xyzModem_get_hdr(void)251f2841d37SMarkus Klotzbuecher xyzModem_get_hdr (void)
252f2841d37SMarkus Klotzbuecher {
253f2841d37SMarkus Klotzbuecher char c;
254f2841d37SMarkus Klotzbuecher int res;
255f2841d37SMarkus Klotzbuecher bool hdr_found = false;
256f2841d37SMarkus Klotzbuecher int i, can_total, hdr_chars;
257f2841d37SMarkus Klotzbuecher unsigned short cksum;
258f2841d37SMarkus Klotzbuecher
259f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_new ());
260cf48eb9aSWolfgang Denk /* Find the start of a header */
261f2841d37SMarkus Klotzbuecher can_total = 0;
262f2841d37SMarkus Klotzbuecher hdr_chars = 0;
263f2841d37SMarkus Klotzbuecher
2647d0432c9SWolfgang Denk if (xyz.tx_ack)
2657d0432c9SWolfgang Denk {
266f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
267f2841d37SMarkus Klotzbuecher xyz.tx_ack = false;
268f2841d37SMarkus Klotzbuecher }
2697d0432c9SWolfgang Denk while (!hdr_found)
2707d0432c9SWolfgang Denk {
271f2841d37SMarkus Klotzbuecher res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
272f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_save (c));
2737d0432c9SWolfgang Denk if (res)
2747d0432c9SWolfgang Denk {
275f2841d37SMarkus Klotzbuecher hdr_chars++;
2767d0432c9SWolfgang Denk switch (c)
2777d0432c9SWolfgang Denk {
278f2841d37SMarkus Klotzbuecher case SOH:
279f2841d37SMarkus Klotzbuecher xyz.total_SOH++;
280f2841d37SMarkus Klotzbuecher case STX:
2817d0432c9SWolfgang Denk if (c == STX)
2827d0432c9SWolfgang Denk xyz.total_STX++;
283f2841d37SMarkus Klotzbuecher hdr_found = true;
284f2841d37SMarkus Klotzbuecher break;
285f2841d37SMarkus Klotzbuecher case CAN:
286f2841d37SMarkus Klotzbuecher xyz.total_CAN++;
287f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dump (__LINE__));
2887d0432c9SWolfgang Denk if (++can_total == xyzModem_CAN_COUNT)
2897d0432c9SWolfgang Denk {
290f2841d37SMarkus Klotzbuecher return xyzModem_cancel;
2917d0432c9SWolfgang Denk }
2927d0432c9SWolfgang Denk else
2937d0432c9SWolfgang Denk {
294cf48eb9aSWolfgang Denk /* Wait for multiple CAN to avoid early quits */
295f2841d37SMarkus Klotzbuecher break;
296f2841d37SMarkus Klotzbuecher }
297f2841d37SMarkus Klotzbuecher case EOT:
298cf48eb9aSWolfgang Denk /* EOT only supported if no noise */
2997d0432c9SWolfgang Denk if (hdr_chars == 1)
3007d0432c9SWolfgang Denk {
301f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
302f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dprintf ("ACK on EOT #%d\n", __LINE__));
303f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dump (__LINE__));
304f2841d37SMarkus Klotzbuecher return xyzModem_eof;
305f2841d37SMarkus Klotzbuecher }
306f2841d37SMarkus Klotzbuecher default:
307cf48eb9aSWolfgang Denk /* Ignore, waiting for start of header */
308f2841d37SMarkus Klotzbuecher ;
309f2841d37SMarkus Klotzbuecher }
3107d0432c9SWolfgang Denk }
3117d0432c9SWolfgang Denk else
3127d0432c9SWolfgang Denk {
313cf48eb9aSWolfgang Denk /* Data stream timed out */
314cf48eb9aSWolfgang Denk xyzModem_flush (); /* Toss any current input */
315f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dump (__LINE__));
316f2841d37SMarkus Klotzbuecher CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
317f2841d37SMarkus Klotzbuecher return xyzModem_timeout;
318f2841d37SMarkus Klotzbuecher }
319f2841d37SMarkus Klotzbuecher }
320f2841d37SMarkus Klotzbuecher
321cf48eb9aSWolfgang Denk /* Header found, now read the data */
322f90a3921SStefan Roese res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.blk);
323f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_save (xyz.blk));
3247d0432c9SWolfgang Denk if (!res)
3257d0432c9SWolfgang Denk {
326f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dump (__LINE__));
327f2841d37SMarkus Klotzbuecher return xyzModem_timeout;
328f2841d37SMarkus Klotzbuecher }
329f90a3921SStefan Roese res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.cblk);
330f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_save (xyz.cblk));
3317d0432c9SWolfgang Denk if (!res)
3327d0432c9SWolfgang Denk {
333f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dump (__LINE__));
334f2841d37SMarkus Klotzbuecher return xyzModem_timeout;
335f2841d37SMarkus Klotzbuecher }
336f2841d37SMarkus Klotzbuecher xyz.len = (c == SOH) ? 128 : 1024;
337f2841d37SMarkus Klotzbuecher xyz.bufp = xyz.pkt;
3387d0432c9SWolfgang Denk for (i = 0; i < xyz.len; i++)
3397d0432c9SWolfgang Denk {
340f2841d37SMarkus Klotzbuecher res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
341f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_save (c));
3427d0432c9SWolfgang Denk if (res)
3437d0432c9SWolfgang Denk {
344f2841d37SMarkus Klotzbuecher xyz.pkt[i] = c;
3457d0432c9SWolfgang Denk }
3467d0432c9SWolfgang Denk else
3477d0432c9SWolfgang Denk {
348f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dump (__LINE__));
349f2841d37SMarkus Klotzbuecher return xyzModem_timeout;
350f2841d37SMarkus Klotzbuecher }
351f2841d37SMarkus Klotzbuecher }
352f90a3921SStefan Roese res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc1);
353f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_save (xyz.crc1));
3547d0432c9SWolfgang Denk if (!res)
3557d0432c9SWolfgang Denk {
356f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dump (__LINE__));
357f2841d37SMarkus Klotzbuecher return xyzModem_timeout;
358f2841d37SMarkus Klotzbuecher }
3597d0432c9SWolfgang Denk if (xyz.crc_mode)
3607d0432c9SWolfgang Denk {
361f90a3921SStefan Roese res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc2);
362f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_save (xyz.crc2));
3637d0432c9SWolfgang Denk if (!res)
3647d0432c9SWolfgang Denk {
365f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dump (__LINE__));
366f2841d37SMarkus Klotzbuecher return xyzModem_timeout;
367f2841d37SMarkus Klotzbuecher }
368f2841d37SMarkus Klotzbuecher }
369f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dump (__LINE__));
370cf48eb9aSWolfgang Denk /* Validate the message */
3717d0432c9SWolfgang Denk if ((xyz.blk ^ xyz.cblk) != (unsigned char) 0xFF)
3727d0432c9SWolfgang Denk {
3737d0432c9SWolfgang Denk ZM_DEBUG (zm_dprintf
3747d0432c9SWolfgang Denk ("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk,
3757d0432c9SWolfgang Denk (xyz.blk ^ xyz.cblk)));
376f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dump_buf (xyz.pkt, xyz.len));
377f2841d37SMarkus Klotzbuecher xyzModem_flush ();
378f2841d37SMarkus Klotzbuecher return xyzModem_frame;
379f2841d37SMarkus Klotzbuecher }
380cf48eb9aSWolfgang Denk /* Verify checksum/CRC */
3817d0432c9SWolfgang Denk if (xyz.crc_mode)
3827d0432c9SWolfgang Denk {
383ecb57f69SStefan Roese cksum = crc16_ccitt(0, xyz.pkt, xyz.len);
3847d0432c9SWolfgang Denk if (cksum != ((xyz.crc1 << 8) | xyz.crc2))
3857d0432c9SWolfgang Denk {
386f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dprintf ("CRC error - recvd: %02x%02x, computed: %x\n",
387f2841d37SMarkus Klotzbuecher xyz.crc1, xyz.crc2, cksum & 0xFFFF));
388f2841d37SMarkus Klotzbuecher return xyzModem_cksum;
389f2841d37SMarkus Klotzbuecher }
3907d0432c9SWolfgang Denk }
3917d0432c9SWolfgang Denk else
3927d0432c9SWolfgang Denk {
393f2841d37SMarkus Klotzbuecher cksum = 0;
3947d0432c9SWolfgang Denk for (i = 0; i < xyz.len; i++)
3957d0432c9SWolfgang Denk {
396f2841d37SMarkus Klotzbuecher cksum += xyz.pkt[i];
397f2841d37SMarkus Klotzbuecher }
3987d0432c9SWolfgang Denk if (xyz.crc1 != (cksum & 0xFF))
3997d0432c9SWolfgang Denk {
4007d0432c9SWolfgang Denk ZM_DEBUG (zm_dprintf
4017d0432c9SWolfgang Denk ("Checksum error - recvd: %x, computed: %x\n", xyz.crc1,
4027d0432c9SWolfgang Denk cksum & 0xFF));
403f2841d37SMarkus Klotzbuecher return xyzModem_cksum;
404f2841d37SMarkus Klotzbuecher }
405f2841d37SMarkus Klotzbuecher }
406cf48eb9aSWolfgang Denk /* If we get here, the message passes [structural] muster */
407f2841d37SMarkus Klotzbuecher return 0;
408f2841d37SMarkus Klotzbuecher }
409f2841d37SMarkus Klotzbuecher
410f2841d37SMarkus Klotzbuecher int
xyzModem_stream_open(connection_info_t * info,int * err)411f2841d37SMarkus Klotzbuecher xyzModem_stream_open (connection_info_t * info, int *err)
412f2841d37SMarkus Klotzbuecher {
413f90a3921SStefan Roese int stat = 0;
414f2841d37SMarkus Klotzbuecher int retries = xyzModem_MAX_RETRIES;
415f2841d37SMarkus Klotzbuecher int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
416f2841d37SMarkus Klotzbuecher
417cf48eb9aSWolfgang Denk /* ZM_DEBUG(zm_out = zm_out_start); */
418f2841d37SMarkus Klotzbuecher #ifdef xyzModem_zmodem
4197d0432c9SWolfgang Denk if (info->mode == xyzModem_zmodem)
4207d0432c9SWolfgang Denk {
421f2841d37SMarkus Klotzbuecher *err = xyzModem_noZmodem;
422f2841d37SMarkus Klotzbuecher return -1;
423f2841d37SMarkus Klotzbuecher }
424f2841d37SMarkus Klotzbuecher #endif
425f2841d37SMarkus Klotzbuecher
426cf48eb9aSWolfgang Denk /* TODO: CHECK ! */
4272a2ed845SKim Phillips int dummy = 0;
428f2841d37SMarkus Klotzbuecher xyz.__chan = &dummy;
429f2841d37SMarkus Klotzbuecher xyz.len = 0;
430f2841d37SMarkus Klotzbuecher xyz.crc_mode = true;
431f2841d37SMarkus Klotzbuecher xyz.at_eof = false;
432f2841d37SMarkus Klotzbuecher xyz.tx_ack = false;
433f2841d37SMarkus Klotzbuecher xyz.mode = info->mode;
434f2841d37SMarkus Klotzbuecher xyz.total_retries = 0;
435f2841d37SMarkus Klotzbuecher xyz.total_SOH = 0;
436f2841d37SMarkus Klotzbuecher xyz.total_STX = 0;
437f2841d37SMarkus Klotzbuecher xyz.total_CAN = 0;
438f2841d37SMarkus Klotzbuecher xyz.read_length = 0;
439f2841d37SMarkus Klotzbuecher xyz.file_length = 0;
440f2841d37SMarkus Klotzbuecher
441f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
442f2841d37SMarkus Klotzbuecher
4437d0432c9SWolfgang Denk if (xyz.mode == xyzModem_xmodem)
4447d0432c9SWolfgang Denk {
445cf48eb9aSWolfgang Denk /* X-modem doesn't have an information header - exit here */
446f2841d37SMarkus Klotzbuecher xyz.next_blk = 1;
447f2841d37SMarkus Klotzbuecher return 0;
448f2841d37SMarkus Klotzbuecher }
449f2841d37SMarkus Klotzbuecher
4507d0432c9SWolfgang Denk while (retries-- > 0)
4517d0432c9SWolfgang Denk {
452f2841d37SMarkus Klotzbuecher stat = xyzModem_get_hdr ();
4537d0432c9SWolfgang Denk if (stat == 0)
4547d0432c9SWolfgang Denk {
455cf48eb9aSWolfgang Denk /* Y-modem file information header */
4567d0432c9SWolfgang Denk if (xyz.blk == 0)
4577d0432c9SWolfgang Denk {
458cf48eb9aSWolfgang Denk /* skip filename */
459f2841d37SMarkus Klotzbuecher while (*xyz.bufp++);
460cf48eb9aSWolfgang Denk /* get the length */
461f90a3921SStefan Roese parse_num ((char *) xyz.bufp, &xyz.file_length, NULL, " ");
462cf48eb9aSWolfgang Denk /* The rest of the file name data block quietly discarded */
463f2841d37SMarkus Klotzbuecher xyz.tx_ack = true;
464f2841d37SMarkus Klotzbuecher }
465f2841d37SMarkus Klotzbuecher xyz.next_blk = 1;
466f2841d37SMarkus Klotzbuecher xyz.len = 0;
467f2841d37SMarkus Klotzbuecher return 0;
4687d0432c9SWolfgang Denk }
4697d0432c9SWolfgang Denk else if (stat == xyzModem_timeout)
4707d0432c9SWolfgang Denk {
4717d0432c9SWolfgang Denk if (--crc_retries <= 0)
4727d0432c9SWolfgang Denk xyz.crc_mode = false;
473cf48eb9aSWolfgang Denk CYGACC_CALL_IF_DELAY_US (5 * 100000); /* Extra delay for startup */
474f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
475f2841d37SMarkus Klotzbuecher xyz.total_retries++;
476f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
477f2841d37SMarkus Klotzbuecher }
4787d0432c9SWolfgang Denk if (stat == xyzModem_cancel)
4797d0432c9SWolfgang Denk {
480f2841d37SMarkus Klotzbuecher break;
481f2841d37SMarkus Klotzbuecher }
482f2841d37SMarkus Klotzbuecher }
483f2841d37SMarkus Klotzbuecher *err = stat;
484f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_flush ());
485f2841d37SMarkus Klotzbuecher return -1;
486f2841d37SMarkus Klotzbuecher }
487f2841d37SMarkus Klotzbuecher
488f2841d37SMarkus Klotzbuecher int
xyzModem_stream_read(char * buf,int size,int * err)489f2841d37SMarkus Klotzbuecher xyzModem_stream_read (char *buf, int size, int *err)
490f2841d37SMarkus Klotzbuecher {
491f2841d37SMarkus Klotzbuecher int stat, total, len;
492f2841d37SMarkus Klotzbuecher int retries;
493f2841d37SMarkus Klotzbuecher
494f2841d37SMarkus Klotzbuecher total = 0;
495f2841d37SMarkus Klotzbuecher stat = xyzModem_cancel;
496cf48eb9aSWolfgang Denk /* Try and get 'size' bytes into the buffer */
4977d0432c9SWolfgang Denk while (!xyz.at_eof && (size > 0))
4987d0432c9SWolfgang Denk {
4997d0432c9SWolfgang Denk if (xyz.len == 0)
5007d0432c9SWolfgang Denk {
501f2841d37SMarkus Klotzbuecher retries = xyzModem_MAX_RETRIES;
5027d0432c9SWolfgang Denk while (retries-- > 0)
5037d0432c9SWolfgang Denk {
504f2841d37SMarkus Klotzbuecher stat = xyzModem_get_hdr ();
5057d0432c9SWolfgang Denk if (stat == 0)
5067d0432c9SWolfgang Denk {
5077d0432c9SWolfgang Denk if (xyz.blk == xyz.next_blk)
5087d0432c9SWolfgang Denk {
509f2841d37SMarkus Klotzbuecher xyz.tx_ack = true;
5107d0432c9SWolfgang Denk ZM_DEBUG (zm_dprintf
5117d0432c9SWolfgang Denk ("ACK block %d (%d)\n", xyz.blk, __LINE__));
512f2841d37SMarkus Klotzbuecher xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
513f2841d37SMarkus Klotzbuecher
5147d0432c9SWolfgang Denk if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0)
5157d0432c9SWolfgang Denk {
516cf48eb9aSWolfgang Denk /* Data blocks can be padded with ^Z (EOF) characters */
517cf48eb9aSWolfgang Denk /* This code tries to detect and remove them */
518f2841d37SMarkus Klotzbuecher if ((xyz.bufp[xyz.len - 1] == EOF) &&
519f2841d37SMarkus Klotzbuecher (xyz.bufp[xyz.len - 2] == EOF) &&
5207d0432c9SWolfgang Denk (xyz.bufp[xyz.len - 3] == EOF))
5217d0432c9SWolfgang Denk {
5227d0432c9SWolfgang Denk while (xyz.len
5237d0432c9SWolfgang Denk && (xyz.bufp[xyz.len - 1] == EOF))
5247d0432c9SWolfgang Denk {
525f2841d37SMarkus Klotzbuecher xyz.len--;
526f2841d37SMarkus Klotzbuecher }
527f2841d37SMarkus Klotzbuecher }
528f2841d37SMarkus Klotzbuecher }
529f2841d37SMarkus Klotzbuecher
530cf48eb9aSWolfgang Denk /*
531cf48eb9aSWolfgang Denk * See if accumulated length exceeds that of the file.
532cf48eb9aSWolfgang Denk * If so, reduce size (i.e., cut out pad bytes)
533cf48eb9aSWolfgang Denk * Only do this for Y-modem (and Z-modem should it ever
534cf48eb9aSWolfgang Denk * be supported since it can fall back to Y-modem mode).
535cf48eb9aSWolfgang Denk */
5367d0432c9SWolfgang Denk if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length)
5377d0432c9SWolfgang Denk {
538f2841d37SMarkus Klotzbuecher xyz.read_length += xyz.len;
5397d0432c9SWolfgang Denk if (xyz.read_length > xyz.file_length)
5407d0432c9SWolfgang Denk {
541f2841d37SMarkus Klotzbuecher xyz.len -= (xyz.read_length - xyz.file_length);
542f2841d37SMarkus Klotzbuecher }
543f2841d37SMarkus Klotzbuecher }
544f2841d37SMarkus Klotzbuecher break;
5457d0432c9SWolfgang Denk }
5467d0432c9SWolfgang Denk else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF))
5477d0432c9SWolfgang Denk {
548cf48eb9aSWolfgang Denk /* Just re-ACK this so sender will get on with it */
549f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
550cf48eb9aSWolfgang Denk continue; /* Need new header */
5517d0432c9SWolfgang Denk }
5527d0432c9SWolfgang Denk else
5537d0432c9SWolfgang Denk {
554f2841d37SMarkus Klotzbuecher stat = xyzModem_sequence;
555f2841d37SMarkus Klotzbuecher }
556f2841d37SMarkus Klotzbuecher }
5577d0432c9SWolfgang Denk if (stat == xyzModem_cancel)
5587d0432c9SWolfgang Denk {
559f2841d37SMarkus Klotzbuecher break;
560f2841d37SMarkus Klotzbuecher }
5617d0432c9SWolfgang Denk if (stat == xyzModem_eof)
5627d0432c9SWolfgang Denk {
563f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
564f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dprintf ("ACK (%d)\n", __LINE__));
5657d0432c9SWolfgang Denk if (xyz.mode == xyzModem_ymodem)
5667d0432c9SWolfgang Denk {
5677d0432c9SWolfgang Denk CYGACC_COMM_IF_PUTC (*xyz.__chan,
5687d0432c9SWolfgang Denk (xyz.crc_mode ? 'C' : NAK));
569f2841d37SMarkus Klotzbuecher xyz.total_retries++;
570f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dprintf ("Reading Final Header\n"));
571f2841d37SMarkus Klotzbuecher stat = xyzModem_get_hdr ();
572f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
573f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dprintf ("FINAL ACK (%d)\n", __LINE__));
574f2841d37SMarkus Klotzbuecher }
575f2841d37SMarkus Klotzbuecher xyz.at_eof = true;
576f2841d37SMarkus Klotzbuecher break;
577f2841d37SMarkus Klotzbuecher }
578f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
579f2841d37SMarkus Klotzbuecher xyz.total_retries++;
580f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
581f2841d37SMarkus Klotzbuecher }
5827d0432c9SWolfgang Denk if (stat < 0)
5837d0432c9SWolfgang Denk {
584f2841d37SMarkus Klotzbuecher *err = stat;
585f2841d37SMarkus Klotzbuecher xyz.len = -1;
586f2841d37SMarkus Klotzbuecher return total;
587f2841d37SMarkus Klotzbuecher }
588f2841d37SMarkus Klotzbuecher }
589cf48eb9aSWolfgang Denk /* Don't "read" data from the EOF protocol package */
5907d0432c9SWolfgang Denk if (!xyz.at_eof)
5917d0432c9SWolfgang Denk {
592f2841d37SMarkus Klotzbuecher len = xyz.len;
5937d0432c9SWolfgang Denk if (size < len)
5947d0432c9SWolfgang Denk len = size;
595f2841d37SMarkus Klotzbuecher memcpy (buf, xyz.bufp, len);
596f2841d37SMarkus Klotzbuecher size -= len;
597f2841d37SMarkus Klotzbuecher buf += len;
598f2841d37SMarkus Klotzbuecher total += len;
599f2841d37SMarkus Klotzbuecher xyz.len -= len;
600f2841d37SMarkus Klotzbuecher xyz.bufp += len;
601f2841d37SMarkus Klotzbuecher }
602f2841d37SMarkus Klotzbuecher }
603f2841d37SMarkus Klotzbuecher return total;
604f2841d37SMarkus Klotzbuecher }
605f2841d37SMarkus Klotzbuecher
606f2841d37SMarkus Klotzbuecher void
xyzModem_stream_close(int * err)607f2841d37SMarkus Klotzbuecher xyzModem_stream_close (int *err)
608f2841d37SMarkus Klotzbuecher {
6097d0432c9SWolfgang Denk diag_printf
6107d0432c9SWolfgang Denk ("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
6117d0432c9SWolfgang Denk xyz.crc_mode ? "CRC" : "Cksum", xyz.total_SOH, xyz.total_STX,
6127d0432c9SWolfgang Denk xyz.total_CAN, xyz.total_retries);
613f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_flush ());
614f2841d37SMarkus Klotzbuecher }
615f2841d37SMarkus Klotzbuecher
616cf48eb9aSWolfgang Denk /* Need to be able to clean out the input buffer, so have to take the */
617cf48eb9aSWolfgang Denk /* getc */
6187d0432c9SWolfgang Denk void
xyzModem_stream_terminate(bool abort,int (* getc)(void))6197d0432c9SWolfgang Denk xyzModem_stream_terminate (bool abort, int (*getc) (void))
620f2841d37SMarkus Klotzbuecher {
621f2841d37SMarkus Klotzbuecher int c;
622f2841d37SMarkus Klotzbuecher
6237d0432c9SWolfgang Denk if (abort)
6247d0432c9SWolfgang Denk {
625f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dprintf ("!!!! TRANSFER ABORT !!!!\n"));
6267d0432c9SWolfgang Denk switch (xyz.mode)
6277d0432c9SWolfgang Denk {
628f2841d37SMarkus Klotzbuecher case xyzModem_xmodem:
629f2841d37SMarkus Klotzbuecher case xyzModem_ymodem:
630cf48eb9aSWolfgang Denk /* The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal */
631cf48eb9aSWolfgang Denk /* number of Backspaces is a friendly way to get the other end to abort. */
632f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
633f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
634f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
635f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
636f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
637f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
638f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
639f2841d37SMarkus Klotzbuecher CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
640cf48eb9aSWolfgang Denk /* Now consume the rest of what's waiting on the line. */
641f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dprintf ("Flushing serial line.\n"));
642f2841d37SMarkus Klotzbuecher xyzModem_flush ();
643f2841d37SMarkus Klotzbuecher xyz.at_eof = true;
644f2841d37SMarkus Klotzbuecher break;
645f2841d37SMarkus Klotzbuecher #ifdef xyzModem_zmodem
646f2841d37SMarkus Klotzbuecher case xyzModem_zmodem:
647cf48eb9aSWolfgang Denk /* Might support it some day I suppose. */
648f2841d37SMarkus Klotzbuecher #endif
649f2841d37SMarkus Klotzbuecher break;
650f2841d37SMarkus Klotzbuecher }
6517d0432c9SWolfgang Denk }
6527d0432c9SWolfgang Denk else
6537d0432c9SWolfgang Denk {
654f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dprintf ("Engaging cleanup mode...\n"));
655cf48eb9aSWolfgang Denk /*
656cf48eb9aSWolfgang Denk * Consume any trailing crap left in the inbuffer from
65716263087SMike Williams * previous received blocks. Since very few files are an exact multiple
658cf48eb9aSWolfgang Denk * of the transfer block size, there will almost always be some gunk here.
659cf48eb9aSWolfgang Denk * If we don't eat it now, RedBoot will think the user typed it.
660cf48eb9aSWolfgang Denk */
661f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dprintf ("Trailing gunk:\n"));
662e153b13cSJeroen Hofstee while ((c = (*getc) ()) > -1)
663e153b13cSJeroen Hofstee ;
664f2841d37SMarkus Klotzbuecher ZM_DEBUG (zm_dprintf ("\n"));
665cf48eb9aSWolfgang Denk /*
666cf48eb9aSWolfgang Denk * Make a small delay to give terminal programs like minicom
667cf48eb9aSWolfgang Denk * time to get control again after their file transfer program
668cf48eb9aSWolfgang Denk * exits.
669cf48eb9aSWolfgang Denk */
670f2841d37SMarkus Klotzbuecher CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
671f2841d37SMarkus Klotzbuecher }
672f2841d37SMarkus Klotzbuecher }
673f2841d37SMarkus Klotzbuecher
674f2841d37SMarkus Klotzbuecher char *
xyzModem_error(int err)675f2841d37SMarkus Klotzbuecher xyzModem_error (int err)
676f2841d37SMarkus Klotzbuecher {
6777d0432c9SWolfgang Denk switch (err)
6787d0432c9SWolfgang Denk {
679f2841d37SMarkus Klotzbuecher case xyzModem_access:
680f2841d37SMarkus Klotzbuecher return "Can't access file";
681f2841d37SMarkus Klotzbuecher break;
682f2841d37SMarkus Klotzbuecher case xyzModem_noZmodem:
683f2841d37SMarkus Klotzbuecher return "Sorry, zModem not available yet";
684f2841d37SMarkus Klotzbuecher break;
685f2841d37SMarkus Klotzbuecher case xyzModem_timeout:
686f2841d37SMarkus Klotzbuecher return "Timed out";
687f2841d37SMarkus Klotzbuecher break;
688f2841d37SMarkus Klotzbuecher case xyzModem_eof:
689f2841d37SMarkus Klotzbuecher return "End of file";
690f2841d37SMarkus Klotzbuecher break;
691f2841d37SMarkus Klotzbuecher case xyzModem_cancel:
692f2841d37SMarkus Klotzbuecher return "Cancelled";
693f2841d37SMarkus Klotzbuecher break;
694f2841d37SMarkus Klotzbuecher case xyzModem_frame:
695f2841d37SMarkus Klotzbuecher return "Invalid framing";
696f2841d37SMarkus Klotzbuecher break;
697f2841d37SMarkus Klotzbuecher case xyzModem_cksum:
698f2841d37SMarkus Klotzbuecher return "CRC/checksum error";
699f2841d37SMarkus Klotzbuecher break;
700f2841d37SMarkus Klotzbuecher case xyzModem_sequence:
701f2841d37SMarkus Klotzbuecher return "Block sequence error";
702f2841d37SMarkus Klotzbuecher break;
703f2841d37SMarkus Klotzbuecher default:
704f2841d37SMarkus Klotzbuecher return "Unknown error";
705f2841d37SMarkus Klotzbuecher break;
706f2841d37SMarkus Klotzbuecher }
707f2841d37SMarkus Klotzbuecher }
708f2841d37SMarkus Klotzbuecher
709cf48eb9aSWolfgang Denk /*
710cf48eb9aSWolfgang Denk * RedBoot interface
711cf48eb9aSWolfgang Denk */
712