load.c 24.6 KB
Newer Older
wdenk's avatar
wdenk committed
1
/*
2
 * (C) Copyright 2000-2004
wdenk's avatar
wdenk committed
3 4
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
5
 * SPDX-License-Identifier:	GPL-2.0+
wdenk's avatar
wdenk committed
6 7 8 9 10 11 12
 */

/*
 * Serial up- and download support
 */
#include <common.h>
#include <command.h>
13
#include <console.h>
wdenk's avatar
wdenk committed
14 15
#include <s_record.h>
#include <net.h>
16
#include <exports.h>
17
#include <xyzModem.h>
wdenk's avatar
wdenk committed
18

19
DECLARE_GLOBAL_DATA_PTR;
wdenk's avatar
wdenk committed
20

21
#if defined(CONFIG_CMD_LOADB)
Angus Ainslie's avatar
Angus Ainslie committed
22
static ulong load_serial_ymodem(ulong offset, int mode);
23 24
#endif

25
#if defined(CONFIG_CMD_LOADS)
26 27
static ulong load_serial(long offset);
static int read_record(char *buf, ulong len);
28
# if defined(CONFIG_CMD_SAVES)
29 30
static int save_serial(ulong offset, ulong size);
static int write_record(char *buf);
31
#endif
wdenk's avatar
wdenk committed
32 33

static int do_echo = 1;
34
#endif
wdenk's avatar
wdenk committed
35 36 37

/* -------------------------------------------------------------------- */

38
#if defined(CONFIG_CMD_LOADS)
39 40
static int do_load_serial(cmd_tbl_t *cmdtp, int flag, int argc,
			  char * const argv[])
wdenk's avatar
wdenk committed
41
{
42
	long offset = 0;
wdenk's avatar
wdenk committed
43 44 45 46
	ulong addr;
	int i;
	char *env_echo;
	int rcode = 0;
47
#ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk's avatar
wdenk committed
48 49 50 51 52 53 54 55 56 57 58
	int load_baudrate, current_baudrate;

	load_baudrate = current_baudrate = gd->baudrate;
#endif

	if (((env_echo = getenv("loads_echo")) != NULL) && (*env_echo == '1')) {
		do_echo = 1;
	} else {
		do_echo = 0;
	}

59
#ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk's avatar
wdenk committed
60
	if (argc >= 2) {
61
		offset = simple_strtol(argv[1], NULL, 16);
wdenk's avatar
wdenk committed
62 63 64 65 66 67 68 69 70
	}
	if (argc == 3) {
		load_baudrate = (int)simple_strtoul(argv[2], NULL, 10);

		/* default to current baudrate */
		if (load_baudrate == 0)
			load_baudrate = current_baudrate;
	}
	if (load_baudrate != current_baudrate) {
71
		printf("## Switch baudrate to %d bps and press ENTER ...\n",
wdenk's avatar
wdenk committed
72 73 74
			load_baudrate);
		udelay(50000);
		gd->baudrate = load_baudrate;
75
		serial_setbrg();
wdenk's avatar
wdenk committed
76 77 78 79 80 81
		udelay(50000);
		for (;;) {
			if (getc() == '\r')
				break;
		}
	}
82
#else	/* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk's avatar
wdenk committed
83
	if (argc == 2) {
84
		offset = simple_strtol(argv[1], NULL, 16);
wdenk's avatar
wdenk committed
85
	}
86
#endif	/* CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk's avatar
wdenk committed
87

88
	printf("## Ready for S-Record download ...\n");
wdenk's avatar
wdenk committed
89

90
	addr = load_serial(offset);
wdenk's avatar
wdenk committed
91 92 93 94 95 96 97

	/*
	 * 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) {
98 99
		if (tstc()) {
			(void) getc();
wdenk's avatar
wdenk committed
100 101 102 103 104
		}
		udelay(1000);
	}

	if (addr == ~0) {
105
		printf("## S-Record download aborted\n");
wdenk's avatar
wdenk committed
106 107
		rcode = 1;
	} else {
108
		printf("## Start Addr      = 0x%08lX\n", addr);
wdenk's avatar
wdenk committed
109 110 111
		load_addr = addr;
	}

112
#ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk's avatar
wdenk committed
113
	if (load_baudrate != current_baudrate) {
114
		printf("## Switch baudrate to %d bps and press ESC ...\n",
wdenk's avatar
wdenk committed
115
			current_baudrate);
116
		udelay(50000);
wdenk's avatar
wdenk committed
117
		gd->baudrate = current_baudrate;
118 119
		serial_setbrg();
		udelay(50000);
wdenk's avatar
wdenk committed
120 121 122 123 124 125 126 127 128
		for (;;) {
			if (getc() == 0x1B) /* ESC */
				break;
		}
	}
#endif
	return rcode;
}

129
static ulong load_serial(long offset)
wdenk's avatar
wdenk committed
130 131 132 133 134 135 136 137 138 139 140 141 142
{
	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	*/
	ulong	store_addr;
	ulong	start_addr = ~0;
	ulong	end_addr   =  0;
	int	line_count =  0;

	while (read_record(record, SREC_MAXRECLEN + 1) >= 0) {
143
		type = srec_decode(record, &binlen, &addr, binbuf);
wdenk's avatar
wdenk committed
144 145 146 147 148 149 150 151 152 153

		if (type < 0) {
			return (~0);		/* Invalid S-Record		*/
		}

		switch (type) {
		case SREC_DATA2:
		case SREC_DATA3:
		case SREC_DATA4:
		    store_addr = addr + offset;
154
#ifndef CONFIG_SYS_NO_FLASH
wdenk's avatar
wdenk committed
155 156 157
		    if (addr2info(store_addr)) {
			int rc;

Wolfgang Denk's avatar
Wolfgang Denk committed
158
			rc = flash_write((char *)binbuf,store_addr,binlen);
wdenk's avatar
wdenk committed
159
			if (rc != 0) {
160
				flash_perror(rc);
wdenk's avatar
wdenk committed
161 162 163 164 165
				return (~0);
			}
		    } else
#endif
		    {
166
			memcpy((char *)(store_addr), binbuf, binlen);
wdenk's avatar
wdenk committed
167 168 169 170 171 172 173 174 175
		    }
		    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:
176
		    udelay(10000);
wdenk's avatar
wdenk committed
177
		    size = end_addr - start_addr + 1;
178
		    printf("\n"
wdenk's avatar
wdenk committed
179 180 181 182 183
			    "## First Load Addr = 0x%08lX\n"
			    "## Last  Load Addr = 0x%08lX\n"
			    "## Total Size      = 0x%08lX = %ld Bytes\n",
			    start_addr, end_addr, size, size
		    );
184
		    flush_cache(start_addr, size);
185
		    setenv_hex("filesize", size);
wdenk's avatar
wdenk committed
186 187 188 189 190 191 192 193
		    return (addr);
		case SREC_START:
		    break;
		default:
		    break;
		}
		if (!do_echo) {	/* print a '.' every 100 lines */
			if ((++line_count % 100) == 0)
194
				putc('.');
wdenk's avatar
wdenk committed
195 196 197 198 199 200
		}
	}

	return (~0);			/* Download aborted		*/
}

201
static int read_record(char *buf, ulong len)
wdenk's avatar
wdenk committed
202 203 204 205 206 207 208
{
	char *p;
	char c;

	--len;	/* always leave room for terminating '\0' byte */

	for (p=buf; p < buf+len; ++p) {
209
		c = getc();		/* read character		*/
wdenk's avatar
wdenk committed
210
		if (do_echo)
211
			putc(c);	/* ... and echo it		*/
wdenk's avatar
wdenk committed
212 213 214 215 216 217 218 219 220 221 222 223 224 225

		switch (c) {
		case '\r':
		case '\n':
			*p = '\0';
			return (p - buf);
		case '\0':
		case 0x03:			/* ^C - Control C		*/
			return (-1);
		default:
			*p = c;
		}

	    /* Check for the console hangup (if any different from serial) */
Martin Dorwig's avatar
Martin Dorwig committed
226
	    if (gd->jt->getc != getc) {
wdenk's avatar
wdenk committed
227 228 229 230 231 232 233 234 235 236 237
		if (ctrlc()) {
		    return (-1);
		}
	    }
	}

	/* line too long - truncate */
	*p = '\0';
	return (p - buf);
}

238
#if defined(CONFIG_CMD_SAVES)
wdenk's avatar
wdenk committed
239

240
int do_save_serial (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
wdenk's avatar
wdenk committed
241 242 243
{
	ulong offset = 0;
	ulong size   = 0;
244
#ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk's avatar
wdenk committed
245 246 247 248 249 250 251 252
	int save_baudrate, current_baudrate;

	save_baudrate = current_baudrate = gd->baudrate;
#endif

	if (argc >= 2) {
		offset = simple_strtoul(argv[1], NULL, 16);
	}
253
#ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk's avatar
wdenk committed
254 255 256 257 258 259 260 261 262 263 264
	if (argc >= 3) {
		size = simple_strtoul(argv[2], NULL, 16);
	}
	if (argc == 4) {
		save_baudrate = (int)simple_strtoul(argv[3], NULL, 10);

		/* default to current baudrate */
		if (save_baudrate == 0)
			save_baudrate = current_baudrate;
	}
	if (save_baudrate != current_baudrate) {
265
		printf("## Switch baudrate to %d bps and press ENTER ...\n",
wdenk's avatar
wdenk committed
266 267 268
			save_baudrate);
		udelay(50000);
		gd->baudrate = save_baudrate;
269
		serial_setbrg();
wdenk's avatar
wdenk committed
270 271 272 273 274 275
		udelay(50000);
		for (;;) {
			if (getc() == '\r')
				break;
		}
	}
276
#else	/* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk's avatar
wdenk committed
277 278 279
	if (argc == 3) {
		size = simple_strtoul(argv[2], NULL, 16);
	}
280
#endif	/* CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk's avatar
wdenk committed
281

282
	printf("## Ready for S-Record upload, press ENTER to proceed ...\n");
wdenk's avatar
wdenk committed
283 284 285 286
	for (;;) {
		if (getc() == '\r')
			break;
	}
287 288
	if (save_serial(offset, size)) {
		printf("## S-Record upload aborted\n");
wdenk's avatar
wdenk committed
289
	} else {
290
		printf("## S-Record upload complete\n");
wdenk's avatar
wdenk committed
291
	}
292
#ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk's avatar
wdenk committed
293
	if (save_baudrate != current_baudrate) {
294
		printf("## Switch baudrate to %d bps and press ESC ...\n",
wdenk's avatar
wdenk committed
295
			(int)current_baudrate);
296
		udelay(50000);
wdenk's avatar
wdenk committed
297
		gd->baudrate = current_baudrate;
298 299
		serial_setbrg();
		udelay(50000);
wdenk's avatar
wdenk committed
300 301 302 303 304 305 306 307 308 309 310 311 312 313
		for (;;) {
			if (getc() == 0x1B) /* ESC */
				break;
		}
	}
#endif
	return 0;
}

#define SREC3_START				"S0030000FC\n"
#define SREC3_FORMAT			"S3%02X%08lX%s%02X\n"
#define SREC3_END				"S70500000000FA\n"
#define SREC_BYTES_PER_RECORD	16

314
static int save_serial(ulong address, ulong count)
wdenk's avatar
wdenk committed
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
{
	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);
}

369
static int write_record(char *buf)
wdenk's avatar
wdenk committed
370 371 372 373
{
	char c;

	while((c = *buf++))
374
		putc(c);
wdenk's avatar
wdenk committed
375 376 377 378 379 380 381 382

	/* Check for the console hangup (if any different from serial) */

	if (ctrlc()) {
	    return (-1);
	}
	return (0);
}
383
# endif
wdenk's avatar
wdenk committed
384

385
#endif
wdenk's avatar
wdenk committed
386 387


388
#if defined(CONFIG_CMD_LOADB)
389 390 391
/*
 * loadb command (load binary) included
 */
wdenk's avatar
wdenk committed
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
#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))

static void set_kerm_bin_mode(unsigned long *);
static int k_recv(void);
409
static ulong load_serial_bin(ulong offset);
wdenk's avatar
wdenk committed
410 411


412 413 414 415
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 */
wdenk's avatar
wdenk committed
416

417 418
static int do_load_serial_bin(cmd_tbl_t *cmdtp, int flag, int argc,
			      char * const argv[])
wdenk's avatar
wdenk committed
419 420 421 422 423 424 425
{
	ulong offset = 0;
	ulong addr;
	int load_baudrate, current_baudrate;
	int rcode = 0;
	char *s;

426 427
	/* pre-set offset from CONFIG_SYS_LOAD_ADDR */
	offset = CONFIG_SYS_LOAD_ADDR;
wdenk's avatar
wdenk committed
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447

	/* pre-set offset from $loadaddr */
	if ((s = getenv("loadaddr")) != NULL) {
		offset = simple_strtoul(s, NULL, 16);
	}

	load_baudrate = current_baudrate = gd->baudrate;

	if (argc >= 2) {
		offset = simple_strtoul(argv[1], NULL, 16);
	}
	if (argc == 3) {
		load_baudrate = (int)simple_strtoul(argv[2], NULL, 10);

		/* default to current baudrate */
		if (load_baudrate == 0)
			load_baudrate = current_baudrate;
	}

	if (load_baudrate != current_baudrate) {
448
		printf("## Switch baudrate to %d bps and press ENTER ...\n",
wdenk's avatar
wdenk committed
449 450 451
			load_baudrate);
		udelay(50000);
		gd->baudrate = load_baudrate;
452
		serial_setbrg();
wdenk's avatar
wdenk committed
453 454 455 456 457 458 459
		udelay(50000);
		for (;;) {
			if (getc() == '\r')
				break;
		}
	}

460
	if (strcmp(argv[0],"loady")==0) {
461
		printf("## Ready for binary (ymodem) download "
462 463 464 465
			"to 0x%08lX at %d bps...\n",
			offset,
			load_baudrate);

Angus Ainslie's avatar
Angus Ainslie committed
466 467 468 469 470 471 472 473 474
		addr = load_serial_ymodem(offset, xyzModem_ymodem);

	} else if (strcmp(argv[0],"loadx")==0) {
		printf("## Ready for binary (xmodem) download "
			"to 0x%08lX at %d bps...\n",
			offset,
			load_baudrate);

		addr = load_serial_ymodem(offset, xyzModem_xmodem);
wdenk's avatar
wdenk committed
475 476 477

	} else {

478
		printf("## Ready for binary (kermit) download "
479 480 481
			"to 0x%08lX at %d bps...\n",
			offset,
			load_baudrate);
482
		addr = load_serial_bin(offset);
483 484 485

		if (addr == ~0) {
			load_addr = 0;
486
			printf("## Binary (kermit) download aborted\n");
487 488
			rcode = 1;
		} else {
489
			printf("## Start Addr      = 0x%08lX\n", addr);
490 491 492
			load_addr = addr;
		}
	}
wdenk's avatar
wdenk committed
493
	if (load_baudrate != current_baudrate) {
494
		printf("## Switch baudrate to %d bps and press ESC ...\n",
wdenk's avatar
wdenk committed
495
			current_baudrate);
496
		udelay(50000);
wdenk's avatar
wdenk committed
497
		gd->baudrate = current_baudrate;
498 499
		serial_setbrg();
		udelay(50000);
wdenk's avatar
wdenk committed
500 501 502 503 504 505 506 507 508 509
		for (;;) {
			if (getc() == 0x1B) /* ESC */
				break;
		}
	}

	return rcode;
}


510
static ulong load_serial_bin(ulong offset)
wdenk's avatar
wdenk committed
511 512 513
{
	int size, i;

514 515
	set_kerm_bin_mode((ulong *) offset);
	size = k_recv();
wdenk's avatar
wdenk committed
516 517 518 519 520 521 522

	/*
	 * 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) {
523 524
		if (tstc()) {
			(void) getc();
wdenk's avatar
wdenk committed
525 526 527 528
		}
		udelay(1000);
	}

529
	flush_cache(offset, size);
wdenk's avatar
wdenk committed
530 531

	printf("## Total Size      = 0x%08x = %d Bytes\n", size, size);
532
	setenv_hex("filesize", size);
wdenk's avatar
wdenk committed
533 534 535 536

	return offset;
}

537
static void send_pad(void)
wdenk's avatar
wdenk committed
538 539 540 541
{
	int count = his_pad_count;

	while (count-- > 0)
542
		putc(his_pad_char);
wdenk's avatar
wdenk committed
543 544 545
}

/* converts escaped kermit char to binary char */
546
static char ktrans(char in)
wdenk's avatar
wdenk committed
547 548 549 550 551 552 553 554 555
{
	if ((in & 0x60) == 0x40) {
		return (char) (in & ~0x40);
	} else if ((in & 0x7f) == 0x3f) {
		return (char) (in | 0x40);
	} else
		return in;
}

556
static int chk1(char *buffer)
wdenk's avatar
wdenk committed
557 558 559 560 561 562 563 564 565
{
	int total = 0;

	while (*buffer) {
		total += *buffer++;
	}
	return (int) ((total + ((total >> 6) & 0x03)) & 0x3f);
}

566
static void s1_sendpacket(char *packet)
wdenk's avatar
wdenk committed
567
{
568
	send_pad();
wdenk's avatar
wdenk committed
569
	while (*packet) {
570
		putc(*packet++);
wdenk's avatar
wdenk committed
571 572 573 574
	}
}

static char a_b[24];
575
static void send_ack(int n)
wdenk's avatar
wdenk committed
576 577
{
	a_b[0] = START_CHAR;
578 579
	a_b[1] = tochar(3);
	a_b[2] = tochar(n);
wdenk's avatar
wdenk committed
580 581
	a_b[3] = ACK_TYPE;
	a_b[4] = '\0';
582
	a_b[4] = tochar(chk1(&a_b[1]));
wdenk's avatar
wdenk committed
583 584
	a_b[5] = his_eol;
	a_b[6] = '\0';
585
	s1_sendpacket(a_b);
wdenk's avatar
wdenk committed
586 587
}

588
static void send_nack(int n)
wdenk's avatar
wdenk committed
589 590
{
	a_b[0] = START_CHAR;
591 592
	a_b[1] = tochar(3);
	a_b[2] = tochar(n);
wdenk's avatar
wdenk committed
593 594
	a_b[3] = NACK_TYPE;
	a_b[4] = '\0';
595
	a_b[4] = tochar(chk1(&a_b[1]));
wdenk's avatar
wdenk committed
596 597
	a_b[5] = his_eol;
	a_b[6] = '\0';
598
	s1_sendpacket(a_b);
wdenk's avatar
wdenk committed
599 600 601
}


602 603
static void (*os_data_init)(void);
static void (*os_data_char)(char new_char);
wdenk's avatar
wdenk committed
604 605 606
static int os_data_state, os_data_state_saved;
static char *os_data_addr, *os_data_addr_saved;
static char *bin_start_address;
607

608
static void bin_data_init(void)
wdenk's avatar
wdenk committed
609 610 611 612
{
	os_data_state = 0;
	os_data_addr = bin_start_address;
}
613

614
static void os_data_save(void)
wdenk's avatar
wdenk committed
615 616 617 618
{
	os_data_state_saved = os_data_state;
	os_data_addr_saved = os_data_addr;
}
619

620
static void os_data_restore(void)
wdenk's avatar
wdenk committed
621 622 623 624
{
	os_data_state = os_data_state_saved;
	os_data_addr = os_data_addr_saved;
}
625

626
static void bin_data_char(char new_char)
wdenk's avatar
wdenk committed
627 628 629 630 631 632 633
{
	switch (os_data_state) {
	case 0:					/* data */
		*os_data_addr++ = new_char;
		break;
	}
}
634

635
static void set_kerm_bin_mode(unsigned long *addr)
wdenk's avatar
wdenk committed
636 637 638 639 640 641 642 643 644
{
	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;
645
static void k_data_init(void)
wdenk's avatar
wdenk committed
646 647
{
	k_data_escape = 0;
648
	os_data_init();
wdenk's avatar
wdenk committed
649
}
650

651
static void k_data_save(void)
wdenk's avatar
wdenk committed
652 653
{
	k_data_escape_saved = k_data_escape;
654
	os_data_save();
wdenk's avatar
wdenk committed
655
}
656

657
static void k_data_restore(void)
wdenk's avatar
wdenk committed
658 659
{
	k_data_escape = k_data_escape_saved;
660
	os_data_restore();
wdenk's avatar
wdenk committed
661
}
662

663
static void k_data_char(char new_char)
wdenk's avatar
wdenk committed
664 665 666
{
	if (k_data_escape) {
		/* last char was escape - translate this character */
667
		os_data_char(ktrans(new_char));
wdenk's avatar
wdenk committed
668 669 670 671 672 673 674
		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 */
675
			os_data_char(new_char);
wdenk's avatar
wdenk committed
676 677 678 679 680
		}
	}
}

#define SEND_DATA_SIZE  20
681 682
static char send_parms[SEND_DATA_SIZE];
static char *send_ptr;
wdenk's avatar
wdenk committed
683 684 685

/* handle_send_packet interprits the protocol info and builds and
   sends an appropriate ack for what we can do */
686
static void handle_send_packet(int n)
wdenk's avatar
wdenk committed
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
{
	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 */
706
		a_b[++length] = tochar(94);
wdenk's avatar
wdenk committed
707 708 709 710
		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 */
711
		a_b[++length] = tochar(1);
wdenk's avatar
wdenk committed
712 713 714 715
		if (bytes-- <= 0)
			break;
		/* handle NPAD - number of pad chars I need */
		/* remember what he says - I need none */
716 717
		his_pad_count = untochar(send_parms[2]);
		a_b[++length] = tochar(0);
wdenk's avatar
wdenk committed
718 719 720 721
		if (bytes-- <= 0)
			break;
		/* handle PADC - pad chars I need */
		/* remember what he says - I need none */
722
		his_pad_char = ktrans(send_parms[3]);
wdenk's avatar
wdenk committed
723 724 725 726 727
		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 */
728 729
		his_eol = untochar(send_parms[4]);
		a_b[++length] = tochar(END_CHAR);
wdenk's avatar
wdenk committed
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
		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 */
755 756 757 758
		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 */
wdenk's avatar
wdenk committed
759 760 761
	} while (0);

	a_b[0] = START_CHAR;
762 763
	a_b[1] = tochar(length);
	a_b[2] = tochar(n);
wdenk's avatar
wdenk committed
764 765
	a_b[3] = ACK_TYPE;
	a_b[++length] = '\0';
766
	a_b[length] = tochar(chk1(&a_b[1]));
wdenk's avatar
wdenk committed
767 768
	a_b[++length] = his_eol;
	a_b[++length] = '\0';
769
	s1_sendpacket(a_b);
wdenk's avatar
wdenk committed
770 771 772
}

/* k_recv receives a OS Open image file over kermit line */
773
static int k_recv(void)
wdenk's avatar
wdenk committed
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
{
	char new_char;
	char k_state, k_state_saved;
	int sum;
	int done;
	int length;
	int n, last_n;
	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;
792
	k_data_init();
wdenk's avatar
wdenk committed
793
	k_state_saved = k_state;
794
	k_data_save();
wdenk's avatar
wdenk committed
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
	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 (;;) {
827
			switch (getc ()) {
wdenk's avatar
wdenk committed
828 829 830 831 832 833 834 835 836 837 838
			case START_CHAR:	/* start packet */
				goto START;
			case ETX_CHAR:		/* ^C waiting for packet */
				return (0);
			default:
				;
			}
		}
START:
		/* get length of packet */
		sum = 0;
839
		new_char = getc();
wdenk's avatar
wdenk committed
840 841 842
		if ((new_char & 0xE0) == 0)
			goto packet_error;
		sum += new_char & 0xff;
843
		length = untochar(new_char);
wdenk's avatar
wdenk committed
844
		/* get sequence number */
845
		new_char = getc();
wdenk's avatar
wdenk committed
846 847 848
		if ((new_char & 0xE0) == 0)
			goto packet_error;
		sum += new_char & 0xff;
849
		n = untochar(new_char);
wdenk's avatar
wdenk committed
850 851 852 853 854 855 856 857 858 859 860 861
		--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;
862
			k_data_restore();
wdenk's avatar
wdenk committed
863 864 865 866
		} else {
			/* new sequence number, checkpoint the download */
			last_n = n;
			k_state_saved = k_state;
867
			k_data_save();
wdenk's avatar
wdenk committed
868 869 870 871
		}
		/* END NEW CODE */

		/* get packet type */
872
		new_char = getc();
wdenk's avatar
wdenk committed
873 874 875 876 877 878 879 880 881
		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 */
882
			new_char = getc();
wdenk's avatar
wdenk committed
883 884 885
			if ((new_char & 0xE0) == 0)
				goto packet_error;
			sum += new_char & 0xff;
886 887
			len_hi = untochar(new_char);
			new_char = getc();
wdenk's avatar
wdenk committed
888 889 890
			if ((new_char & 0xE0) == 0)
				goto packet_error;
			sum += new_char & 0xff;
891
			len_lo = untochar(new_char);
wdenk's avatar
wdenk committed
892 893
			length = len_hi * 95 + len_lo;
			/* check header checksum */
894
			new_char = getc();
wdenk's avatar
wdenk committed
895 896
			if ((new_char & 0xE0) == 0)
				goto packet_error;
897
			if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
wdenk's avatar
wdenk committed
898 899 900 901 902 903
				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) {
904
			new_char = getc();
wdenk's avatar
wdenk committed
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
			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 */
921
		new_char = getc();
wdenk's avatar
wdenk committed
922 923
		if ((new_char & 0xE0) == 0)
			goto packet_error;
924
		if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
wdenk's avatar
wdenk committed
925 926
			goto packet_error;
		/* get END_CHAR */
927
		new_char = getc();
wdenk's avatar
wdenk committed
928 929 930 931
		if (new_char != END_CHAR) {
		  packet_error:
			/* restore state machines */
			k_state = k_state_saved;
932
			k_data_restore();
wdenk's avatar
wdenk committed
933
			/* send a negative acknowledge packet in */
934
			send_nack(n);
wdenk's avatar
wdenk committed
935 936
		} else if (k_state == SEND_TYPE) {
			/* crack the protocol parms, build an appropriate ack packet */
937
			handle_send_packet(n);
wdenk's avatar
wdenk committed
938 939
		} else {
			/* send simple acknowledge packet in */
940
			send_ack(n);
wdenk's avatar
wdenk committed
941 942 943 944 945 946 947
			/* quit if end of transmission */
			if (k_state == BREAK_TYPE)
				done = 1;
		}
	}
	return ((ulong) os_data_addr - (ulong) bin_start_address);
}
948 949

static int getcxmodem(void) {
Wolfgang Denk's avatar
Wolfgang Denk committed
950
	if (tstc())
951 952 953
		return (getc());
	return -1;
}
Angus Ainslie's avatar
Angus Ainslie committed
954
static ulong load_serial_ymodem(ulong offset, int mode)
955 956 957 958 959
{
	int size;
	int err;
	int res;
	connection_info_t info;
Wolfgang Denk's avatar
Wolfgang Denk committed
960 961 962
	char ymodemBuf[1024];
	ulong store_addr = ~0;
	ulong addr = 0;
963

Wolfgang Denk's avatar
Wolfgang Denk committed
964
	size = 0;
Angus Ainslie's avatar
Angus Ainslie committed
965
	info.mode = mode;
966
	res = xyzModem_stream_open(&info, &err);
967 968
	if (!res) {

Wolfgang Denk's avatar
Wolfgang Denk committed
969
		while ((res =
970
			xyzModem_stream_read(ymodemBuf, 1024, &err)) > 0) {
Wolfgang Denk's avatar
Wolfgang Denk committed
971 972 973
			store_addr = addr + offset;
			size += res;
			addr += res;
974
#ifndef CONFIG_SYS_NO_FLASH
975
			if (addr2info(store_addr)) {
Wolfgang Denk's avatar
Wolfgang Denk committed
976 977
				int rc;

978
				rc = flash_write((char *) ymodemBuf,
Wolfgang Denk's avatar
Wolfgang Denk committed
979 980 981 982 983 984
						  store_addr, res);
				if (rc != 0) {
					flash_perror (rc);
					return (~0);
				}
			} else
985
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
986
			{
987
				memcpy((char *)(store_addr), ymodemBuf,
Wolfgang Denk's avatar
Wolfgang Denk committed
988 989 990 991 992
					res);
			}

		}
	} else {
993
		printf("%s\n", xyzModem_error(err));
994
	}
Wolfgang Denk's avatar
Wolfgang Denk committed
995

996 997
	xyzModem_stream_close(&err);
	xyzModem_stream_terminate(false, &getcxmodem);
998 999


1000
	flush_cache(offset, size);
1001

1002
	printf("## Total Size      = 0x%08x = %d Bytes\n", size, size);
1003
	setenv_hex("filesize", size);
1004 1005 1006 1007

	return offset;
}

1008
#endif
wdenk's avatar
wdenk committed
1009 1010 1011

/* -------------------------------------------------------------------- */

1012
#if defined(CONFIG_CMD_LOADS)
wdenk's avatar
wdenk committed
1013

1014
#ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
1015 1016
U_BOOT_CMD(
	loads, 3, 0,	do_load_serial,
Peter Tyser's avatar
Peter Tyser committed
1017
	"load S-Record file over serial line",
wdenk's avatar
wdenk committed
1018 1019
	"[ off ] [ baud ]\n"
	"    - load S-Record file over serial line"
1020
	" with offset 'off' and baudrate 'baud'"
wdenk's avatar
wdenk committed
1021 1022
);

1023
#else	/* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
1024 1025
U_BOOT_CMD(
	loads, 2, 0,	do_load_serial,
Peter Tyser's avatar
Peter Tyser committed
1026
	"load S-Record file over serial line",
wdenk's avatar
wdenk committed
1027
	"[ off ]\n"
1028
	"    - load S-Record file over serial line with offset 'off'"
wdenk's avatar
wdenk committed
1029
);
1030
#endif	/* CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk's avatar
wdenk committed
1031 1032 1033 1034 1035 1036

/*
 * SAVES always requires LOADS support, but not vice versa
 */


1037
#if defined(CONFIG_CMD_SAVES)
1038
#ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
1039 1040
U_BOOT_CMD(
	saves, 4, 0,	do_save_serial,
Peter Tyser's avatar
Peter Tyser committed
1041
	"save S-Record file over serial line",
wdenk's avatar
wdenk committed
1042 1043
	"[ off ] [size] [ baud ]\n"
	"    - save S-Record file over serial line"
1044
	" with offset 'off', size 'size' and baudrate 'baud'"
wdenk's avatar
wdenk committed
1045
);
1046
#else	/* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
1047 1048
U_BOOT_CMD(
	saves, 3, 0,	do_save_serial,
Peter Tyser's avatar
Peter Tyser committed
1049
	"save S-Record file over serial line",
wdenk's avatar
wdenk committed
1050
	"[ off ] [size]\n"
1051
	"    - save S-Record file over serial line with offset 'off' and size 'size'"
wdenk's avatar
wdenk committed
1052
);
1053
#endif	/* CONFIG_SYS_LOADS_BAUD_CHANGE */
1054 1055
#endif	/* CONFIG_CMD_SAVES */
#endif	/* CONFIG_CMD_LOADS */
wdenk's avatar
wdenk committed
1056 1057


1058
#if defined(CONFIG_CMD_LOADB)
1059 1060
U_BOOT_CMD(
	loadb, 3, 0,	do_load_serial_bin,
Peter Tyser's avatar
Peter Tyser committed
1061
	"load binary file over serial line (kermit mode)",
wdenk's avatar
wdenk committed
1062 1063
	"[ off ] [ baud ]\n"
	"    - load binary file over serial line"
Angus Ainslie's avatar
Angus Ainslie committed
1064 1065 1066 1067 1068 1069 1070 1071
	" with offset 'off' and baudrate 'baud'"
);

U_BOOT_CMD(
	loadx, 3, 0,	do_load_serial_bin,
	"load binary file over serial line (xmodem mode)",
	"[ off ] [ baud ]\n"
	"    - load binary file over serial line"
1072
	" with offset 'off' and baudrate 'baud'"
wdenk's avatar
wdenk committed
1073 1074
);

1075 1076
U_BOOT_CMD(
	loady, 3, 0,	do_load_serial_bin,
Peter Tyser's avatar
Peter Tyser committed
1077
	"load binary file over serial line (ymodem mode)",
1078 1079
	"[ off ] [ baud ]\n"
	"    - load binary file over serial line"
1080
	" with offset 'off' and baudrate 'baud'"
1081 1082
);

1083
#endif	/* CONFIG_CMD_LOADB */