Use CONFIG_TPLINK_IMAGE_HEADER define to select TP-Link img header, plus minor change...
[oweals/u-boot_mod.git] / u-boot / common / xyzModem.c
1 /*
2  *==========================================================================
3  *
4  *        xyzModem.c
5  *
6  *        RedBoot stream handler for xyzModem protocol
7  *
8  *==========================================================================
9  *####ECOSGPLCOPYRIGHTBEGIN####
10  * -------------------------------------------
11  * This file is part of eCos, the Embedded Configurable Operating System.
12  * Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
13  * Copyright (C) 2002 Gary Thomas
14  *
15  * eCos is free software; you can redistribute it and/or modify it under
16  * the terms of the GNU General Public License as published by the Free
17  * Software Foundation; either version 2 or (at your option) any later version.
18  *
19  * eCos is distributed in the hope that it will be useful, but WITHOUT ANY
20  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
22  * for more details.
23  *
24  * You should have received a copy of the GNU General Public License along
25  * with eCos; if not, write to the Free Software Foundation, Inc.,
26  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
27  *
28  * As a special exception, if other files instantiate templates or use macros
29  * or inline functions from this file, or you compile this file and link it
30  * with other works to produce a work based on this file, this file does not
31  * by itself cause the resulting work to be covered by the GNU General Public
32  * License. However the source code for this file must still be made available
33  * in accordance with section (3) of the GNU General Public License.
34  *
35  * This exception does not invalidate any other reasons why a work based on
36  * this file might be covered by the GNU General Public License.
37  *
38  * Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
39  * at http: *sources.redhat.com/ecos/ecos-license/
40  * -------------------------------------------
41  *####ECOSGPLCOPYRIGHTEND####
42  *==========================================================================
43  *#####DESCRIPTIONBEGIN####
44  *
45  * Author(s):   gthomas
46  * Contributors: gthomas, tsmith, Yoshinori Sato
47  * Date:                 2000-07-14
48  * Purpose:
49  * Description:
50  *
51  * This code is part of RedBoot (tm).
52  *
53  *####DESCRIPTIONEND####
54  *
55  *==========================================================================
56  */
57 #include <common.h>
58 #include <xyzModem.h>
59 #include <stdarg.h>
60 #include <crc.h>
61
62 /* Assumption - run xyzModem protocol over the console port */
63
64 /* Values magic to the protocol */
65 #define SOH 0x01
66 #define STX 0x02
67 #define EOT 0x04
68 #define ACK 0x06
69 #define BSP 0x08
70 #define NAK 0x15
71 #define CAN 0x18
72 #define EOF 0x1A /* ^Z for DOS officionados */
73
74 #define USE_YMODEM_LENGTH
75
76 /* Data & state local to the protocol */
77 static struct {
78         int *__chan;
79         unsigned char pkt[1024], *bufp;
80         unsigned char blk,cblk,crc1,crc2;
81         unsigned char next_blk;  /* Expected block */
82         int len, mode, total_retries;
83         int total_SOH, total_STX, total_CAN;
84         unsigned int crc_mode, at_eof, tx_ack;
85 #ifdef USE_YMODEM_LENGTH
86         unsigned long file_length, read_length;
87 #endif
88 } xyz;
89
90 #define xyzModem_CHAR_TIMEOUT                   2000    /* 2 seconds */
91 #define xyzModem_MAX_RETRIES                    20
92 #define xyzModem_MAX_RETRIES_WITH_CRC   10
93 #define xyzModem_CAN_COUNT                              3               /* Wait for 3 CAN before quitting */
94
95 int comm_if_getc_tout(char *c)
96 {
97         unsigned long counter=0;
98         #define DELAY 20
99
100         while(!tstc() && (counter < xyzModem_CHAR_TIMEOUT*1000/DELAY)){
101                 udelay(DELAY);
102                 counter++;
103         }
104
105         if(tstc()){
106                 *c=getc();
107                 return 1;
108         }
109
110         return 0;
111 }
112
113 /* Validate a hex character */
114 inline static unsigned int _is_hex(char c)
115 {
116         return(((c >= '0') && (c <= '9')) ||
117                    ((c >= 'A') && (c <= 'F')) ||
118                    ((c >= 'a') && (c <= 'f')));
119 }
120
121 /* Convert a single hex nibble */
122 inline static int _from_hex(char c)
123 {
124         int ret = 0;
125
126         if((c >= '0') && (c <= '9')){
127                 ret = (c - '0');
128         } else if((c >= 'a') && (c <= 'f')){
129                 ret = (c - 'a' + 0x0a);
130         } else if((c >= 'A') && (c <= 'F')){
131                 ret = (c - 'A' + 0x0A);
132         }
133
134         return ret;
135 }
136
137 /* Convert a character to lower case */
138 inline static char _tolower(char c)
139 {
140         if((c >= 'A') && (c <= 'Z')){
141                 c = (c - 'A') + 'a';
142         }
143
144         return c;
145 }
146
147 /* Parse (scan) a number */
148 unsigned int parse_num(char *s, unsigned long *val, char **es, char *delim)
149 {
150         unsigned int first = 1;
151         int radix = 10;
152         char c;
153         unsigned long result = 0;
154         int digit;
155
156         while(*s == ' ')
157                 s++;
158
159         while(*s){
160                 if(first && (s[0] == '0') && (_tolower(s[1]) == 'x')){
161                         radix = 16;
162                         s += 2;
163                 }
164
165                 first = 0;
166                 c = *s++;
167
168                 if(_is_hex(c) && ((digit = _from_hex(c)) < radix)){
169                         /* Valid digit */
170                         result = (result * radix) + digit;
171                 } else {
172                         if(delim != (char *)0){
173                                 /* See if this character is one of the delimiters */
174                                 char *dp = delim;
175
176                                 while(*dp && (c != *dp))
177                                         dp++;
178
179                                 /* Found a good delimiter */
180                                 if(*dp)
181                                         break;
182                         }
183
184                         /* Malformatted number */
185                         return 0;
186                 }
187         }
188
189         *val = result;
190
191         if(es != (char **)0){
192                 *es = s;
193         }
194
195         return 1;
196 }
197
198 /* Wait for the line to go idle */
199 static void xyzModem_flush(void)
200 {
201         int res;
202         char c;
203
204         while(1){
205                 res = comm_if_getc_tout(&c);
206                 if(!res)
207                         return;
208         }
209 }
210
211 static int xyzModem_get_hdr(void)
212 {
213         char c;
214         int res;
215         unsigned int hdr_found = 0;
216         int i, can_total, hdr_chars;
217         unsigned short cksum;
218
219         /* Find the start of a header */
220         can_total = 0;
221         hdr_chars = 0;
222
223         if(xyz.tx_ack){
224                 putc(ACK);
225                 xyz.tx_ack = 0;
226         }
227
228         while(!hdr_found){
229                 res = comm_if_getc_tout(&c);
230
231                 if(res){
232                         hdr_chars++;
233
234                         switch(c){
235                         case SOH:
236                                 xyz.total_SOH++;
237                         case STX:
238                                 if(c == STX)
239                                         xyz.total_STX++;
240
241                                 hdr_found = 1;
242                                 break;
243                         case CAN:
244                                 xyz.total_CAN++;
245
246                                 if(++can_total == xyzModem_CAN_COUNT){
247                                         return xyzModem_cancel;
248                                 } else {
249                                         /* Wait for multiple CAN to avoid early quits */
250                                         break;
251                                 }
252                         case EOT:
253                                 /* EOT only supported if no noise */
254                                 if(hdr_chars == 1){
255                                         putc(ACK);
256                                         return xyzModem_eof;
257                                 }
258                         default:
259                                 /* Ignore, waiting for start of header */
260                                 ;
261                         }
262                 } else {
263                         /* Data stream timed out */
264                         xyzModem_flush(); /* Toss any current input */
265                         udelay(250000);
266                         return xyzModem_timeout;
267                 }
268         }
269
270         /* Header found, now read the data */
271         res = comm_if_getc_tout((char *)&xyz.blk);
272         if(!res){
273                 return xyzModem_timeout;
274         }
275
276         res = comm_if_getc_tout((char *)&xyz.cblk);
277         if(!res){
278                 return xyzModem_timeout;
279         }
280
281         xyz.len = (c == SOH) ? 128 : 1024;
282         xyz.bufp = xyz.pkt;
283         for(i = 0;  i < xyz.len;  i++){
284                 res = comm_if_getc_tout(&c);
285                 if(res){
286                         xyz.pkt[i] = c;
287                 } else {
288                         return xyzModem_timeout;
289                 }
290         }
291
292         res = comm_if_getc_tout((char *)&xyz.crc1);
293         if(!res){
294                 return xyzModem_timeout;
295         }
296
297         if(xyz.crc_mode){
298                 res = comm_if_getc_tout((char *)&xyz.crc2);
299                 if(!res){
300                         return xyzModem_timeout;
301                 }
302         }
303
304         /* Validate the message */
305         if((xyz.blk ^ xyz.cblk) != (unsigned char)0xFF){
306                 xyzModem_flush();
307                 return xyzModem_frame;
308         }
309
310         /* Verify checksum/CRC */
311         if(xyz.crc_mode){
312                 cksum = cyg_crc16(xyz.pkt, xyz.len);
313                 if(cksum != ((xyz.crc1 << 8) | xyz.crc2)){
314                         return xyzModem_cksum;
315                 }
316         } else {
317                 cksum = 0;
318                 for(i = 0;  i < xyz.len;  i++){
319                         cksum += xyz.pkt[i];
320                 }
321
322                 if(xyz.crc1 != (cksum & 0xFF)){
323                         return xyzModem_cksum;
324                 }
325         }
326
327         /* If we get here, the message passes [structural] muster */
328         return 0;
329 }
330
331 int xyzModem_stream_open(connection_info_t *info, int *err)
332 {
333         int stat = 0;
334         int retries = xyzModem_MAX_RETRIES;
335         int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
336
337 #ifdef xyzModem_zmodem
338         if(info->mode == xyzModem_zmodem){
339                 *err = xyzModem_noZmodem;
340                 return -1;
341         }
342 #endif
343
344 /* TODO: CHECK ! */
345         int dummy;
346         xyz.__chan=&dummy;
347         xyz.len = 0;
348         xyz.crc_mode = 1;
349         xyz.at_eof = 0;
350         xyz.tx_ack = 0;
351         xyz.mode = info->mode;
352         xyz.total_retries = 0;
353         xyz.total_SOH = 0;
354         xyz.total_STX = 0;
355         xyz.total_CAN = 0;
356 #ifdef USE_YMODEM_LENGTH
357         xyz.read_length = 0;
358         xyz.file_length = 0;
359 #endif
360
361         putc(xyz.crc_mode ? 'C' : NAK);
362
363         if(xyz.mode == xyzModem_xmodem){
364                 /* X-modem doesn't have an information header - exit here */
365                 xyz.next_blk = 1;
366                 return 0;
367         }
368
369         while(retries-- > 0){
370                 stat = xyzModem_get_hdr();
371                 if(stat == 0){
372                         /* Y-modem file information header */
373                         if(xyz.blk == 0){
374 #ifdef USE_YMODEM_LENGTH
375                                 /* skip filename */
376                                 while(*xyz.bufp++);
377
378                                 /* get the length */
379                                 parse_num((char *)xyz.bufp, &xyz.file_length, NULL, " ");
380 #endif
381                                 /* The rest of the file name data block quietly discarded */
382                                 xyz.tx_ack = 1;
383                         }
384
385                         xyz.next_blk = 1;
386                         xyz.len = 0;
387
388                         return 0;
389                 } else if(stat == xyzModem_timeout){
390                         if(--crc_retries <= 0)
391                                 xyz.crc_mode = 0;
392
393                         /* Extra delay for startup */
394                         udelay(5*100000);
395
396                         putc(xyz.crc_mode ? 'C' : NAK);
397                         xyz.total_retries++;
398                 }
399
400                 if(stat == xyzModem_cancel){
401                         break;
402                 }
403         }
404
405         *err = stat;
406
407         return -1;
408 }
409
410 int xyzModem_stream_read(char *buf, int size, int *err)
411 {
412         int stat, total, len;
413         int retries;
414
415         total = 0;
416         stat = xyzModem_cancel;
417
418         /* Try and get 'size' bytes into the buffer */
419         while(!xyz.at_eof && (size > 0)){
420                 if(xyz.len == 0){
421                         retries = xyzModem_MAX_RETRIES;
422
423                         while(retries-- > 0){
424                                 stat = xyzModem_get_hdr();
425                                 if(stat == 0){
426                                         if(xyz.blk == xyz.next_blk){
427                                                 xyz.tx_ack = 1;
428                                                 xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
429
430 #if defined(xyzModem_zmodem) || defined(USE_YMODEM_LENGTH)
431                                                 if(xyz.mode == xyzModem_xmodem || xyz.file_length == 0){
432 #else
433                                                 if(1){
434 #endif
435                                                         /* Data blocks can be padded with ^Z (EOF) characters */
436                                                         /* This code tries to detect and remove them */
437                                                         if( (xyz.bufp[xyz.len-1] == EOF) &&
438                                                                 (xyz.bufp[xyz.len-2] == EOF) &&
439                                                                 (xyz.bufp[xyz.len-3] == EOF) ){
440                                                                 while(xyz.len && (xyz.bufp[xyz.len-1] == EOF)){
441                                                                         xyz.len--;
442                                                                 }
443                                                         }
444                                                 }
445
446 #ifdef USE_YMODEM_LENGTH
447                                                 /*
448                                                  * See if accumulated length exceeds that of the file.
449                                                  * If so, reduce size (i.e., cut out pad bytes)
450                                                  * Only do this for Y-modem (and Z-modem should it ever
451                                                  * be supported since it can fall back to Y-modem mode).
452                                                  */
453                                                 if(xyz.mode != xyzModem_xmodem && 0 != xyz.file_length){
454                                                         xyz.read_length += xyz.len;
455                                                         if(xyz.read_length > xyz.file_length){
456                                                                 xyz.len -= (xyz.read_length - xyz.file_length);
457                                                         }
458                                                 }
459 #endif
460                                                 break;
461                                         } else if(xyz.blk == ((xyz.next_blk - 1) & 0xFF)){
462                                                 /* Just re-ACK this so sender will get on with it */
463                                                 putc(ACK);
464                                                 continue; /* Need new header */
465                                         } else {
466                                                 stat = xyzModem_sequence;
467                                         }
468                                 }
469
470                                 if(stat == xyzModem_cancel){
471                                         break;
472                                 }
473
474                                 if(stat == xyzModem_eof){
475                                         putc(ACK);
476
477                                         if(xyz.mode == xyzModem_ymodem){
478                                                 putc(xyz.crc_mode ? 'C' : NAK);
479                                                 xyz.total_retries++;
480                                                 stat = xyzModem_get_hdr();
481                                                 putc(ACK);
482                                         }
483
484                                         xyz.at_eof = 1;
485
486                                         break;
487                                 }
488
489                                 putc(xyz.crc_mode ? 'C' : NAK);
490                                 xyz.total_retries++;
491                         }
492
493                         if(stat < 0){
494                                 *err = stat;
495                                 xyz.len = -1;
496
497                                 return total;
498                         }
499
500                 }
501
502                 /* Don't "read" data from the EOF protocol package */
503                 if(!xyz.at_eof){
504                         len = xyz.len;
505                         if(size < len)
506                                 len = size;
507
508                         memcpy(buf, xyz.bufp, len);
509                         size     -= len;
510                         buf      += len;
511                         total    += len;
512                         xyz.len  -= len;
513                         xyz.bufp += len;
514                 }
515         }
516
517         return total;
518 }
519
520 void xyzModem_stream_close(int *err)
521 {
522         printf("\n\nxyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
523                                 xyz.crc_mode ? "CRC" : "Cksum",
524                                 xyz.total_SOH, xyz.total_STX, xyz.total_CAN,
525                                 xyz.total_retries);
526 }
527
528 /* Need to be able to clean out the input buffer, so have to take the */
529 /* getc */
530 void xyzModem_stream_terminate(unsigned int abort, int (*getc)(void))
531 {
532         int c;
533
534         if(abort){
535                 switch(xyz.mode){
536                 case xyzModem_xmodem:
537                 case xyzModem_ymodem:
538                         /* The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal */
539                         /* number of Backspaces is a friendly way to get the other end to abort. */
540                         putc(CAN);
541                         putc(CAN);
542                         putc(CAN);
543                         putc(CAN);
544                         putc(BSP);
545                         putc(BSP);
546                         putc(BSP);
547                         putc(BSP);
548                         /* Now consume the rest of what's waiting on the line. */
549                         xyzModem_flush();
550                         xyz.at_eof = 1;
551                         break;
552 #ifdef xyzModem_zmodem
553                 case xyzModem_zmodem:
554                         /* Might support it some day I suppose. */
555 #endif
556                         break;
557                 }
558         } else {
559                 /*
560                  * Consume any trailing crap left in the inbuffer from
561                  * previous recieved blocks. Since very few files are an exact multiple
562                  * of the transfer block size, there will almost always be some gunk here.
563                  * If we don't eat it now, RedBoot will think the user typed it.
564                  */
565
566                 while((c = (*getc)()) > -1);
567                 /*
568                 * Make a small delay to give terminal programs like minicom
569                 * time to get control again after their file transfer program
570                 * exits.
571                 */
572                 udelay(250000);
573         }
574 }
575
576 char *xyzModem_error(int err)
577 {
578         switch (err) {
579         case xyzModem_access:
580                 return "can't access file";
581                 break;
582         case xyzModem_noZmodem:
583                 return "sorry, zModem not available yet";
584                 break;
585         case xyzModem_timeout:
586                 return "timed out";
587                 break;
588         case xyzModem_eof:
589                 return "end of file";
590                 break;
591         case xyzModem_cancel:
592                 return "cancelled";
593                 break;
594         case xyzModem_frame:
595                 return "invalid framing";
596                 break;
597         case xyzModem_cksum:
598                 return "CRC/checksum error";
599                 break;
600         case xyzModem_sequence:
601                 return "block sequence error";
602                 break;
603         default:
604                 return "unknown error";
605                 break;
606         }
607 }