xref: /openbmc/u-boot/cmd/load.c (revision 0a6036da)
12e192b24SSimon Glass /*
22e192b24SSimon Glass  * (C) Copyright 2000-2004
32e192b24SSimon Glass  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
42e192b24SSimon Glass  *
52e192b24SSimon Glass  * SPDX-License-Identifier:	GPL-2.0+
62e192b24SSimon Glass  */
72e192b24SSimon Glass 
82e192b24SSimon Glass /*
92e192b24SSimon Glass  * Serial up- and download support
102e192b24SSimon Glass  */
112e192b24SSimon Glass #include <common.h>
122e192b24SSimon Glass #include <command.h>
132e192b24SSimon Glass #include <console.h>
142e192b24SSimon Glass #include <s_record.h>
152e192b24SSimon Glass #include <net.h>
162e192b24SSimon Glass #include <exports.h>
172e192b24SSimon Glass #include <xyzModem.h>
182e192b24SSimon Glass 
192e192b24SSimon Glass DECLARE_GLOBAL_DATA_PTR;
202e192b24SSimon Glass 
212e192b24SSimon Glass #if defined(CONFIG_CMD_LOADB)
222e192b24SSimon Glass static ulong load_serial_ymodem(ulong offset, int mode);
232e192b24SSimon Glass #endif
242e192b24SSimon Glass 
252e192b24SSimon Glass #if defined(CONFIG_CMD_LOADS)
262e192b24SSimon Glass static ulong load_serial(long offset);
272e192b24SSimon Glass static int read_record(char *buf, ulong len);
282e192b24SSimon Glass # if defined(CONFIG_CMD_SAVES)
292e192b24SSimon Glass static int save_serial(ulong offset, ulong size);
302e192b24SSimon Glass static int write_record(char *buf);
312e192b24SSimon Glass #endif
322e192b24SSimon Glass 
332e192b24SSimon Glass static int do_echo = 1;
342e192b24SSimon Glass #endif
352e192b24SSimon Glass 
362e192b24SSimon Glass /* -------------------------------------------------------------------- */
372e192b24SSimon Glass 
382e192b24SSimon Glass #if defined(CONFIG_CMD_LOADS)
392e192b24SSimon Glass static int do_load_serial(cmd_tbl_t *cmdtp, int flag, int argc,
402e192b24SSimon Glass 			  char * const argv[])
412e192b24SSimon Glass {
422e192b24SSimon Glass 	long offset = 0;
432e192b24SSimon Glass 	ulong addr;
442e192b24SSimon Glass 	int i;
452e192b24SSimon Glass 	char *env_echo;
462e192b24SSimon Glass 	int rcode = 0;
472e192b24SSimon Glass #ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
482e192b24SSimon Glass 	int load_baudrate, current_baudrate;
492e192b24SSimon Glass 
502e192b24SSimon Glass 	load_baudrate = current_baudrate = gd->baudrate;
512e192b24SSimon Glass #endif
522e192b24SSimon Glass 
532e192b24SSimon Glass 	if (((env_echo = getenv("loads_echo")) != NULL) && (*env_echo == '1')) {
542e192b24SSimon Glass 		do_echo = 1;
552e192b24SSimon Glass 	} else {
562e192b24SSimon Glass 		do_echo = 0;
572e192b24SSimon Glass 	}
582e192b24SSimon Glass 
592e192b24SSimon Glass #ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
602e192b24SSimon Glass 	if (argc >= 2) {
612e192b24SSimon Glass 		offset = simple_strtol(argv[1], NULL, 16);
622e192b24SSimon Glass 	}
632e192b24SSimon Glass 	if (argc == 3) {
642e192b24SSimon Glass 		load_baudrate = (int)simple_strtoul(argv[2], NULL, 10);
652e192b24SSimon Glass 
662e192b24SSimon Glass 		/* default to current baudrate */
672e192b24SSimon Glass 		if (load_baudrate == 0)
682e192b24SSimon Glass 			load_baudrate = current_baudrate;
692e192b24SSimon Glass 	}
702e192b24SSimon Glass 	if (load_baudrate != current_baudrate) {
712e192b24SSimon Glass 		printf("## Switch baudrate to %d bps and press ENTER ...\n",
722e192b24SSimon Glass 			load_baudrate);
732e192b24SSimon Glass 		udelay(50000);
742e192b24SSimon Glass 		gd->baudrate = load_baudrate;
752e192b24SSimon Glass 		serial_setbrg();
762e192b24SSimon Glass 		udelay(50000);
772e192b24SSimon Glass 		for (;;) {
782e192b24SSimon Glass 			if (getc() == '\r')
792e192b24SSimon Glass 				break;
802e192b24SSimon Glass 		}
812e192b24SSimon Glass 	}
822e192b24SSimon Glass #else	/* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
832e192b24SSimon Glass 	if (argc == 2) {
842e192b24SSimon Glass 		offset = simple_strtol(argv[1], NULL, 16);
852e192b24SSimon Glass 	}
862e192b24SSimon Glass #endif	/* CONFIG_SYS_LOADS_BAUD_CHANGE */
872e192b24SSimon Glass 
882e192b24SSimon Glass 	printf("## Ready for S-Record download ...\n");
892e192b24SSimon Glass 
902e192b24SSimon Glass 	addr = load_serial(offset);
912e192b24SSimon Glass 
922e192b24SSimon Glass 	/*
932e192b24SSimon Glass 	 * Gather any trailing characters (for instance, the ^D which
942e192b24SSimon Glass 	 * is sent by 'cu' after sending a file), and give the
952e192b24SSimon Glass 	 * box some time (100 * 1 ms)
962e192b24SSimon Glass 	 */
972e192b24SSimon Glass 	for (i=0; i<100; ++i) {
982e192b24SSimon Glass 		if (tstc()) {
992e192b24SSimon Glass 			(void) getc();
1002e192b24SSimon Glass 		}
1012e192b24SSimon Glass 		udelay(1000);
1022e192b24SSimon Glass 	}
1032e192b24SSimon Glass 
1042e192b24SSimon Glass 	if (addr == ~0) {
1052e192b24SSimon Glass 		printf("## S-Record download aborted\n");
1062e192b24SSimon Glass 		rcode = 1;
1072e192b24SSimon Glass 	} else {
1082e192b24SSimon Glass 		printf("## Start Addr      = 0x%08lX\n", addr);
1092e192b24SSimon Glass 		load_addr = addr;
1102e192b24SSimon Glass 	}
1112e192b24SSimon Glass 
1122e192b24SSimon Glass #ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
1132e192b24SSimon Glass 	if (load_baudrate != current_baudrate) {
1142e192b24SSimon Glass 		printf("## Switch baudrate to %d bps and press ESC ...\n",
1152e192b24SSimon Glass 			current_baudrate);
1162e192b24SSimon Glass 		udelay(50000);
1172e192b24SSimon Glass 		gd->baudrate = current_baudrate;
1182e192b24SSimon Glass 		serial_setbrg();
1192e192b24SSimon Glass 		udelay(50000);
1202e192b24SSimon Glass 		for (;;) {
1212e192b24SSimon Glass 			if (getc() == 0x1B) /* ESC */
1222e192b24SSimon Glass 				break;
1232e192b24SSimon Glass 		}
1242e192b24SSimon Glass 	}
1252e192b24SSimon Glass #endif
1262e192b24SSimon Glass 	return rcode;
1272e192b24SSimon Glass }
1282e192b24SSimon Glass 
1292e192b24SSimon Glass static ulong load_serial(long offset)
1302e192b24SSimon Glass {
1312e192b24SSimon Glass 	char	record[SREC_MAXRECLEN + 1];	/* buffer for one S-Record	*/
1322e192b24SSimon Glass 	char	binbuf[SREC_MAXBINLEN];		/* buffer for binary data	*/
1332e192b24SSimon Glass 	int	binlen;				/* no. of data bytes in S-Rec.	*/
1342e192b24SSimon Glass 	int	type;				/* return code for record type	*/
1352e192b24SSimon Glass 	ulong	addr;				/* load address from S-Record	*/
1362e192b24SSimon Glass 	ulong	size;				/* number of bytes transferred	*/
1372e192b24SSimon Glass 	ulong	store_addr;
1382e192b24SSimon Glass 	ulong	start_addr = ~0;
1392e192b24SSimon Glass 	ulong	end_addr   =  0;
1402e192b24SSimon Glass 	int	line_count =  0;
1412e192b24SSimon Glass 
1422e192b24SSimon Glass 	while (read_record(record, SREC_MAXRECLEN + 1) >= 0) {
1432e192b24SSimon Glass 		type = srec_decode(record, &binlen, &addr, binbuf);
1442e192b24SSimon Glass 
1452e192b24SSimon Glass 		if (type < 0) {
1462e192b24SSimon Glass 			return (~0);		/* Invalid S-Record		*/
1472e192b24SSimon Glass 		}
1482e192b24SSimon Glass 
1492e192b24SSimon Glass 		switch (type) {
1502e192b24SSimon Glass 		case SREC_DATA2:
1512e192b24SSimon Glass 		case SREC_DATA3:
1522e192b24SSimon Glass 		case SREC_DATA4:
1532e192b24SSimon Glass 		    store_addr = addr + offset;
1542e192b24SSimon Glass #ifndef CONFIG_SYS_NO_FLASH
1552e192b24SSimon Glass 		    if (addr2info(store_addr)) {
1562e192b24SSimon Glass 			int rc;
1572e192b24SSimon Glass 
1582e192b24SSimon Glass 			rc = flash_write((char *)binbuf,store_addr,binlen);
1592e192b24SSimon Glass 			if (rc != 0) {
1602e192b24SSimon Glass 				flash_perror(rc);
1612e192b24SSimon Glass 				return (~0);
1622e192b24SSimon Glass 			}
1632e192b24SSimon Glass 		    } else
1642e192b24SSimon Glass #endif
1652e192b24SSimon Glass 		    {
1662e192b24SSimon Glass 			memcpy((char *)(store_addr), binbuf, binlen);
1672e192b24SSimon Glass 		    }
1682e192b24SSimon Glass 		    if ((store_addr) < start_addr)
1692e192b24SSimon Glass 			start_addr = store_addr;
1702e192b24SSimon Glass 		    if ((store_addr + binlen - 1) > end_addr)
1712e192b24SSimon Glass 			end_addr = store_addr + binlen - 1;
1722e192b24SSimon Glass 		    break;
1732e192b24SSimon Glass 		case SREC_END2:
1742e192b24SSimon Glass 		case SREC_END3:
1752e192b24SSimon Glass 		case SREC_END4:
1762e192b24SSimon Glass 		    udelay(10000);
1772e192b24SSimon Glass 		    size = end_addr - start_addr + 1;
1782e192b24SSimon Glass 		    printf("\n"
1792e192b24SSimon Glass 			    "## First Load Addr = 0x%08lX\n"
1802e192b24SSimon Glass 			    "## Last  Load Addr = 0x%08lX\n"
1812e192b24SSimon Glass 			    "## Total Size      = 0x%08lX = %ld Bytes\n",
1822e192b24SSimon Glass 			    start_addr, end_addr, size, size
1832e192b24SSimon Glass 		    );
1842e192b24SSimon Glass 		    flush_cache(start_addr, size);
1852e192b24SSimon Glass 		    setenv_hex("filesize", size);
1862e192b24SSimon Glass 		    return (addr);
1872e192b24SSimon Glass 		case SREC_START:
1882e192b24SSimon Glass 		    break;
1892e192b24SSimon Glass 		default:
1902e192b24SSimon Glass 		    break;
1912e192b24SSimon Glass 		}
1922e192b24SSimon Glass 		if (!do_echo) {	/* print a '.' every 100 lines */
1932e192b24SSimon Glass 			if ((++line_count % 100) == 0)
1942e192b24SSimon Glass 				putc('.');
1952e192b24SSimon Glass 		}
1962e192b24SSimon Glass 	}
1972e192b24SSimon Glass 
1982e192b24SSimon Glass 	return (~0);			/* Download aborted		*/
1992e192b24SSimon Glass }
2002e192b24SSimon Glass 
2012e192b24SSimon Glass static int read_record(char *buf, ulong len)
2022e192b24SSimon Glass {
2032e192b24SSimon Glass 	char *p;
2042e192b24SSimon Glass 	char c;
2052e192b24SSimon Glass 
2062e192b24SSimon Glass 	--len;	/* always leave room for terminating '\0' byte */
2072e192b24SSimon Glass 
2082e192b24SSimon Glass 	for (p=buf; p < buf+len; ++p) {
2092e192b24SSimon Glass 		c = getc();		/* read character		*/
2102e192b24SSimon Glass 		if (do_echo)
2112e192b24SSimon Glass 			putc(c);	/* ... and echo it		*/
2122e192b24SSimon Glass 
2132e192b24SSimon Glass 		switch (c) {
2142e192b24SSimon Glass 		case '\r':
2152e192b24SSimon Glass 		case '\n':
2162e192b24SSimon Glass 			*p = '\0';
2172e192b24SSimon Glass 			return (p - buf);
2182e192b24SSimon Glass 		case '\0':
2192e192b24SSimon Glass 		case 0x03:			/* ^C - Control C		*/
2202e192b24SSimon Glass 			return (-1);
2212e192b24SSimon Glass 		default:
2222e192b24SSimon Glass 			*p = c;
2232e192b24SSimon Glass 		}
2242e192b24SSimon Glass 
2252e192b24SSimon Glass 	    /* Check for the console hangup (if any different from serial) */
2262e192b24SSimon Glass 	    if (gd->jt->getc != getc) {
2272e192b24SSimon Glass 		if (ctrlc()) {
2282e192b24SSimon Glass 		    return (-1);
2292e192b24SSimon Glass 		}
2302e192b24SSimon Glass 	    }
2312e192b24SSimon Glass 	}
2322e192b24SSimon Glass 
2332e192b24SSimon Glass 	/* line too long - truncate */
2342e192b24SSimon Glass 	*p = '\0';
2352e192b24SSimon Glass 	return (p - buf);
2362e192b24SSimon Glass }
2372e192b24SSimon Glass 
2382e192b24SSimon Glass #if defined(CONFIG_CMD_SAVES)
2392e192b24SSimon Glass 
2402e192b24SSimon Glass int do_save_serial (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
2412e192b24SSimon Glass {
2422e192b24SSimon Glass 	ulong offset = 0;
2432e192b24SSimon Glass 	ulong size   = 0;
2442e192b24SSimon Glass #ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
2452e192b24SSimon Glass 	int save_baudrate, current_baudrate;
2462e192b24SSimon Glass 
2472e192b24SSimon Glass 	save_baudrate = current_baudrate = gd->baudrate;
2482e192b24SSimon Glass #endif
2492e192b24SSimon Glass 
2502e192b24SSimon Glass 	if (argc >= 2) {
2512e192b24SSimon Glass 		offset = simple_strtoul(argv[1], NULL, 16);
2522e192b24SSimon Glass 	}
2532e192b24SSimon Glass #ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
2542e192b24SSimon Glass 	if (argc >= 3) {
2552e192b24SSimon Glass 		size = simple_strtoul(argv[2], NULL, 16);
2562e192b24SSimon Glass 	}
2572e192b24SSimon Glass 	if (argc == 4) {
2582e192b24SSimon Glass 		save_baudrate = (int)simple_strtoul(argv[3], NULL, 10);
2592e192b24SSimon Glass 
2602e192b24SSimon Glass 		/* default to current baudrate */
2612e192b24SSimon Glass 		if (save_baudrate == 0)
2622e192b24SSimon Glass 			save_baudrate = current_baudrate;
2632e192b24SSimon Glass 	}
2642e192b24SSimon Glass 	if (save_baudrate != current_baudrate) {
2652e192b24SSimon Glass 		printf("## Switch baudrate to %d bps and press ENTER ...\n",
2662e192b24SSimon Glass 			save_baudrate);
2672e192b24SSimon Glass 		udelay(50000);
2682e192b24SSimon Glass 		gd->baudrate = save_baudrate;
2692e192b24SSimon Glass 		serial_setbrg();
2702e192b24SSimon Glass 		udelay(50000);
2712e192b24SSimon Glass 		for (;;) {
2722e192b24SSimon Glass 			if (getc() == '\r')
2732e192b24SSimon Glass 				break;
2742e192b24SSimon Glass 		}
2752e192b24SSimon Glass 	}
2762e192b24SSimon Glass #else	/* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
2772e192b24SSimon Glass 	if (argc == 3) {
2782e192b24SSimon Glass 		size = simple_strtoul(argv[2], NULL, 16);
2792e192b24SSimon Glass 	}
2802e192b24SSimon Glass #endif	/* CONFIG_SYS_LOADS_BAUD_CHANGE */
2812e192b24SSimon Glass 
2822e192b24SSimon Glass 	printf("## Ready for S-Record upload, press ENTER to proceed ...\n");
2832e192b24SSimon Glass 	for (;;) {
2842e192b24SSimon Glass 		if (getc() == '\r')
2852e192b24SSimon Glass 			break;
2862e192b24SSimon Glass 	}
2872e192b24SSimon Glass 	if (save_serial(offset, size)) {
2882e192b24SSimon Glass 		printf("## S-Record upload aborted\n");
2892e192b24SSimon Glass 	} else {
2902e192b24SSimon Glass 		printf("## S-Record upload complete\n");
2912e192b24SSimon Glass 	}
2922e192b24SSimon Glass #ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
2932e192b24SSimon Glass 	if (save_baudrate != current_baudrate) {
2942e192b24SSimon Glass 		printf("## Switch baudrate to %d bps and press ESC ...\n",
2952e192b24SSimon Glass 			(int)current_baudrate);
2962e192b24SSimon Glass 		udelay(50000);
2972e192b24SSimon Glass 		gd->baudrate = current_baudrate;
2982e192b24SSimon Glass 		serial_setbrg();
2992e192b24SSimon Glass 		udelay(50000);
3002e192b24SSimon Glass 		for (;;) {
3012e192b24SSimon Glass 			if (getc() == 0x1B) /* ESC */
3022e192b24SSimon Glass 				break;
3032e192b24SSimon Glass 		}
3042e192b24SSimon Glass 	}
3052e192b24SSimon Glass #endif
3062e192b24SSimon Glass 	return 0;
3072e192b24SSimon Glass }
3082e192b24SSimon Glass 
3092e192b24SSimon Glass #define SREC3_START				"S0030000FC\n"
3102e192b24SSimon Glass #define SREC3_FORMAT			"S3%02X%08lX%s%02X\n"
3112e192b24SSimon Glass #define SREC3_END				"S70500000000FA\n"
3122e192b24SSimon Glass #define SREC_BYTES_PER_RECORD	16
3132e192b24SSimon Glass 
3142e192b24SSimon Glass static int save_serial(ulong address, ulong count)
3152e192b24SSimon Glass {
3162e192b24SSimon Glass 	int i, c, reclen, checksum, length;
3172e192b24SSimon Glass 	char *hex = "0123456789ABCDEF";
3182e192b24SSimon Glass 	char	record[2*SREC_BYTES_PER_RECORD+16];	/* buffer for one S-Record	*/
3192e192b24SSimon Glass 	char	data[2*SREC_BYTES_PER_RECORD+1];	/* buffer for hex data	*/
3202e192b24SSimon Glass 
3212e192b24SSimon Glass 	reclen = 0;
3222e192b24SSimon Glass 	checksum  = 0;
3232e192b24SSimon Glass 
3242e192b24SSimon Glass 	if(write_record(SREC3_START))			/* write the header */
3252e192b24SSimon Glass 		return (-1);
3262e192b24SSimon Glass 	do {
3272e192b24SSimon Glass 		if(count) {						/* collect hex data in the buffer  */
3282e192b24SSimon Glass 			c = *(volatile uchar*)(address + reclen);	/* get one byte    */
3292e192b24SSimon Glass 			checksum += c;							/* accumulate checksum */
3302e192b24SSimon Glass 			data[2*reclen]   = hex[(c>>4)&0x0f];
3312e192b24SSimon Glass 			data[2*reclen+1] = hex[c & 0x0f];
3322e192b24SSimon Glass 			data[2*reclen+2] = '\0';
3332e192b24SSimon Glass 			++reclen;
3342e192b24SSimon Glass 			--count;
3352e192b24SSimon Glass 		}
3362e192b24SSimon Glass 		if(reclen == SREC_BYTES_PER_RECORD || count == 0) {
3372e192b24SSimon Glass 			/* enough data collected for one record: dump it */
3382e192b24SSimon Glass 			if(reclen) {	/* build & write a data record: */
3392e192b24SSimon Glass 				/* address + data + checksum */
3402e192b24SSimon Glass 				length = 4 + reclen + 1;
3412e192b24SSimon Glass 
3422e192b24SSimon Glass 				/* accumulate length bytes into checksum */
3432e192b24SSimon Glass 				for(i = 0; i < 2; i++)
3442e192b24SSimon Glass 					checksum += (length >> (8*i)) & 0xff;
3452e192b24SSimon Glass 
3462e192b24SSimon Glass 				/* accumulate address bytes into checksum: */
3472e192b24SSimon Glass 				for(i = 0; i < 4; i++)
3482e192b24SSimon Glass 					checksum += (address >> (8*i)) & 0xff;
3492e192b24SSimon Glass 
3502e192b24SSimon Glass 				/* make proper checksum byte: */
3512e192b24SSimon Glass 				checksum = ~checksum & 0xff;
3522e192b24SSimon Glass 
3532e192b24SSimon Glass 				/* output one record: */
3542e192b24SSimon Glass 				sprintf(record, SREC3_FORMAT, length, address, data, checksum);
3552e192b24SSimon Glass 				if(write_record(record))
3562e192b24SSimon Glass 					return (-1);
3572e192b24SSimon Glass 			}
3582e192b24SSimon Glass 			address  += reclen;  /* increment address */
3592e192b24SSimon Glass 			checksum  = 0;
3602e192b24SSimon Glass 			reclen    = 0;
3612e192b24SSimon Glass 		}
3622e192b24SSimon Glass 	}
3632e192b24SSimon Glass 	while(count);
3642e192b24SSimon Glass 	if(write_record(SREC3_END))	/* write the final record */
3652e192b24SSimon Glass 		return (-1);
3662e192b24SSimon Glass 	return(0);
3672e192b24SSimon Glass }
3682e192b24SSimon Glass 
3692e192b24SSimon Glass static int write_record(char *buf)
3702e192b24SSimon Glass {
3712e192b24SSimon Glass 	char c;
3722e192b24SSimon Glass 
3732e192b24SSimon Glass 	while((c = *buf++))
3742e192b24SSimon Glass 		putc(c);
3752e192b24SSimon Glass 
3762e192b24SSimon Glass 	/* Check for the console hangup (if any different from serial) */
3772e192b24SSimon Glass 
3782e192b24SSimon Glass 	if (ctrlc()) {
3792e192b24SSimon Glass 	    return (-1);
3802e192b24SSimon Glass 	}
3812e192b24SSimon Glass 	return (0);
3822e192b24SSimon Glass }
3832e192b24SSimon Glass # endif
3842e192b24SSimon Glass 
3852e192b24SSimon Glass #endif
3862e192b24SSimon Glass 
3872e192b24SSimon Glass 
3882e192b24SSimon Glass #if defined(CONFIG_CMD_LOADB)
3892e192b24SSimon Glass /*
3902e192b24SSimon Glass  * loadb command (load binary) included
3912e192b24SSimon Glass  */
3922e192b24SSimon Glass #define XON_CHAR        17
3932e192b24SSimon Glass #define XOFF_CHAR       19
3942e192b24SSimon Glass #define START_CHAR      0x01
3952e192b24SSimon Glass #define ETX_CHAR	0x03
3962e192b24SSimon Glass #define END_CHAR        0x0D
3972e192b24SSimon Glass #define SPACE           0x20
3982e192b24SSimon Glass #define K_ESCAPE        0x23
3992e192b24SSimon Glass #define SEND_TYPE       'S'
4002e192b24SSimon Glass #define DATA_TYPE       'D'
4012e192b24SSimon Glass #define ACK_TYPE        'Y'
4022e192b24SSimon Glass #define NACK_TYPE       'N'
4032e192b24SSimon Glass #define BREAK_TYPE      'B'
4042e192b24SSimon Glass #define tochar(x) ((char) (((x) + SPACE) & 0xff))
4052e192b24SSimon Glass #define untochar(x) ((int) (((x) - SPACE) & 0xff))
4062e192b24SSimon Glass 
4072e192b24SSimon Glass static void set_kerm_bin_mode(unsigned long *);
4082e192b24SSimon Glass static int k_recv(void);
4092e192b24SSimon Glass static ulong load_serial_bin(ulong offset);
4102e192b24SSimon Glass 
4112e192b24SSimon Glass 
4122e192b24SSimon Glass static char his_eol;        /* character he needs at end of packet */
4132e192b24SSimon Glass static int  his_pad_count;  /* number of pad chars he needs */
4142e192b24SSimon Glass static char his_pad_char;   /* pad chars he needs */
4152e192b24SSimon Glass static char his_quote;      /* quote chars he'll use */
4162e192b24SSimon Glass 
4172e192b24SSimon Glass static int do_load_serial_bin(cmd_tbl_t *cmdtp, int flag, int argc,
4182e192b24SSimon Glass 			      char * const argv[])
4192e192b24SSimon Glass {
4202e192b24SSimon Glass 	ulong offset = 0;
4212e192b24SSimon Glass 	ulong addr;
4222e192b24SSimon Glass 	int load_baudrate, current_baudrate;
4232e192b24SSimon Glass 	int rcode = 0;
4242e192b24SSimon Glass 	char *s;
4252e192b24SSimon Glass 
4262e192b24SSimon Glass 	/* pre-set offset from CONFIG_SYS_LOAD_ADDR */
4272e192b24SSimon Glass 	offset = CONFIG_SYS_LOAD_ADDR;
4282e192b24SSimon Glass 
4292e192b24SSimon Glass 	/* pre-set offset from $loadaddr */
4302e192b24SSimon Glass 	if ((s = getenv("loadaddr")) != NULL) {
4312e192b24SSimon Glass 		offset = simple_strtoul(s, NULL, 16);
4322e192b24SSimon Glass 	}
4332e192b24SSimon Glass 
4342e192b24SSimon Glass 	load_baudrate = current_baudrate = gd->baudrate;
4352e192b24SSimon Glass 
4362e192b24SSimon Glass 	if (argc >= 2) {
4372e192b24SSimon Glass 		offset = simple_strtoul(argv[1], NULL, 16);
4382e192b24SSimon Glass 	}
4392e192b24SSimon Glass 	if (argc == 3) {
4402e192b24SSimon Glass 		load_baudrate = (int)simple_strtoul(argv[2], NULL, 10);
4412e192b24SSimon Glass 
4422e192b24SSimon Glass 		/* default to current baudrate */
4432e192b24SSimon Glass 		if (load_baudrate == 0)
4442e192b24SSimon Glass 			load_baudrate = current_baudrate;
4452e192b24SSimon Glass 	}
4462e192b24SSimon Glass 
4472e192b24SSimon Glass 	if (load_baudrate != current_baudrate) {
4482e192b24SSimon Glass 		printf("## Switch baudrate to %d bps and press ENTER ...\n",
4492e192b24SSimon Glass 			load_baudrate);
4502e192b24SSimon Glass 		udelay(50000);
4512e192b24SSimon Glass 		gd->baudrate = load_baudrate;
4522e192b24SSimon Glass 		serial_setbrg();
4532e192b24SSimon Glass 		udelay(50000);
4542e192b24SSimon Glass 		for (;;) {
4552e192b24SSimon Glass 			if (getc() == '\r')
4562e192b24SSimon Glass 				break;
4572e192b24SSimon Glass 		}
4582e192b24SSimon Glass 	}
4592e192b24SSimon Glass 
4602e192b24SSimon Glass 	if (strcmp(argv[0],"loady")==0) {
4612e192b24SSimon Glass 		printf("## Ready for binary (ymodem) download "
4622e192b24SSimon Glass 			"to 0x%08lX at %d bps...\n",
4632e192b24SSimon Glass 			offset,
4642e192b24SSimon Glass 			load_baudrate);
4652e192b24SSimon Glass 
4662e192b24SSimon Glass 		addr = load_serial_ymodem(offset, xyzModem_ymodem);
4672e192b24SSimon Glass 
4682e192b24SSimon Glass 	} else if (strcmp(argv[0],"loadx")==0) {
4692e192b24SSimon Glass 		printf("## Ready for binary (xmodem) download "
4702e192b24SSimon Glass 			"to 0x%08lX at %d bps...\n",
4712e192b24SSimon Glass 			offset,
4722e192b24SSimon Glass 			load_baudrate);
4732e192b24SSimon Glass 
4742e192b24SSimon Glass 		addr = load_serial_ymodem(offset, xyzModem_xmodem);
4752e192b24SSimon Glass 
4762e192b24SSimon Glass 	} else {
4772e192b24SSimon Glass 
4782e192b24SSimon Glass 		printf("## Ready for binary (kermit) download "
4792e192b24SSimon Glass 			"to 0x%08lX at %d bps...\n",
4802e192b24SSimon Glass 			offset,
4812e192b24SSimon Glass 			load_baudrate);
4822e192b24SSimon Glass 		addr = load_serial_bin(offset);
4832e192b24SSimon Glass 
4842e192b24SSimon Glass 		if (addr == ~0) {
4852e192b24SSimon Glass 			load_addr = 0;
4862e192b24SSimon Glass 			printf("## Binary (kermit) download aborted\n");
4872e192b24SSimon Glass 			rcode = 1;
4882e192b24SSimon Glass 		} else {
4892e192b24SSimon Glass 			printf("## Start Addr      = 0x%08lX\n", addr);
4902e192b24SSimon Glass 			load_addr = addr;
4912e192b24SSimon Glass 		}
4922e192b24SSimon Glass 	}
4932e192b24SSimon Glass 	if (load_baudrate != current_baudrate) {
4942e192b24SSimon Glass 		printf("## Switch baudrate to %d bps and press ESC ...\n",
4952e192b24SSimon Glass 			current_baudrate);
4962e192b24SSimon Glass 		udelay(50000);
4972e192b24SSimon Glass 		gd->baudrate = current_baudrate;
4982e192b24SSimon Glass 		serial_setbrg();
4992e192b24SSimon Glass 		udelay(50000);
5002e192b24SSimon Glass 		for (;;) {
5012e192b24SSimon Glass 			if (getc() == 0x1B) /* ESC */
5022e192b24SSimon Glass 				break;
5032e192b24SSimon Glass 		}
5042e192b24SSimon Glass 	}
5052e192b24SSimon Glass 
5062e192b24SSimon Glass 	return rcode;
5072e192b24SSimon Glass }
5082e192b24SSimon Glass 
5092e192b24SSimon Glass 
5102e192b24SSimon Glass static ulong load_serial_bin(ulong offset)
5112e192b24SSimon Glass {
5122e192b24SSimon Glass 	int size, i;
5132e192b24SSimon Glass 
5142e192b24SSimon Glass 	set_kerm_bin_mode((ulong *) offset);
5152e192b24SSimon Glass 	size = k_recv();
5162e192b24SSimon Glass 
5172e192b24SSimon Glass 	/*
5182e192b24SSimon Glass 	 * Gather any trailing characters (for instance, the ^D which
5192e192b24SSimon Glass 	 * is sent by 'cu' after sending a file), and give the
5202e192b24SSimon Glass 	 * box some time (100 * 1 ms)
5212e192b24SSimon Glass 	 */
5222e192b24SSimon Glass 	for (i=0; i<100; ++i) {
5232e192b24SSimon Glass 		if (tstc()) {
5242e192b24SSimon Glass 			(void) getc();
5252e192b24SSimon Glass 		}
5262e192b24SSimon Glass 		udelay(1000);
5272e192b24SSimon Glass 	}
5282e192b24SSimon Glass 
5292e192b24SSimon Glass 	flush_cache(offset, size);
5302e192b24SSimon Glass 
5312e192b24SSimon Glass 	printf("## Total Size      = 0x%08x = %d Bytes\n", size, size);
5322e192b24SSimon Glass 	setenv_hex("filesize", size);
5332e192b24SSimon Glass 
5342e192b24SSimon Glass 	return offset;
5352e192b24SSimon Glass }
5362e192b24SSimon Glass 
5372e192b24SSimon Glass static void send_pad(void)
5382e192b24SSimon Glass {
5392e192b24SSimon Glass 	int count = his_pad_count;
5402e192b24SSimon Glass 
5412e192b24SSimon Glass 	while (count-- > 0)
5422e192b24SSimon Glass 		putc(his_pad_char);
5432e192b24SSimon Glass }
5442e192b24SSimon Glass 
5452e192b24SSimon Glass /* converts escaped kermit char to binary char */
5462e192b24SSimon Glass static char ktrans(char in)
5472e192b24SSimon Glass {
5482e192b24SSimon Glass 	if ((in & 0x60) == 0x40) {
5492e192b24SSimon Glass 		return (char) (in & ~0x40);
5502e192b24SSimon Glass 	} else if ((in & 0x7f) == 0x3f) {
5512e192b24SSimon Glass 		return (char) (in | 0x40);
5522e192b24SSimon Glass 	} else
5532e192b24SSimon Glass 		return in;
5542e192b24SSimon Glass }
5552e192b24SSimon Glass 
5562e192b24SSimon Glass static int chk1(char *buffer)
5572e192b24SSimon Glass {
5582e192b24SSimon Glass 	int total = 0;
5592e192b24SSimon Glass 
5602e192b24SSimon Glass 	while (*buffer) {
5612e192b24SSimon Glass 		total += *buffer++;
5622e192b24SSimon Glass 	}
5632e192b24SSimon Glass 	return (int) ((total + ((total >> 6) & 0x03)) & 0x3f);
5642e192b24SSimon Glass }
5652e192b24SSimon Glass 
5662e192b24SSimon Glass static void s1_sendpacket(char *packet)
5672e192b24SSimon Glass {
5682e192b24SSimon Glass 	send_pad();
5692e192b24SSimon Glass 	while (*packet) {
5702e192b24SSimon Glass 		putc(*packet++);
5712e192b24SSimon Glass 	}
5722e192b24SSimon Glass }
5732e192b24SSimon Glass 
5742e192b24SSimon Glass static char a_b[24];
5752e192b24SSimon Glass static void send_ack(int n)
5762e192b24SSimon Glass {
5772e192b24SSimon Glass 	a_b[0] = START_CHAR;
5782e192b24SSimon Glass 	a_b[1] = tochar(3);
5792e192b24SSimon Glass 	a_b[2] = tochar(n);
5802e192b24SSimon Glass 	a_b[3] = ACK_TYPE;
5812e192b24SSimon Glass 	a_b[4] = '\0';
5822e192b24SSimon Glass 	a_b[4] = tochar(chk1(&a_b[1]));
5832e192b24SSimon Glass 	a_b[5] = his_eol;
5842e192b24SSimon Glass 	a_b[6] = '\0';
5852e192b24SSimon Glass 	s1_sendpacket(a_b);
5862e192b24SSimon Glass }
5872e192b24SSimon Glass 
5882e192b24SSimon Glass static void send_nack(int n)
5892e192b24SSimon Glass {
5902e192b24SSimon Glass 	a_b[0] = START_CHAR;
5912e192b24SSimon Glass 	a_b[1] = tochar(3);
5922e192b24SSimon Glass 	a_b[2] = tochar(n);
5932e192b24SSimon Glass 	a_b[3] = NACK_TYPE;
5942e192b24SSimon Glass 	a_b[4] = '\0';
5952e192b24SSimon Glass 	a_b[4] = tochar(chk1(&a_b[1]));
5962e192b24SSimon Glass 	a_b[5] = his_eol;
5972e192b24SSimon Glass 	a_b[6] = '\0';
5982e192b24SSimon Glass 	s1_sendpacket(a_b);
5992e192b24SSimon Glass }
6002e192b24SSimon Glass 
6012e192b24SSimon Glass 
6022e192b24SSimon Glass static void (*os_data_init)(void);
6032e192b24SSimon Glass static void (*os_data_char)(char new_char);
6042e192b24SSimon Glass static int os_data_state, os_data_state_saved;
6052e192b24SSimon Glass static char *os_data_addr, *os_data_addr_saved;
6062e192b24SSimon Glass static char *bin_start_address;
6072e192b24SSimon Glass 
6082e192b24SSimon Glass static void bin_data_init(void)
6092e192b24SSimon Glass {
6102e192b24SSimon Glass 	os_data_state = 0;
6112e192b24SSimon Glass 	os_data_addr = bin_start_address;
6122e192b24SSimon Glass }
6132e192b24SSimon Glass 
6142e192b24SSimon Glass static void os_data_save(void)
6152e192b24SSimon Glass {
6162e192b24SSimon Glass 	os_data_state_saved = os_data_state;
6172e192b24SSimon Glass 	os_data_addr_saved = os_data_addr;
6182e192b24SSimon Glass }
6192e192b24SSimon Glass 
6202e192b24SSimon Glass static void os_data_restore(void)
6212e192b24SSimon Glass {
6222e192b24SSimon Glass 	os_data_state = os_data_state_saved;
6232e192b24SSimon Glass 	os_data_addr = os_data_addr_saved;
6242e192b24SSimon Glass }
6252e192b24SSimon Glass 
6262e192b24SSimon Glass static void bin_data_char(char new_char)
6272e192b24SSimon Glass {
6282e192b24SSimon Glass 	switch (os_data_state) {
6292e192b24SSimon Glass 	case 0:					/* data */
6302e192b24SSimon Glass 		*os_data_addr++ = new_char;
6312e192b24SSimon Glass 		break;
6322e192b24SSimon Glass 	}
6332e192b24SSimon Glass }
6342e192b24SSimon Glass 
6352e192b24SSimon Glass static void set_kerm_bin_mode(unsigned long *addr)
6362e192b24SSimon Glass {
6372e192b24SSimon Glass 	bin_start_address = (char *) addr;
6382e192b24SSimon Glass 	os_data_init = bin_data_init;
6392e192b24SSimon Glass 	os_data_char = bin_data_char;
6402e192b24SSimon Glass }
6412e192b24SSimon Glass 
6422e192b24SSimon Glass 
6432e192b24SSimon Glass /* k_data_* simply handles the kermit escape translations */
6442e192b24SSimon Glass static int k_data_escape, k_data_escape_saved;
6452e192b24SSimon Glass static void k_data_init(void)
6462e192b24SSimon Glass {
6472e192b24SSimon Glass 	k_data_escape = 0;
6482e192b24SSimon Glass 	os_data_init();
6492e192b24SSimon Glass }
6502e192b24SSimon Glass 
6512e192b24SSimon Glass static void k_data_save(void)
6522e192b24SSimon Glass {
6532e192b24SSimon Glass 	k_data_escape_saved = k_data_escape;
6542e192b24SSimon Glass 	os_data_save();
6552e192b24SSimon Glass }
6562e192b24SSimon Glass 
6572e192b24SSimon Glass static void k_data_restore(void)
6582e192b24SSimon Glass {
6592e192b24SSimon Glass 	k_data_escape = k_data_escape_saved;
6602e192b24SSimon Glass 	os_data_restore();
6612e192b24SSimon Glass }
6622e192b24SSimon Glass 
6632e192b24SSimon Glass static void k_data_char(char new_char)
6642e192b24SSimon Glass {
6652e192b24SSimon Glass 	if (k_data_escape) {
6662e192b24SSimon Glass 		/* last char was escape - translate this character */
6672e192b24SSimon Glass 		os_data_char(ktrans(new_char));
6682e192b24SSimon Glass 		k_data_escape = 0;
6692e192b24SSimon Glass 	} else {
6702e192b24SSimon Glass 		if (new_char == his_quote) {
6712e192b24SSimon Glass 			/* this char is escape - remember */
6722e192b24SSimon Glass 			k_data_escape = 1;
6732e192b24SSimon Glass 		} else {
6742e192b24SSimon Glass 			/* otherwise send this char as-is */
6752e192b24SSimon Glass 			os_data_char(new_char);
6762e192b24SSimon Glass 		}
6772e192b24SSimon Glass 	}
6782e192b24SSimon Glass }
6792e192b24SSimon Glass 
6802e192b24SSimon Glass #define SEND_DATA_SIZE  20
6812e192b24SSimon Glass static char send_parms[SEND_DATA_SIZE];
6822e192b24SSimon Glass static char *send_ptr;
6832e192b24SSimon Glass 
6842e192b24SSimon Glass /* handle_send_packet interprits the protocol info and builds and
6852e192b24SSimon Glass    sends an appropriate ack for what we can do */
6862e192b24SSimon Glass static void handle_send_packet(int n)
6872e192b24SSimon Glass {
6882e192b24SSimon Glass 	int length = 3;
6892e192b24SSimon Glass 	int bytes;
6902e192b24SSimon Glass 
6912e192b24SSimon Glass 	/* initialize some protocol parameters */
6922e192b24SSimon Glass 	his_eol = END_CHAR;		/* default end of line character */
6932e192b24SSimon Glass 	his_pad_count = 0;
6942e192b24SSimon Glass 	his_pad_char = '\0';
6952e192b24SSimon Glass 	his_quote = K_ESCAPE;
6962e192b24SSimon Glass 
6972e192b24SSimon Glass 	/* ignore last character if it filled the buffer */
6982e192b24SSimon Glass 	if (send_ptr == &send_parms[SEND_DATA_SIZE - 1])
6992e192b24SSimon Glass 		--send_ptr;
7002e192b24SSimon Glass 	bytes = send_ptr - send_parms;	/* how many bytes we'll process */
7012e192b24SSimon Glass 	do {
7022e192b24SSimon Glass 		if (bytes-- <= 0)
7032e192b24SSimon Glass 			break;
7042e192b24SSimon Glass 		/* handle MAXL - max length */
7052e192b24SSimon Glass 		/* ignore what he says - most I'll take (here) is 94 */
7062e192b24SSimon Glass 		a_b[++length] = tochar(94);
7072e192b24SSimon Glass 		if (bytes-- <= 0)
7082e192b24SSimon Glass 			break;
7092e192b24SSimon Glass 		/* handle TIME - time you should wait for my packets */
7102e192b24SSimon Glass 		/* ignore what he says - don't wait for my ack longer than 1 second */
7112e192b24SSimon Glass 		a_b[++length] = tochar(1);
7122e192b24SSimon Glass 		if (bytes-- <= 0)
7132e192b24SSimon Glass 			break;
7142e192b24SSimon Glass 		/* handle NPAD - number of pad chars I need */
7152e192b24SSimon Glass 		/* remember what he says - I need none */
7162e192b24SSimon Glass 		his_pad_count = untochar(send_parms[2]);
7172e192b24SSimon Glass 		a_b[++length] = tochar(0);
7182e192b24SSimon Glass 		if (bytes-- <= 0)
7192e192b24SSimon Glass 			break;
7202e192b24SSimon Glass 		/* handle PADC - pad chars I need */
7212e192b24SSimon Glass 		/* remember what he says - I need none */
7222e192b24SSimon Glass 		his_pad_char = ktrans(send_parms[3]);
7232e192b24SSimon Glass 		a_b[++length] = 0x40;	/* He should ignore this */
7242e192b24SSimon Glass 		if (bytes-- <= 0)
7252e192b24SSimon Glass 			break;
7262e192b24SSimon Glass 		/* handle EOL - end of line he needs */
7272e192b24SSimon Glass 		/* remember what he says - I need CR */
7282e192b24SSimon Glass 		his_eol = untochar(send_parms[4]);
7292e192b24SSimon Glass 		a_b[++length] = tochar(END_CHAR);
7302e192b24SSimon Glass 		if (bytes-- <= 0)
7312e192b24SSimon Glass 			break;
7322e192b24SSimon Glass 		/* handle QCTL - quote control char he'll use */
7332e192b24SSimon Glass 		/* remember what he says - I'll use '#' */
7342e192b24SSimon Glass 		his_quote = send_parms[5];
7352e192b24SSimon Glass 		a_b[++length] = '#';
7362e192b24SSimon Glass 		if (bytes-- <= 0)
7372e192b24SSimon Glass 			break;
7382e192b24SSimon Glass 		/* handle QBIN - 8-th bit prefixing */
7392e192b24SSimon Glass 		/* ignore what he says - I refuse */
7402e192b24SSimon Glass 		a_b[++length] = 'N';
7412e192b24SSimon Glass 		if (bytes-- <= 0)
7422e192b24SSimon Glass 			break;
7432e192b24SSimon Glass 		/* handle CHKT - the clock check type */
7442e192b24SSimon Glass 		/* ignore what he says - I do type 1 (for now) */
7452e192b24SSimon Glass 		a_b[++length] = '1';
7462e192b24SSimon Glass 		if (bytes-- <= 0)
7472e192b24SSimon Glass 			break;
7482e192b24SSimon Glass 		/* handle REPT - the repeat prefix */
7492e192b24SSimon Glass 		/* ignore what he says - I refuse (for now) */
7502e192b24SSimon Glass 		a_b[++length] = 'N';
7512e192b24SSimon Glass 		if (bytes-- <= 0)
7522e192b24SSimon Glass 			break;
7532e192b24SSimon Glass 		/* handle CAPAS - the capabilities mask */
7542e192b24SSimon Glass 		/* ignore what he says - I only do long packets - I don't do windows */
7552e192b24SSimon Glass 		a_b[++length] = tochar(2);	/* only long packets */
7562e192b24SSimon Glass 		a_b[++length] = tochar(0);	/* no windows */
7572e192b24SSimon Glass 		a_b[++length] = tochar(94);	/* large packet msb */
7582e192b24SSimon Glass 		a_b[++length] = tochar(94);	/* large packet lsb */
7592e192b24SSimon Glass 	} while (0);
7602e192b24SSimon Glass 
7612e192b24SSimon Glass 	a_b[0] = START_CHAR;
7622e192b24SSimon Glass 	a_b[1] = tochar(length);
7632e192b24SSimon Glass 	a_b[2] = tochar(n);
7642e192b24SSimon Glass 	a_b[3] = ACK_TYPE;
7652e192b24SSimon Glass 	a_b[++length] = '\0';
7662e192b24SSimon Glass 	a_b[length] = tochar(chk1(&a_b[1]));
7672e192b24SSimon Glass 	a_b[++length] = his_eol;
7682e192b24SSimon Glass 	a_b[++length] = '\0';
7692e192b24SSimon Glass 	s1_sendpacket(a_b);
7702e192b24SSimon Glass }
7712e192b24SSimon Glass 
7722e192b24SSimon Glass /* k_recv receives a OS Open image file over kermit line */
7732e192b24SSimon Glass static int k_recv(void)
7742e192b24SSimon Glass {
7752e192b24SSimon Glass 	char new_char;
7762e192b24SSimon Glass 	char k_state, k_state_saved;
7772e192b24SSimon Glass 	int sum;
7782e192b24SSimon Glass 	int done;
7792e192b24SSimon Glass 	int length;
7802e192b24SSimon Glass 	int n, last_n;
7812e192b24SSimon Glass 	int len_lo, len_hi;
7822e192b24SSimon Glass 
7832e192b24SSimon Glass 	/* initialize some protocol parameters */
7842e192b24SSimon Glass 	his_eol = END_CHAR;		/* default end of line character */
7852e192b24SSimon Glass 	his_pad_count = 0;
7862e192b24SSimon Glass 	his_pad_char = '\0';
7872e192b24SSimon Glass 	his_quote = K_ESCAPE;
7882e192b24SSimon Glass 
7892e192b24SSimon Glass 	/* initialize the k_recv and k_data state machine */
7902e192b24SSimon Glass 	done = 0;
7912e192b24SSimon Glass 	k_state = 0;
7922e192b24SSimon Glass 	k_data_init();
7932e192b24SSimon Glass 	k_state_saved = k_state;
7942e192b24SSimon Glass 	k_data_save();
7952e192b24SSimon Glass 	n = 0;				/* just to get rid of a warning */
7962e192b24SSimon Glass 	last_n = -1;
7972e192b24SSimon Glass 
7982e192b24SSimon Glass 	/* expect this "type" sequence (but don't check):
7992e192b24SSimon Glass 	   S: send initiate
8002e192b24SSimon Glass 	   F: file header
8012e192b24SSimon Glass 	   D: data (multiple)
8022e192b24SSimon Glass 	   Z: end of file
8032e192b24SSimon Glass 	   B: break transmission
8042e192b24SSimon Glass 	 */
8052e192b24SSimon Glass 
8062e192b24SSimon Glass 	/* enter main loop */
8072e192b24SSimon Glass 	while (!done) {
8082e192b24SSimon Glass 		/* set the send packet pointer to begining of send packet parms */
8092e192b24SSimon Glass 		send_ptr = send_parms;
8102e192b24SSimon Glass 
8112e192b24SSimon Glass 		/* With each packet, start summing the bytes starting with the length.
8122e192b24SSimon Glass 		   Save the current sequence number.
8132e192b24SSimon Glass 		   Note the type of the packet.
8142e192b24SSimon Glass 		   If a character less than SPACE (0x20) is received - error.
8152e192b24SSimon Glass 		 */
8162e192b24SSimon Glass 
8172e192b24SSimon Glass #if 0
8182e192b24SSimon Glass 		/* OLD CODE, Prior to checking sequence numbers */
8192e192b24SSimon Glass 		/* first have all state machines save current states */
8202e192b24SSimon Glass 		k_state_saved = k_state;
8212e192b24SSimon Glass 		k_data_save ();
8222e192b24SSimon Glass #endif
8232e192b24SSimon Glass 
8242e192b24SSimon Glass 		/* get a packet */
8252e192b24SSimon Glass 		/* wait for the starting character or ^C */
8262e192b24SSimon Glass 		for (;;) {
8272e192b24SSimon Glass 			switch (getc ()) {
8282e192b24SSimon Glass 			case START_CHAR:	/* start packet */
8292e192b24SSimon Glass 				goto START;
8302e192b24SSimon Glass 			case ETX_CHAR:		/* ^C waiting for packet */
8312e192b24SSimon Glass 				return (0);
8322e192b24SSimon Glass 			default:
8332e192b24SSimon Glass 				;
8342e192b24SSimon Glass 			}
8352e192b24SSimon Glass 		}
8362e192b24SSimon Glass START:
8372e192b24SSimon Glass 		/* get length of packet */
8382e192b24SSimon Glass 		sum = 0;
8392e192b24SSimon Glass 		new_char = getc();
8402e192b24SSimon Glass 		if ((new_char & 0xE0) == 0)
8412e192b24SSimon Glass 			goto packet_error;
8422e192b24SSimon Glass 		sum += new_char & 0xff;
8432e192b24SSimon Glass 		length = untochar(new_char);
8442e192b24SSimon Glass 		/* get sequence number */
8452e192b24SSimon Glass 		new_char = getc();
8462e192b24SSimon Glass 		if ((new_char & 0xE0) == 0)
8472e192b24SSimon Glass 			goto packet_error;
8482e192b24SSimon Glass 		sum += new_char & 0xff;
8492e192b24SSimon Glass 		n = untochar(new_char);
8502e192b24SSimon Glass 		--length;
8512e192b24SSimon Glass 
8522e192b24SSimon Glass 		/* NEW CODE - check sequence numbers for retried packets */
8532e192b24SSimon Glass 		/* Note - this new code assumes that the sequence number is correctly
8542e192b24SSimon Glass 		 * received.  Handling an invalid sequence number adds another layer
8552e192b24SSimon Glass 		 * of complexity that may not be needed - yet!  At this time, I'm hoping
8562e192b24SSimon Glass 		 * that I don't need to buffer the incoming data packets and can write
8572e192b24SSimon Glass 		 * the data into memory in real time.
8582e192b24SSimon Glass 		 */
8592e192b24SSimon Glass 		if (n == last_n) {
8602e192b24SSimon Glass 			/* same sequence number, restore the previous state */
8612e192b24SSimon Glass 			k_state = k_state_saved;
8622e192b24SSimon Glass 			k_data_restore();
8632e192b24SSimon Glass 		} else {
8642e192b24SSimon Glass 			/* new sequence number, checkpoint the download */
8652e192b24SSimon Glass 			last_n = n;
8662e192b24SSimon Glass 			k_state_saved = k_state;
8672e192b24SSimon Glass 			k_data_save();
8682e192b24SSimon Glass 		}
8692e192b24SSimon Glass 		/* END NEW CODE */
8702e192b24SSimon Glass 
8712e192b24SSimon Glass 		/* get packet type */
8722e192b24SSimon Glass 		new_char = getc();
8732e192b24SSimon Glass 		if ((new_char & 0xE0) == 0)
8742e192b24SSimon Glass 			goto packet_error;
8752e192b24SSimon Glass 		sum += new_char & 0xff;
8762e192b24SSimon Glass 		k_state = new_char;
8772e192b24SSimon Glass 		--length;
8782e192b24SSimon Glass 		/* check for extended length */
8792e192b24SSimon Glass 		if (length == -2) {
8802e192b24SSimon Glass 			/* (length byte was 0, decremented twice) */
8812e192b24SSimon Glass 			/* get the two length bytes */
8822e192b24SSimon Glass 			new_char = getc();
8832e192b24SSimon Glass 			if ((new_char & 0xE0) == 0)
8842e192b24SSimon Glass 				goto packet_error;
8852e192b24SSimon Glass 			sum += new_char & 0xff;
8862e192b24SSimon Glass 			len_hi = untochar(new_char);
8872e192b24SSimon Glass 			new_char = getc();
8882e192b24SSimon Glass 			if ((new_char & 0xE0) == 0)
8892e192b24SSimon Glass 				goto packet_error;
8902e192b24SSimon Glass 			sum += new_char & 0xff;
8912e192b24SSimon Glass 			len_lo = untochar(new_char);
8922e192b24SSimon Glass 			length = len_hi * 95 + len_lo;
8932e192b24SSimon Glass 			/* check header checksum */
8942e192b24SSimon Glass 			new_char = getc();
8952e192b24SSimon Glass 			if ((new_char & 0xE0) == 0)
8962e192b24SSimon Glass 				goto packet_error;
8972e192b24SSimon Glass 			if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
8982e192b24SSimon Glass 				goto packet_error;
8992e192b24SSimon Glass 			sum += new_char & 0xff;
9002e192b24SSimon Glass /* --length; */ /* new length includes only data and block check to come */
9012e192b24SSimon Glass 		}
9022e192b24SSimon Glass 		/* bring in rest of packet */
9032e192b24SSimon Glass 		while (length > 1) {
9042e192b24SSimon Glass 			new_char = getc();
9052e192b24SSimon Glass 			if ((new_char & 0xE0) == 0)
9062e192b24SSimon Glass 				goto packet_error;
9072e192b24SSimon Glass 			sum += new_char & 0xff;
9082e192b24SSimon Glass 			--length;
9092e192b24SSimon Glass 			if (k_state == DATA_TYPE) {
9102e192b24SSimon Glass 				/* pass on the data if this is a data packet */
9112e192b24SSimon Glass 				k_data_char (new_char);
9122e192b24SSimon Glass 			} else if (k_state == SEND_TYPE) {
9132e192b24SSimon Glass 				/* save send pack in buffer as is */
9142e192b24SSimon Glass 				*send_ptr++ = new_char;
9152e192b24SSimon Glass 				/* if too much data, back off the pointer */
9162e192b24SSimon Glass 				if (send_ptr >= &send_parms[SEND_DATA_SIZE])
9172e192b24SSimon Glass 					--send_ptr;
9182e192b24SSimon Glass 			}
9192e192b24SSimon Glass 		}
9202e192b24SSimon Glass 		/* get and validate checksum character */
9212e192b24SSimon Glass 		new_char = getc();
9222e192b24SSimon Glass 		if ((new_char & 0xE0) == 0)
9232e192b24SSimon Glass 			goto packet_error;
9242e192b24SSimon Glass 		if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
9252e192b24SSimon Glass 			goto packet_error;
9262e192b24SSimon Glass 		/* get END_CHAR */
9272e192b24SSimon Glass 		new_char = getc();
9282e192b24SSimon Glass 		if (new_char != END_CHAR) {
9292e192b24SSimon Glass 		  packet_error:
9302e192b24SSimon Glass 			/* restore state machines */
9312e192b24SSimon Glass 			k_state = k_state_saved;
9322e192b24SSimon Glass 			k_data_restore();
9332e192b24SSimon Glass 			/* send a negative acknowledge packet in */
9342e192b24SSimon Glass 			send_nack(n);
9352e192b24SSimon Glass 		} else if (k_state == SEND_TYPE) {
9362e192b24SSimon Glass 			/* crack the protocol parms, build an appropriate ack packet */
9372e192b24SSimon Glass 			handle_send_packet(n);
9382e192b24SSimon Glass 		} else {
9392e192b24SSimon Glass 			/* send simple acknowledge packet in */
9402e192b24SSimon Glass 			send_ack(n);
9412e192b24SSimon Glass 			/* quit if end of transmission */
9422e192b24SSimon Glass 			if (k_state == BREAK_TYPE)
9432e192b24SSimon Glass 				done = 1;
9442e192b24SSimon Glass 		}
9452e192b24SSimon Glass 	}
9462e192b24SSimon Glass 	return ((ulong) os_data_addr - (ulong) bin_start_address);
9472e192b24SSimon Glass }
9482e192b24SSimon Glass 
9492e192b24SSimon Glass static int getcxmodem(void) {
9502e192b24SSimon Glass 	if (tstc())
9512e192b24SSimon Glass 		return (getc());
9522e192b24SSimon Glass 	return -1;
9532e192b24SSimon Glass }
9542e192b24SSimon Glass static ulong load_serial_ymodem(ulong offset, int mode)
9552e192b24SSimon Glass {
9562e192b24SSimon Glass 	int size;
9572e192b24SSimon Glass 	int err;
9582e192b24SSimon Glass 	int res;
9592e192b24SSimon Glass 	connection_info_t info;
9602e192b24SSimon Glass 	char ymodemBuf[1024];
9612e192b24SSimon Glass 	ulong store_addr = ~0;
9622e192b24SSimon Glass 	ulong addr = 0;
9632e192b24SSimon Glass 
9642e192b24SSimon Glass 	size = 0;
9652e192b24SSimon Glass 	info.mode = mode;
9662e192b24SSimon Glass 	res = xyzModem_stream_open(&info, &err);
9672e192b24SSimon Glass 	if (!res) {
9682e192b24SSimon Glass 
9692e192b24SSimon Glass 		while ((res =
9702e192b24SSimon Glass 			xyzModem_stream_read(ymodemBuf, 1024, &err)) > 0) {
9712e192b24SSimon Glass 			store_addr = addr + offset;
9722e192b24SSimon Glass 			size += res;
9732e192b24SSimon Glass 			addr += res;
9742e192b24SSimon Glass #ifndef CONFIG_SYS_NO_FLASH
9752e192b24SSimon Glass 			if (addr2info(store_addr)) {
9762e192b24SSimon Glass 				int rc;
9772e192b24SSimon Glass 
9782e192b24SSimon Glass 				rc = flash_write((char *) ymodemBuf,
9792e192b24SSimon Glass 						  store_addr, res);
9802e192b24SSimon Glass 				if (rc != 0) {
9812e192b24SSimon Glass 					flash_perror (rc);
9822e192b24SSimon Glass 					return (~0);
9832e192b24SSimon Glass 				}
9842e192b24SSimon Glass 			} else
9852e192b24SSimon Glass #endif
9862e192b24SSimon Glass 			{
9872e192b24SSimon Glass 				memcpy((char *)(store_addr), ymodemBuf,
9882e192b24SSimon Glass 					res);
9892e192b24SSimon Glass 			}
9902e192b24SSimon Glass 
9912e192b24SSimon Glass 		}
9922e192b24SSimon Glass 	} else {
9932e192b24SSimon Glass 		printf("%s\n", xyzModem_error(err));
9942e192b24SSimon Glass 	}
9952e192b24SSimon Glass 
9962e192b24SSimon Glass 	xyzModem_stream_close(&err);
9972e192b24SSimon Glass 	xyzModem_stream_terminate(false, &getcxmodem);
9982e192b24SSimon Glass 
9992e192b24SSimon Glass 
1000*0a6036daSChris Packham 	flush_cache(offset, ALIGN(size, ARCH_DMA_MINALIGN));
10012e192b24SSimon Glass 
10022e192b24SSimon Glass 	printf("## Total Size      = 0x%08x = %d Bytes\n", size, size);
10032e192b24SSimon Glass 	setenv_hex("filesize", size);
10042e192b24SSimon Glass 
10052e192b24SSimon Glass 	return offset;
10062e192b24SSimon Glass }
10072e192b24SSimon Glass 
10082e192b24SSimon Glass #endif
10092e192b24SSimon Glass 
10102e192b24SSimon Glass /* -------------------------------------------------------------------- */
10112e192b24SSimon Glass 
10122e192b24SSimon Glass #if defined(CONFIG_CMD_LOADS)
10132e192b24SSimon Glass 
10142e192b24SSimon Glass #ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
10152e192b24SSimon Glass U_BOOT_CMD(
10162e192b24SSimon Glass 	loads, 3, 0,	do_load_serial,
10172e192b24SSimon Glass 	"load S-Record file over serial line",
10182e192b24SSimon Glass 	"[ off ] [ baud ]\n"
10192e192b24SSimon Glass 	"    - load S-Record file over serial line"
10202e192b24SSimon Glass 	" with offset 'off' and baudrate 'baud'"
10212e192b24SSimon Glass );
10222e192b24SSimon Glass 
10232e192b24SSimon Glass #else	/* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
10242e192b24SSimon Glass U_BOOT_CMD(
10252e192b24SSimon Glass 	loads, 2, 0,	do_load_serial,
10262e192b24SSimon Glass 	"load S-Record file over serial line",
10272e192b24SSimon Glass 	"[ off ]\n"
10282e192b24SSimon Glass 	"    - load S-Record file over serial line with offset 'off'"
10292e192b24SSimon Glass );
10302e192b24SSimon Glass #endif	/* CONFIG_SYS_LOADS_BAUD_CHANGE */
10312e192b24SSimon Glass 
10322e192b24SSimon Glass /*
10332e192b24SSimon Glass  * SAVES always requires LOADS support, but not vice versa
10342e192b24SSimon Glass  */
10352e192b24SSimon Glass 
10362e192b24SSimon Glass 
10372e192b24SSimon Glass #if defined(CONFIG_CMD_SAVES)
10382e192b24SSimon Glass #ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
10392e192b24SSimon Glass U_BOOT_CMD(
10402e192b24SSimon Glass 	saves, 4, 0,	do_save_serial,
10412e192b24SSimon Glass 	"save S-Record file over serial line",
10422e192b24SSimon Glass 	"[ off ] [size] [ baud ]\n"
10432e192b24SSimon Glass 	"    - save S-Record file over serial line"
10442e192b24SSimon Glass 	" with offset 'off', size 'size' and baudrate 'baud'"
10452e192b24SSimon Glass );
10462e192b24SSimon Glass #else	/* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
10472e192b24SSimon Glass U_BOOT_CMD(
10482e192b24SSimon Glass 	saves, 3, 0,	do_save_serial,
10492e192b24SSimon Glass 	"save S-Record file over serial line",
10502e192b24SSimon Glass 	"[ off ] [size]\n"
10512e192b24SSimon Glass 	"    - save S-Record file over serial line with offset 'off' and size 'size'"
10522e192b24SSimon Glass );
10532e192b24SSimon Glass #endif	/* CONFIG_SYS_LOADS_BAUD_CHANGE */
10542e192b24SSimon Glass #endif	/* CONFIG_CMD_SAVES */
10552e192b24SSimon Glass #endif	/* CONFIG_CMD_LOADS */
10562e192b24SSimon Glass 
10572e192b24SSimon Glass 
10582e192b24SSimon Glass #if defined(CONFIG_CMD_LOADB)
10592e192b24SSimon Glass U_BOOT_CMD(
10602e192b24SSimon Glass 	loadb, 3, 0,	do_load_serial_bin,
10612e192b24SSimon Glass 	"load binary file over serial line (kermit mode)",
10622e192b24SSimon Glass 	"[ off ] [ baud ]\n"
10632e192b24SSimon Glass 	"    - load binary file over serial line"
10642e192b24SSimon Glass 	" with offset 'off' and baudrate 'baud'"
10652e192b24SSimon Glass );
10662e192b24SSimon Glass 
10672e192b24SSimon Glass U_BOOT_CMD(
10682e192b24SSimon Glass 	loadx, 3, 0,	do_load_serial_bin,
10692e192b24SSimon Glass 	"load binary file over serial line (xmodem mode)",
10702e192b24SSimon Glass 	"[ off ] [ baud ]\n"
10712e192b24SSimon Glass 	"    - load binary file over serial line"
10722e192b24SSimon Glass 	" with offset 'off' and baudrate 'baud'"
10732e192b24SSimon Glass );
10742e192b24SSimon Glass 
10752e192b24SSimon Glass U_BOOT_CMD(
10762e192b24SSimon Glass 	loady, 3, 0,	do_load_serial_bin,
10772e192b24SSimon Glass 	"load binary file over serial line (ymodem mode)",
10782e192b24SSimon Glass 	"[ off ] [ baud ]\n"
10792e192b24SSimon Glass 	"    - load binary file over serial line"
10802e192b24SSimon Glass 	" with offset 'off' and baudrate 'baud'"
10812e192b24SSimon Glass );
10822e192b24SSimon Glass 
10832e192b24SSimon Glass #endif	/* CONFIG_CMD_LOADB */
1084