]> Git Repo - J-u-boot.git/blob - common/xyzModem.c
Merge patch series "examples: fix building on arm64"
[J-u-boot.git] / common / xyzModem.c
1 // SPDX-License-Identifier: eCos-2.0
2 /*
3  *==========================================================================
4  *
5  *      xyzModem.c
6  *
7  *      RedBoot stream handler for xyzModem protocol
8  *
9  *==========================================================================
10  *#####DESCRIPTIONBEGIN####
11  *
12  * Author(s):    gthomas
13  * Contributors: gthomas, tsmith, Yoshinori Sato
14  * Date:         2000-07-14
15  * Purpose:
16  * Description:
17  *
18  * This code is part of RedBoot (tm).
19  *
20  *####DESCRIPTIONEND####
21  *
22  *==========================================================================
23  */
24 #include <xyzModem.h>
25 #include <stdarg.h>
26 #include <time.h>
27 #include <u-boot/crc.h>
28 #include <watchdog.h>
29 #include <env.h>
30 #include <vsprintf.h>
31
32 /* Assumption - run xyzModem protocol over the console port */
33
34 /* Values magic to the protocol */
35 #define SOH 0x01
36 #define STX 0x02
37 #define ETX 0x03                /* ^C for interrupt */
38 #define EOT 0x04
39 #define ACK 0x06
40 #define BSP 0x08
41 #define NAK 0x15
42 #define CAN 0x18
43 #define EOF 0x1A                /* ^Z for DOS officionados */
44
45 /* Data & state local to the protocol */
46 static struct
47 {
48   int *__chan;
49   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;
55   bool first_xmodem_packet;
56   ulong initial_time, timeout;
57   unsigned long file_length, read_length;
58 } xyz;
59
60 #define xyzModem_CHAR_TIMEOUT            2000   /* 2 seconds */
61 #define xyzModem_MAX_RETRIES             20
62 #define xyzModem_MAX_RETRIES_WITH_CRC    10
63 #define xyzModem_CAN_COUNT                3     /* Wait for 3 CAN before quitting */
64
65 typedef int cyg_int32;
66 static int
67 CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c)
68 {
69
70   ulong now = get_timer(0);
71   schedule();
72   while (!tstc ())
73     {
74       if (get_timer(now) > xyzModem_CHAR_TIMEOUT)
75         break;
76     }
77   if (tstc ())
78     {
79       *c = getchar();
80       return 1;
81     }
82   return 0;
83 }
84
85 static void
86 CYGACC_COMM_IF_PUTC (char x, char y)
87 {
88   putc (y);
89 }
90
91 /* Validate a hex character */
92 __inline__ static bool
93 _is_hex (char c)
94 {
95   return (((c >= '0') && (c <= '9')) ||
96           ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f')));
97 }
98
99 /* Convert a single hex nibble */
100 __inline__ static int
101 _from_hex (char c)
102 {
103   int ret = 0;
104
105   if ((c >= '0') && (c <= '9'))
106     {
107       ret = (c - '0');
108     }
109   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;
118 }
119
120 /* Convert a character to lower case */
121 __inline__ static char
122 _tolower (char c)
123 {
124   if ((c >= 'A') && (c <= 'Z'))
125     {
126       c = (c - 'A') + 'a';
127     }
128   return c;
129 }
130
131 /* Parse (scan) a number */
132 static bool
133 parse_num (char *s, unsigned long *val, char **es, char *delim)
134 {
135   bool first = true;
136   int radix = 10;
137   char c;
138   unsigned long result = 0;
139   int digit;
140
141   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 */
155           result = (result * radix) + digit;
156         }
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         }
170     }
171   *val = result;
172   if (es != (char **) 0)
173     {
174       *es = s;
175     }
176   return true;
177 }
178
179 #if defined(DEBUG) && !CONFIG_IS_ENABLED(USE_TINY_PRINTF)
180 /*
181  * Note: this debug setup works by storing the strings in a fixed buffer
182  */
183 static char zm_debug_buf[8192];
184 static char *zm_out = zm_debug_buf;
185 static char *zm_out_start = zm_debug_buf;
186
187 static int
188 zm_dprintf(char *fmt, ...)
189 {
190         int len;
191         va_list args;
192
193         va_start(args, fmt);
194         len = diag_vsprintf(zm_out, fmt, args);
195         va_end(args);
196         zm_out += len;
197         return len;
198 }
199
200 static void
201 zm_flush (void)
202 {
203   zm_out = zm_out_start;
204 }
205
206 static void
207 zm_dump_buf (void *buf, int len)
208 {
209
210 }
211
212 static unsigned char zm_buf[2048];
213 static unsigned char *zm_bp;
214
215 static void
216 zm_new (void)
217 {
218   zm_bp = zm_buf;
219 }
220
221 static void
222 zm_save (unsigned char c)
223 {
224   *zm_bp++ = c;
225 }
226
227 static void
228 zm_dump (int line)
229 {
230   zm_dprintf ("Packet at line: %d\n", line);
231   zm_dump_buf (zm_buf, zm_bp - zm_buf);
232 }
233
234 #define ZM_DEBUG(x) x
235 #else
236 #define ZM_DEBUG(x)
237 #endif
238
239 /* Wait for the line to go idle */
240 static void
241 xyzModem_flush (void)
242 {
243   int res;
244   char c;
245   while (true)
246     {
247       res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
248       if (!res)
249         return;
250     }
251 }
252
253 static int
254 xyzModem_get_hdr (void)
255 {
256   char c;
257   int res;
258   bool hdr_found = false;
259   int i, can_total, hdr_chars;
260   unsigned short cksum;
261
262   ZM_DEBUG (zm_new ());
263   /* Find the start of a header */
264   can_total = 0;
265   hdr_chars = 0;
266
267   if (xyz.tx_ack)
268     {
269       CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
270       xyz.tx_ack = false;
271     }
272   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:
289             case ETX:
290               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         }
323     }
324
325   /* 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;
332     }
333   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;
339     }
340   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;
362     }
363   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         }
372     }
373   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;
383     }
384   /* Verify checksum/CRC */
385   if (xyz.crc_mode)
386     {
387       cksum = crc16_ccitt(0, xyz.pkt, xyz.len);
388       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         }
394     }
395   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         }
409     }
410   /* If we get here, the message passes [structural] muster */
411   return 0;
412 }
413
414 static
415 ulong
416 xyzModem_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
427 int
428 xyzModem_stream_open (connection_info_t * info, int *err)
429 {
430   int stat = 0;
431   int retries = xyzModem_MAX_RETRIES;
432   int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
433
434 /*    ZM_DEBUG(zm_out = zm_out_start); */
435 #ifdef xyzModem_zmodem
436   if (info->mode == xyzModem_zmodem)
437     {
438       *err = xyzModem_noZmodem;
439       return -1;
440     }
441 #endif
442
443 /* TODO: CHECK ! */
444   int dummy = 0;
445   xyz.__chan = &dummy;
446   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;
455   xyz.read_length = 0;
456   xyz.file_length = 0;
457   xyz.first_xmodem_packet = false;
458   xyz.initial_time = get_timer(0);
459   xyz.timeout = xyzModem_get_initial_timeout();
460
461   CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
462
463   if (xyz.mode == xyzModem_xmodem)
464     {
465       /* X-modem doesn't have an information header - exit here */
466       xyz.first_xmodem_packet = true;
467       xyz.next_blk = 1;
468       return 0;
469     }
470
471   while (!(xyz.timeout && get_timer(xyz.initial_time) > xyz.timeout))
472     {
473       if (--retries <= 0)
474         {
475           retries = xyzModem_MAX_RETRIES;
476           crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
477           xyz.crc_mode = true;
478         }
479       stat = xyzModem_get_hdr ();
480       if (stat == 0)
481         {
482           /* Y-modem file information header */
483           if (xyz.blk == 0)
484             {
485               /* skip filename */
486               while (*xyz.bufp++);
487               /* get the length */
488               parse_num ((char *) xyz.bufp, &xyz.file_length, NULL, " ");
489               /* 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         }
509     }
510   *err = stat;
511   ZM_DEBUG (zm_flush ());
512   return -1;
513 }
514
515 int
516 xyzModem_stream_read (char *buf, int size, int *err)
517 {
518   int stat, total, len;
519   int retries;
520
521   total = 0;
522   stat = xyzModem_cancel;
523   /* Try and get 'size' bytes into the buffer */
524   while (!xyz.at_eof && xyz.len >= 0 && (size > 0))
525     {
526       if (xyz.len == 0)
527         {
528           retries = xyzModem_MAX_RETRIES;
529           while (retries-- > 0)
530             {
531               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
539               stat = xyzModem_get_hdr ();
540               if (stat == 0)
541                 {
542                   if (xyz.mode == xyzModem_xmodem && xyz.first_xmodem_packet)
543                     xyz.first_xmodem_packet = false;
544                   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;
550
551                       if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0)
552                         {
553                           /* 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                         }
566
567                       /*
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                         }
581                       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                     }
612                   else
613                     stat = 0;
614                   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             }
621           if (stat < 0 && (!xyz.first_xmodem_packet || stat != xyzModem_timeout))
622             {
623               *err = stat;
624               xyz.len = -1;
625               return total;
626             }
627         }
628       /* Don't "read" data from the EOF protocol package */
629       if (!xyz.at_eof && xyz.len > 0)
630         {
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         }
641     }
642   return total;
643 }
644
645 void
646 xyzModem_stream_close (int *err)
647 {
648   ZM_DEBUG (zm_dprintf
649     ("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,
651      xyz.total_CAN, xyz.total_retries));
652   ZM_DEBUG (zm_flush ());
653 }
654
655 /* Need to be able to clean out the input buffer, so have to take the */
656 /* getc */
657 void
658 xyzModem_stream_terminate (bool abort, int (*getc) (void))
659 {
660   int c;
661
662   if (abort)
663     {
664       ZM_DEBUG (zm_dprintf ("!!!! TRANSFER ABORT !!!!\n"));
665       switch (xyz.mode)
666         {
667         case xyzModem_xmodem:
668         case xyzModem_ymodem:
669           /* 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. */
671           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);
679           /* Now consume the rest of what's waiting on the line. */
680           ZM_DEBUG (zm_dprintf ("Flushing serial line.\n"));
681           xyzModem_flush ();
682           xyz.at_eof = true;
683           break;
684 #ifdef xyzModem_zmodem
685         case xyzModem_zmodem:
686           /* Might support it some day I suppose. */
687 #endif
688           break;
689         }
690     }
691   else
692     {
693       ZM_DEBUG (zm_dprintf ("Engaging cleanup mode...\n"));
694       /*
695        * Consume any trailing crap left in the inbuffer from
696        * previous received blocks. Since very few files are an exact multiple
697        * 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        */
700       ZM_DEBUG (zm_dprintf ("Trailing gunk:\n"));
701       while ((c = (*getc) ()) > -1)
702         ;
703       ZM_DEBUG (zm_dprintf ("\n"));
704       /*
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        */
709       CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
710     }
711 }
712
713 char *
714 xyzModem_error (int err)
715 {
716   switch (err)
717     {
718     case xyzModem_access:
719       return "Can't access file";
720       break;
721     case xyzModem_noZmodem:
722       return "Sorry, zModem not available yet";
723       break;
724     case xyzModem_timeout:
725       return "Timed out";
726       break;
727     case xyzModem_eof:
728       return "End of file";
729       break;
730     case xyzModem_cancel:
731       return "Cancelled";
732       break;
733     case xyzModem_frame:
734       return "Invalid framing";
735       break;
736     case xyzModem_cksum:
737       return "CRC/checksum error";
738       break;
739     case xyzModem_sequence:
740       return "Block sequence error";
741       break;
742     default:
743       return "Unknown error";
744       break;
745     }
746 }
747
748 /*
749  * RedBoot interface
750  */
This page took 0.064764 seconds and 4 git commands to generate.