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