Blame view
cmd/load.c
26.1 KB
83d290c56
|
1 |
// SPDX-License-Identifier: GPL-2.0+ |
8bde7f776
|
2 |
/* |
232c150a2
|
3 |
* (C) Copyright 2000-2004 |
8bde7f776
|
4 |
* Wolfgang Denk, DENX Software Engineering, wd@denx.de. |
8bde7f776
|
5 6 7 8 9 10 11 |
*/ /* * Serial up- and download support */ #include <common.h> #include <command.h> |
24b852a7a
|
12 |
#include <console.h> |
1eb69ae49
|
13 |
#include <cpu_func.h> |
76b0a1902
|
14 |
#include <efi_loader.h> |
c7694dd48
|
15 |
#include <env.h> |
76b0a1902
|
16 |
#include <exports.h> |
0ee48252b
|
17 |
#include <flash.h> |
8e8ccfe1a
|
18 |
#include <image.h> |
c6855195e
|
19 |
#include <lmb.h> |
76b0a1902
|
20 |
#include <mapmem.h> |
8bde7f776
|
21 |
#include <net.h> |
76b0a1902
|
22 |
#include <s_record.h> |
b03e0510d
|
23 |
#include <serial.h> |
f2841d377
|
24 |
#include <xyzModem.h> |
90526e9fb
|
25 |
#include <asm/cache.h> |
401d1c4f5
|
26 |
#include <asm/global_data.h> |
c05ed00af
|
27 |
#include <linux/delay.h> |
8bde7f776
|
28 |
|
d87080b72
|
29 |
DECLARE_GLOBAL_DATA_PTR; |
8bde7f776
|
30 |
|
c76fe4742
|
31 |
#if defined(CONFIG_CMD_LOADB) |
6e66bd559
|
32 |
static ulong load_serial_ymodem(ulong offset, int mode); |
8546e2390
|
33 |
#endif |
c76fe4742
|
34 |
#if defined(CONFIG_CMD_LOADS) |
088f1b199
|
35 36 |
static ulong load_serial(long offset); static int read_record(char *buf, ulong len); |
c76fe4742
|
37 |
# if defined(CONFIG_CMD_SAVES) |
088f1b199
|
38 39 |
static int save_serial(ulong offset, ulong size); static int write_record(char *buf); |
902531788
|
40 |
#endif |
8bde7f776
|
41 42 |
static int do_echo = 1; |
902531788
|
43 |
#endif |
8bde7f776
|
44 45 |
/* -------------------------------------------------------------------- */ |
c76fe4742
|
46 |
#if defined(CONFIG_CMD_LOADS) |
091401131
|
47 48 |
static int do_load_serial(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) |
8bde7f776
|
49 |
{ |
2b22d608f
|
50 |
long offset = 0; |
8bde7f776
|
51 52 53 54 |
ulong addr; int i; char *env_echo; int rcode = 0; |
6d0f6bcf3
|
55 |
#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE |
8bde7f776
|
56 57 58 59 |
int load_baudrate, current_baudrate; load_baudrate = current_baudrate = gd->baudrate; #endif |
00caae6d4
|
60 61 |
env_echo = env_get("loads_echo"); if (env_echo && *env_echo == '1') |
8bde7f776
|
62 |
do_echo = 1; |
00caae6d4
|
63 |
else |
8bde7f776
|
64 |
do_echo = 0; |
8bde7f776
|
65 |
|
6d0f6bcf3
|
66 |
#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE |
8bde7f776
|
67 |
if (argc >= 2) { |
2b22d608f
|
68 |
offset = simple_strtol(argv[1], NULL, 16); |
8bde7f776
|
69 70 |
} if (argc == 3) { |
0b1284eb5
|
71 |
load_baudrate = (int)dectoul(argv[2], NULL); |
8bde7f776
|
72 73 74 75 76 77 |
/* default to current baudrate */ if (load_baudrate == 0) load_baudrate = current_baudrate; } if (load_baudrate != current_baudrate) { |
088f1b199
|
78 79 |
printf("## Switch baudrate to %d bps and press ENTER ... ", |
8bde7f776
|
80 81 82 |
load_baudrate); udelay(50000); gd->baudrate = load_baudrate; |
088f1b199
|
83 |
serial_setbrg(); |
8bde7f776
|
84 85 |
udelay(50000); for (;;) { |
c670aeee3
|
86 |
if (getchar() == '\r') |
8bde7f776
|
87 88 89 |
break; } } |
6d0f6bcf3
|
90 |
#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */ |
8bde7f776
|
91 |
if (argc == 2) { |
2b22d608f
|
92 |
offset = simple_strtol(argv[1], NULL, 16); |
8bde7f776
|
93 |
} |
6d0f6bcf3
|
94 |
#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */ |
8bde7f776
|
95 |
|
088f1b199
|
96 97 |
printf("## Ready for S-Record download ... "); |
8bde7f776
|
98 |
|
088f1b199
|
99 |
addr = load_serial(offset); |
8bde7f776
|
100 101 102 103 104 105 106 |
/* * Gather any trailing characters (for instance, the ^D which * is sent by 'cu' after sending a file), and give the * box some time (100 * 1 ms) */ for (i=0; i<100; ++i) { |
232c150a2
|
107 |
if (tstc()) { |
c670aeee3
|
108 |
getchar(); |
8bde7f776
|
109 110 111 112 113 |
} udelay(1000); } if (addr == ~0) { |
088f1b199
|
114 115 |
printf("## S-Record download aborted "); |
8bde7f776
|
116 117 |
rcode = 1; } else { |
088f1b199
|
118 119 |
printf("## Start Addr = 0x%08lX ", addr); |
bb872dd93
|
120 |
image_load_addr = addr; |
8bde7f776
|
121 |
} |
6d0f6bcf3
|
122 |
#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE |
8bde7f776
|
123 |
if (load_baudrate != current_baudrate) { |
088f1b199
|
124 125 |
printf("## Switch baudrate to %d bps and press ESC ... ", |
8bde7f776
|
126 |
current_baudrate); |
088f1b199
|
127 |
udelay(50000); |
8bde7f776
|
128 |
gd->baudrate = current_baudrate; |
088f1b199
|
129 130 |
serial_setbrg(); udelay(50000); |
8bde7f776
|
131 |
for (;;) { |
c670aeee3
|
132 |
if (getchar() == 0x1B) /* ESC */ |
8bde7f776
|
133 134 135 136 137 138 |
break; } } #endif return rcode; } |
088f1b199
|
139 |
static ulong load_serial(long offset) |
8bde7f776
|
140 |
{ |
c6855195e
|
141 |
struct lmb lmb; |
8bde7f776
|
142 143 144 145 146 147 |
char record[SREC_MAXRECLEN + 1]; /* buffer for one S-Record */ char binbuf[SREC_MAXBINLEN]; /* buffer for binary data */ int binlen; /* no. of data bytes in S-Rec. */ int type; /* return code for record type */ ulong addr; /* load address from S-Record */ ulong size; /* number of bytes transferred */ |
8bde7f776
|
148 149 150 151 |
ulong store_addr; ulong start_addr = ~0; ulong end_addr = 0; int line_count = 0; |
c6855195e
|
152 153 154 |
long ret; lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob); |
8bde7f776
|
155 156 |
while (read_record(record, SREC_MAXRECLEN + 1) >= 0) { |
088f1b199
|
157 |
type = srec_decode(record, &binlen, &addr, binbuf); |
8bde7f776
|
158 159 160 161 162 163 164 165 166 167 |
if (type < 0) { return (~0); /* Invalid S-Record */ } switch (type) { case SREC_DATA2: case SREC_DATA3: case SREC_DATA4: store_addr = addr + offset; |
e856bdcfb
|
168 |
#ifdef CONFIG_MTD_NOR_FLASH |
8bde7f776
|
169 170 |
if (addr2info(store_addr)) { int rc; |
77ddac948
|
171 |
rc = flash_write((char *)binbuf,store_addr,binlen); |
8bde7f776
|
172 |
if (rc != 0) { |
088f1b199
|
173 |
flash_perror(rc); |
8bde7f776
|
174 175 176 177 178 |
return (~0); } } else #endif { |
c6855195e
|
179 180 181 182 183 184 185 186 |
ret = lmb_reserve(&lmb, store_addr, binlen); if (ret) { printf(" Cannot overwrite reserved area (%08lx..%08lx) ", store_addr, store_addr + binlen); return ret; } |
088f1b199
|
187 |
memcpy((char *)(store_addr), binbuf, binlen); |
c6855195e
|
188 |
lmb_free(&lmb, store_addr, binlen); |
8bde7f776
|
189 190 191 192 193 194 195 196 197 |
} if ((store_addr) < start_addr) start_addr = store_addr; if ((store_addr + binlen - 1) > end_addr) end_addr = store_addr + binlen - 1; break; case SREC_END2: case SREC_END3: case SREC_END4: |
088f1b199
|
198 |
udelay(10000); |
8bde7f776
|
199 |
size = end_addr - start_addr + 1; |
088f1b199
|
200 201 |
printf(" " |
8bde7f776
|
202 203 204 205 206 207 208 209 |
"## First Load Addr = 0x%08lX " "## Last Load Addr = 0x%08lX " "## Total Size = 0x%08lX = %ld Bytes ", start_addr, end_addr, size, size ); |
088f1b199
|
210 |
flush_cache(start_addr, size); |
018f53032
|
211 |
env_set_hex("filesize", size); |
8bde7f776
|
212 213 214 215 216 217 218 219 |
return (addr); case SREC_START: break; default: break; } if (!do_echo) { /* print a '.' every 100 lines */ if ((++line_count % 100) == 0) |
088f1b199
|
220 |
putc('.'); |
8bde7f776
|
221 222 223 224 225 |
} } return (~0); /* Download aborted */ } |
088f1b199
|
226 |
static int read_record(char *buf, ulong len) |
8bde7f776
|
227 228 229 230 231 232 233 |
{ char *p; char c; --len; /* always leave room for terminating '\0' byte */ for (p=buf; p < buf+len; ++p) { |
c670aeee3
|
234 |
c = getchar(); /* read character */ |
8bde7f776
|
235 |
if (do_echo) |
088f1b199
|
236 |
putc(c); /* ... and echo it */ |
8bde7f776
|
237 238 239 240 241 242 243 244 245 246 247 248 249 |
switch (c) { case '\r': case ' ': *p = '\0'; return (p - buf); case '\0': case 0x03: /* ^C - Control C */ return (-1); default: *p = c; } |
e7fcfef43
|
250 251 252 253 |
/* Check for the console hangup (if any different from serial) */ if (gd->jt->getc != getchar) { if (ctrlc()) return (-1); |
8bde7f776
|
254 |
} |
8bde7f776
|
255 256 257 258 259 260 |
} /* line too long - truncate */ *p = '\0'; return (p - buf); } |
c76fe4742
|
261 |
#if defined(CONFIG_CMD_SAVES) |
8bde7f776
|
262 |
|
091401131
|
263 264 |
int do_save_serial(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) |
8bde7f776
|
265 266 267 |
{ ulong offset = 0; ulong size = 0; |
6d0f6bcf3
|
268 |
#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE |
8bde7f776
|
269 270 271 272 273 274 |
int save_baudrate, current_baudrate; save_baudrate = current_baudrate = gd->baudrate; #endif if (argc >= 2) { |
7e5f460ec
|
275 |
offset = hextoul(argv[1], NULL); |
8bde7f776
|
276 |
} |
6d0f6bcf3
|
277 |
#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE |
8bde7f776
|
278 |
if (argc >= 3) { |
7e5f460ec
|
279 |
size = hextoul(argv[2], NULL); |
8bde7f776
|
280 281 |
} if (argc == 4) { |
0b1284eb5
|
282 |
save_baudrate = (int)dectoul(argv[3], NULL); |
8bde7f776
|
283 284 285 286 287 288 |
/* default to current baudrate */ if (save_baudrate == 0) save_baudrate = current_baudrate; } if (save_baudrate != current_baudrate) { |
088f1b199
|
289 290 |
printf("## Switch baudrate to %d bps and press ENTER ... ", |
8bde7f776
|
291 292 293 |
save_baudrate); udelay(50000); gd->baudrate = save_baudrate; |
088f1b199
|
294 |
serial_setbrg(); |
8bde7f776
|
295 296 |
udelay(50000); for (;;) { |
c670aeee3
|
297 |
if (getchar() == '\r') |
8bde7f776
|
298 299 300 |
break; } } |
6d0f6bcf3
|
301 |
#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */ |
8bde7f776
|
302 |
if (argc == 3) { |
7e5f460ec
|
303 |
size = hextoul(argv[2], NULL); |
8bde7f776
|
304 |
} |
6d0f6bcf3
|
305 |
#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */ |
8bde7f776
|
306 |
|
088f1b199
|
307 308 |
printf("## Ready for S-Record upload, press ENTER to proceed ... "); |
8bde7f776
|
309 |
for (;;) { |
c670aeee3
|
310 |
if (getchar() == '\r') |
8bde7f776
|
311 312 |
break; } |
088f1b199
|
313 314 315 |
if (save_serial(offset, size)) { printf("## S-Record upload aborted "); |
8bde7f776
|
316 |
} else { |
088f1b199
|
317 318 |
printf("## S-Record upload complete "); |
8bde7f776
|
319 |
} |
6d0f6bcf3
|
320 |
#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE |
8bde7f776
|
321 |
if (save_baudrate != current_baudrate) { |
088f1b199
|
322 323 |
printf("## Switch baudrate to %d bps and press ESC ... ", |
8bde7f776
|
324 |
(int)current_baudrate); |
088f1b199
|
325 |
udelay(50000); |
8bde7f776
|
326 |
gd->baudrate = current_baudrate; |
088f1b199
|
327 328 |
serial_setbrg(); udelay(50000); |
8bde7f776
|
329 |
for (;;) { |
c670aeee3
|
330 |
if (getchar() == 0x1B) /* ESC */ |
8bde7f776
|
331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
break; } } #endif return 0; } #define SREC3_START "S0030000FC " #define SREC3_FORMAT "S3%02X%08lX%s%02X " #define SREC3_END "S70500000000FA " #define SREC_BYTES_PER_RECORD 16 |
088f1b199
|
345 |
static int save_serial(ulong address, ulong count) |
8bde7f776
|
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 |
{ int i, c, reclen, checksum, length; char *hex = "0123456789ABCDEF"; char record[2*SREC_BYTES_PER_RECORD+16]; /* buffer for one S-Record */ char data[2*SREC_BYTES_PER_RECORD+1]; /* buffer for hex data */ reclen = 0; checksum = 0; if(write_record(SREC3_START)) /* write the header */ return (-1); do { if(count) { /* collect hex data in the buffer */ c = *(volatile uchar*)(address + reclen); /* get one byte */ checksum += c; /* accumulate checksum */ data[2*reclen] = hex[(c>>4)&0x0f]; data[2*reclen+1] = hex[c & 0x0f]; data[2*reclen+2] = '\0'; ++reclen; --count; } if(reclen == SREC_BYTES_PER_RECORD || count == 0) { /* enough data collected for one record: dump it */ if(reclen) { /* build & write a data record: */ /* address + data + checksum */ length = 4 + reclen + 1; /* accumulate length bytes into checksum */ for(i = 0; i < 2; i++) checksum += (length >> (8*i)) & 0xff; /* accumulate address bytes into checksum: */ for(i = 0; i < 4; i++) checksum += (address >> (8*i)) & 0xff; /* make proper checksum byte: */ checksum = ~checksum & 0xff; /* output one record: */ sprintf(record, SREC3_FORMAT, length, address, data, checksum); if(write_record(record)) return (-1); } address += reclen; /* increment address */ checksum = 0; reclen = 0; } } while(count); if(write_record(SREC3_END)) /* write the final record */ return (-1); return(0); } |
088f1b199
|
399 |
static int write_record(char *buf) |
8bde7f776
|
400 401 402 403 |
{ char c; while((c = *buf++)) |
232c150a2
|
404 |
putc(c); |
8bde7f776
|
405 406 407 408 409 410 411 412 |
/* Check for the console hangup (if any different from serial) */ if (ctrlc()) { return (-1); } return (0); } |
902531788
|
413 |
# endif |
8bde7f776
|
414 |
|
902531788
|
415 |
#endif |
8bde7f776
|
416 |
|
c76fe4742
|
417 |
#if defined(CONFIG_CMD_LOADB) |
65c450b47
|
418 419 420 |
/* * loadb command (load binary) included */ |
8bde7f776
|
421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
#define XON_CHAR 17 #define XOFF_CHAR 19 #define START_CHAR 0x01 #define ETX_CHAR 0x03 #define END_CHAR 0x0D #define SPACE 0x20 #define K_ESCAPE 0x23 #define SEND_TYPE 'S' #define DATA_TYPE 'D' #define ACK_TYPE 'Y' #define NACK_TYPE 'N' #define BREAK_TYPE 'B' #define tochar(x) ((char) (((x) + SPACE) & 0xff)) #define untochar(x) ((int) (((x) - SPACE) & 0xff)) |
8bde7f776
|
435 436 |
static void set_kerm_bin_mode(unsigned long *); static int k_recv(void); |
088f1b199
|
437 |
static ulong load_serial_bin(ulong offset); |
8bde7f776
|
438 |
|
088f1b199
|
439 440 441 442 |
static char his_eol; /* character he needs at end of packet */ static int his_pad_count; /* number of pad chars he needs */ static char his_pad_char; /* pad chars he needs */ static char his_quote; /* quote chars he'll use */ |
8bde7f776
|
443 |
|
091401131
|
444 445 |
static int do_load_serial_bin(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) |
8bde7f776
|
446 |
{ |
8bde7f776
|
447 448 449 450 451 |
ulong offset = 0; ulong addr; int load_baudrate, current_baudrate; int rcode = 0; char *s; |
6d0f6bcf3
|
452 453 |
/* pre-set offset from CONFIG_SYS_LOAD_ADDR */ offset = CONFIG_SYS_LOAD_ADDR; |
8bde7f776
|
454 455 |
/* pre-set offset from $loadaddr */ |
00caae6d4
|
456 457 |
s = env_get("loadaddr"); if (s) |
7e5f460ec
|
458 |
offset = hextoul(s, NULL); |
8bde7f776
|
459 460 461 462 |
load_baudrate = current_baudrate = gd->baudrate; if (argc >= 2) { |
7e5f460ec
|
463 |
offset = hextoul(argv[1], NULL); |
8bde7f776
|
464 465 |
} if (argc == 3) { |
0b1284eb5
|
466 |
load_baudrate = (int)dectoul(argv[2], NULL); |
8bde7f776
|
467 468 469 470 471 472 473 |
/* default to current baudrate */ if (load_baudrate == 0) load_baudrate = current_baudrate; } if (load_baudrate != current_baudrate) { |
088f1b199
|
474 475 |
printf("## Switch baudrate to %d bps and press ENTER ... ", |
8bde7f776
|
476 477 478 |
load_baudrate); udelay(50000); gd->baudrate = load_baudrate; |
088f1b199
|
479 |
serial_setbrg(); |
8bde7f776
|
480 481 |
udelay(50000); for (;;) { |
c670aeee3
|
482 |
if (getchar() == '\r') |
8bde7f776
|
483 484 485 |
break; } } |
f2841d377
|
486 |
if (strcmp(argv[0],"loady")==0) { |
088f1b199
|
487 |
printf("## Ready for binary (ymodem) download " |
f2841d377
|
488 489 490 491 |
"to 0x%08lX at %d bps... ", offset, load_baudrate); |
6e66bd559
|
492 |
addr = load_serial_ymodem(offset, xyzModem_ymodem); |
c97b2557b
|
493 494 495 496 497 498 499 500 501 502 |
if (addr == ~0) { image_load_addr = 0; printf("## Binary (ymodem) download aborted "); rcode = 1; } else { printf("## Start Addr = 0x%08lX ", addr); image_load_addr = addr; } |
6e66bd559
|
503 504 505 506 507 508 509 510 |
} else if (strcmp(argv[0],"loadx")==0) { printf("## Ready for binary (xmodem) download " "to 0x%08lX at %d bps... ", offset, load_baudrate); addr = load_serial_ymodem(offset, xyzModem_xmodem); |
8bde7f776
|
511 |
|
c97b2557b
|
512 513 514 515 516 517 518 519 520 521 |
if (addr == ~0) { image_load_addr = 0; printf("## Binary (xmodem) download aborted "); rcode = 1; } else { printf("## Start Addr = 0x%08lX ", addr); image_load_addr = addr; } |
8bde7f776
|
522 |
} else { |
8bde7f776
|
523 |
|
088f1b199
|
524 |
printf("## Ready for binary (kermit) download " |
f2841d377
|
525 526 527 528 |
"to 0x%08lX at %d bps... ", offset, load_baudrate); |
088f1b199
|
529 |
addr = load_serial_bin(offset); |
f2841d377
|
530 531 |
if (addr == ~0) { |
bb872dd93
|
532 |
image_load_addr = 0; |
088f1b199
|
533 534 |
printf("## Binary (kermit) download aborted "); |
f2841d377
|
535 536 |
rcode = 1; } else { |
088f1b199
|
537 538 |
printf("## Start Addr = 0x%08lX ", addr); |
bb872dd93
|
539 |
image_load_addr = addr; |
f2841d377
|
540 541 |
} } |
8bde7f776
|
542 |
if (load_baudrate != current_baudrate) { |
088f1b199
|
543 544 |
printf("## Switch baudrate to %d bps and press ESC ... ", |
8bde7f776
|
545 |
current_baudrate); |
088f1b199
|
546 |
udelay(50000); |
8bde7f776
|
547 |
gd->baudrate = current_baudrate; |
088f1b199
|
548 549 |
serial_setbrg(); udelay(50000); |
8bde7f776
|
550 |
for (;;) { |
c670aeee3
|
551 |
if (getchar() == 0x1B) /* ESC */ |
8bde7f776
|
552 553 554 |
break; } } |
8bde7f776
|
555 556 |
return rcode; } |
088f1b199
|
557 |
static ulong load_serial_bin(ulong offset) |
8bde7f776
|
558 559 |
{ int size, i; |
8bde7f776
|
560 |
|
088f1b199
|
561 562 |
set_kerm_bin_mode((ulong *) offset); size = k_recv(); |
8bde7f776
|
563 564 565 566 567 568 569 |
/* * Gather any trailing characters (for instance, the ^D which * is sent by 'cu' after sending a file), and give the * box some time (100 * 1 ms) */ for (i=0; i<100; ++i) { |
232c150a2
|
570 |
if (tstc()) { |
c670aeee3
|
571 |
getchar(); |
8bde7f776
|
572 573 574 |
} udelay(1000); } |
515381414
|
575 576 |
if (size == 0) return ~0; /* Download aborted */ |
088f1b199
|
577 |
flush_cache(offset, size); |
8bde7f776
|
578 579 580 |
printf("## Total Size = 0x%08x = %d Bytes ", size, size); |
018f53032
|
581 |
env_set_hex("filesize", size); |
8bde7f776
|
582 583 584 |
return offset; } |
088f1b199
|
585 |
static void send_pad(void) |
8bde7f776
|
586 587 588 589 |
{ int count = his_pad_count; while (count-- > 0) |
088f1b199
|
590 |
putc(his_pad_char); |
8bde7f776
|
591 592 593 |
} /* converts escaped kermit char to binary char */ |
088f1b199
|
594 |
static char ktrans(char in) |
8bde7f776
|
595 596 597 598 599 600 601 602 |
{ if ((in & 0x60) == 0x40) { return (char) (in & ~0x40); } else if ((in & 0x7f) == 0x3f) { return (char) (in | 0x40); } else return in; } |
088f1b199
|
603 |
static int chk1(char *buffer) |
8bde7f776
|
604 605 606 607 608 609 610 611 |
{ int total = 0; while (*buffer) { total += *buffer++; } return (int) ((total + ((total >> 6) & 0x03)) & 0x3f); } |
088f1b199
|
612 |
static void s1_sendpacket(char *packet) |
8bde7f776
|
613 |
{ |
088f1b199
|
614 |
send_pad(); |
8bde7f776
|
615 |
while (*packet) { |
088f1b199
|
616 |
putc(*packet++); |
8bde7f776
|
617 618 619 620 |
} } static char a_b[24]; |
088f1b199
|
621 |
static void send_ack(int n) |
8bde7f776
|
622 623 |
{ a_b[0] = START_CHAR; |
088f1b199
|
624 625 |
a_b[1] = tochar(3); a_b[2] = tochar(n); |
8bde7f776
|
626 627 |
a_b[3] = ACK_TYPE; a_b[4] = '\0'; |
088f1b199
|
628 |
a_b[4] = tochar(chk1(&a_b[1])); |
8bde7f776
|
629 630 |
a_b[5] = his_eol; a_b[6] = '\0'; |
088f1b199
|
631 |
s1_sendpacket(a_b); |
8bde7f776
|
632 |
} |
088f1b199
|
633 |
static void send_nack(int n) |
8bde7f776
|
634 635 |
{ a_b[0] = START_CHAR; |
088f1b199
|
636 637 |
a_b[1] = tochar(3); a_b[2] = tochar(n); |
8bde7f776
|
638 639 |
a_b[3] = NACK_TYPE; a_b[4] = '\0'; |
088f1b199
|
640 |
a_b[4] = tochar(chk1(&a_b[1])); |
8bde7f776
|
641 642 |
a_b[5] = his_eol; a_b[6] = '\0'; |
088f1b199
|
643 |
s1_sendpacket(a_b); |
8bde7f776
|
644 |
} |
088f1b199
|
645 646 |
static void (*os_data_init)(void); static void (*os_data_char)(char new_char); |
8bde7f776
|
647 |
static int os_data_state, os_data_state_saved; |
8bde7f776
|
648 649 |
static char *os_data_addr, *os_data_addr_saved; static char *bin_start_address; |
a9fe0c3e7
|
650 |
|
088f1b199
|
651 |
static void bin_data_init(void) |
8bde7f776
|
652 653 |
{ os_data_state = 0; |
8bde7f776
|
654 655 |
os_data_addr = bin_start_address; } |
a9fe0c3e7
|
656 |
|
088f1b199
|
657 |
static void os_data_save(void) |
8bde7f776
|
658 659 |
{ os_data_state_saved = os_data_state; |
8bde7f776
|
660 661 |
os_data_addr_saved = os_data_addr; } |
a9fe0c3e7
|
662 |
|
088f1b199
|
663 |
static void os_data_restore(void) |
8bde7f776
|
664 665 |
{ os_data_state = os_data_state_saved; |
8bde7f776
|
666 667 |
os_data_addr = os_data_addr_saved; } |
a9fe0c3e7
|
668 |
|
088f1b199
|
669 |
static void bin_data_char(char new_char) |
8bde7f776
|
670 671 672 673 |
{ switch (os_data_state) { case 0: /* data */ *os_data_addr++ = new_char; |
8bde7f776
|
674 675 676 |
break; } } |
a9fe0c3e7
|
677 |
|
088f1b199
|
678 |
static void set_kerm_bin_mode(unsigned long *addr) |
8bde7f776
|
679 680 681 682 683 684 685 686 687 |
{ bin_start_address = (char *) addr; os_data_init = bin_data_init; os_data_char = bin_data_char; } /* k_data_* simply handles the kermit escape translations */ static int k_data_escape, k_data_escape_saved; |
088f1b199
|
688 |
static void k_data_init(void) |
8bde7f776
|
689 690 |
{ k_data_escape = 0; |
088f1b199
|
691 |
os_data_init(); |
8bde7f776
|
692 |
} |
a9fe0c3e7
|
693 |
|
088f1b199
|
694 |
static void k_data_save(void) |
8bde7f776
|
695 696 |
{ k_data_escape_saved = k_data_escape; |
088f1b199
|
697 |
os_data_save(); |
8bde7f776
|
698 |
} |
a9fe0c3e7
|
699 |
|
088f1b199
|
700 |
static void k_data_restore(void) |
8bde7f776
|
701 702 |
{ k_data_escape = k_data_escape_saved; |
088f1b199
|
703 |
os_data_restore(); |
8bde7f776
|
704 |
} |
a9fe0c3e7
|
705 |
|
088f1b199
|
706 |
static void k_data_char(char new_char) |
8bde7f776
|
707 708 709 |
{ if (k_data_escape) { /* last char was escape - translate this character */ |
088f1b199
|
710 |
os_data_char(ktrans(new_char)); |
8bde7f776
|
711 712 713 714 715 716 717 |
k_data_escape = 0; } else { if (new_char == his_quote) { /* this char is escape - remember */ k_data_escape = 1; } else { /* otherwise send this char as-is */ |
088f1b199
|
718 |
os_data_char(new_char); |
8bde7f776
|
719 720 721 722 723 |
} } } #define SEND_DATA_SIZE 20 |
088f1b199
|
724 725 |
static char send_parms[SEND_DATA_SIZE]; static char *send_ptr; |
8bde7f776
|
726 727 728 |
/* handle_send_packet interprits the protocol info and builds and sends an appropriate ack for what we can do */ |
088f1b199
|
729 |
static void handle_send_packet(int n) |
8bde7f776
|
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 |
{ int length = 3; int bytes; /* initialize some protocol parameters */ his_eol = END_CHAR; /* default end of line character */ his_pad_count = 0; his_pad_char = '\0'; his_quote = K_ESCAPE; /* ignore last character if it filled the buffer */ if (send_ptr == &send_parms[SEND_DATA_SIZE - 1]) --send_ptr; bytes = send_ptr - send_parms; /* how many bytes we'll process */ do { if (bytes-- <= 0) break; /* handle MAXL - max length */ /* ignore what he says - most I'll take (here) is 94 */ |
088f1b199
|
749 |
a_b[++length] = tochar(94); |
8bde7f776
|
750 751 752 753 |
if (bytes-- <= 0) break; /* handle TIME - time you should wait for my packets */ /* ignore what he says - don't wait for my ack longer than 1 second */ |
088f1b199
|
754 |
a_b[++length] = tochar(1); |
8bde7f776
|
755 756 757 758 |
if (bytes-- <= 0) break; /* handle NPAD - number of pad chars I need */ /* remember what he says - I need none */ |
088f1b199
|
759 760 |
his_pad_count = untochar(send_parms[2]); a_b[++length] = tochar(0); |
8bde7f776
|
761 762 763 764 |
if (bytes-- <= 0) break; /* handle PADC - pad chars I need */ /* remember what he says - I need none */ |
088f1b199
|
765 |
his_pad_char = ktrans(send_parms[3]); |
8bde7f776
|
766 767 768 769 770 |
a_b[++length] = 0x40; /* He should ignore this */ if (bytes-- <= 0) break; /* handle EOL - end of line he needs */ /* remember what he says - I need CR */ |
088f1b199
|
771 772 |
his_eol = untochar(send_parms[4]); a_b[++length] = tochar(END_CHAR); |
8bde7f776
|
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 |
if (bytes-- <= 0) break; /* handle QCTL - quote control char he'll use */ /* remember what he says - I'll use '#' */ his_quote = send_parms[5]; a_b[++length] = '#'; if (bytes-- <= 0) break; /* handle QBIN - 8-th bit prefixing */ /* ignore what he says - I refuse */ a_b[++length] = 'N'; if (bytes-- <= 0) break; /* handle CHKT - the clock check type */ /* ignore what he says - I do type 1 (for now) */ a_b[++length] = '1'; if (bytes-- <= 0) break; /* handle REPT - the repeat prefix */ /* ignore what he says - I refuse (for now) */ a_b[++length] = 'N'; if (bytes-- <= 0) break; /* handle CAPAS - the capabilities mask */ /* ignore what he says - I only do long packets - I don't do windows */ |
088f1b199
|
798 799 800 801 |
a_b[++length] = tochar(2); /* only long packets */ a_b[++length] = tochar(0); /* no windows */ a_b[++length] = tochar(94); /* large packet msb */ a_b[++length] = tochar(94); /* large packet lsb */ |
8bde7f776
|
802 803 804 |
} while (0); a_b[0] = START_CHAR; |
088f1b199
|
805 806 |
a_b[1] = tochar(length); a_b[2] = tochar(n); |
8bde7f776
|
807 808 |
a_b[3] = ACK_TYPE; a_b[++length] = '\0'; |
088f1b199
|
809 |
a_b[length] = tochar(chk1(&a_b[1])); |
8bde7f776
|
810 811 |
a_b[++length] = his_eol; a_b[++length] = '\0'; |
088f1b199
|
812 |
s1_sendpacket(a_b); |
8bde7f776
|
813 814 815 |
} /* k_recv receives a OS Open image file over kermit line */ |
088f1b199
|
816 |
static int k_recv(void) |
8bde7f776
|
817 818 819 820 821 822 823 |
{ char new_char; char k_state, k_state_saved; int sum; int done; int length; int n, last_n; |
8bde7f776
|
824 825 826 827 828 829 830 831 832 833 834 |
int len_lo, len_hi; /* initialize some protocol parameters */ his_eol = END_CHAR; /* default end of line character */ his_pad_count = 0; his_pad_char = '\0'; his_quote = K_ESCAPE; /* initialize the k_recv and k_data state machine */ done = 0; k_state = 0; |
088f1b199
|
835 |
k_data_init(); |
8bde7f776
|
836 |
k_state_saved = k_state; |
088f1b199
|
837 |
k_data_save(); |
8bde7f776
|
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 |
n = 0; /* just to get rid of a warning */ last_n = -1; /* expect this "type" sequence (but don't check): S: send initiate F: file header D: data (multiple) Z: end of file B: break transmission */ /* enter main loop */ while (!done) { /* set the send packet pointer to begining of send packet parms */ send_ptr = send_parms; /* With each packet, start summing the bytes starting with the length. Save the current sequence number. Note the type of the packet. If a character less than SPACE (0x20) is received - error. */ #if 0 /* OLD CODE, Prior to checking sequence numbers */ /* first have all state machines save current states */ k_state_saved = k_state; k_data_save (); #endif /* get a packet */ /* wait for the starting character or ^C */ for (;;) { |
c670aeee3
|
870 |
switch (getchar()) { |
8bde7f776
|
871 872 873 874 875 876 877 878 879 880 881 |
case START_CHAR: /* start packet */ goto START; case ETX_CHAR: /* ^C waiting for packet */ return (0); default: ; } } START: /* get length of packet */ sum = 0; |
c670aeee3
|
882 |
new_char = getchar(); |
8bde7f776
|
883 884 885 |
if ((new_char & 0xE0) == 0) goto packet_error; sum += new_char & 0xff; |
088f1b199
|
886 |
length = untochar(new_char); |
8bde7f776
|
887 |
/* get sequence number */ |
c670aeee3
|
888 |
new_char = getchar(); |
8bde7f776
|
889 890 891 |
if ((new_char & 0xE0) == 0) goto packet_error; sum += new_char & 0xff; |
088f1b199
|
892 |
n = untochar(new_char); |
8bde7f776
|
893 894 895 896 897 898 899 900 901 902 903 904 |
--length; /* NEW CODE - check sequence numbers for retried packets */ /* Note - this new code assumes that the sequence number is correctly * received. Handling an invalid sequence number adds another layer * of complexity that may not be needed - yet! At this time, I'm hoping * that I don't need to buffer the incoming data packets and can write * the data into memory in real time. */ if (n == last_n) { /* same sequence number, restore the previous state */ k_state = k_state_saved; |
088f1b199
|
905 |
k_data_restore(); |
8bde7f776
|
906 907 908 909 |
} else { /* new sequence number, checkpoint the download */ last_n = n; k_state_saved = k_state; |
088f1b199
|
910 |
k_data_save(); |
8bde7f776
|
911 912 913 914 |
} /* END NEW CODE */ /* get packet type */ |
c670aeee3
|
915 |
new_char = getchar(); |
8bde7f776
|
916 917 918 919 920 921 922 923 924 |
if ((new_char & 0xE0) == 0) goto packet_error; sum += new_char & 0xff; k_state = new_char; --length; /* check for extended length */ if (length == -2) { /* (length byte was 0, decremented twice) */ /* get the two length bytes */ |
c670aeee3
|
925 |
new_char = getchar(); |
8bde7f776
|
926 927 928 |
if ((new_char & 0xE0) == 0) goto packet_error; sum += new_char & 0xff; |
088f1b199
|
929 |
len_hi = untochar(new_char); |
c670aeee3
|
930 |
new_char = getchar(); |
8bde7f776
|
931 932 933 |
if ((new_char & 0xE0) == 0) goto packet_error; sum += new_char & 0xff; |
088f1b199
|
934 |
len_lo = untochar(new_char); |
8bde7f776
|
935 936 |
length = len_hi * 95 + len_lo; /* check header checksum */ |
c670aeee3
|
937 |
new_char = getchar(); |
8bde7f776
|
938 939 |
if ((new_char & 0xE0) == 0) goto packet_error; |
088f1b199
|
940 |
if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f)) |
8bde7f776
|
941 942 943 944 945 946 |
goto packet_error; sum += new_char & 0xff; /* --length; */ /* new length includes only data and block check to come */ } /* bring in rest of packet */ while (length > 1) { |
c670aeee3
|
947 |
new_char = getchar(); |
8bde7f776
|
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 |
if ((new_char & 0xE0) == 0) goto packet_error; sum += new_char & 0xff; --length; if (k_state == DATA_TYPE) { /* pass on the data if this is a data packet */ k_data_char (new_char); } else if (k_state == SEND_TYPE) { /* save send pack in buffer as is */ *send_ptr++ = new_char; /* if too much data, back off the pointer */ if (send_ptr >= &send_parms[SEND_DATA_SIZE]) --send_ptr; } } /* get and validate checksum character */ |
c670aeee3
|
964 |
new_char = getchar(); |
8bde7f776
|
965 966 |
if ((new_char & 0xE0) == 0) goto packet_error; |
088f1b199
|
967 |
if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f)) |
8bde7f776
|
968 969 |
goto packet_error; /* get END_CHAR */ |
c670aeee3
|
970 |
new_char = getchar(); |
8bde7f776
|
971 972 973 974 |
if (new_char != END_CHAR) { packet_error: /* restore state machines */ k_state = k_state_saved; |
088f1b199
|
975 |
k_data_restore(); |
8bde7f776
|
976 |
/* send a negative acknowledge packet in */ |
088f1b199
|
977 |
send_nack(n); |
8bde7f776
|
978 979 |
} else if (k_state == SEND_TYPE) { /* crack the protocol parms, build an appropriate ack packet */ |
088f1b199
|
980 |
handle_send_packet(n); |
8bde7f776
|
981 982 |
} else { /* send simple acknowledge packet in */ |
088f1b199
|
983 |
send_ack(n); |
8bde7f776
|
984 985 986 987 |
/* quit if end of transmission */ if (k_state == BREAK_TYPE) done = 1; } |
8bde7f776
|
988 989 990 |
} return ((ulong) os_data_addr - (ulong) bin_start_address); } |
f2841d377
|
991 992 |
static int getcxmodem(void) { |
cf48eb9ab
|
993 |
if (tstc()) |
c670aeee3
|
994 |
return (getchar()); |
f2841d377
|
995 996 |
return -1; } |
6e66bd559
|
997 |
static ulong load_serial_ymodem(ulong offset, int mode) |
f2841d377
|
998 999 |
{ int size; |
f2841d377
|
1000 1001 1002 |
int err; int res; connection_info_t info; |
cf48eb9ab
|
1003 1004 1005 |
char ymodemBuf[1024]; ulong store_addr = ~0; ulong addr = 0; |
f2841d377
|
1006 |
|
cf48eb9ab
|
1007 |
size = 0; |
6e66bd559
|
1008 |
info.mode = mode; |
088f1b199
|
1009 |
res = xyzModem_stream_open(&info, &err); |
f2841d377
|
1010 |
if (!res) { |
f2841d377
|
1011 |
|
081bd249d
|
1012 |
err = 0; |
cf48eb9ab
|
1013 |
while ((res = |
088f1b199
|
1014 |
xyzModem_stream_read(ymodemBuf, 1024, &err)) > 0) { |
cf48eb9ab
|
1015 1016 1017 |
store_addr = addr + offset; size += res; addr += res; |
e856bdcfb
|
1018 |
#ifdef CONFIG_MTD_NOR_FLASH |
088f1b199
|
1019 |
if (addr2info(store_addr)) { |
cf48eb9ab
|
1020 |
int rc; |
088f1b199
|
1021 |
rc = flash_write((char *) ymodemBuf, |
cf48eb9ab
|
1022 1023 |
store_addr, res); if (rc != 0) { |
081bd249d
|
1024 1025 1026 1027 |
xyzModem_stream_terminate(true, &getcxmodem); xyzModem_stream_close(&err); printf(" "); |
0ee48252b
|
1028 |
flash_perror(rc); |
cf48eb9ab
|
1029 1030 1031 |
return (~0); } } else |
f2841d377
|
1032 |
#endif |
cf48eb9ab
|
1033 |
{ |
088f1b199
|
1034 |
memcpy((char *)(store_addr), ymodemBuf, |
cf48eb9ab
|
1035 1036 1037 1038 |
res); } } |
081bd249d
|
1039 1040 1041 1042 1043 1044 1045 1046 |
if (err) { xyzModem_stream_terminate((err == xyzModem_cancel) ? false : true, &getcxmodem); xyzModem_stream_close(&err); printf(" %s ", xyzModem_error(err)); return (~0); /* Download aborted */ } |
76b0a1902
|
1047 1048 1049 |
if (IS_ENABLED(CONFIG_CMD_BOOTEFI)) efi_set_bootdev("Uart", "", "", map_sysmem(offset, 0), size); |
cf48eb9ab
|
1050 |
} else { |
081bd249d
|
1051 1052 1053 1054 |
printf(" %s ", xyzModem_error(err)); return (~0); /* Download aborted */ |
f2841d377
|
1055 |
} |
cf48eb9ab
|
1056 |
|
088f1b199
|
1057 |
xyzModem_stream_terminate(false, &getcxmodem); |
1f26c49ea
|
1058 |
xyzModem_stream_close(&err); |
f2841d377
|
1059 |
|
0a6036da6
|
1060 |
flush_cache(offset, ALIGN(size, ARCH_DMA_MINALIGN)); |
f2841d377
|
1061 |
|
088f1b199
|
1062 1063 |
printf("## Total Size = 0x%08x = %d Bytes ", size, size); |
018f53032
|
1064 |
env_set_hex("filesize", size); |
f2841d377
|
1065 1066 1067 |
return offset; } |
902531788
|
1068 |
#endif |
8bde7f776
|
1069 1070 |
/* -------------------------------------------------------------------- */ |
c76fe4742
|
1071 |
#if defined(CONFIG_CMD_LOADS) |
8bde7f776
|
1072 |
|
6d0f6bcf3
|
1073 |
#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE |
0d4983930
|
1074 1075 |
U_BOOT_CMD( loads, 3, 0, do_load_serial, |
2fb2604d5
|
1076 |
"load S-Record file over serial line", |
8bde7f776
|
1077 1078 1079 |
"[ off ] [ baud ] " " - load S-Record file over serial line" |
a89c33db9
|
1080 |
" with offset 'off' and baudrate 'baud'" |
8bde7f776
|
1081 |
); |
6d0f6bcf3
|
1082 |
#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */ |
0d4983930
|
1083 1084 |
U_BOOT_CMD( loads, 2, 0, do_load_serial, |
2fb2604d5
|
1085 |
"load S-Record file over serial line", |
8bde7f776
|
1086 1087 |
"[ off ] " |
a89c33db9
|
1088 |
" - load S-Record file over serial line with offset 'off'" |
8bde7f776
|
1089 |
); |
6d0f6bcf3
|
1090 |
#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */ |
8bde7f776
|
1091 1092 1093 1094 |
/* * SAVES always requires LOADS support, but not vice versa */ |
c76fe4742
|
1095 |
#if defined(CONFIG_CMD_SAVES) |
6d0f6bcf3
|
1096 |
#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE |
0d4983930
|
1097 1098 |
U_BOOT_CMD( saves, 4, 0, do_save_serial, |
2fb2604d5
|
1099 |
"save S-Record file over serial line", |
8bde7f776
|
1100 1101 1102 |
"[ off ] [size] [ baud ] " " - save S-Record file over serial line" |
a89c33db9
|
1103 |
" with offset 'off', size 'size' and baudrate 'baud'" |
8bde7f776
|
1104 |
); |
6d0f6bcf3
|
1105 |
#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */ |
0d4983930
|
1106 1107 |
U_BOOT_CMD( saves, 3, 0, do_save_serial, |
2fb2604d5
|
1108 |
"save S-Record file over serial line", |
8bde7f776
|
1109 1110 |
"[ off ] [size] " |
a89c33db9
|
1111 |
" - save S-Record file over serial line with offset 'off' and size 'size'" |
8bde7f776
|
1112 |
); |
6d0f6bcf3
|
1113 |
#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */ |
70d7cb925
|
1114 1115 |
#endif /* CONFIG_CMD_SAVES */ #endif /* CONFIG_CMD_LOADS */ |
8bde7f776
|
1116 |
|
c76fe4742
|
1117 |
#if defined(CONFIG_CMD_LOADB) |
0d4983930
|
1118 1119 |
U_BOOT_CMD( loadb, 3, 0, do_load_serial_bin, |
2fb2604d5
|
1120 |
"load binary file over serial line (kermit mode)", |
b911208f4
|
1121 1122 |
"[ addr [ baud ] ] " |
8bde7f776
|
1123 |
" - load binary file over serial line" |
b911208f4
|
1124 |
" at address 'addr' with baudrate 'baud'" |
6e66bd559
|
1125 1126 1127 1128 1129 |
); U_BOOT_CMD( loadx, 3, 0, do_load_serial_bin, "load binary file over serial line (xmodem mode)", |
b911208f4
|
1130 1131 |
"[ addr [ baud ] ] " |
6e66bd559
|
1132 |
" - load binary file over serial line" |
b911208f4
|
1133 |
" at address 'addr' with baudrate 'baud'" |
8bde7f776
|
1134 |
); |
f2841d377
|
1135 1136 |
U_BOOT_CMD( loady, 3, 0, do_load_serial_bin, |
2fb2604d5
|
1137 |
"load binary file over serial line (ymodem mode)", |
b911208f4
|
1138 1139 |
"[ addr [ baud ] ] " |
f2841d377
|
1140 |
" - load binary file over serial line" |
b911208f4
|
1141 |
" at address 'addr' with baudrate 'baud'" |
f2841d377
|
1142 |
); |
70d7cb925
|
1143 |
#endif /* CONFIG_CMD_LOADB */ |