94901a9b18118e2a319c7125ed08f340dd1847d3
[pandora-x-loader.git] / common / cmd_load.c
1 /*
2  * (C) Copyright 2000-2004
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 /*
25  * Serial up- and download support
26  */
27 #include <common.h>
28
29 #define putc serial_putc
30 #define tstc serial_tstc
31
32 /*******************************************************
33  * Routine: delay
34  * Description: spinning delay to use before udelay works
35  ******************************************************/
36 static inline void delay(unsigned long loops)
37 {
38         __asm__ volatile ("1:\n" "subs %0, %1, #1\n"
39                           "bne 1b":"=r" (loops):"0"(loops));
40 }
41 static inline void udelay(unsigned long us)
42 {
43         delay(us * 200); /* approximate */
44 }
45
46 #ifdef CFG_CMD_FAT
47 extern void * memcpy(void * dest,const void *src,size_t count);
48 #else
49 void * memcpy(void * dest,const void *src,size_t count)
50 {
51         char *tmp = (char *) dest, *s = (char *) src;
52
53         while (count--)
54                 *tmp++ = *s++;
55
56         return dest;
57 }
58 #endif
59
60 /* -------------------------------------------------------------------- */
61
62 #define XON_CHAR        17
63 #define XOFF_CHAR       19
64 #define START_CHAR      0x01
65 #define ETX_CHAR        0x03
66 #define END_CHAR        0x0D
67 #define SPACE           0x20
68 #define K_ESCAPE        0x23
69 #define SEND_TYPE       'S'
70 #define DATA_TYPE       'D'
71 #define ACK_TYPE        'Y'
72 #define NACK_TYPE       'N'
73 #define BREAK_TYPE      'B'
74 #define tochar(x) ((char) (((x) + SPACE) & 0xff))
75 #define untochar(x) ((int) (((x) - SPACE) & 0xff))
76
77 extern int os_data_count;
78 extern int os_data_header[8];
79
80 static void set_kerm_bin_mode(unsigned long *);
81 static int k_recv(void);
82 static ulong load_serial_bin (ulong offset);
83
84
85 char his_eol;        /* character he needs at end of packet */
86 int  his_pad_count;  /* number of pad chars he needs */
87 char his_pad_char;   /* pad chars he needs */
88 char his_quote;      /* quote chars he'll use */
89
90 int do_load_serial_bin (ulong offset, int baudrate)
91 {
92         ulong addr;
93         int rcode = 0;
94
95         printf ("## Ready for binary (kermit) download "
96                 "to 0x%08lX at %d bps...\n",
97                 offset,
98                 baudrate);
99         addr = load_serial_bin (offset);
100         if (addr == ~0) {
101                 printf ("## Binary (kermit) download aborted\n");
102                 rcode = 1;
103         } else {
104                 printf ("## Start Addr      = 0x%08lX\n", addr);
105         }
106
107         return rcode;
108 }
109
110
111 static ulong load_serial_bin (ulong offset)
112 {
113         int size, i;
114
115         set_kerm_bin_mode ((ulong *) offset);
116         size = k_recv ();
117
118         /*
119          * Gather any trailing characters (for instance, the ^D which
120          * is sent by 'cu' after sending a file), and give the
121          * box some time (100 * 1 ms)
122          */
123         for (i=0; i<100; ++i) {
124                 if (tstc()) {
125                         (void) getc();
126                 }
127                 udelay(1000);
128         }
129
130         printf("## Total Size      = 0x%08x = %d Bytes\n", size, size);
131
132         return offset;
133 }
134
135 void send_pad (void)
136 {
137         int count = his_pad_count;
138
139         while (count-- > 0)
140                 putc (his_pad_char);
141 }
142
143 /* converts escaped kermit char to binary char */
144 char ktrans (char in)
145 {
146         if ((in & 0x60) == 0x40) {
147                 return (char) (in & ~0x40);
148         } else if ((in & 0x7f) == 0x3f) {
149                 return (char) (in | 0x40);
150         } else
151                 return in;
152 }
153
154 int chk1 (char *buffer)
155 {
156         int total = 0;
157
158         while (*buffer) {
159                 total += *buffer++;
160         }
161         return (int) ((total + ((total >> 6) & 0x03)) & 0x3f);
162 }
163
164 void s1_sendpacket (char *packet)
165 {
166         send_pad ();
167         while (*packet) {
168                 putc (*packet++);
169         }
170 }
171
172 static char a_b[24];
173 void send_ack (int n)
174 {
175         a_b[0] = START_CHAR;
176         a_b[1] = tochar (3);
177         a_b[2] = tochar (n);
178         a_b[3] = ACK_TYPE;
179         a_b[4] = '\0';
180         a_b[4] = tochar (chk1 (&a_b[1]));
181         a_b[5] = his_eol;
182         a_b[6] = '\0';
183         s1_sendpacket (a_b);
184 }
185
186 void send_nack (int n)
187 {
188         a_b[0] = START_CHAR;
189         a_b[1] = tochar (3);
190         a_b[2] = tochar (n);
191         a_b[3] = NACK_TYPE;
192         a_b[4] = '\0';
193         a_b[4] = tochar (chk1 (&a_b[1]));
194         a_b[5] = his_eol;
195         a_b[6] = '\0';
196         s1_sendpacket (a_b);
197 }
198
199
200 /* os_data_* takes an OS Open image and puts it into memory, and
201    puts the boot header in an array named os_data_header
202
203    if image is binary, no header is stored in os_data_header.
204 */
205 void (*os_data_init) (void);
206 void (*os_data_char) (char new_char);
207 static int os_data_state, os_data_state_saved;
208 int os_data_count;
209 static int os_data_count_saved;
210 static char *os_data_addr, *os_data_addr_saved;
211 static char *bin_start_address;
212 int os_data_header[8];
213 static void bin_data_init (void)
214 {
215         os_data_state = 0;
216         os_data_count = 0;
217         os_data_addr = bin_start_address;
218 }
219 static void os_data_save (void)
220 {
221         os_data_state_saved = os_data_state;
222         os_data_count_saved = os_data_count;
223         os_data_addr_saved = os_data_addr;
224 }
225 static void os_data_restore (void)
226 {
227         os_data_state = os_data_state_saved;
228         os_data_count = os_data_count_saved;
229         os_data_addr = os_data_addr_saved;
230 }
231 static void bin_data_char (char new_char)
232 {
233         switch (os_data_state) {
234         case 0:                                 /* data */
235                 *os_data_addr++ = new_char;
236                 --os_data_count;
237                 break;
238         }
239 }
240 static void set_kerm_bin_mode (unsigned long *addr)
241 {
242         bin_start_address = (char *) addr;
243         os_data_init = bin_data_init;
244         os_data_char = bin_data_char;
245 }
246
247
248 /* k_data_* simply handles the kermit escape translations */
249 static int k_data_escape, k_data_escape_saved;
250 void k_data_init (void)
251 {
252         k_data_escape = 0;
253         os_data_init ();
254 }
255 void k_data_save (void)
256 {
257         k_data_escape_saved = k_data_escape;
258         os_data_save ();
259 }
260 void k_data_restore (void)
261 {
262         k_data_escape = k_data_escape_saved;
263         os_data_restore ();
264 }
265 void k_data_char (char new_char)
266 {
267         if (k_data_escape) {
268                 /* last char was escape - translate this character */
269                 os_data_char (ktrans (new_char));
270                 k_data_escape = 0;
271         } else {
272                 if (new_char == his_quote) {
273                         /* this char is escape - remember */
274                         k_data_escape = 1;
275                 } else {
276                         /* otherwise send this char as-is */
277                         os_data_char (new_char);
278                 }
279         }
280 }
281
282 #define SEND_DATA_SIZE  20
283 char send_parms[SEND_DATA_SIZE];
284 char *send_ptr;
285
286 /* handle_send_packet interprits the protocol info and builds and
287    sends an appropriate ack for what we can do */
288 void handle_send_packet (int n)
289 {
290         int length = 3;
291         int bytes;
292
293         /* initialize some protocol parameters */
294         his_eol = END_CHAR;             /* default end of line character */
295         his_pad_count = 0;
296         his_pad_char = '\0';
297         his_quote = K_ESCAPE;
298
299         /* ignore last character if it filled the buffer */
300         if (send_ptr == &send_parms[SEND_DATA_SIZE - 1])
301                 --send_ptr;
302         bytes = send_ptr - send_parms;  /* how many bytes we'll process */
303         do {
304                 if (bytes-- <= 0)
305                         break;
306                 /* handle MAXL - max length */
307                 /* ignore what he says - most I'll take (here) is 94 */
308                 a_b[++length] = tochar (94);
309                 if (bytes-- <= 0)
310                         break;
311                 /* handle TIME - time you should wait for my packets */
312                 /* ignore what he says - don't wait for my ack longer than 1 second */
313                 a_b[++length] = tochar (1);
314                 if (bytes-- <= 0)
315                         break;
316                 /* handle NPAD - number of pad chars I need */
317                 /* remember what he says - I need none */
318                 his_pad_count = untochar (send_parms[2]);
319                 a_b[++length] = tochar (0);
320                 if (bytes-- <= 0)
321                         break;
322                 /* handle PADC - pad chars I need */
323                 /* remember what he says - I need none */
324                 his_pad_char = ktrans (send_parms[3]);
325                 a_b[++length] = 0x40;   /* He should ignore this */
326                 if (bytes-- <= 0)
327                         break;
328                 /* handle EOL - end of line he needs */
329                 /* remember what he says - I need CR */
330                 his_eol = untochar (send_parms[4]);
331                 a_b[++length] = tochar (END_CHAR);
332                 if (bytes-- <= 0)
333                         break;
334                 /* handle QCTL - quote control char he'll use */
335                 /* remember what he says - I'll use '#' */
336                 his_quote = send_parms[5];
337                 a_b[++length] = '#';
338                 if (bytes-- <= 0)
339                         break;
340                 /* handle QBIN - 8-th bit prefixing */
341                 /* ignore what he says - I refuse */
342                 a_b[++length] = 'N';
343                 if (bytes-- <= 0)
344                         break;
345                 /* handle CHKT - the clock check type */
346                 /* ignore what he says - I do type 1 (for now) */
347                 a_b[++length] = '1';
348                 if (bytes-- <= 0)
349                         break;
350                 /* handle REPT - the repeat prefix */
351                 /* ignore what he says - I refuse (for now) */
352                 a_b[++length] = 'N';
353                 if (bytes-- <= 0)
354                         break;
355                 /* handle CAPAS - the capabilities mask */
356                 /* ignore what he says - I only do long packets - I don't do windows */
357                 a_b[++length] = tochar (2);     /* only long packets */
358                 a_b[++length] = tochar (0);     /* no windows */
359                 a_b[++length] = tochar (94);    /* large packet msb */
360                 a_b[++length] = tochar (94);    /* large packet lsb */
361         } while (0);
362
363         a_b[0] = START_CHAR;
364         a_b[1] = tochar (length);
365         a_b[2] = tochar (n);
366         a_b[3] = ACK_TYPE;
367         a_b[++length] = '\0';
368         a_b[length] = tochar (chk1 (&a_b[1]));
369         a_b[++length] = his_eol;
370         a_b[++length] = '\0';
371         s1_sendpacket (a_b);
372 }
373
374 /* k_recv receives a OS Open image file over kermit line */
375 static int k_recv (void)
376 {
377         char new_char;
378         char k_state, k_state_saved;
379         int sum;
380         int done;
381         int length;
382         int n, last_n;
383         int z = 0;
384         int len_lo, len_hi;
385
386         /* initialize some protocol parameters */
387         his_eol = END_CHAR;             /* default end of line character */
388         his_pad_count = 0;
389         his_pad_char = '\0';
390         his_quote = K_ESCAPE;
391
392         /* initialize the k_recv and k_data state machine */
393         done = 0;
394         k_state = 0;
395         k_data_init ();
396         k_state_saved = k_state;
397         k_data_save ();
398         n = 0;                          /* just to get rid of a warning */
399         last_n = -1;
400
401         /* expect this "type" sequence (but don't check):
402            S: send initiate
403            F: file header
404            D: data (multiple)
405            Z: end of file
406            B: break transmission
407          */
408
409         /* enter main loop */
410         while (!done) {
411                 /* set the send packet pointer to begining of send packet parms */
412                 send_ptr = send_parms;
413
414                 /* With each packet, start summing the bytes starting with the length.
415                    Save the current sequence number.
416                    Note the type of the packet.
417                    If a character less than SPACE (0x20) is received - error.
418                  */
419
420 #if 0
421                 /* OLD CODE, Prior to checking sequence numbers */
422                 /* first have all state machines save current states */
423                 k_state_saved = k_state;
424                 k_data_save ();
425 #endif
426
427                 /* get a packet */
428                 /* wait for the starting character or ^C */
429                 for (;;) {
430                         switch (getc ()) {
431                         case START_CHAR:        /* start packet */
432                                 goto START;
433                         case ETX_CHAR:          /* ^C waiting for packet */
434                                 return (0);
435                         default:
436                                 ;
437                         }
438                 }
439 START:
440                 /* get length of packet */
441                 sum = 0;
442                 new_char = getc ();
443                 if ((new_char & 0xE0) == 0)
444                         goto packet_error;
445                 sum += new_char & 0xff;
446                 length = untochar (new_char);
447                 /* get sequence number */
448                 new_char = getc ();
449                 if ((new_char & 0xE0) == 0)
450                         goto packet_error;
451                 sum += new_char & 0xff;
452                 n = untochar (new_char);
453                 --length;
454
455                 /* NEW CODE - check sequence numbers for retried packets */
456                 /* Note - this new code assumes that the sequence number is correctly
457                  * received.  Handling an invalid sequence number adds another layer
458                  * of complexity that may not be needed - yet!  At this time, I'm hoping
459                  * that I don't need to buffer the incoming data packets and can write
460                  * the data into memory in real time.
461                  */
462                 if (n == last_n) {
463                         /* same sequence number, restore the previous state */
464                         k_state = k_state_saved;
465                         k_data_restore ();
466                 } else {
467                         /* new sequence number, checkpoint the download */
468                         last_n = n;
469                         k_state_saved = k_state;
470                         k_data_save ();
471                 }
472                 /* END NEW CODE */
473
474                 /* get packet type */
475                 new_char = getc ();
476                 if ((new_char & 0xE0) == 0)
477                         goto packet_error;
478                 sum += new_char & 0xff;
479                 k_state = new_char;
480                 --length;
481                 /* check for extended length */
482                 if (length == -2) {
483                         /* (length byte was 0, decremented twice) */
484                         /* get the two length bytes */
485                         new_char = getc ();
486                         if ((new_char & 0xE0) == 0)
487                                 goto packet_error;
488                         sum += new_char & 0xff;
489                         len_hi = untochar (new_char);
490                         new_char = getc ();
491                         if ((new_char & 0xE0) == 0)
492                                 goto packet_error;
493                         sum += new_char & 0xff;
494                         len_lo = untochar (new_char);
495                         length = len_hi * 95 + len_lo;
496                         /* check header checksum */
497                         new_char = getc ();
498                         if ((new_char & 0xE0) == 0)
499                                 goto packet_error;
500                         if (new_char != tochar ((sum + ((sum >> 6) & 0x03)) & 0x3f))
501                                 goto packet_error;
502                         sum += new_char & 0xff;
503 /* --length; */ /* new length includes only data and block check to come */
504                 }
505                 /* bring in rest of packet */
506                 while (length > 1) {
507                         new_char = getc ();
508                         if ((new_char & 0xE0) == 0)
509                                 goto packet_error;
510                         sum += new_char & 0xff;
511                         --length;
512                         if (k_state == DATA_TYPE) {
513                                 /* pass on the data if this is a data packet */
514                                 k_data_char (new_char);
515                         } else if (k_state == SEND_TYPE) {
516                                 /* save send pack in buffer as is */
517                                 *send_ptr++ = new_char;
518                                 /* if too much data, back off the pointer */
519                                 if (send_ptr >= &send_parms[SEND_DATA_SIZE])
520                                         --send_ptr;
521                         }
522                 }
523                 /* get and validate checksum character */
524                 new_char = getc ();
525                 if ((new_char & 0xE0) == 0)
526                         goto packet_error;
527                 if (new_char != tochar ((sum + ((sum >> 6) & 0x03)) & 0x3f))
528                         goto packet_error;
529                 /* get END_CHAR */
530                 new_char = getc ();
531                 if (new_char != END_CHAR) {
532                   packet_error:
533                         /* restore state machines */
534                         k_state = k_state_saved;
535                         k_data_restore ();
536                         /* send a negative acknowledge packet in */
537                         send_nack (n);
538                 } else if (k_state == SEND_TYPE) {
539                         /* crack the protocol parms, build an appropriate ack packet */
540                         handle_send_packet (n);
541                 } else {
542                         /* send simple acknowledge packet in */
543                         send_ack (n);
544                         /* quit if end of transmission */
545                         if (k_state == BREAK_TYPE)
546                                 done = 1;
547                 }
548                 ++z;
549         }
550         return ((ulong) os_data_addr - (ulong) bin_start_address);
551 }