blob: 5c4f34781d452e83448ed8f35142e860c157f4bc [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
wdenk8bde7f72003-06-27 21:31:46 +00002/*
wdenk232c1502004-03-12 00:14:09 +00003 * (C) Copyright 2000-2004
wdenk8bde7f72003-06-27 21:31:46 +00004 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
wdenk8bde7f72003-06-27 21:31:46 +00005 */
6
7/*
8 * Serial up- and download support
9 */
10#include <common.h>
11#include <command.h>
Simon Glass24b852a2015-11-08 23:47:45 -070012#include <console.h>
Simon Glass1eb69ae2019-11-14 12:57:39 -070013#include <cpu_func.h>
Heinrich Schuchardt76b0a192021-03-19 02:50:57 +000014#include <efi_loader.h>
Simon Glassc7694dd2019-08-01 09:46:46 -060015#include <env.h>
Heinrich Schuchardt76b0a192021-03-19 02:50:57 +000016#include <exports.h>
Tom Rini17ead042022-07-23 13:05:03 -040017#ifdef CONFIG_MTD_NOR_FLASH
Simon Glass0ee482522019-12-28 10:44:40 -070018#include <flash.h>
Tom Rini17ead042022-07-23 13:05:03 -040019#endif
Simon Glass8e8ccfe2019-12-28 10:45:03 -070020#include <image.h>
Marek Vasutc6855192021-10-10 23:52:41 +020021#include <lmb.h>
Heinrich Schuchardt76b0a192021-03-19 02:50:57 +000022#include <mapmem.h>
wdenk8bde7f72003-06-27 21:31:46 +000023#include <net.h>
Heinrich Schuchardt76b0a192021-03-19 02:50:57 +000024#include <s_record.h>
Simon Glassb03e0512019-11-14 12:57:24 -070025#include <serial.h>
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +020026#include <xyzModem.h>
Simon Glass90526e92020-05-10 11:39:56 -060027#include <asm/cache.h>
Simon Glass401d1c42020-10-30 21:38:53 -060028#include <asm/global_data.h>
Simon Glassc05ed002020-05-10 11:40:11 -060029#include <linux/delay.h>
wdenk8bde7f72003-06-27 21:31:46 +000030
Wolfgang Denkd87080b2006-03-31 18:32:53 +020031DECLARE_GLOBAL_DATA_PTR;
wdenk8bde7f72003-06-27 21:31:46 +000032
Jon Loeligerc76fe472007-07-08 18:02:23 -050033#if defined(CONFIG_CMD_LOADB)
Angus Ainslie6e66bd52013-06-26 16:54:24 -060034static ulong load_serial_ymodem(ulong offset, int mode);
Wolfgang Denk8546e232006-05-02 00:11:25 +020035#endif
36
Jon Loeligerc76fe472007-07-08 18:02:23 -050037#if defined(CONFIG_CMD_LOADS)
Kim Phillips088f1b12012-10-29 13:34:31 +000038static ulong load_serial(long offset);
39static int read_record(char *buf, ulong len);
Jon Loeligerc76fe472007-07-08 18:02:23 -050040# if defined(CONFIG_CMD_SAVES)
Kim Phillips088f1b12012-10-29 13:34:31 +000041static int save_serial(ulong offset, ulong size);
42static int write_record(char *buf);
Jon Loeliger90253172007-07-10 11:02:44 -050043#endif
wdenk8bde7f72003-06-27 21:31:46 +000044
45static int do_echo = 1;
Jon Loeliger90253172007-07-10 11:02:44 -050046#endif
wdenk8bde7f72003-06-27 21:31:46 +000047
48/* -------------------------------------------------------------------- */
49
Jon Loeligerc76fe472007-07-08 18:02:23 -050050#if defined(CONFIG_CMD_LOADS)
Simon Glass09140112020-05-10 11:40:03 -060051static int do_load_serial(struct cmd_tbl *cmdtp, int flag, int argc,
52 char *const argv[])
wdenk8bde7f72003-06-27 21:31:46 +000053{
Ricardo Ribalda Delgado2b22d602008-07-30 12:39:29 +020054 long offset = 0;
wdenk8bde7f72003-06-27 21:31:46 +000055 ulong addr;
56 int i;
57 char *env_echo;
58 int rcode = 0;
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +020059#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk8bde7f72003-06-27 21:31:46 +000060 int load_baudrate, current_baudrate;
61
62 load_baudrate = current_baudrate = gd->baudrate;
63#endif
64
Simon Glass00caae62017-08-03 12:22:12 -060065 env_echo = env_get("loads_echo");
66 if (env_echo && *env_echo == '1')
wdenk8bde7f72003-06-27 21:31:46 +000067 do_echo = 1;
Simon Glass00caae62017-08-03 12:22:12 -060068 else
wdenk8bde7f72003-06-27 21:31:46 +000069 do_echo = 0;
wdenk8bde7f72003-06-27 21:31:46 +000070
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +020071#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk8bde7f72003-06-27 21:31:46 +000072 if (argc >= 2) {
Ricardo Ribalda Delgado2b22d602008-07-30 12:39:29 +020073 offset = simple_strtol(argv[1], NULL, 16);
wdenk8bde7f72003-06-27 21:31:46 +000074 }
75 if (argc == 3) {
Simon Glass0b1284e2021-07-24 09:03:30 -060076 load_baudrate = (int)dectoul(argv[2], NULL);
wdenk8bde7f72003-06-27 21:31:46 +000077
78 /* default to current baudrate */
79 if (load_baudrate == 0)
80 load_baudrate = current_baudrate;
81 }
82 if (load_baudrate != current_baudrate) {
Kim Phillips088f1b12012-10-29 13:34:31 +000083 printf("## Switch baudrate to %d bps and press ENTER ...\n",
wdenk8bde7f72003-06-27 21:31:46 +000084 load_baudrate);
85 udelay(50000);
Pali Rohár989cc402022-09-05 11:31:20 +020086 flush();
wdenk8bde7f72003-06-27 21:31:46 +000087 gd->baudrate = load_baudrate;
Kim Phillips088f1b12012-10-29 13:34:31 +000088 serial_setbrg();
wdenk8bde7f72003-06-27 21:31:46 +000089 udelay(50000);
90 for (;;) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +020091 if (getchar() == '\r')
wdenk8bde7f72003-06-27 21:31:46 +000092 break;
93 }
94 }
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +020095#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk8bde7f72003-06-27 21:31:46 +000096 if (argc == 2) {
Ricardo Ribalda Delgado2b22d602008-07-30 12:39:29 +020097 offset = simple_strtol(argv[1], NULL, 16);
wdenk8bde7f72003-06-27 21:31:46 +000098 }
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +020099#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk8bde7f72003-06-27 21:31:46 +0000100
Kim Phillips088f1b12012-10-29 13:34:31 +0000101 printf("## Ready for S-Record download ...\n");
wdenk8bde7f72003-06-27 21:31:46 +0000102
Kim Phillips088f1b12012-10-29 13:34:31 +0000103 addr = load_serial(offset);
wdenk8bde7f72003-06-27 21:31:46 +0000104
105 /*
106 * Gather any trailing characters (for instance, the ^D which
107 * is sent by 'cu' after sending a file), and give the
108 * box some time (100 * 1 ms)
109 */
110 for (i=0; i<100; ++i) {
wdenk232c1502004-03-12 00:14:09 +0000111 if (tstc()) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200112 getchar();
wdenk8bde7f72003-06-27 21:31:46 +0000113 }
114 udelay(1000);
115 }
116
117 if (addr == ~0) {
Kim Phillips088f1b12012-10-29 13:34:31 +0000118 printf("## S-Record download aborted\n");
wdenk8bde7f72003-06-27 21:31:46 +0000119 rcode = 1;
120 } else {
Kim Phillips088f1b12012-10-29 13:34:31 +0000121 printf("## Start Addr = 0x%08lX\n", addr);
Simon Glassbb872dd2019-12-28 10:45:02 -0700122 image_load_addr = addr;
wdenk8bde7f72003-06-27 21:31:46 +0000123 }
124
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200125#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk8bde7f72003-06-27 21:31:46 +0000126 if (load_baudrate != current_baudrate) {
Kim Phillips088f1b12012-10-29 13:34:31 +0000127 printf("## Switch baudrate to %d bps and press ESC ...\n",
wdenk8bde7f72003-06-27 21:31:46 +0000128 current_baudrate);
Kim Phillips088f1b12012-10-29 13:34:31 +0000129 udelay(50000);
Pali Rohár989cc402022-09-05 11:31:20 +0200130 flush();
wdenk8bde7f72003-06-27 21:31:46 +0000131 gd->baudrate = current_baudrate;
Kim Phillips088f1b12012-10-29 13:34:31 +0000132 serial_setbrg();
133 udelay(50000);
wdenk8bde7f72003-06-27 21:31:46 +0000134 for (;;) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200135 if (getchar() == 0x1B) /* ESC */
wdenk8bde7f72003-06-27 21:31:46 +0000136 break;
137 }
138 }
139#endif
140 return rcode;
141}
142
Kim Phillips088f1b12012-10-29 13:34:31 +0000143static ulong load_serial(long offset)
wdenk8bde7f72003-06-27 21:31:46 +0000144{
Marek Vasutc6855192021-10-10 23:52:41 +0200145 struct lmb lmb;
wdenk8bde7f72003-06-27 21:31:46 +0000146 char record[SREC_MAXRECLEN + 1]; /* buffer for one S-Record */
147 char binbuf[SREC_MAXBINLEN]; /* buffer for binary data */
148 int binlen; /* no. of data bytes in S-Rec. */
149 int type; /* return code for record type */
150 ulong addr; /* load address from S-Record */
151 ulong size; /* number of bytes transferred */
wdenk8bde7f72003-06-27 21:31:46 +0000152 ulong store_addr;
153 ulong start_addr = ~0;
154 ulong end_addr = 0;
155 int line_count = 0;
Marek Vasutc6855192021-10-10 23:52:41 +0200156 long ret;
157
158 lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob);
wdenk8bde7f72003-06-27 21:31:46 +0000159
160 while (read_record(record, SREC_MAXRECLEN + 1) >= 0) {
Kim Phillips088f1b12012-10-29 13:34:31 +0000161 type = srec_decode(record, &binlen, &addr, binbuf);
wdenk8bde7f72003-06-27 21:31:46 +0000162
163 if (type < 0) {
164 return (~0); /* Invalid S-Record */
165 }
166
167 switch (type) {
168 case SREC_DATA2:
169 case SREC_DATA3:
170 case SREC_DATA4:
171 store_addr = addr + offset;
Masahiro Yamadae856bdc2017-02-11 22:43:54 +0900172#ifdef CONFIG_MTD_NOR_FLASH
wdenk8bde7f72003-06-27 21:31:46 +0000173 if (addr2info(store_addr)) {
174 int rc;
175
Wolfgang Denk77ddac92005-10-13 16:45:02 +0200176 rc = flash_write((char *)binbuf,store_addr,binlen);
wdenk8bde7f72003-06-27 21:31:46 +0000177 if (rc != 0) {
Kim Phillips088f1b12012-10-29 13:34:31 +0000178 flash_perror(rc);
wdenk8bde7f72003-06-27 21:31:46 +0000179 return (~0);
180 }
181 } else
182#endif
183 {
Marek Vasutc6855192021-10-10 23:52:41 +0200184 ret = lmb_reserve(&lmb, store_addr, binlen);
185 if (ret) {
186 printf("\nCannot overwrite reserved area (%08lx..%08lx)\n",
187 store_addr, store_addr + binlen);
188 return ret;
189 }
Kim Phillips088f1b12012-10-29 13:34:31 +0000190 memcpy((char *)(store_addr), binbuf, binlen);
Marek Vasutc6855192021-10-10 23:52:41 +0200191 lmb_free(&lmb, store_addr, binlen);
wdenk8bde7f72003-06-27 21:31:46 +0000192 }
193 if ((store_addr) < start_addr)
194 start_addr = store_addr;
195 if ((store_addr + binlen - 1) > end_addr)
196 end_addr = store_addr + binlen - 1;
197 break;
198 case SREC_END2:
199 case SREC_END3:
200 case SREC_END4:
Kim Phillips088f1b12012-10-29 13:34:31 +0000201 udelay(10000);
wdenk8bde7f72003-06-27 21:31:46 +0000202 size = end_addr - start_addr + 1;
Kim Phillips088f1b12012-10-29 13:34:31 +0000203 printf("\n"
wdenk8bde7f72003-06-27 21:31:46 +0000204 "## First Load Addr = 0x%08lX\n"
205 "## Last Load Addr = 0x%08lX\n"
206 "## Total Size = 0x%08lX = %ld Bytes\n",
207 start_addr, end_addr, size, size
208 );
Kim Phillips088f1b12012-10-29 13:34:31 +0000209 flush_cache(start_addr, size);
Simon Glass018f5302017-08-03 12:22:10 -0600210 env_set_hex("filesize", size);
wdenk8bde7f72003-06-27 21:31:46 +0000211 return (addr);
212 case SREC_START:
213 break;
214 default:
215 break;
216 }
217 if (!do_echo) { /* print a '.' every 100 lines */
218 if ((++line_count % 100) == 0)
Kim Phillips088f1b12012-10-29 13:34:31 +0000219 putc('.');
wdenk8bde7f72003-06-27 21:31:46 +0000220 }
221 }
222
223 return (~0); /* Download aborted */
224}
225
Kim Phillips088f1b12012-10-29 13:34:31 +0000226static int read_record(char *buf, ulong len)
wdenk8bde7f72003-06-27 21:31:46 +0000227{
228 char *p;
229 char c;
230
231 --len; /* always leave room for terminating '\0' byte */
232
233 for (p=buf; p < buf+len; ++p) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200234 c = getchar(); /* read character */
wdenk8bde7f72003-06-27 21:31:46 +0000235 if (do_echo)
Kim Phillips088f1b12012-10-29 13:34:31 +0000236 putc(c); /* ... and echo it */
wdenk8bde7f72003-06-27 21:31:46 +0000237
238 switch (c) {
239 case '\r':
240 case '\n':
241 *p = '\0';
242 return (p - buf);
243 case '\0':
244 case 0x03: /* ^C - Control C */
245 return (-1);
246 default:
247 *p = c;
248 }
249
Michal Simeke7fcfef2020-12-01 13:58:28 +0100250 /* Check for the console hangup (if any different from serial) */
251 if (gd->jt->getc != getchar) {
252 if (ctrlc())
253 return (-1);
wdenk8bde7f72003-06-27 21:31:46 +0000254 }
wdenk8bde7f72003-06-27 21:31:46 +0000255 }
256
257 /* line too long - truncate */
258 *p = '\0';
259 return (p - buf);
260}
261
Jon Loeligerc76fe472007-07-08 18:02:23 -0500262#if defined(CONFIG_CMD_SAVES)
wdenk8bde7f72003-06-27 21:31:46 +0000263
Simon Glass09140112020-05-10 11:40:03 -0600264int do_save_serial(struct cmd_tbl *cmdtp, int flag, int argc,
265 char *const argv[])
wdenk8bde7f72003-06-27 21:31:46 +0000266{
267 ulong offset = 0;
268 ulong size = 0;
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200269#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk8bde7f72003-06-27 21:31:46 +0000270 int save_baudrate, current_baudrate;
271
272 save_baudrate = current_baudrate = gd->baudrate;
273#endif
274
275 if (argc >= 2) {
Simon Glass7e5f4602021-07-24 09:03:29 -0600276 offset = hextoul(argv[1], NULL);
wdenk8bde7f72003-06-27 21:31:46 +0000277 }
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200278#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk8bde7f72003-06-27 21:31:46 +0000279 if (argc >= 3) {
Simon Glass7e5f4602021-07-24 09:03:29 -0600280 size = hextoul(argv[2], NULL);
wdenk8bde7f72003-06-27 21:31:46 +0000281 }
282 if (argc == 4) {
Simon Glass0b1284e2021-07-24 09:03:30 -0600283 save_baudrate = (int)dectoul(argv[3], NULL);
wdenk8bde7f72003-06-27 21:31:46 +0000284
285 /* default to current baudrate */
286 if (save_baudrate == 0)
287 save_baudrate = current_baudrate;
288 }
289 if (save_baudrate != current_baudrate) {
Kim Phillips088f1b12012-10-29 13:34:31 +0000290 printf("## Switch baudrate to %d bps and press ENTER ...\n",
wdenk8bde7f72003-06-27 21:31:46 +0000291 save_baudrate);
292 udelay(50000);
293 gd->baudrate = save_baudrate;
Kim Phillips088f1b12012-10-29 13:34:31 +0000294 serial_setbrg();
wdenk8bde7f72003-06-27 21:31:46 +0000295 udelay(50000);
296 for (;;) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200297 if (getchar() == '\r')
wdenk8bde7f72003-06-27 21:31:46 +0000298 break;
299 }
300 }
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200301#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk8bde7f72003-06-27 21:31:46 +0000302 if (argc == 3) {
Simon Glass7e5f4602021-07-24 09:03:29 -0600303 size = hextoul(argv[2], NULL);
wdenk8bde7f72003-06-27 21:31:46 +0000304 }
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200305#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk8bde7f72003-06-27 21:31:46 +0000306
Kim Phillips088f1b12012-10-29 13:34:31 +0000307 printf("## Ready for S-Record upload, press ENTER to proceed ...\n");
wdenk8bde7f72003-06-27 21:31:46 +0000308 for (;;) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200309 if (getchar() == '\r')
wdenk8bde7f72003-06-27 21:31:46 +0000310 break;
311 }
Kim Phillips088f1b12012-10-29 13:34:31 +0000312 if (save_serial(offset, size)) {
313 printf("## S-Record upload aborted\n");
wdenk8bde7f72003-06-27 21:31:46 +0000314 } else {
Kim Phillips088f1b12012-10-29 13:34:31 +0000315 printf("## S-Record upload complete\n");
wdenk8bde7f72003-06-27 21:31:46 +0000316 }
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200317#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk8bde7f72003-06-27 21:31:46 +0000318 if (save_baudrate != current_baudrate) {
Kim Phillips088f1b12012-10-29 13:34:31 +0000319 printf("## Switch baudrate to %d bps and press ESC ...\n",
wdenk8bde7f72003-06-27 21:31:46 +0000320 (int)current_baudrate);
Kim Phillips088f1b12012-10-29 13:34:31 +0000321 udelay(50000);
Pali Rohár989cc402022-09-05 11:31:20 +0200322 flush();
wdenk8bde7f72003-06-27 21:31:46 +0000323 gd->baudrate = current_baudrate;
Kim Phillips088f1b12012-10-29 13:34:31 +0000324 serial_setbrg();
325 udelay(50000);
wdenk8bde7f72003-06-27 21:31:46 +0000326 for (;;) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200327 if (getchar() == 0x1B) /* ESC */
wdenk8bde7f72003-06-27 21:31:46 +0000328 break;
329 }
330 }
331#endif
332 return 0;
333}
334
335#define SREC3_START "S0030000FC\n"
336#define SREC3_FORMAT "S3%02X%08lX%s%02X\n"
337#define SREC3_END "S70500000000FA\n"
338#define SREC_BYTES_PER_RECORD 16
339
Kim Phillips088f1b12012-10-29 13:34:31 +0000340static int save_serial(ulong address, ulong count)
wdenk8bde7f72003-06-27 21:31:46 +0000341{
342 int i, c, reclen, checksum, length;
343 char *hex = "0123456789ABCDEF";
344 char record[2*SREC_BYTES_PER_RECORD+16]; /* buffer for one S-Record */
345 char data[2*SREC_BYTES_PER_RECORD+1]; /* buffer for hex data */
346
347 reclen = 0;
348 checksum = 0;
349
350 if(write_record(SREC3_START)) /* write the header */
351 return (-1);
352 do {
353 if(count) { /* collect hex data in the buffer */
354 c = *(volatile uchar*)(address + reclen); /* get one byte */
355 checksum += c; /* accumulate checksum */
356 data[2*reclen] = hex[(c>>4)&0x0f];
357 data[2*reclen+1] = hex[c & 0x0f];
358 data[2*reclen+2] = '\0';
359 ++reclen;
360 --count;
361 }
362 if(reclen == SREC_BYTES_PER_RECORD || count == 0) {
363 /* enough data collected for one record: dump it */
364 if(reclen) { /* build & write a data record: */
365 /* address + data + checksum */
366 length = 4 + reclen + 1;
367
368 /* accumulate length bytes into checksum */
369 for(i = 0; i < 2; i++)
370 checksum += (length >> (8*i)) & 0xff;
371
372 /* accumulate address bytes into checksum: */
373 for(i = 0; i < 4; i++)
374 checksum += (address >> (8*i)) & 0xff;
375
376 /* make proper checksum byte: */
377 checksum = ~checksum & 0xff;
378
379 /* output one record: */
380 sprintf(record, SREC3_FORMAT, length, address, data, checksum);
381 if(write_record(record))
382 return (-1);
383 }
384 address += reclen; /* increment address */
385 checksum = 0;
386 reclen = 0;
387 }
388 }
389 while(count);
390 if(write_record(SREC3_END)) /* write the final record */
391 return (-1);
392 return(0);
393}
394
Kim Phillips088f1b12012-10-29 13:34:31 +0000395static int write_record(char *buf)
wdenk8bde7f72003-06-27 21:31:46 +0000396{
397 char c;
398
399 while((c = *buf++))
wdenk232c1502004-03-12 00:14:09 +0000400 putc(c);
wdenk8bde7f72003-06-27 21:31:46 +0000401
402 /* Check for the console hangup (if any different from serial) */
403
404 if (ctrlc()) {
405 return (-1);
406 }
407 return (0);
408}
Jon Loeliger90253172007-07-10 11:02:44 -0500409# endif
wdenk8bde7f72003-06-27 21:31:46 +0000410
Jon Loeliger90253172007-07-10 11:02:44 -0500411#endif
wdenk8bde7f72003-06-27 21:31:46 +0000412
413
Jon Loeligerc76fe472007-07-08 18:02:23 -0500414#if defined(CONFIG_CMD_LOADB)
Jon Loeliger65c450b2007-06-11 19:01:54 -0500415/*
416 * loadb command (load binary) included
417 */
wdenk8bde7f72003-06-27 21:31:46 +0000418#define XON_CHAR 17
419#define XOFF_CHAR 19
420#define START_CHAR 0x01
421#define ETX_CHAR 0x03
422#define END_CHAR 0x0D
423#define SPACE 0x20
424#define K_ESCAPE 0x23
425#define SEND_TYPE 'S'
426#define DATA_TYPE 'D'
427#define ACK_TYPE 'Y'
428#define NACK_TYPE 'N'
429#define BREAK_TYPE 'B'
430#define tochar(x) ((char) (((x) + SPACE) & 0xff))
431#define untochar(x) ((int) (((x) - SPACE) & 0xff))
432
wdenk8bde7f72003-06-27 21:31:46 +0000433static void set_kerm_bin_mode(unsigned long *);
434static int k_recv(void);
Kim Phillips088f1b12012-10-29 13:34:31 +0000435static ulong load_serial_bin(ulong offset);
wdenk8bde7f72003-06-27 21:31:46 +0000436
437
Kim Phillips088f1b12012-10-29 13:34:31 +0000438static char his_eol; /* character he needs at end of packet */
439static int his_pad_count; /* number of pad chars he needs */
440static char his_pad_char; /* pad chars he needs */
441static char his_quote; /* quote chars he'll use */
wdenk8bde7f72003-06-27 21:31:46 +0000442
Simon Glass09140112020-05-10 11:40:03 -0600443static int do_load_serial_bin(struct cmd_tbl *cmdtp, int flag, int argc,
444 char *const argv[])
wdenk8bde7f72003-06-27 21:31:46 +0000445{
wdenk8bde7f72003-06-27 21:31:46 +0000446 ulong offset = 0;
447 ulong addr;
448 int load_baudrate, current_baudrate;
449 int rcode = 0;
450 char *s;
451
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200452 /* pre-set offset from CONFIG_SYS_LOAD_ADDR */
453 offset = CONFIG_SYS_LOAD_ADDR;
wdenk8bde7f72003-06-27 21:31:46 +0000454
455 /* pre-set offset from $loadaddr */
Simon Glass00caae62017-08-03 12:22:12 -0600456 s = env_get("loadaddr");
457 if (s)
Simon Glass7e5f4602021-07-24 09:03:29 -0600458 offset = hextoul(s, NULL);
wdenk8bde7f72003-06-27 21:31:46 +0000459
460 load_baudrate = current_baudrate = gd->baudrate;
461
462 if (argc >= 2) {
Simon Glass7e5f4602021-07-24 09:03:29 -0600463 offset = hextoul(argv[1], NULL);
wdenk8bde7f72003-06-27 21:31:46 +0000464 }
465 if (argc == 3) {
Simon Glass0b1284e2021-07-24 09:03:30 -0600466 load_baudrate = (int)dectoul(argv[2], NULL);
wdenk8bde7f72003-06-27 21:31:46 +0000467
468 /* default to current baudrate */
469 if (load_baudrate == 0)
470 load_baudrate = current_baudrate;
471 }
472
473 if (load_baudrate != current_baudrate) {
Kim Phillips088f1b12012-10-29 13:34:31 +0000474 printf("## Switch baudrate to %d bps and press ENTER ...\n",
wdenk8bde7f72003-06-27 21:31:46 +0000475 load_baudrate);
476 udelay(50000);
Pali Rohár989cc402022-09-05 11:31:20 +0200477 flush();
wdenk8bde7f72003-06-27 21:31:46 +0000478 gd->baudrate = load_baudrate;
Kim Phillips088f1b12012-10-29 13:34:31 +0000479 serial_setbrg();
wdenk8bde7f72003-06-27 21:31:46 +0000480 udelay(50000);
481 for (;;) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200482 if (getchar() == '\r')
wdenk8bde7f72003-06-27 21:31:46 +0000483 break;
484 }
485 }
486
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200487 if (strcmp(argv[0],"loady")==0) {
Kim Phillips088f1b12012-10-29 13:34:31 +0000488 printf("## Ready for binary (ymodem) download "
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200489 "to 0x%08lX at %d bps...\n",
490 offset,
491 load_baudrate);
wdenk8bde7f72003-06-27 21:31:46 +0000492
Angus Ainslie6e66bd52013-06-26 16:54:24 -0600493 addr = load_serial_ymodem(offset, xyzModem_ymodem);
494
Pali Rohárc97b2552021-08-03 16:28:43 +0200495 if (addr == ~0) {
496 image_load_addr = 0;
497 printf("## Binary (ymodem) download aborted\n");
498 rcode = 1;
499 } else {
500 printf("## Start Addr = 0x%08lX\n", addr);
501 image_load_addr = addr;
502 }
Angus Ainslie6e66bd52013-06-26 16:54:24 -0600503 } else if (strcmp(argv[0],"loadx")==0) {
504 printf("## Ready for binary (xmodem) download "
505 "to 0x%08lX at %d bps...\n",
506 offset,
507 load_baudrate);
508
509 addr = load_serial_ymodem(offset, xyzModem_xmodem);
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200510
Pali Rohárc97b2552021-08-03 16:28:43 +0200511 if (addr == ~0) {
512 image_load_addr = 0;
513 printf("## Binary (xmodem) download aborted\n");
514 rcode = 1;
515 } else {
516 printf("## Start Addr = 0x%08lX\n", addr);
517 image_load_addr = addr;
518 }
wdenk8bde7f72003-06-27 21:31:46 +0000519 } else {
wdenk8bde7f72003-06-27 21:31:46 +0000520
Kim Phillips088f1b12012-10-29 13:34:31 +0000521 printf("## Ready for binary (kermit) download "
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200522 "to 0x%08lX at %d bps...\n",
523 offset,
524 load_baudrate);
Kim Phillips088f1b12012-10-29 13:34:31 +0000525 addr = load_serial_bin(offset);
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200526
527 if (addr == ~0) {
Simon Glassbb872dd2019-12-28 10:45:02 -0700528 image_load_addr = 0;
Kim Phillips088f1b12012-10-29 13:34:31 +0000529 printf("## Binary (kermit) download aborted\n");
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200530 rcode = 1;
531 } else {
Kim Phillips088f1b12012-10-29 13:34:31 +0000532 printf("## Start Addr = 0x%08lX\n", addr);
Simon Glassbb872dd2019-12-28 10:45:02 -0700533 image_load_addr = addr;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200534 }
535 }
wdenk8bde7f72003-06-27 21:31:46 +0000536 if (load_baudrate != current_baudrate) {
Kim Phillips088f1b12012-10-29 13:34:31 +0000537 printf("## Switch baudrate to %d bps and press ESC ...\n",
wdenk8bde7f72003-06-27 21:31:46 +0000538 current_baudrate);
Kim Phillips088f1b12012-10-29 13:34:31 +0000539 udelay(50000);
Pali Rohár989cc402022-09-05 11:31:20 +0200540 flush();
wdenk8bde7f72003-06-27 21:31:46 +0000541 gd->baudrate = current_baudrate;
Kim Phillips088f1b12012-10-29 13:34:31 +0000542 serial_setbrg();
543 udelay(50000);
wdenk8bde7f72003-06-27 21:31:46 +0000544 for (;;) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200545 if (getchar() == 0x1B) /* ESC */
wdenk8bde7f72003-06-27 21:31:46 +0000546 break;
547 }
548 }
549
wdenk8bde7f72003-06-27 21:31:46 +0000550 return rcode;
551}
552
553
Kim Phillips088f1b12012-10-29 13:34:31 +0000554static ulong load_serial_bin(ulong offset)
wdenk8bde7f72003-06-27 21:31:46 +0000555{
556 int size, i;
wdenk8bde7f72003-06-27 21:31:46 +0000557
Kim Phillips088f1b12012-10-29 13:34:31 +0000558 set_kerm_bin_mode((ulong *) offset);
559 size = k_recv();
wdenk8bde7f72003-06-27 21:31:46 +0000560
561 /*
562 * Gather any trailing characters (for instance, the ^D which
563 * is sent by 'cu' after sending a file), and give the
564 * box some time (100 * 1 ms)
565 */
566 for (i=0; i<100; ++i) {
wdenk232c1502004-03-12 00:14:09 +0000567 if (tstc()) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200568 getchar();
wdenk8bde7f72003-06-27 21:31:46 +0000569 }
570 udelay(1000);
571 }
572
Pali Rohár51538142021-08-06 18:07:39 +0200573 if (size == 0)
574 return ~0; /* Download aborted */
575
Kim Phillips088f1b12012-10-29 13:34:31 +0000576 flush_cache(offset, size);
wdenk8bde7f72003-06-27 21:31:46 +0000577
578 printf("## Total Size = 0x%08x = %d Bytes\n", size, size);
Simon Glass018f5302017-08-03 12:22:10 -0600579 env_set_hex("filesize", size);
wdenk8bde7f72003-06-27 21:31:46 +0000580
581 return offset;
582}
583
Kim Phillips088f1b12012-10-29 13:34:31 +0000584static void send_pad(void)
wdenk8bde7f72003-06-27 21:31:46 +0000585{
586 int count = his_pad_count;
587
588 while (count-- > 0)
Kim Phillips088f1b12012-10-29 13:34:31 +0000589 putc(his_pad_char);
wdenk8bde7f72003-06-27 21:31:46 +0000590}
591
592/* converts escaped kermit char to binary char */
Kim Phillips088f1b12012-10-29 13:34:31 +0000593static char ktrans(char in)
wdenk8bde7f72003-06-27 21:31:46 +0000594{
595 if ((in & 0x60) == 0x40) {
596 return (char) (in & ~0x40);
597 } else if ((in & 0x7f) == 0x3f) {
598 return (char) (in | 0x40);
599 } else
600 return in;
601}
602
Kim Phillips088f1b12012-10-29 13:34:31 +0000603static int chk1(char *buffer)
wdenk8bde7f72003-06-27 21:31:46 +0000604{
605 int total = 0;
606
607 while (*buffer) {
608 total += *buffer++;
609 }
610 return (int) ((total + ((total >> 6) & 0x03)) & 0x3f);
611}
612
Kim Phillips088f1b12012-10-29 13:34:31 +0000613static void s1_sendpacket(char *packet)
wdenk8bde7f72003-06-27 21:31:46 +0000614{
Kim Phillips088f1b12012-10-29 13:34:31 +0000615 send_pad();
wdenk8bde7f72003-06-27 21:31:46 +0000616 while (*packet) {
Kim Phillips088f1b12012-10-29 13:34:31 +0000617 putc(*packet++);
wdenk8bde7f72003-06-27 21:31:46 +0000618 }
619}
620
621static char a_b[24];
Kim Phillips088f1b12012-10-29 13:34:31 +0000622static void send_ack(int n)
wdenk8bde7f72003-06-27 21:31:46 +0000623{
624 a_b[0] = START_CHAR;
Kim Phillips088f1b12012-10-29 13:34:31 +0000625 a_b[1] = tochar(3);
626 a_b[2] = tochar(n);
wdenk8bde7f72003-06-27 21:31:46 +0000627 a_b[3] = ACK_TYPE;
628 a_b[4] = '\0';
Kim Phillips088f1b12012-10-29 13:34:31 +0000629 a_b[4] = tochar(chk1(&a_b[1]));
wdenk8bde7f72003-06-27 21:31:46 +0000630 a_b[5] = his_eol;
631 a_b[6] = '\0';
Kim Phillips088f1b12012-10-29 13:34:31 +0000632 s1_sendpacket(a_b);
wdenk8bde7f72003-06-27 21:31:46 +0000633}
634
Kim Phillips088f1b12012-10-29 13:34:31 +0000635static void send_nack(int n)
wdenk8bde7f72003-06-27 21:31:46 +0000636{
637 a_b[0] = START_CHAR;
Kim Phillips088f1b12012-10-29 13:34:31 +0000638 a_b[1] = tochar(3);
639 a_b[2] = tochar(n);
wdenk8bde7f72003-06-27 21:31:46 +0000640 a_b[3] = NACK_TYPE;
641 a_b[4] = '\0';
Kim Phillips088f1b12012-10-29 13:34:31 +0000642 a_b[4] = tochar(chk1(&a_b[1]));
wdenk8bde7f72003-06-27 21:31:46 +0000643 a_b[5] = his_eol;
644 a_b[6] = '\0';
Kim Phillips088f1b12012-10-29 13:34:31 +0000645 s1_sendpacket(a_b);
wdenk8bde7f72003-06-27 21:31:46 +0000646}
647
648
Kim Phillips088f1b12012-10-29 13:34:31 +0000649static void (*os_data_init)(void);
650static void (*os_data_char)(char new_char);
wdenk8bde7f72003-06-27 21:31:46 +0000651static int os_data_state, os_data_state_saved;
wdenk8bde7f72003-06-27 21:31:46 +0000652static char *os_data_addr, *os_data_addr_saved;
653static char *bin_start_address;
Gururaja Hebbar K Ra9fe0c32008-08-07 13:13:27 +0530654
Kim Phillips088f1b12012-10-29 13:34:31 +0000655static void bin_data_init(void)
wdenk8bde7f72003-06-27 21:31:46 +0000656{
657 os_data_state = 0;
wdenk8bde7f72003-06-27 21:31:46 +0000658 os_data_addr = bin_start_address;
659}
Gururaja Hebbar K Ra9fe0c32008-08-07 13:13:27 +0530660
Kim Phillips088f1b12012-10-29 13:34:31 +0000661static void os_data_save(void)
wdenk8bde7f72003-06-27 21:31:46 +0000662{
663 os_data_state_saved = os_data_state;
wdenk8bde7f72003-06-27 21:31:46 +0000664 os_data_addr_saved = os_data_addr;
665}
Gururaja Hebbar K Ra9fe0c32008-08-07 13:13:27 +0530666
Kim Phillips088f1b12012-10-29 13:34:31 +0000667static void os_data_restore(void)
wdenk8bde7f72003-06-27 21:31:46 +0000668{
669 os_data_state = os_data_state_saved;
wdenk8bde7f72003-06-27 21:31:46 +0000670 os_data_addr = os_data_addr_saved;
671}
Gururaja Hebbar K Ra9fe0c32008-08-07 13:13:27 +0530672
Kim Phillips088f1b12012-10-29 13:34:31 +0000673static void bin_data_char(char new_char)
wdenk8bde7f72003-06-27 21:31:46 +0000674{
675 switch (os_data_state) {
676 case 0: /* data */
677 *os_data_addr++ = new_char;
wdenk8bde7f72003-06-27 21:31:46 +0000678 break;
679 }
680}
Gururaja Hebbar K Ra9fe0c32008-08-07 13:13:27 +0530681
Kim Phillips088f1b12012-10-29 13:34:31 +0000682static void set_kerm_bin_mode(unsigned long *addr)
wdenk8bde7f72003-06-27 21:31:46 +0000683{
684 bin_start_address = (char *) addr;
685 os_data_init = bin_data_init;
686 os_data_char = bin_data_char;
687}
688
689
690/* k_data_* simply handles the kermit escape translations */
691static int k_data_escape, k_data_escape_saved;
Kim Phillips088f1b12012-10-29 13:34:31 +0000692static void k_data_init(void)
wdenk8bde7f72003-06-27 21:31:46 +0000693{
694 k_data_escape = 0;
Kim Phillips088f1b12012-10-29 13:34:31 +0000695 os_data_init();
wdenk8bde7f72003-06-27 21:31:46 +0000696}
Gururaja Hebbar K Ra9fe0c32008-08-07 13:13:27 +0530697
Kim Phillips088f1b12012-10-29 13:34:31 +0000698static void k_data_save(void)
wdenk8bde7f72003-06-27 21:31:46 +0000699{
700 k_data_escape_saved = k_data_escape;
Kim Phillips088f1b12012-10-29 13:34:31 +0000701 os_data_save();
wdenk8bde7f72003-06-27 21:31:46 +0000702}
Gururaja Hebbar K Ra9fe0c32008-08-07 13:13:27 +0530703
Kim Phillips088f1b12012-10-29 13:34:31 +0000704static void k_data_restore(void)
wdenk8bde7f72003-06-27 21:31:46 +0000705{
706 k_data_escape = k_data_escape_saved;
Kim Phillips088f1b12012-10-29 13:34:31 +0000707 os_data_restore();
wdenk8bde7f72003-06-27 21:31:46 +0000708}
Gururaja Hebbar K Ra9fe0c32008-08-07 13:13:27 +0530709
Kim Phillips088f1b12012-10-29 13:34:31 +0000710static void k_data_char(char new_char)
wdenk8bde7f72003-06-27 21:31:46 +0000711{
712 if (k_data_escape) {
713 /* last char was escape - translate this character */
Kim Phillips088f1b12012-10-29 13:34:31 +0000714 os_data_char(ktrans(new_char));
wdenk8bde7f72003-06-27 21:31:46 +0000715 k_data_escape = 0;
716 } else {
717 if (new_char == his_quote) {
718 /* this char is escape - remember */
719 k_data_escape = 1;
720 } else {
721 /* otherwise send this char as-is */
Kim Phillips088f1b12012-10-29 13:34:31 +0000722 os_data_char(new_char);
wdenk8bde7f72003-06-27 21:31:46 +0000723 }
724 }
725}
726
727#define SEND_DATA_SIZE 20
Kim Phillips088f1b12012-10-29 13:34:31 +0000728static char send_parms[SEND_DATA_SIZE];
729static char *send_ptr;
wdenk8bde7f72003-06-27 21:31:46 +0000730
731/* handle_send_packet interprits the protocol info and builds and
732 sends an appropriate ack for what we can do */
Kim Phillips088f1b12012-10-29 13:34:31 +0000733static void handle_send_packet(int n)
wdenk8bde7f72003-06-27 21:31:46 +0000734{
735 int length = 3;
736 int bytes;
737
738 /* initialize some protocol parameters */
739 his_eol = END_CHAR; /* default end of line character */
740 his_pad_count = 0;
741 his_pad_char = '\0';
742 his_quote = K_ESCAPE;
743
744 /* ignore last character if it filled the buffer */
745 if (send_ptr == &send_parms[SEND_DATA_SIZE - 1])
746 --send_ptr;
747 bytes = send_ptr - send_parms; /* how many bytes we'll process */
748 do {
749 if (bytes-- <= 0)
750 break;
751 /* handle MAXL - max length */
752 /* ignore what he says - most I'll take (here) is 94 */
Kim Phillips088f1b12012-10-29 13:34:31 +0000753 a_b[++length] = tochar(94);
wdenk8bde7f72003-06-27 21:31:46 +0000754 if (bytes-- <= 0)
755 break;
756 /* handle TIME - time you should wait for my packets */
757 /* ignore what he says - don't wait for my ack longer than 1 second */
Kim Phillips088f1b12012-10-29 13:34:31 +0000758 a_b[++length] = tochar(1);
wdenk8bde7f72003-06-27 21:31:46 +0000759 if (bytes-- <= 0)
760 break;
761 /* handle NPAD - number of pad chars I need */
762 /* remember what he says - I need none */
Kim Phillips088f1b12012-10-29 13:34:31 +0000763 his_pad_count = untochar(send_parms[2]);
764 a_b[++length] = tochar(0);
wdenk8bde7f72003-06-27 21:31:46 +0000765 if (bytes-- <= 0)
766 break;
767 /* handle PADC - pad chars I need */
768 /* remember what he says - I need none */
Kim Phillips088f1b12012-10-29 13:34:31 +0000769 his_pad_char = ktrans(send_parms[3]);
wdenk8bde7f72003-06-27 21:31:46 +0000770 a_b[++length] = 0x40; /* He should ignore this */
771 if (bytes-- <= 0)
772 break;
773 /* handle EOL - end of line he needs */
774 /* remember what he says - I need CR */
Kim Phillips088f1b12012-10-29 13:34:31 +0000775 his_eol = untochar(send_parms[4]);
776 a_b[++length] = tochar(END_CHAR);
wdenk8bde7f72003-06-27 21:31:46 +0000777 if (bytes-- <= 0)
778 break;
779 /* handle QCTL - quote control char he'll use */
780 /* remember what he says - I'll use '#' */
781 his_quote = send_parms[5];
782 a_b[++length] = '#';
783 if (bytes-- <= 0)
784 break;
785 /* handle QBIN - 8-th bit prefixing */
786 /* ignore what he says - I refuse */
787 a_b[++length] = 'N';
788 if (bytes-- <= 0)
789 break;
790 /* handle CHKT - the clock check type */
791 /* ignore what he says - I do type 1 (for now) */
792 a_b[++length] = '1';
793 if (bytes-- <= 0)
794 break;
795 /* handle REPT - the repeat prefix */
796 /* ignore what he says - I refuse (for now) */
797 a_b[++length] = 'N';
798 if (bytes-- <= 0)
799 break;
800 /* handle CAPAS - the capabilities mask */
801 /* ignore what he says - I only do long packets - I don't do windows */
Kim Phillips088f1b12012-10-29 13:34:31 +0000802 a_b[++length] = tochar(2); /* only long packets */
803 a_b[++length] = tochar(0); /* no windows */
804 a_b[++length] = tochar(94); /* large packet msb */
805 a_b[++length] = tochar(94); /* large packet lsb */
wdenk8bde7f72003-06-27 21:31:46 +0000806 } while (0);
807
808 a_b[0] = START_CHAR;
Kim Phillips088f1b12012-10-29 13:34:31 +0000809 a_b[1] = tochar(length);
810 a_b[2] = tochar(n);
wdenk8bde7f72003-06-27 21:31:46 +0000811 a_b[3] = ACK_TYPE;
812 a_b[++length] = '\0';
Kim Phillips088f1b12012-10-29 13:34:31 +0000813 a_b[length] = tochar(chk1(&a_b[1]));
wdenk8bde7f72003-06-27 21:31:46 +0000814 a_b[++length] = his_eol;
815 a_b[++length] = '\0';
Kim Phillips088f1b12012-10-29 13:34:31 +0000816 s1_sendpacket(a_b);
wdenk8bde7f72003-06-27 21:31:46 +0000817}
818
819/* k_recv receives a OS Open image file over kermit line */
Kim Phillips088f1b12012-10-29 13:34:31 +0000820static int k_recv(void)
wdenk8bde7f72003-06-27 21:31:46 +0000821{
822 char new_char;
823 char k_state, k_state_saved;
824 int sum;
825 int done;
826 int length;
827 int n, last_n;
wdenk8bde7f72003-06-27 21:31:46 +0000828 int len_lo, len_hi;
829
830 /* initialize some protocol parameters */
831 his_eol = END_CHAR; /* default end of line character */
832 his_pad_count = 0;
833 his_pad_char = '\0';
834 his_quote = K_ESCAPE;
835
836 /* initialize the k_recv and k_data state machine */
837 done = 0;
838 k_state = 0;
Kim Phillips088f1b12012-10-29 13:34:31 +0000839 k_data_init();
wdenk8bde7f72003-06-27 21:31:46 +0000840 k_state_saved = k_state;
Kim Phillips088f1b12012-10-29 13:34:31 +0000841 k_data_save();
wdenk8bde7f72003-06-27 21:31:46 +0000842 n = 0; /* just to get rid of a warning */
843 last_n = -1;
844
845 /* expect this "type" sequence (but don't check):
846 S: send initiate
847 F: file header
848 D: data (multiple)
849 Z: end of file
850 B: break transmission
851 */
852
853 /* enter main loop */
854 while (!done) {
855 /* set the send packet pointer to begining of send packet parms */
856 send_ptr = send_parms;
857
858 /* With each packet, start summing the bytes starting with the length.
859 Save the current sequence number.
860 Note the type of the packet.
861 If a character less than SPACE (0x20) is received - error.
862 */
863
864#if 0
865 /* OLD CODE, Prior to checking sequence numbers */
866 /* first have all state machines save current states */
867 k_state_saved = k_state;
868 k_data_save ();
869#endif
870
871 /* get a packet */
872 /* wait for the starting character or ^C */
873 for (;;) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200874 switch (getchar()) {
wdenk8bde7f72003-06-27 21:31:46 +0000875 case START_CHAR: /* start packet */
876 goto START;
877 case ETX_CHAR: /* ^C waiting for packet */
878 return (0);
879 default:
880 ;
881 }
882 }
883START:
884 /* get length of packet */
885 sum = 0;
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200886 new_char = getchar();
wdenk8bde7f72003-06-27 21:31:46 +0000887 if ((new_char & 0xE0) == 0)
888 goto packet_error;
889 sum += new_char & 0xff;
Kim Phillips088f1b12012-10-29 13:34:31 +0000890 length = untochar(new_char);
wdenk8bde7f72003-06-27 21:31:46 +0000891 /* get sequence number */
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200892 new_char = getchar();
wdenk8bde7f72003-06-27 21:31:46 +0000893 if ((new_char & 0xE0) == 0)
894 goto packet_error;
895 sum += new_char & 0xff;
Kim Phillips088f1b12012-10-29 13:34:31 +0000896 n = untochar(new_char);
wdenk8bde7f72003-06-27 21:31:46 +0000897 --length;
898
899 /* NEW CODE - check sequence numbers for retried packets */
900 /* Note - this new code assumes that the sequence number is correctly
901 * received. Handling an invalid sequence number adds another layer
902 * of complexity that may not be needed - yet! At this time, I'm hoping
903 * that I don't need to buffer the incoming data packets and can write
904 * the data into memory in real time.
905 */
906 if (n == last_n) {
907 /* same sequence number, restore the previous state */
908 k_state = k_state_saved;
Kim Phillips088f1b12012-10-29 13:34:31 +0000909 k_data_restore();
wdenk8bde7f72003-06-27 21:31:46 +0000910 } else {
911 /* new sequence number, checkpoint the download */
912 last_n = n;
913 k_state_saved = k_state;
Kim Phillips088f1b12012-10-29 13:34:31 +0000914 k_data_save();
wdenk8bde7f72003-06-27 21:31:46 +0000915 }
916 /* END NEW CODE */
917
918 /* get packet type */
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200919 new_char = getchar();
wdenk8bde7f72003-06-27 21:31:46 +0000920 if ((new_char & 0xE0) == 0)
921 goto packet_error;
922 sum += new_char & 0xff;
923 k_state = new_char;
924 --length;
925 /* check for extended length */
926 if (length == -2) {
927 /* (length byte was 0, decremented twice) */
928 /* get the two length bytes */
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200929 new_char = getchar();
wdenk8bde7f72003-06-27 21:31:46 +0000930 if ((new_char & 0xE0) == 0)
931 goto packet_error;
932 sum += new_char & 0xff;
Kim Phillips088f1b12012-10-29 13:34:31 +0000933 len_hi = untochar(new_char);
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200934 new_char = getchar();
wdenk8bde7f72003-06-27 21:31:46 +0000935 if ((new_char & 0xE0) == 0)
936 goto packet_error;
937 sum += new_char & 0xff;
Kim Phillips088f1b12012-10-29 13:34:31 +0000938 len_lo = untochar(new_char);
wdenk8bde7f72003-06-27 21:31:46 +0000939 length = len_hi * 95 + len_lo;
940 /* check header checksum */
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200941 new_char = getchar();
wdenk8bde7f72003-06-27 21:31:46 +0000942 if ((new_char & 0xE0) == 0)
943 goto packet_error;
Kim Phillips088f1b12012-10-29 13:34:31 +0000944 if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
wdenk8bde7f72003-06-27 21:31:46 +0000945 goto packet_error;
946 sum += new_char & 0xff;
947/* --length; */ /* new length includes only data and block check to come */
948 }
949 /* bring in rest of packet */
950 while (length > 1) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200951 new_char = getchar();
wdenk8bde7f72003-06-27 21:31:46 +0000952 if ((new_char & 0xE0) == 0)
953 goto packet_error;
954 sum += new_char & 0xff;
955 --length;
956 if (k_state == DATA_TYPE) {
957 /* pass on the data if this is a data packet */
958 k_data_char (new_char);
959 } else if (k_state == SEND_TYPE) {
960 /* save send pack in buffer as is */
961 *send_ptr++ = new_char;
962 /* if too much data, back off the pointer */
963 if (send_ptr >= &send_parms[SEND_DATA_SIZE])
964 --send_ptr;
965 }
966 }
967 /* get and validate checksum character */
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200968 new_char = getchar();
wdenk8bde7f72003-06-27 21:31:46 +0000969 if ((new_char & 0xE0) == 0)
970 goto packet_error;
Kim Phillips088f1b12012-10-29 13:34:31 +0000971 if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
wdenk8bde7f72003-06-27 21:31:46 +0000972 goto packet_error;
973 /* get END_CHAR */
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200974 new_char = getchar();
wdenk8bde7f72003-06-27 21:31:46 +0000975 if (new_char != END_CHAR) {
976 packet_error:
977 /* restore state machines */
978 k_state = k_state_saved;
Kim Phillips088f1b12012-10-29 13:34:31 +0000979 k_data_restore();
wdenk8bde7f72003-06-27 21:31:46 +0000980 /* send a negative acknowledge packet in */
Kim Phillips088f1b12012-10-29 13:34:31 +0000981 send_nack(n);
wdenk8bde7f72003-06-27 21:31:46 +0000982 } else if (k_state == SEND_TYPE) {
983 /* crack the protocol parms, build an appropriate ack packet */
Kim Phillips088f1b12012-10-29 13:34:31 +0000984 handle_send_packet(n);
wdenk8bde7f72003-06-27 21:31:46 +0000985 } else {
986 /* send simple acknowledge packet in */
Kim Phillips088f1b12012-10-29 13:34:31 +0000987 send_ack(n);
wdenk8bde7f72003-06-27 21:31:46 +0000988 /* quit if end of transmission */
989 if (k_state == BREAK_TYPE)
990 done = 1;
991 }
wdenk8bde7f72003-06-27 21:31:46 +0000992 }
993 return ((ulong) os_data_addr - (ulong) bin_start_address);
994}
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200995
996static int getcxmodem(void) {
Wolfgang Denkcf48eb92006-04-16 10:51:58 +0200997 if (tstc())
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200998 return (getchar());
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +0200999 return -1;
1000}
Angus Ainslie6e66bd52013-06-26 16:54:24 -06001001static ulong load_serial_ymodem(ulong offset, int mode)
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001002{
1003 int size;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001004 int err;
1005 int res;
1006 connection_info_t info;
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02001007 char ymodemBuf[1024];
1008 ulong store_addr = ~0;
1009 ulong addr = 0;
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001010
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02001011 size = 0;
Angus Ainslie6e66bd52013-06-26 16:54:24 -06001012 info.mode = mode;
Kim Phillips088f1b12012-10-29 13:34:31 +00001013 res = xyzModem_stream_open(&info, &err);
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001014 if (!res) {
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001015
Pali Rohár081bd242021-08-03 16:28:42 +02001016 err = 0;
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02001017 while ((res =
Kim Phillips088f1b12012-10-29 13:34:31 +00001018 xyzModem_stream_read(ymodemBuf, 1024, &err)) > 0) {
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02001019 store_addr = addr + offset;
1020 size += res;
1021 addr += res;
Masahiro Yamadae856bdc2017-02-11 22:43:54 +09001022#ifdef CONFIG_MTD_NOR_FLASH
Kim Phillips088f1b12012-10-29 13:34:31 +00001023 if (addr2info(store_addr)) {
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02001024 int rc;
1025
Kim Phillips088f1b12012-10-29 13:34:31 +00001026 rc = flash_write((char *) ymodemBuf,
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02001027 store_addr, res);
1028 if (rc != 0) {
Pali Rohár081bd242021-08-03 16:28:42 +02001029 xyzModem_stream_terminate(true, &getcxmodem);
1030 xyzModem_stream_close(&err);
1031 printf("\n");
Simon Glass0ee482522019-12-28 10:44:40 -07001032 flash_perror(rc);
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02001033 return (~0);
1034 }
1035 } else
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001036#endif
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02001037 {
Kim Phillips088f1b12012-10-29 13:34:31 +00001038 memcpy((char *)(store_addr), ymodemBuf,
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02001039 res);
1040 }
1041
1042 }
Pali Rohár081bd242021-08-03 16:28:42 +02001043 if (err) {
1044 xyzModem_stream_terminate((err == xyzModem_cancel) ? false : true, &getcxmodem);
1045 xyzModem_stream_close(&err);
1046 printf("\n%s\n", xyzModem_error(err));
1047 return (~0); /* Download aborted */
1048 }
1049
Heinrich Schuchardt76b0a192021-03-19 02:50:57 +00001050 if (IS_ENABLED(CONFIG_CMD_BOOTEFI))
1051 efi_set_bootdev("Uart", "", "",
1052 map_sysmem(offset, 0), size);
1053
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02001054 } else {
Pali Rohár081bd242021-08-03 16:28:42 +02001055 printf("\n%s\n", xyzModem_error(err));
1056 return (~0); /* Download aborted */
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001057 }
Wolfgang Denkcf48eb92006-04-16 10:51:58 +02001058
Kim Phillips088f1b12012-10-29 13:34:31 +00001059 xyzModem_stream_terminate(false, &getcxmodem);
Pali Rohár1f26c492021-08-03 16:28:41 +02001060 xyzModem_stream_close(&err);
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001061
1062
Chris Packham0a6036d2016-10-25 20:22:48 +13001063 flush_cache(offset, ALIGN(size, ARCH_DMA_MINALIGN));
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001064
Kim Phillips088f1b12012-10-29 13:34:31 +00001065 printf("## Total Size = 0x%08x = %d Bytes\n", size, size);
Simon Glass018f5302017-08-03 12:22:10 -06001066 env_set_hex("filesize", size);
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001067
1068 return offset;
1069}
1070
Jon Loeliger90253172007-07-10 11:02:44 -05001071#endif
wdenk8bde7f72003-06-27 21:31:46 +00001072
Rui Miguel Silvabfef72e2022-05-11 10:55:40 +01001073#if defined(CONFIG_CMD_LOADM)
1074static int do_load_memory_bin(struct cmd_tbl *cmdtp, int flag, int argc,
1075 char *const argv[])
1076{
1077 ulong addr, dest, size;
1078 void *src, *dst;
1079
1080 if (argc != 4)
1081 return CMD_RET_USAGE;
1082
1083 addr = simple_strtoul(argv[1], NULL, 16);
1084
1085 dest = simple_strtoul(argv[2], NULL, 16);
1086
1087 size = simple_strtoul(argv[3], NULL, 16);
1088
1089 if (!size) {
1090 printf("loadm: can not load zero bytes\n");
1091 return 1;
1092 }
1093
1094 src = map_sysmem(addr, size);
1095 dst = map_sysmem(dest, size);
1096
1097 memcpy(dst, src, size);
1098
1099 unmap_sysmem(src);
1100 unmap_sysmem(dst);
1101
1102 if (IS_ENABLED(CONFIG_CMD_BOOTEFI))
1103 efi_set_bootdev("Mem", "", "", map_sysmem(dest, 0), size);
1104
1105 printf("loaded bin to memory: size: %lu\n", size);
1106
1107 return 0;
1108}
1109#endif
1110
wdenk8bde7f72003-06-27 21:31:46 +00001111/* -------------------------------------------------------------------- */
1112
Jon Loeligerc76fe472007-07-08 18:02:23 -05001113#if defined(CONFIG_CMD_LOADS)
wdenk8bde7f72003-06-27 21:31:46 +00001114
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +02001115#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk0d498392003-07-01 21:06:45 +00001116U_BOOT_CMD(
1117 loads, 3, 0, do_load_serial,
Peter Tyser2fb26042009-01-27 18:03:12 -06001118 "load S-Record file over serial line",
wdenk8bde7f72003-06-27 21:31:46 +00001119 "[ off ] [ baud ]\n"
1120 " - load S-Record file over serial line"
Wolfgang Denka89c33d2009-05-24 17:06:54 +02001121 " with offset 'off' and baudrate 'baud'"
wdenk8bde7f72003-06-27 21:31:46 +00001122);
1123
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +02001124#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk0d498392003-07-01 21:06:45 +00001125U_BOOT_CMD(
1126 loads, 2, 0, do_load_serial,
Peter Tyser2fb26042009-01-27 18:03:12 -06001127 "load S-Record file over serial line",
wdenk8bde7f72003-06-27 21:31:46 +00001128 "[ off ]\n"
Wolfgang Denka89c33d2009-05-24 17:06:54 +02001129 " - load S-Record file over serial line with offset 'off'"
wdenk8bde7f72003-06-27 21:31:46 +00001130);
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +02001131#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk8bde7f72003-06-27 21:31:46 +00001132
1133/*
1134 * SAVES always requires LOADS support, but not vice versa
1135 */
1136
1137
Jon Loeligerc76fe472007-07-08 18:02:23 -05001138#if defined(CONFIG_CMD_SAVES)
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +02001139#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
wdenk0d498392003-07-01 21:06:45 +00001140U_BOOT_CMD(
1141 saves, 4, 0, do_save_serial,
Peter Tyser2fb26042009-01-27 18:03:12 -06001142 "save S-Record file over serial line",
wdenk8bde7f72003-06-27 21:31:46 +00001143 "[ off ] [size] [ baud ]\n"
1144 " - save S-Record file over serial line"
Wolfgang Denka89c33d2009-05-24 17:06:54 +02001145 " with offset 'off', size 'size' and baudrate 'baud'"
wdenk8bde7f72003-06-27 21:31:46 +00001146);
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +02001147#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
wdenk0d498392003-07-01 21:06:45 +00001148U_BOOT_CMD(
1149 saves, 3, 0, do_save_serial,
Peter Tyser2fb26042009-01-27 18:03:12 -06001150 "save S-Record file over serial line",
wdenk8bde7f72003-06-27 21:31:46 +00001151 "[ off ] [size]\n"
Wolfgang Denka89c33d2009-05-24 17:06:54 +02001152 " - save S-Record file over serial line with offset 'off' and size 'size'"
wdenk8bde7f72003-06-27 21:31:46 +00001153);
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +02001154#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
Robert P. J. Day70d7cb92013-02-03 05:46:13 +00001155#endif /* CONFIG_CMD_SAVES */
1156#endif /* CONFIG_CMD_LOADS */
wdenk8bde7f72003-06-27 21:31:46 +00001157
1158
Jon Loeligerc76fe472007-07-08 18:02:23 -05001159#if defined(CONFIG_CMD_LOADB)
wdenk0d498392003-07-01 21:06:45 +00001160U_BOOT_CMD(
1161 loadb, 3, 0, do_load_serial_bin,
Peter Tyser2fb26042009-01-27 18:03:12 -06001162 "load binary file over serial line (kermit mode)",
Heinrich Schuchardtb9112082021-01-22 13:16:04 +01001163 "[ addr [ baud ] ]\n"
wdenk8bde7f72003-06-27 21:31:46 +00001164 " - load binary file over serial line"
Heinrich Schuchardtb9112082021-01-22 13:16:04 +01001165 " at address 'addr' with baudrate 'baud'"
wdenk8bde7f72003-06-27 21:31:46 +00001166);
1167
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001168U_BOOT_CMD(
Angus Ainslie6e66bd52013-06-26 16:54:24 -06001169 loadx, 3, 0, do_load_serial_bin,
1170 "load binary file over serial line (xmodem mode)",
Heinrich Schuchardtb9112082021-01-22 13:16:04 +01001171 "[ addr [ baud ] ]\n"
Angus Ainslie6e66bd52013-06-26 16:54:24 -06001172 " - load binary file over serial line"
Heinrich Schuchardtb9112082021-01-22 13:16:04 +01001173 " at address 'addr' with baudrate 'baud'"
Angus Ainslie6e66bd52013-06-26 16:54:24 -06001174);
1175
1176U_BOOT_CMD(
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001177 loady, 3, 0, do_load_serial_bin,
Peter Tyser2fb26042009-01-27 18:03:12 -06001178 "load binary file over serial line (ymodem mode)",
Heinrich Schuchardtb9112082021-01-22 13:16:04 +01001179 "[ addr [ baud ] ]\n"
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001180 " - load binary file over serial line"
Heinrich Schuchardtb9112082021-01-22 13:16:04 +01001181 " at address 'addr' with baudrate 'baud'"
Markus Klotzbuecherf2841d32006-03-30 13:40:55 +02001182);
1183
Robert P. J. Day70d7cb92013-02-03 05:46:13 +00001184#endif /* CONFIG_CMD_LOADB */
Rui Miguel Silvabfef72e2022-05-11 10:55:40 +01001185
1186#if defined(CONFIG_CMD_LOADM)
1187U_BOOT_CMD(
1188 loadm, 4, 0, do_load_memory_bin,
1189 "load binary blob from source address to destination address",
1190 "[src_addr] [dst_addr] [size]\n"
1191 " - load a binary blob from one memory location to other"
1192 " from src_addr to dst_addr by size bytes"
1193);
1194#endif /* CONFIG_CMD_LOADM */