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