blob: d773a25d70c59669d9de8efe0841e84c754e9410 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
wdenk57b2d802003-06-27 21:31:46 +00002/*
wdenk29e7f5a2004-03-12 00:14:09 +00003 * (C) Copyright 2000-2004
wdenk57b2d802003-06-27 21:31:46 +00004 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
wdenk57b2d802003-06-27 21:31:46 +00005 */
6
7/*
8 * Serial up- and download support
9 */
wdenk57b2d802003-06-27 21:31:46 +000010#include <command.h>
Simon Glassa73bda42015-11-08 23:47:45 -070011#include <console.h>
Simon Glass63334482019-11-14 12:57:39 -070012#include <cpu_func.h>
Heinrich Schuchardta89adb02021-03-19 02:50:57 +000013#include <efi_loader.h>
Simon Glass313112a2019-08-01 09:46:46 -060014#include <env.h>
Heinrich Schuchardta89adb02021-03-19 02:50:57 +000015#include <exports.h>
Tom Rini063c9382022-07-23 13:05:03 -040016#ifdef CONFIG_MTD_NOR_FLASH
Simon Glassa606ffc2019-12-28 10:44:40 -070017#include <flash.h>
Tom Rini063c9382022-07-23 13:05:03 -040018#endif
Simon Glass85f13782019-12-28 10:45:03 -070019#include <image.h>
Marek Vasut4d2be382021-10-10 23:52:41 +020020#include <lmb.h>
Heinrich Schuchardta89adb02021-03-19 02:50:57 +000021#include <mapmem.h>
wdenk57b2d802003-06-27 21:31:46 +000022#include <net.h>
Heinrich Schuchardta89adb02021-03-19 02:50:57 +000023#include <s_record.h>
Simon Glass36736182019-11-14 12:57:24 -070024#include <serial.h>
Markus Klotzbuecher387f5412006-03-30 13:40:55 +020025#include <xyzModem.h>
Simon Glass274e0b02020-05-10 11:39:56 -060026#include <asm/cache.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060027#include <asm/global_data.h>
Simon Glassdbd79542020-05-10 11:40:11 -060028#include <linux/delay.h>
wdenk57b2d802003-06-27 21:31:46 +000029
Wolfgang Denk6405a152006-03-31 18:32:53 +020030DECLARE_GLOBAL_DATA_PTR;
wdenk57b2d802003-06-27 21:31:46 +000031
Jon Loeligerd76b5c12007-07-08 18:02:23 -050032#if defined(CONFIG_CMD_LOADB)
Angus Ainslief283bd02013-06-26 16:54:24 -060033static ulong load_serial_ymodem(ulong offset, int mode);
Wolfgang Denkda916932006-05-02 00:11:25 +020034#endif
35
Jon Loeligerd76b5c12007-07-08 18:02:23 -050036#if defined(CONFIG_CMD_LOADS)
Kim Phillipsdc00a682012-10-29 13:34:31 +000037static ulong load_serial(long offset);
38static int read_record(char *buf, ulong len);
Jon Loeligerd76b5c12007-07-08 18:02:23 -050039# if defined(CONFIG_CMD_SAVES)
Kim Phillipsdc00a682012-10-29 13:34:31 +000040static int save_serial(ulong offset, ulong size);
41static int write_record(char *buf);
Jon Loeligerd704d912007-07-10 11:02:44 -050042#endif
wdenk57b2d802003-06-27 21:31:46 +000043
44static int do_echo = 1;
Jon Loeligerd704d912007-07-10 11:02:44 -050045#endif
wdenk57b2d802003-06-27 21:31:46 +000046
47/* -------------------------------------------------------------------- */
48
Jon Loeligerd76b5c12007-07-08 18:02:23 -050049#if defined(CONFIG_CMD_LOADS)
Simon Glassed38aef2020-05-10 11:40:03 -060050static int do_load_serial(struct cmd_tbl *cmdtp, int flag, int argc,
51 char *const argv[])
wdenk57b2d802003-06-27 21:31:46 +000052{
Ricardo Ribalda Delgado9cde8302008-07-30 12:39:29 +020053 long offset = 0;
wdenk57b2d802003-06-27 21:31:46 +000054 ulong addr;
55 int i;
56 char *env_echo;
57 int rcode = 0;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020058#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk57b2d802003-06-27 21:31:46 +000059 int load_baudrate, current_baudrate;
60
61 load_baudrate = current_baudrate = gd->baudrate;
62#endif
63
Simon Glass64b723f2017-08-03 12:22:12 -060064 env_echo = env_get("loads_echo");
65 if (env_echo && *env_echo == '1')
wdenk57b2d802003-06-27 21:31:46 +000066 do_echo = 1;
Simon Glass64b723f2017-08-03 12:22:12 -060067 else
wdenk57b2d802003-06-27 21:31:46 +000068 do_echo = 0;
wdenk57b2d802003-06-27 21:31:46 +000069
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020070#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk57b2d802003-06-27 21:31:46 +000071 if (argc >= 2) {
Ricardo Ribalda Delgado9cde8302008-07-30 12:39:29 +020072 offset = simple_strtol(argv[1], NULL, 16);
wdenk57b2d802003-06-27 21:31:46 +000073 }
74 if (argc == 3) {
Simon Glassff9b9032021-07-24 09:03:30 -060075 load_baudrate = (int)dectoul(argv[2], NULL);
wdenk57b2d802003-06-27 21:31:46 +000076
77 /* default to current baudrate */
78 if (load_baudrate == 0)
79 load_baudrate = current_baudrate;
80 }
81 if (load_baudrate != current_baudrate) {
Kim Phillipsdc00a682012-10-29 13:34:31 +000082 printf("## Switch baudrate to %d bps and press ENTER ...\n",
wdenk57b2d802003-06-27 21:31:46 +000083 load_baudrate);
84 udelay(50000);
Pali Rohár8e67a8a2022-09-05 11:31:20 +020085 flush();
wdenk57b2d802003-06-27 21:31:46 +000086 gd->baudrate = load_baudrate;
Kim Phillipsdc00a682012-10-29 13:34:31 +000087 serial_setbrg();
wdenk57b2d802003-06-27 21:31:46 +000088 udelay(50000);
89 for (;;) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +020090 if (getchar() == '\r')
wdenk57b2d802003-06-27 21:31:46 +000091 break;
92 }
93 }
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020094#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk57b2d802003-06-27 21:31:46 +000095 if (argc == 2) {
Ricardo Ribalda Delgado9cde8302008-07-30 12:39:29 +020096 offset = simple_strtol(argv[1], NULL, 16);
wdenk57b2d802003-06-27 21:31:46 +000097 }
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020098#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk57b2d802003-06-27 21:31:46 +000099
Kim Phillipsdc00a682012-10-29 13:34:31 +0000100 printf("## Ready for S-Record download ...\n");
wdenk57b2d802003-06-27 21:31:46 +0000101
Kim Phillipsdc00a682012-10-29 13:34:31 +0000102 addr = load_serial(offset);
wdenk57b2d802003-06-27 21:31:46 +0000103
104 /*
105 * Gather any trailing characters (for instance, the ^D which
106 * is sent by 'cu' after sending a file), and give the
107 * box some time (100 * 1 ms)
108 */
109 for (i=0; i<100; ++i) {
wdenk29e7f5a2004-03-12 00:14:09 +0000110 if (tstc()) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200111 getchar();
wdenk57b2d802003-06-27 21:31:46 +0000112 }
113 udelay(1000);
114 }
115
116 if (addr == ~0) {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000117 printf("## S-Record download aborted\n");
wdenk57b2d802003-06-27 21:31:46 +0000118 rcode = 1;
119 } else {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000120 printf("## Start Addr = 0x%08lX\n", addr);
Simon Glass892265d2019-12-28 10:45:02 -0700121 image_load_addr = addr;
wdenk57b2d802003-06-27 21:31:46 +0000122 }
123
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200124#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk57b2d802003-06-27 21:31:46 +0000125 if (load_baudrate != current_baudrate) {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000126 printf("## Switch baudrate to %d bps and press ESC ...\n",
wdenk57b2d802003-06-27 21:31:46 +0000127 current_baudrate);
Kim Phillipsdc00a682012-10-29 13:34:31 +0000128 udelay(50000);
Pali Rohár8e67a8a2022-09-05 11:31:20 +0200129 flush();
wdenk57b2d802003-06-27 21:31:46 +0000130 gd->baudrate = current_baudrate;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000131 serial_setbrg();
132 udelay(50000);
wdenk57b2d802003-06-27 21:31:46 +0000133 for (;;) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200134 if (getchar() == 0x1B) /* ESC */
wdenk57b2d802003-06-27 21:31:46 +0000135 break;
136 }
137 }
138#endif
139 return rcode;
140}
141
Kim Phillipsdc00a682012-10-29 13:34:31 +0000142static ulong load_serial(long offset)
wdenk57b2d802003-06-27 21:31:46 +0000143{
Marek Vasut4d2be382021-10-10 23:52:41 +0200144 struct lmb lmb;
wdenk57b2d802003-06-27 21:31:46 +0000145 char record[SREC_MAXRECLEN + 1]; /* buffer for one S-Record */
146 char binbuf[SREC_MAXBINLEN]; /* buffer for binary data */
147 int binlen; /* no. of data bytes in S-Rec. */
148 int type; /* return code for record type */
149 ulong addr; /* load address from S-Record */
150 ulong size; /* number of bytes transferred */
wdenk57b2d802003-06-27 21:31:46 +0000151 ulong store_addr;
152 ulong start_addr = ~0;
153 ulong end_addr = 0;
154 int line_count = 0;
Marek Vasut4d2be382021-10-10 23:52:41 +0200155 long ret;
156
157 lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob);
wdenk57b2d802003-06-27 21:31:46 +0000158
159 while (read_record(record, SREC_MAXRECLEN + 1) >= 0) {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000160 type = srec_decode(record, &binlen, &addr, binbuf);
wdenk57b2d802003-06-27 21:31:46 +0000161
162 if (type < 0) {
163 return (~0); /* Invalid S-Record */
164 }
165
166 switch (type) {
167 case SREC_DATA2:
168 case SREC_DATA3:
169 case SREC_DATA4:
170 store_addr = addr + offset;
Masahiro Yamada8cea9b52017-02-11 22:43:54 +0900171#ifdef CONFIG_MTD_NOR_FLASH
wdenk57b2d802003-06-27 21:31:46 +0000172 if (addr2info(store_addr)) {
173 int rc;
174
Wolfgang Denk7fb52662005-10-13 16:45:02 +0200175 rc = flash_write((char *)binbuf,store_addr,binlen);
wdenk57b2d802003-06-27 21:31:46 +0000176 if (rc != 0) {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000177 flash_perror(rc);
wdenk57b2d802003-06-27 21:31:46 +0000178 return (~0);
179 }
180 } else
181#endif
182 {
Heinrich Schuchardt604404b2023-06-25 11:54:23 +0200183 void *dst;
184
Marek Vasut4d2be382021-10-10 23:52:41 +0200185 ret = lmb_reserve(&lmb, store_addr, binlen);
186 if (ret) {
187 printf("\nCannot overwrite reserved area (%08lx..%08lx)\n",
188 store_addr, store_addr + binlen);
189 return ret;
190 }
Heinrich Schuchardt604404b2023-06-25 11:54:23 +0200191 dst = map_sysmem(store_addr, binlen);
192 memcpy(dst, binbuf, binlen);
193 unmap_sysmem(dst);
Marek Vasut4d2be382021-10-10 23:52:41 +0200194 lmb_free(&lmb, store_addr, binlen);
wdenk57b2d802003-06-27 21:31:46 +0000195 }
196 if ((store_addr) < start_addr)
197 start_addr = store_addr;
198 if ((store_addr + binlen - 1) > end_addr)
199 end_addr = store_addr + binlen - 1;
200 break;
201 case SREC_END2:
202 case SREC_END3:
203 case SREC_END4:
Kim Phillipsdc00a682012-10-29 13:34:31 +0000204 udelay(10000);
wdenk57b2d802003-06-27 21:31:46 +0000205 size = end_addr - start_addr + 1;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000206 printf("\n"
wdenk57b2d802003-06-27 21:31:46 +0000207 "## First Load Addr = 0x%08lX\n"
208 "## Last Load Addr = 0x%08lX\n"
209 "## Total Size = 0x%08lX = %ld Bytes\n",
210 start_addr, end_addr, size, size
211 );
Kim Phillipsdc00a682012-10-29 13:34:31 +0000212 flush_cache(start_addr, size);
Simon Glass4d949a22017-08-03 12:22:10 -0600213 env_set_hex("filesize", size);
wdenk57b2d802003-06-27 21:31:46 +0000214 return (addr);
215 case SREC_START:
216 break;
217 default:
218 break;
219 }
220 if (!do_echo) { /* print a '.' every 100 lines */
221 if ((++line_count % 100) == 0)
Kim Phillipsdc00a682012-10-29 13:34:31 +0000222 putc('.');
wdenk57b2d802003-06-27 21:31:46 +0000223 }
224 }
225
226 return (~0); /* Download aborted */
227}
228
Kim Phillipsdc00a682012-10-29 13:34:31 +0000229static int read_record(char *buf, ulong len)
wdenk57b2d802003-06-27 21:31:46 +0000230{
231 char *p;
Tom Rini479f54a2024-01-09 17:57:16 -0500232 int c;
wdenk57b2d802003-06-27 21:31:46 +0000233
234 --len; /* always leave room for terminating '\0' byte */
235
236 for (p=buf; p < buf+len; ++p) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200237 c = getchar(); /* read character */
wdenk57b2d802003-06-27 21:31:46 +0000238 if (do_echo)
Kim Phillipsdc00a682012-10-29 13:34:31 +0000239 putc(c); /* ... and echo it */
wdenk57b2d802003-06-27 21:31:46 +0000240
241 switch (c) {
242 case '\r':
243 case '\n':
244 *p = '\0';
245 return (p - buf);
246 case '\0':
247 case 0x03: /* ^C - Control C */
248 return (-1);
249 default:
250 *p = c;
251 }
252
Michal Simekb6e8ba12020-12-01 13:58:28 +0100253 /* Check for the console hangup (if any different from serial) */
254 if (gd->jt->getc != getchar) {
255 if (ctrlc())
256 return (-1);
wdenk57b2d802003-06-27 21:31:46 +0000257 }
wdenk57b2d802003-06-27 21:31:46 +0000258 }
259
260 /* line too long - truncate */
261 *p = '\0';
262 return (p - buf);
263}
264
Jon Loeligerd76b5c12007-07-08 18:02:23 -0500265#if defined(CONFIG_CMD_SAVES)
wdenk57b2d802003-06-27 21:31:46 +0000266
Simon Glassed38aef2020-05-10 11:40:03 -0600267int do_save_serial(struct cmd_tbl *cmdtp, int flag, int argc,
268 char *const argv[])
wdenk57b2d802003-06-27 21:31:46 +0000269{
270 ulong offset = 0;
271 ulong size = 0;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200272#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk57b2d802003-06-27 21:31:46 +0000273 int save_baudrate, current_baudrate;
274
275 save_baudrate = current_baudrate = gd->baudrate;
276#endif
277
278 if (argc >= 2) {
Simon Glass3ff49ec2021-07-24 09:03:29 -0600279 offset = hextoul(argv[1], NULL);
wdenk57b2d802003-06-27 21:31:46 +0000280 }
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200281#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk57b2d802003-06-27 21:31:46 +0000282 if (argc >= 3) {
Simon Glass3ff49ec2021-07-24 09:03:29 -0600283 size = hextoul(argv[2], NULL);
wdenk57b2d802003-06-27 21:31:46 +0000284 }
285 if (argc == 4) {
Simon Glassff9b9032021-07-24 09:03:30 -0600286 save_baudrate = (int)dectoul(argv[3], NULL);
wdenk57b2d802003-06-27 21:31:46 +0000287
288 /* default to current baudrate */
289 if (save_baudrate == 0)
290 save_baudrate = current_baudrate;
291 }
292 if (save_baudrate != current_baudrate) {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000293 printf("## Switch baudrate to %d bps and press ENTER ...\n",
wdenk57b2d802003-06-27 21:31:46 +0000294 save_baudrate);
295 udelay(50000);
296 gd->baudrate = save_baudrate;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000297 serial_setbrg();
wdenk57b2d802003-06-27 21:31:46 +0000298 udelay(50000);
299 for (;;) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200300 if (getchar() == '\r')
wdenk57b2d802003-06-27 21:31:46 +0000301 break;
302 }
303 }
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200304#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk57b2d802003-06-27 21:31:46 +0000305 if (argc == 3) {
Simon Glass3ff49ec2021-07-24 09:03:29 -0600306 size = hextoul(argv[2], NULL);
wdenk57b2d802003-06-27 21:31:46 +0000307 }
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200308#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk57b2d802003-06-27 21:31:46 +0000309
Kim Phillipsdc00a682012-10-29 13:34:31 +0000310 printf("## Ready for S-Record upload, press ENTER to proceed ...\n");
wdenk57b2d802003-06-27 21:31:46 +0000311 for (;;) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200312 if (getchar() == '\r')
wdenk57b2d802003-06-27 21:31:46 +0000313 break;
314 }
Kim Phillipsdc00a682012-10-29 13:34:31 +0000315 if (save_serial(offset, size)) {
316 printf("## S-Record upload aborted\n");
wdenk57b2d802003-06-27 21:31:46 +0000317 } else {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000318 printf("## S-Record upload complete\n");
wdenk57b2d802003-06-27 21:31:46 +0000319 }
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200320#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk57b2d802003-06-27 21:31:46 +0000321 if (save_baudrate != current_baudrate) {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000322 printf("## Switch baudrate to %d bps and press ESC ...\n",
wdenk57b2d802003-06-27 21:31:46 +0000323 (int)current_baudrate);
Kim Phillipsdc00a682012-10-29 13:34:31 +0000324 udelay(50000);
Pali Rohár8e67a8a2022-09-05 11:31:20 +0200325 flush();
wdenk57b2d802003-06-27 21:31:46 +0000326 gd->baudrate = current_baudrate;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000327 serial_setbrg();
328 udelay(50000);
wdenk57b2d802003-06-27 21:31:46 +0000329 for (;;) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200330 if (getchar() == 0x1B) /* ESC */
wdenk57b2d802003-06-27 21:31:46 +0000331 break;
332 }
333 }
334#endif
335 return 0;
336}
337
338#define SREC3_START "S0030000FC\n"
339#define SREC3_FORMAT "S3%02X%08lX%s%02X\n"
340#define SREC3_END "S70500000000FA\n"
341#define SREC_BYTES_PER_RECORD 16
342
Kim Phillipsdc00a682012-10-29 13:34:31 +0000343static int save_serial(ulong address, ulong count)
wdenk57b2d802003-06-27 21:31:46 +0000344{
345 int i, c, reclen, checksum, length;
346 char *hex = "0123456789ABCDEF";
347 char record[2*SREC_BYTES_PER_RECORD+16]; /* buffer for one S-Record */
348 char data[2*SREC_BYTES_PER_RECORD+1]; /* buffer for hex data */
349
350 reclen = 0;
351 checksum = 0;
352
353 if(write_record(SREC3_START)) /* write the header */
354 return (-1);
355 do {
Heinrich Schuchardt604404b2023-06-25 11:54:23 +0200356 volatile uchar *src;
357
358 src = map_sysmem(address, count);
359 if (count) { /* collect hex data in the buffer */
360 c = src[reclen]; /* get one byte */
361 checksum += c; /* accumulate checksum */
wdenk57b2d802003-06-27 21:31:46 +0000362 data[2*reclen] = hex[(c>>4)&0x0f];
363 data[2*reclen+1] = hex[c & 0x0f];
364 data[2*reclen+2] = '\0';
365 ++reclen;
366 --count;
367 }
Heinrich Schuchardt604404b2023-06-25 11:54:23 +0200368 unmap_sysmem((void *)src);
wdenk57b2d802003-06-27 21:31:46 +0000369 if(reclen == SREC_BYTES_PER_RECORD || count == 0) {
370 /* enough data collected for one record: dump it */
371 if(reclen) { /* build & write a data record: */
372 /* address + data + checksum */
373 length = 4 + reclen + 1;
374
375 /* accumulate length bytes into checksum */
376 for(i = 0; i < 2; i++)
377 checksum += (length >> (8*i)) & 0xff;
378
379 /* accumulate address bytes into checksum: */
380 for(i = 0; i < 4; i++)
381 checksum += (address >> (8*i)) & 0xff;
382
383 /* make proper checksum byte: */
384 checksum = ~checksum & 0xff;
385
386 /* output one record: */
387 sprintf(record, SREC3_FORMAT, length, address, data, checksum);
388 if(write_record(record))
389 return (-1);
390 }
391 address += reclen; /* increment address */
392 checksum = 0;
393 reclen = 0;
394 }
395 }
396 while(count);
397 if(write_record(SREC3_END)) /* write the final record */
398 return (-1);
399 return(0);
400}
401
Kim Phillipsdc00a682012-10-29 13:34:31 +0000402static int write_record(char *buf)
wdenk57b2d802003-06-27 21:31:46 +0000403{
404 char c;
405
406 while((c = *buf++))
wdenk29e7f5a2004-03-12 00:14:09 +0000407 putc(c);
wdenk57b2d802003-06-27 21:31:46 +0000408
409 /* Check for the console hangup (if any different from serial) */
410
411 if (ctrlc()) {
412 return (-1);
413 }
414 return (0);
415}
Jon Loeligerd704d912007-07-10 11:02:44 -0500416# endif
wdenk57b2d802003-06-27 21:31:46 +0000417
Jon Loeligerd704d912007-07-10 11:02:44 -0500418#endif
wdenk57b2d802003-06-27 21:31:46 +0000419
Jon Loeligerd76b5c12007-07-08 18:02:23 -0500420#if defined(CONFIG_CMD_LOADB)
Jon Loeliger3de8b242007-06-11 19:01:54 -0500421/*
422 * loadb command (load binary) included
423 */
wdenk57b2d802003-06-27 21:31:46 +0000424#define XON_CHAR 17
425#define XOFF_CHAR 19
426#define START_CHAR 0x01
427#define ETX_CHAR 0x03
428#define END_CHAR 0x0D
429#define SPACE 0x20
430#define K_ESCAPE 0x23
431#define SEND_TYPE 'S'
432#define DATA_TYPE 'D'
433#define ACK_TYPE 'Y'
434#define NACK_TYPE 'N'
435#define BREAK_TYPE 'B'
436#define tochar(x) ((char) (((x) + SPACE) & 0xff))
437#define untochar(x) ((int) (((x) - SPACE) & 0xff))
438
wdenk57b2d802003-06-27 21:31:46 +0000439static void set_kerm_bin_mode(unsigned long *);
440static int k_recv(void);
Kim Phillipsdc00a682012-10-29 13:34:31 +0000441static ulong load_serial_bin(ulong offset);
wdenk57b2d802003-06-27 21:31:46 +0000442
Kim Phillipsdc00a682012-10-29 13:34:31 +0000443static char his_eol; /* character he needs at end of packet */
444static int his_pad_count; /* number of pad chars he needs */
445static char his_pad_char; /* pad chars he needs */
446static char his_quote; /* quote chars he'll use */
wdenk57b2d802003-06-27 21:31:46 +0000447
Simon Glassed38aef2020-05-10 11:40:03 -0600448static int do_load_serial_bin(struct cmd_tbl *cmdtp, int flag, int argc,
449 char *const argv[])
wdenk57b2d802003-06-27 21:31:46 +0000450{
wdenk57b2d802003-06-27 21:31:46 +0000451 ulong offset = 0;
452 ulong addr;
453 int load_baudrate, current_baudrate;
454 int rcode = 0;
455 char *s;
456
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200457 /* pre-set offset from CONFIG_SYS_LOAD_ADDR */
458 offset = CONFIG_SYS_LOAD_ADDR;
wdenk57b2d802003-06-27 21:31:46 +0000459
460 /* pre-set offset from $loadaddr */
Simon Glass64b723f2017-08-03 12:22:12 -0600461 s = env_get("loadaddr");
462 if (s)
Simon Glass3ff49ec2021-07-24 09:03:29 -0600463 offset = hextoul(s, NULL);
wdenk57b2d802003-06-27 21:31:46 +0000464
465 load_baudrate = current_baudrate = gd->baudrate;
466
467 if (argc >= 2) {
Simon Glass3ff49ec2021-07-24 09:03:29 -0600468 offset = hextoul(argv[1], NULL);
wdenk57b2d802003-06-27 21:31:46 +0000469 }
470 if (argc == 3) {
Simon Glassff9b9032021-07-24 09:03:30 -0600471 load_baudrate = (int)dectoul(argv[2], NULL);
wdenk57b2d802003-06-27 21:31:46 +0000472
473 /* default to current baudrate */
474 if (load_baudrate == 0)
475 load_baudrate = current_baudrate;
476 }
477
478 if (load_baudrate != current_baudrate) {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000479 printf("## Switch baudrate to %d bps and press ENTER ...\n",
wdenk57b2d802003-06-27 21:31:46 +0000480 load_baudrate);
481 udelay(50000);
Pali Rohár8e67a8a2022-09-05 11:31:20 +0200482 flush();
wdenk57b2d802003-06-27 21:31:46 +0000483 gd->baudrate = load_baudrate;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000484 serial_setbrg();
wdenk57b2d802003-06-27 21:31:46 +0000485 udelay(50000);
486 for (;;) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200487 if (getchar() == '\r')
wdenk57b2d802003-06-27 21:31:46 +0000488 break;
489 }
490 }
491
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200492 if (strcmp(argv[0],"loady")==0) {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000493 printf("## Ready for binary (ymodem) download "
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200494 "to 0x%08lX at %d bps...\n",
495 offset,
496 load_baudrate);
wdenk57b2d802003-06-27 21:31:46 +0000497
Angus Ainslief283bd02013-06-26 16:54:24 -0600498 addr = load_serial_ymodem(offset, xyzModem_ymodem);
499
Pali Roháre37155f2021-08-03 16:28:43 +0200500 if (addr == ~0) {
501 image_load_addr = 0;
502 printf("## Binary (ymodem) download aborted\n");
503 rcode = 1;
504 } else {
505 printf("## Start Addr = 0x%08lX\n", addr);
506 image_load_addr = addr;
507 }
Angus Ainslief283bd02013-06-26 16:54:24 -0600508 } else if (strcmp(argv[0],"loadx")==0) {
509 printf("## Ready for binary (xmodem) download "
510 "to 0x%08lX at %d bps...\n",
511 offset,
512 load_baudrate);
513
514 addr = load_serial_ymodem(offset, xyzModem_xmodem);
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200515
Pali Roháre37155f2021-08-03 16:28:43 +0200516 if (addr == ~0) {
517 image_load_addr = 0;
518 printf("## Binary (xmodem) download aborted\n");
519 rcode = 1;
520 } else {
521 printf("## Start Addr = 0x%08lX\n", addr);
522 image_load_addr = addr;
523 }
wdenk57b2d802003-06-27 21:31:46 +0000524 } else {
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200525
Kim Phillipsdc00a682012-10-29 13:34:31 +0000526 printf("## Ready for binary (kermit) download "
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200527 "to 0x%08lX at %d bps...\n",
528 offset,
529 load_baudrate);
Kim Phillipsdc00a682012-10-29 13:34:31 +0000530 addr = load_serial_bin(offset);
wdenk57b2d802003-06-27 21:31:46 +0000531
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200532 if (addr == ~0) {
Simon Glass892265d2019-12-28 10:45:02 -0700533 image_load_addr = 0;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000534 printf("## Binary (kermit) download aborted\n");
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200535 rcode = 1;
536 } else {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000537 printf("## Start Addr = 0x%08lX\n", addr);
Simon Glass892265d2019-12-28 10:45:02 -0700538 image_load_addr = addr;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200539 }
540 }
wdenk57b2d802003-06-27 21:31:46 +0000541 if (load_baudrate != current_baudrate) {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000542 printf("## Switch baudrate to %d bps and press ESC ...\n",
wdenk57b2d802003-06-27 21:31:46 +0000543 current_baudrate);
Kim Phillipsdc00a682012-10-29 13:34:31 +0000544 udelay(50000);
Pali Rohár8e67a8a2022-09-05 11:31:20 +0200545 flush();
wdenk57b2d802003-06-27 21:31:46 +0000546 gd->baudrate = current_baudrate;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000547 serial_setbrg();
548 udelay(50000);
wdenk57b2d802003-06-27 21:31:46 +0000549 for (;;) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200550 if (getchar() == 0x1B) /* ESC */
wdenk57b2d802003-06-27 21:31:46 +0000551 break;
552 }
553 }
554
wdenk57b2d802003-06-27 21:31:46 +0000555 return rcode;
556}
557
Kim Phillipsdc00a682012-10-29 13:34:31 +0000558static ulong load_serial_bin(ulong offset)
wdenk57b2d802003-06-27 21:31:46 +0000559{
560 int size, i;
wdenk57b2d802003-06-27 21:31:46 +0000561
Kim Phillipsdc00a682012-10-29 13:34:31 +0000562 set_kerm_bin_mode((ulong *) offset);
563 size = k_recv();
wdenk57b2d802003-06-27 21:31:46 +0000564
565 /*
566 * Gather any trailing characters (for instance, the ^D which
567 * is sent by 'cu' after sending a file), and give the
568 * box some time (100 * 1 ms)
569 */
570 for (i=0; i<100; ++i) {
wdenk29e7f5a2004-03-12 00:14:09 +0000571 if (tstc()) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200572 getchar();
wdenk57b2d802003-06-27 21:31:46 +0000573 }
574 udelay(1000);
575 }
576
Pali Rohár8398dd32021-08-06 18:07:39 +0200577 if (size == 0)
578 return ~0; /* Download aborted */
579
Kim Phillipsdc00a682012-10-29 13:34:31 +0000580 flush_cache(offset, size);
wdenk57b2d802003-06-27 21:31:46 +0000581
582 printf("## Total Size = 0x%08x = %d Bytes\n", size, size);
Simon Glass4d949a22017-08-03 12:22:10 -0600583 env_set_hex("filesize", size);
wdenk57b2d802003-06-27 21:31:46 +0000584
585 return offset;
586}
587
Kim Phillipsdc00a682012-10-29 13:34:31 +0000588static void send_pad(void)
wdenk57b2d802003-06-27 21:31:46 +0000589{
590 int count = his_pad_count;
591
592 while (count-- > 0)
Kim Phillipsdc00a682012-10-29 13:34:31 +0000593 putc(his_pad_char);
wdenk57b2d802003-06-27 21:31:46 +0000594}
595
596/* converts escaped kermit char to binary char */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000597static char ktrans(char in)
wdenk57b2d802003-06-27 21:31:46 +0000598{
599 if ((in & 0x60) == 0x40) {
600 return (char) (in & ~0x40);
601 } else if ((in & 0x7f) == 0x3f) {
602 return (char) (in | 0x40);
603 } else
604 return in;
605}
606
Kim Phillipsdc00a682012-10-29 13:34:31 +0000607static int chk1(char *buffer)
wdenk57b2d802003-06-27 21:31:46 +0000608{
609 int total = 0;
610
611 while (*buffer) {
612 total += *buffer++;
613 }
614 return (int) ((total + ((total >> 6) & 0x03)) & 0x3f);
615}
616
Kim Phillipsdc00a682012-10-29 13:34:31 +0000617static void s1_sendpacket(char *packet)
wdenk57b2d802003-06-27 21:31:46 +0000618{
Kim Phillipsdc00a682012-10-29 13:34:31 +0000619 send_pad();
wdenk57b2d802003-06-27 21:31:46 +0000620 while (*packet) {
Kim Phillipsdc00a682012-10-29 13:34:31 +0000621 putc(*packet++);
wdenk57b2d802003-06-27 21:31:46 +0000622 }
623}
624
625static char a_b[24];
Kim Phillipsdc00a682012-10-29 13:34:31 +0000626static void send_ack(int n)
wdenk57b2d802003-06-27 21:31:46 +0000627{
628 a_b[0] = START_CHAR;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000629 a_b[1] = tochar(3);
630 a_b[2] = tochar(n);
wdenk57b2d802003-06-27 21:31:46 +0000631 a_b[3] = ACK_TYPE;
632 a_b[4] = '\0';
Kim Phillipsdc00a682012-10-29 13:34:31 +0000633 a_b[4] = tochar(chk1(&a_b[1]));
wdenk57b2d802003-06-27 21:31:46 +0000634 a_b[5] = his_eol;
635 a_b[6] = '\0';
Kim Phillipsdc00a682012-10-29 13:34:31 +0000636 s1_sendpacket(a_b);
wdenk57b2d802003-06-27 21:31:46 +0000637}
638
Kim Phillipsdc00a682012-10-29 13:34:31 +0000639static void send_nack(int n)
wdenk57b2d802003-06-27 21:31:46 +0000640{
641 a_b[0] = START_CHAR;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000642 a_b[1] = tochar(3);
643 a_b[2] = tochar(n);
wdenk57b2d802003-06-27 21:31:46 +0000644 a_b[3] = NACK_TYPE;
645 a_b[4] = '\0';
Kim Phillipsdc00a682012-10-29 13:34:31 +0000646 a_b[4] = tochar(chk1(&a_b[1]));
wdenk57b2d802003-06-27 21:31:46 +0000647 a_b[5] = his_eol;
648 a_b[6] = '\0';
Kim Phillipsdc00a682012-10-29 13:34:31 +0000649 s1_sendpacket(a_b);
wdenk57b2d802003-06-27 21:31:46 +0000650}
651
Kim Phillipsdc00a682012-10-29 13:34:31 +0000652static void (*os_data_init)(void);
653static void (*os_data_char)(char new_char);
wdenk57b2d802003-06-27 21:31:46 +0000654static int os_data_state, os_data_state_saved;
wdenk57b2d802003-06-27 21:31:46 +0000655static char *os_data_addr, *os_data_addr_saved;
656static char *bin_start_address;
Gururaja Hebbar K Rb4d7eb22008-08-07 13:13:27 +0530657
Kim Phillipsdc00a682012-10-29 13:34:31 +0000658static void bin_data_init(void)
wdenk57b2d802003-06-27 21:31:46 +0000659{
660 os_data_state = 0;
wdenk57b2d802003-06-27 21:31:46 +0000661 os_data_addr = bin_start_address;
662}
Gururaja Hebbar K Rb4d7eb22008-08-07 13:13:27 +0530663
Kim Phillipsdc00a682012-10-29 13:34:31 +0000664static void os_data_save(void)
wdenk57b2d802003-06-27 21:31:46 +0000665{
666 os_data_state_saved = os_data_state;
wdenk57b2d802003-06-27 21:31:46 +0000667 os_data_addr_saved = os_data_addr;
668}
Gururaja Hebbar K Rb4d7eb22008-08-07 13:13:27 +0530669
Kim Phillipsdc00a682012-10-29 13:34:31 +0000670static void os_data_restore(void)
wdenk57b2d802003-06-27 21:31:46 +0000671{
672 os_data_state = os_data_state_saved;
wdenk57b2d802003-06-27 21:31:46 +0000673 os_data_addr = os_data_addr_saved;
674}
Gururaja Hebbar K Rb4d7eb22008-08-07 13:13:27 +0530675
Kim Phillipsdc00a682012-10-29 13:34:31 +0000676static void bin_data_char(char new_char)
wdenk57b2d802003-06-27 21:31:46 +0000677{
678 switch (os_data_state) {
679 case 0: /* data */
680 *os_data_addr++ = new_char;
wdenk57b2d802003-06-27 21:31:46 +0000681 break;
682 }
683}
Gururaja Hebbar K Rb4d7eb22008-08-07 13:13:27 +0530684
Kim Phillipsdc00a682012-10-29 13:34:31 +0000685static void set_kerm_bin_mode(unsigned long *addr)
wdenk57b2d802003-06-27 21:31:46 +0000686{
687 bin_start_address = (char *) addr;
688 os_data_init = bin_data_init;
689 os_data_char = bin_data_char;
690}
691
wdenk57b2d802003-06-27 21:31:46 +0000692/* k_data_* simply handles the kermit escape translations */
693static int k_data_escape, k_data_escape_saved;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000694static void k_data_init(void)
wdenk57b2d802003-06-27 21:31:46 +0000695{
696 k_data_escape = 0;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000697 os_data_init();
wdenk57b2d802003-06-27 21:31:46 +0000698}
Gururaja Hebbar K Rb4d7eb22008-08-07 13:13:27 +0530699
Kim Phillipsdc00a682012-10-29 13:34:31 +0000700static void k_data_save(void)
wdenk57b2d802003-06-27 21:31:46 +0000701{
702 k_data_escape_saved = k_data_escape;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000703 os_data_save();
wdenk57b2d802003-06-27 21:31:46 +0000704}
Gururaja Hebbar K Rb4d7eb22008-08-07 13:13:27 +0530705
Kim Phillipsdc00a682012-10-29 13:34:31 +0000706static void k_data_restore(void)
wdenk57b2d802003-06-27 21:31:46 +0000707{
708 k_data_escape = k_data_escape_saved;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000709 os_data_restore();
wdenk57b2d802003-06-27 21:31:46 +0000710}
Gururaja Hebbar K Rb4d7eb22008-08-07 13:13:27 +0530711
Kim Phillipsdc00a682012-10-29 13:34:31 +0000712static void k_data_char(char new_char)
wdenk57b2d802003-06-27 21:31:46 +0000713{
714 if (k_data_escape) {
715 /* last char was escape - translate this character */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000716 os_data_char(ktrans(new_char));
wdenk57b2d802003-06-27 21:31:46 +0000717 k_data_escape = 0;
718 } else {
719 if (new_char == his_quote) {
720 /* this char is escape - remember */
721 k_data_escape = 1;
722 } else {
723 /* otherwise send this char as-is */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000724 os_data_char(new_char);
wdenk57b2d802003-06-27 21:31:46 +0000725 }
726 }
727}
728
729#define SEND_DATA_SIZE 20
Kim Phillipsdc00a682012-10-29 13:34:31 +0000730static char send_parms[SEND_DATA_SIZE];
731static char *send_ptr;
wdenk57b2d802003-06-27 21:31:46 +0000732
733/* handle_send_packet interprits the protocol info and builds and
734 sends an appropriate ack for what we can do */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000735static void handle_send_packet(int n)
wdenk57b2d802003-06-27 21:31:46 +0000736{
737 int length = 3;
738 int bytes;
739
740 /* initialize some protocol parameters */
741 his_eol = END_CHAR; /* default end of line character */
742 his_pad_count = 0;
743 his_pad_char = '\0';
744 his_quote = K_ESCAPE;
745
746 /* ignore last character if it filled the buffer */
747 if (send_ptr == &send_parms[SEND_DATA_SIZE - 1])
748 --send_ptr;
749 bytes = send_ptr - send_parms; /* how many bytes we'll process */
750 do {
751 if (bytes-- <= 0)
752 break;
753 /* handle MAXL - max length */
754 /* ignore what he says - most I'll take (here) is 94 */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000755 a_b[++length] = tochar(94);
wdenk57b2d802003-06-27 21:31:46 +0000756 if (bytes-- <= 0)
757 break;
758 /* handle TIME - time you should wait for my packets */
759 /* ignore what he says - don't wait for my ack longer than 1 second */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000760 a_b[++length] = tochar(1);
wdenk57b2d802003-06-27 21:31:46 +0000761 if (bytes-- <= 0)
762 break;
763 /* handle NPAD - number of pad chars I need */
764 /* remember what he says - I need none */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000765 his_pad_count = untochar(send_parms[2]);
766 a_b[++length] = tochar(0);
wdenk57b2d802003-06-27 21:31:46 +0000767 if (bytes-- <= 0)
768 break;
769 /* handle PADC - pad chars I need */
770 /* remember what he says - I need none */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000771 his_pad_char = ktrans(send_parms[3]);
wdenk57b2d802003-06-27 21:31:46 +0000772 a_b[++length] = 0x40; /* He should ignore this */
773 if (bytes-- <= 0)
774 break;
775 /* handle EOL - end of line he needs */
776 /* remember what he says - I need CR */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000777 his_eol = untochar(send_parms[4]);
778 a_b[++length] = tochar(END_CHAR);
wdenk57b2d802003-06-27 21:31:46 +0000779 if (bytes-- <= 0)
780 break;
781 /* handle QCTL - quote control char he'll use */
782 /* remember what he says - I'll use '#' */
783 his_quote = send_parms[5];
784 a_b[++length] = '#';
785 if (bytes-- <= 0)
786 break;
787 /* handle QBIN - 8-th bit prefixing */
788 /* ignore what he says - I refuse */
789 a_b[++length] = 'N';
790 if (bytes-- <= 0)
791 break;
792 /* handle CHKT - the clock check type */
793 /* ignore what he says - I do type 1 (for now) */
794 a_b[++length] = '1';
795 if (bytes-- <= 0)
796 break;
797 /* handle REPT - the repeat prefix */
798 /* ignore what he says - I refuse (for now) */
799 a_b[++length] = 'N';
800 if (bytes-- <= 0)
801 break;
802 /* handle CAPAS - the capabilities mask */
803 /* ignore what he says - I only do long packets - I don't do windows */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000804 a_b[++length] = tochar(2); /* only long packets */
805 a_b[++length] = tochar(0); /* no windows */
806 a_b[++length] = tochar(94); /* large packet msb */
807 a_b[++length] = tochar(94); /* large packet lsb */
wdenk57b2d802003-06-27 21:31:46 +0000808 } while (0);
809
810 a_b[0] = START_CHAR;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000811 a_b[1] = tochar(length);
812 a_b[2] = tochar(n);
wdenk57b2d802003-06-27 21:31:46 +0000813 a_b[3] = ACK_TYPE;
814 a_b[++length] = '\0';
Kim Phillipsdc00a682012-10-29 13:34:31 +0000815 a_b[length] = tochar(chk1(&a_b[1]));
wdenk57b2d802003-06-27 21:31:46 +0000816 a_b[++length] = his_eol;
817 a_b[++length] = '\0';
Kim Phillipsdc00a682012-10-29 13:34:31 +0000818 s1_sendpacket(a_b);
wdenk57b2d802003-06-27 21:31:46 +0000819}
820
821/* k_recv receives a OS Open image file over kermit line */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000822static int k_recv(void)
wdenk57b2d802003-06-27 21:31:46 +0000823{
Tom Rini479f54a2024-01-09 17:57:16 -0500824 int new_char;
wdenk57b2d802003-06-27 21:31:46 +0000825 char k_state, k_state_saved;
826 int sum;
827 int done;
828 int length;
829 int n, last_n;
wdenk57b2d802003-06-27 21:31:46 +0000830 int len_lo, len_hi;
831
832 /* initialize some protocol parameters */
833 his_eol = END_CHAR; /* default end of line character */
834 his_pad_count = 0;
835 his_pad_char = '\0';
836 his_quote = K_ESCAPE;
837
838 /* initialize the k_recv and k_data state machine */
839 done = 0;
840 k_state = 0;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000841 k_data_init();
wdenk57b2d802003-06-27 21:31:46 +0000842 k_state_saved = k_state;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000843 k_data_save();
wdenk57b2d802003-06-27 21:31:46 +0000844 n = 0; /* just to get rid of a warning */
845 last_n = -1;
846
847 /* expect this "type" sequence (but don't check):
848 S: send initiate
849 F: file header
850 D: data (multiple)
851 Z: end of file
852 B: break transmission
853 */
854
855 /* enter main loop */
856 while (!done) {
857 /* set the send packet pointer to begining of send packet parms */
858 send_ptr = send_parms;
859
860 /* With each packet, start summing the bytes starting with the length.
861 Save the current sequence number.
862 Note the type of the packet.
863 If a character less than SPACE (0x20) is received - error.
864 */
865
866#if 0
867 /* OLD CODE, Prior to checking sequence numbers */
868 /* first have all state machines save current states */
869 k_state_saved = k_state;
870 k_data_save ();
871#endif
872
873 /* get a packet */
874 /* wait for the starting character or ^C */
875 for (;;) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200876 switch (getchar()) {
wdenk57b2d802003-06-27 21:31:46 +0000877 case START_CHAR: /* start packet */
878 goto START;
879 case ETX_CHAR: /* ^C waiting for packet */
880 return (0);
881 default:
882 ;
883 }
884 }
885START:
886 /* get length of packet */
887 sum = 0;
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200888 new_char = getchar();
wdenk57b2d802003-06-27 21:31:46 +0000889 if ((new_char & 0xE0) == 0)
890 goto packet_error;
891 sum += new_char & 0xff;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000892 length = untochar(new_char);
wdenk57b2d802003-06-27 21:31:46 +0000893 /* get sequence number */
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200894 new_char = getchar();
wdenk57b2d802003-06-27 21:31:46 +0000895 if ((new_char & 0xE0) == 0)
896 goto packet_error;
897 sum += new_char & 0xff;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000898 n = untochar(new_char);
wdenk57b2d802003-06-27 21:31:46 +0000899 --length;
900
901 /* NEW CODE - check sequence numbers for retried packets */
902 /* Note - this new code assumes that the sequence number is correctly
903 * received. Handling an invalid sequence number adds another layer
904 * of complexity that may not be needed - yet! At this time, I'm hoping
905 * that I don't need to buffer the incoming data packets and can write
906 * the data into memory in real time.
907 */
908 if (n == last_n) {
909 /* same sequence number, restore the previous state */
910 k_state = k_state_saved;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000911 k_data_restore();
wdenk57b2d802003-06-27 21:31:46 +0000912 } else {
913 /* new sequence number, checkpoint the download */
914 last_n = n;
915 k_state_saved = k_state;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000916 k_data_save();
wdenk57b2d802003-06-27 21:31:46 +0000917 }
918 /* END NEW CODE */
919
920 /* get packet type */
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200921 new_char = getchar();
wdenk57b2d802003-06-27 21:31:46 +0000922 if ((new_char & 0xE0) == 0)
923 goto packet_error;
924 sum += new_char & 0xff;
925 k_state = new_char;
926 --length;
927 /* check for extended length */
928 if (length == -2) {
929 /* (length byte was 0, decremented twice) */
930 /* get the two length bytes */
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200931 new_char = getchar();
wdenk57b2d802003-06-27 21:31:46 +0000932 if ((new_char & 0xE0) == 0)
933 goto packet_error;
934 sum += new_char & 0xff;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000935 len_hi = untochar(new_char);
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200936 new_char = getchar();
wdenk57b2d802003-06-27 21:31:46 +0000937 if ((new_char & 0xE0) == 0)
938 goto packet_error;
939 sum += new_char & 0xff;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000940 len_lo = untochar(new_char);
wdenk57b2d802003-06-27 21:31:46 +0000941 length = len_hi * 95 + len_lo;
942 /* check header checksum */
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200943 new_char = getchar();
wdenk57b2d802003-06-27 21:31:46 +0000944 if ((new_char & 0xE0) == 0)
945 goto packet_error;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000946 if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
wdenk57b2d802003-06-27 21:31:46 +0000947 goto packet_error;
948 sum += new_char & 0xff;
949/* --length; */ /* new length includes only data and block check to come */
950 }
951 /* bring in rest of packet */
952 while (length > 1) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200953 new_char = getchar();
wdenk57b2d802003-06-27 21:31:46 +0000954 if ((new_char & 0xE0) == 0)
955 goto packet_error;
956 sum += new_char & 0xff;
957 --length;
958 if (k_state == DATA_TYPE) {
959 /* pass on the data if this is a data packet */
960 k_data_char (new_char);
961 } else if (k_state == SEND_TYPE) {
962 /* save send pack in buffer as is */
963 *send_ptr++ = new_char;
964 /* if too much data, back off the pointer */
965 if (send_ptr >= &send_parms[SEND_DATA_SIZE])
966 --send_ptr;
967 }
968 }
969 /* get and validate checksum character */
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200970 new_char = getchar();
wdenk57b2d802003-06-27 21:31:46 +0000971 if ((new_char & 0xE0) == 0)
972 goto packet_error;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000973 if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
wdenk57b2d802003-06-27 21:31:46 +0000974 goto packet_error;
975 /* get END_CHAR */
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200976 new_char = getchar();
wdenk57b2d802003-06-27 21:31:46 +0000977 if (new_char != END_CHAR) {
978 packet_error:
979 /* restore state machines */
980 k_state = k_state_saved;
Kim Phillipsdc00a682012-10-29 13:34:31 +0000981 k_data_restore();
wdenk57b2d802003-06-27 21:31:46 +0000982 /* send a negative acknowledge packet in */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000983 send_nack(n);
wdenk57b2d802003-06-27 21:31:46 +0000984 } else if (k_state == SEND_TYPE) {
985 /* crack the protocol parms, build an appropriate ack packet */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000986 handle_send_packet(n);
wdenk57b2d802003-06-27 21:31:46 +0000987 } else {
988 /* send simple acknowledge packet in */
Kim Phillipsdc00a682012-10-29 13:34:31 +0000989 send_ack(n);
wdenk57b2d802003-06-27 21:31:46 +0000990 /* quit if end of transmission */
991 if (k_state == BREAK_TYPE)
992 done = 1;
993 }
wdenk57b2d802003-06-27 21:31:46 +0000994 }
995 return ((ulong) os_data_addr - (ulong) bin_start_address);
996}
Markus Klotzbuecher387f5412006-03-30 13:40:55 +0200997
998static int getcxmodem(void) {
Wolfgang Denkebd3deb2006-04-16 10:51:58 +0200999 if (tstc())
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +02001000 return (getchar());
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001001 return -1;
1002}
Angus Ainslief283bd02013-06-26 16:54:24 -06001003static ulong load_serial_ymodem(ulong offset, int mode)
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001004{
1005 int size;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001006 int err;
1007 int res;
1008 connection_info_t info;
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02001009 char ymodemBuf[1024];
1010 ulong store_addr = ~0;
1011 ulong addr = 0;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001012
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02001013 size = 0;
Angus Ainslief283bd02013-06-26 16:54:24 -06001014 info.mode = mode;
Kim Phillipsdc00a682012-10-29 13:34:31 +00001015 res = xyzModem_stream_open(&info, &err);
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001016 if (!res) {
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02001017
Pali Rohárb6f177b2021-08-03 16:28:42 +02001018 err = 0;
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02001019 while ((res =
Kim Phillipsdc00a682012-10-29 13:34:31 +00001020 xyzModem_stream_read(ymodemBuf, 1024, &err)) > 0) {
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02001021 store_addr = addr + offset;
1022 size += res;
1023 addr += res;
Masahiro Yamada8cea9b52017-02-11 22:43:54 +09001024#ifdef CONFIG_MTD_NOR_FLASH
Kim Phillipsdc00a682012-10-29 13:34:31 +00001025 if (addr2info(store_addr)) {
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02001026 int rc;
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001027
Kim Phillipsdc00a682012-10-29 13:34:31 +00001028 rc = flash_write((char *) ymodemBuf,
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02001029 store_addr, res);
1030 if (rc != 0) {
Pali Rohárb6f177b2021-08-03 16:28:42 +02001031 xyzModem_stream_terminate(true, &getcxmodem);
1032 xyzModem_stream_close(&err);
1033 printf("\n");
Simon Glassa606ffc2019-12-28 10:44:40 -07001034 flash_perror(rc);
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02001035 return (~0);
1036 }
1037 } else
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001038#endif
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02001039 {
Kim Phillipsdc00a682012-10-29 13:34:31 +00001040 memcpy((char *)(store_addr), ymodemBuf,
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02001041 res);
1042 }
1043
1044 }
Pali Rohárb6f177b2021-08-03 16:28:42 +02001045 if (err) {
1046 xyzModem_stream_terminate((err == xyzModem_cancel) ? false : true, &getcxmodem);
1047 xyzModem_stream_close(&err);
1048 printf("\n%s\n", xyzModem_error(err));
1049 return (~0); /* Download aborted */
1050 }
1051
Heinrich Schuchardta89adb02021-03-19 02:50:57 +00001052 if (IS_ENABLED(CONFIG_CMD_BOOTEFI))
1053 efi_set_bootdev("Uart", "", "",
1054 map_sysmem(offset, 0), size);
1055
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02001056 } else {
Pali Rohárb6f177b2021-08-03 16:28:42 +02001057 printf("\n%s\n", xyzModem_error(err));
1058 return (~0); /* Download aborted */
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001059 }
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001060
Kim Phillipsdc00a682012-10-29 13:34:31 +00001061 xyzModem_stream_terminate(false, &getcxmodem);
Pali Rohárd9c805e2021-08-03 16:28:41 +02001062 xyzModem_stream_close(&err);
Wolfgang Denkebd3deb2006-04-16 10:51:58 +02001063
Chris Packhambbaf4242016-10-25 20:22:48 +13001064 flush_cache(offset, ALIGN(size, ARCH_DMA_MINALIGN));
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001065
Kim Phillipsdc00a682012-10-29 13:34:31 +00001066 printf("## Total Size = 0x%08x = %d Bytes\n", size, size);
Simon Glass4d949a22017-08-03 12:22:10 -06001067 env_set_hex("filesize", size);
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001068
1069 return offset;
1070}
1071
Jon Loeligerd704d912007-07-10 11:02:44 -05001072#endif
wdenk57b2d802003-06-27 21:31:46 +00001073
Rui Miguel Silva433f15a2022-05-11 10:55:40 +01001074#if defined(CONFIG_CMD_LOADM)
1075static int do_load_memory_bin(struct cmd_tbl *cmdtp, int flag, int argc,
1076 char *const argv[])
1077{
1078 ulong addr, dest, size;
1079 void *src, *dst;
1080
1081 if (argc != 4)
1082 return CMD_RET_USAGE;
1083
1084 addr = simple_strtoul(argv[1], NULL, 16);
1085
1086 dest = simple_strtoul(argv[2], NULL, 16);
1087
1088 size = simple_strtoul(argv[3], NULL, 16);
1089
1090 if (!size) {
1091 printf("loadm: can not load zero bytes\n");
1092 return 1;
1093 }
1094
1095 src = map_sysmem(addr, size);
1096 dst = map_sysmem(dest, size);
1097
1098 memcpy(dst, src, size);
1099
1100 unmap_sysmem(src);
1101 unmap_sysmem(dst);
1102
1103 if (IS_ENABLED(CONFIG_CMD_BOOTEFI))
1104 efi_set_bootdev("Mem", "", "", map_sysmem(dest, 0), size);
1105
1106 printf("loaded bin to memory: size: %lu\n", size);
1107
1108 return 0;
1109}
1110#endif
1111
wdenk57b2d802003-06-27 21:31:46 +00001112/* -------------------------------------------------------------------- */
1113
Jon Loeligerd76b5c12007-07-08 18:02:23 -05001114#if defined(CONFIG_CMD_LOADS)
wdenk57b2d802003-06-27 21:31:46 +00001115
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001116#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenkf287a242003-07-01 21:06:45 +00001117U_BOOT_CMD(
1118 loads, 3, 0, do_load_serial,
Peter Tyserdfb72b82009-01-27 18:03:12 -06001119 "load S-Record file over serial line",
wdenk57b2d802003-06-27 21:31:46 +00001120 "[ off ] [ baud ]\n"
1121 " - load S-Record file over serial line"
Wolfgang Denkc54781c2009-05-24 17:06:54 +02001122 " with offset 'off' and baudrate 'baud'"
wdenk57b2d802003-06-27 21:31:46 +00001123);
1124
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001125#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenkf287a242003-07-01 21:06:45 +00001126U_BOOT_CMD(
1127 loads, 2, 0, do_load_serial,
Peter Tyserdfb72b82009-01-27 18:03:12 -06001128 "load S-Record file over serial line",
wdenk57b2d802003-06-27 21:31:46 +00001129 "[ off ]\n"
Wolfgang Denkc54781c2009-05-24 17:06:54 +02001130 " - load S-Record file over serial line with offset 'off'"
wdenk57b2d802003-06-27 21:31:46 +00001131);
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001132#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk57b2d802003-06-27 21:31:46 +00001133
1134/*
1135 * SAVES always requires LOADS support, but not vice versa
1136 */
1137
Jon Loeligerd76b5c12007-07-08 18:02:23 -05001138#if defined(CONFIG_CMD_SAVES)
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001139#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenkf287a242003-07-01 21:06:45 +00001140U_BOOT_CMD(
1141 saves, 4, 0, do_save_serial,
Peter Tyserdfb72b82009-01-27 18:03:12 -06001142 "save S-Record file over serial line",
wdenk57b2d802003-06-27 21:31:46 +00001143 "[ off ] [size] [ baud ]\n"
1144 " - save S-Record file over serial line"
Wolfgang Denkc54781c2009-05-24 17:06:54 +02001145 " with offset 'off', size 'size' and baudrate 'baud'"
wdenk57b2d802003-06-27 21:31:46 +00001146);
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001147#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenkf287a242003-07-01 21:06:45 +00001148U_BOOT_CMD(
1149 saves, 3, 0, do_save_serial,
Peter Tyserdfb72b82009-01-27 18:03:12 -06001150 "save S-Record file over serial line",
wdenk57b2d802003-06-27 21:31:46 +00001151 "[ off ] [size]\n"
Wolfgang Denkc54781c2009-05-24 17:06:54 +02001152 " - save S-Record file over serial line with offset 'off' and size 'size'"
wdenk57b2d802003-06-27 21:31:46 +00001153);
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +02001154#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
Robert P. J. Dayaec64f02013-02-03 05:46:13 +00001155#endif /* CONFIG_CMD_SAVES */
1156#endif /* CONFIG_CMD_LOADS */
wdenk57b2d802003-06-27 21:31:46 +00001157
Jon Loeligerd76b5c12007-07-08 18:02:23 -05001158#if defined(CONFIG_CMD_LOADB)
wdenkf287a242003-07-01 21:06:45 +00001159U_BOOT_CMD(
1160 loadb, 3, 0, do_load_serial_bin,
Peter Tyserdfb72b82009-01-27 18:03:12 -06001161 "load binary file over serial line (kermit mode)",
Heinrich Schuchardtcd373b52021-01-22 13:16:04 +01001162 "[ addr [ baud ] ]\n"
wdenk57b2d802003-06-27 21:31:46 +00001163 " - load binary file over serial line"
Heinrich Schuchardtcd373b52021-01-22 13:16:04 +01001164 " at address 'addr' with baudrate 'baud'"
wdenk57b2d802003-06-27 21:31:46 +00001165);
1166
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001167U_BOOT_CMD(
Angus Ainslief283bd02013-06-26 16:54:24 -06001168 loadx, 3, 0, do_load_serial_bin,
1169 "load binary file over serial line (xmodem mode)",
Heinrich Schuchardtcd373b52021-01-22 13:16:04 +01001170 "[ addr [ baud ] ]\n"
Angus Ainslief283bd02013-06-26 16:54:24 -06001171 " - load binary file over serial line"
Heinrich Schuchardtcd373b52021-01-22 13:16:04 +01001172 " at address 'addr' with baudrate 'baud'"
Angus Ainslief283bd02013-06-26 16:54:24 -06001173);
1174
1175U_BOOT_CMD(
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001176 loady, 3, 0, do_load_serial_bin,
Peter Tyserdfb72b82009-01-27 18:03:12 -06001177 "load binary file over serial line (ymodem mode)",
Heinrich Schuchardtcd373b52021-01-22 13:16:04 +01001178 "[ addr [ baud ] ]\n"
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001179 " - load binary file over serial line"
Heinrich Schuchardtcd373b52021-01-22 13:16:04 +01001180 " at address 'addr' with baudrate 'baud'"
Markus Klotzbuecher387f5412006-03-30 13:40:55 +02001181);
1182
Robert P. J. Dayaec64f02013-02-03 05:46:13 +00001183#endif /* CONFIG_CMD_LOADB */
Rui Miguel Silva433f15a2022-05-11 10:55:40 +01001184
1185#if defined(CONFIG_CMD_LOADM)
1186U_BOOT_CMD(
1187 loadm, 4, 0, do_load_memory_bin,
1188 "load binary blob from source address to destination address",
1189 "[src_addr] [dst_addr] [size]\n"
1190 " - load a binary blob from one memory location to other"
1191 " from src_addr to dst_addr by size bytes"
1192);
1193#endif /* CONFIG_CMD_LOADM */