1 // SPDX-License-Identifier: GPL-2.0+
3 * (C) Copyright 2000-2004
8 * Serial up- and download support
21 DECLARE_GLOBAL_DATA_PTR;
23 #if defined(CONFIG_CMD_LOADB)
24 static ulong load_serial_ymodem(ulong offset, int mode);
27 #if defined(CONFIG_CMD_LOADS)
28 static ulong load_serial(long offset);
29 static int read_record(char *buf, ulong len);
30 # if defined(CONFIG_CMD_SAVES)
31 static int save_serial(ulong offset, ulong size);
32 static int write_record(char *buf);
35 static int do_echo = 1;
38 /* -------------------------------------------------------------------- */
40 #if defined(CONFIG_CMD_LOADS)
41 static int do_load_serial(cmd_tbl_t *cmdtp, int flag, int argc,
49 #ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
50 int load_baudrate, current_baudrate;
52 load_baudrate = current_baudrate = gd->baudrate;
55 env_echo = env_get("loads_echo");
56 if (env_echo && *env_echo == '1')
61 #ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
63 offset = simple_strtol(argv[1], NULL, 16);
66 load_baudrate = (int)simple_strtoul(argv[2], NULL, 10);
68 /* default to current baudrate */
69 if (load_baudrate == 0)
70 load_baudrate = current_baudrate;
72 if (load_baudrate != current_baudrate) {
73 printf("## Switch baudrate to %d bps and press ENTER ...\n",
76 gd->baudrate = load_baudrate;
84 #else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
86 offset = simple_strtol(argv[1], NULL, 16);
88 #endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
90 printf("## Ready for S-Record download ...\n");
92 addr = load_serial(offset);
95 * Gather any trailing characters (for instance, the ^D which
96 * is sent by 'cu' after sending a file), and give the
97 * box some time (100 * 1 ms)
99 for (i=0; i<100; ++i) {
107 printf("## S-Record download aborted\n");
110 printf("## Start Addr = 0x%08lX\n", addr);
114 #ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
115 if (load_baudrate != current_baudrate) {
116 printf("## Switch baudrate to %d bps and press ESC ...\n",
119 gd->baudrate = current_baudrate;
123 if (getc() == 0x1B) /* ESC */
131 static ulong load_serial(long offset)
133 char record[SREC_MAXRECLEN + 1]; /* buffer for one S-Record */
134 char binbuf[SREC_MAXBINLEN]; /* buffer for binary data */
135 int binlen; /* no. of data bytes in S-Rec. */
136 int type; /* return code for record type */
137 ulong addr; /* load address from S-Record */
138 ulong size; /* number of bytes transferred */
140 ulong start_addr = ~0;
144 while (read_record(record, SREC_MAXRECLEN + 1) >= 0) {
145 type = srec_decode(record, &binlen, &addr, binbuf);
148 return (~0); /* Invalid S-Record */
155 store_addr = addr + offset;
156 #ifdef CONFIG_MTD_NOR_FLASH
157 if (addr2info(store_addr)) {
160 rc = flash_write((char *)binbuf,store_addr,binlen);
168 memcpy((char *)(store_addr), binbuf, binlen);
170 if ((store_addr) < start_addr)
171 start_addr = store_addr;
172 if ((store_addr + binlen - 1) > end_addr)
173 end_addr = store_addr + binlen - 1;
179 size = end_addr - start_addr + 1;
181 "## First Load Addr = 0x%08lX\n"
182 "## Last Load Addr = 0x%08lX\n"
183 "## Total Size = 0x%08lX = %ld Bytes\n",
184 start_addr, end_addr, size, size
186 flush_cache(start_addr, size);
187 env_set_hex("filesize", size);
194 if (!do_echo) { /* print a '.' every 100 lines */
195 if ((++line_count % 100) == 0)
200 return (~0); /* Download aborted */
203 static int read_record(char *buf, ulong len)
208 --len; /* always leave room for terminating '\0' byte */
210 for (p=buf; p < buf+len; ++p) {
211 c = getc(); /* read character */
213 putc(c); /* ... and echo it */
221 case 0x03: /* ^C - Control C */
227 /* Check for the console hangup (if any different from serial) */
228 if (gd->jt->getc != getc) {
235 /* line too long - truncate */
240 #if defined(CONFIG_CMD_SAVES)
242 int do_save_serial (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
246 #ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
247 int save_baudrate, current_baudrate;
249 save_baudrate = current_baudrate = gd->baudrate;
253 offset = simple_strtoul(argv[1], NULL, 16);
255 #ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
257 size = simple_strtoul(argv[2], NULL, 16);
260 save_baudrate = (int)simple_strtoul(argv[3], NULL, 10);
262 /* default to current baudrate */
263 if (save_baudrate == 0)
264 save_baudrate = current_baudrate;
266 if (save_baudrate != current_baudrate) {
267 printf("## Switch baudrate to %d bps and press ENTER ...\n",
270 gd->baudrate = save_baudrate;
278 #else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
280 size = simple_strtoul(argv[2], NULL, 16);
282 #endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
284 printf("## Ready for S-Record upload, press ENTER to proceed ...\n");
289 if (save_serial(offset, size)) {
290 printf("## S-Record upload aborted\n");
292 printf("## S-Record upload complete\n");
294 #ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
295 if (save_baudrate != current_baudrate) {
296 printf("## Switch baudrate to %d bps and press ESC ...\n",
297 (int)current_baudrate);
299 gd->baudrate = current_baudrate;
303 if (getc() == 0x1B) /* ESC */
311 #define SREC3_START "S0030000FC\n"
312 #define SREC3_FORMAT "S3%02X%08lX%s%02X\n"
313 #define SREC3_END "S70500000000FA\n"
314 #define SREC_BYTES_PER_RECORD 16
316 static int save_serial(ulong address, ulong count)
318 int i, c, reclen, checksum, length;
319 char *hex = "0123456789ABCDEF";
320 char record[2*SREC_BYTES_PER_RECORD+16]; /* buffer for one S-Record */
321 char data[2*SREC_BYTES_PER_RECORD+1]; /* buffer for hex data */
326 if(write_record(SREC3_START)) /* write the header */
329 if(count) { /* collect hex data in the buffer */
330 c = *(volatile uchar*)(address + reclen); /* get one byte */
331 checksum += c; /* accumulate checksum */
332 data[2*reclen] = hex[(c>>4)&0x0f];
333 data[2*reclen+1] = hex[c & 0x0f];
334 data[2*reclen+2] = '\0';
338 if(reclen == SREC_BYTES_PER_RECORD || count == 0) {
339 /* enough data collected for one record: dump it */
340 if(reclen) { /* build & write a data record: */
341 /* address + data + checksum */
342 length = 4 + reclen + 1;
344 /* accumulate length bytes into checksum */
345 for(i = 0; i < 2; i++)
346 checksum += (length >> (8*i)) & 0xff;
348 /* accumulate address bytes into checksum: */
349 for(i = 0; i < 4; i++)
350 checksum += (address >> (8*i)) & 0xff;
352 /* make proper checksum byte: */
353 checksum = ~checksum & 0xff;
355 /* output one record: */
356 sprintf(record, SREC3_FORMAT, length, address, data, checksum);
357 if(write_record(record))
360 address += reclen; /* increment address */
366 if(write_record(SREC3_END)) /* write the final record */
371 static int write_record(char *buf)
378 /* Check for the console hangup (if any different from serial) */
390 #if defined(CONFIG_CMD_LOADB)
392 * loadb command (load binary) included
396 #define START_CHAR 0x01
397 #define ETX_CHAR 0x03
398 #define END_CHAR 0x0D
400 #define K_ESCAPE 0x23
401 #define SEND_TYPE 'S'
402 #define DATA_TYPE 'D'
404 #define NACK_TYPE 'N'
405 #define BREAK_TYPE 'B'
406 #define tochar(x) ((char) (((x) + SPACE) & 0xff))
407 #define untochar(x) ((int) (((x) - SPACE) & 0xff))
409 static void set_kerm_bin_mode(unsigned long *);
410 static int k_recv(void);
411 static ulong load_serial_bin(ulong offset);
414 static char his_eol; /* character he needs at end of packet */
415 static int his_pad_count; /* number of pad chars he needs */
416 static char his_pad_char; /* pad chars he needs */
417 static char his_quote; /* quote chars he'll use */
419 static int do_load_serial_bin(cmd_tbl_t *cmdtp, int flag, int argc,
424 int load_baudrate, current_baudrate;
428 /* pre-set offset from CONFIG_SYS_LOAD_ADDR */
429 offset = CONFIG_SYS_LOAD_ADDR;
431 /* pre-set offset from $loadaddr */
432 s = env_get("loadaddr");
434 offset = simple_strtoul(s, NULL, 16);
436 load_baudrate = current_baudrate = gd->baudrate;
439 offset = simple_strtoul(argv[1], NULL, 16);
442 load_baudrate = (int)simple_strtoul(argv[2], NULL, 10);
444 /* default to current baudrate */
445 if (load_baudrate == 0)
446 load_baudrate = current_baudrate;
449 if (load_baudrate != current_baudrate) {
450 printf("## Switch baudrate to %d bps and press ENTER ...\n",
453 gd->baudrate = load_baudrate;
462 if (strcmp(argv[0],"loady")==0) {
463 printf("## Ready for binary (ymodem) download "
464 "to 0x%08lX at %d bps...\n",
468 addr = load_serial_ymodem(offset, xyzModem_ymodem);
470 } else if (strcmp(argv[0],"loadx")==0) {
471 printf("## Ready for binary (xmodem) download "
472 "to 0x%08lX at %d bps...\n",
476 addr = load_serial_ymodem(offset, xyzModem_xmodem);
480 printf("## Ready for binary (kermit) download "
481 "to 0x%08lX at %d bps...\n",
484 addr = load_serial_bin(offset);
488 printf("## Binary (kermit) download aborted\n");
491 printf("## Start Addr = 0x%08lX\n", addr);
495 if (load_baudrate != current_baudrate) {
496 printf("## Switch baudrate to %d bps and press ESC ...\n",
499 gd->baudrate = current_baudrate;
503 if (getc() == 0x1B) /* ESC */
512 static ulong load_serial_bin(ulong offset)
516 set_kerm_bin_mode((ulong *) offset);
520 * Gather any trailing characters (for instance, the ^D which
521 * is sent by 'cu' after sending a file), and give the
522 * box some time (100 * 1 ms)
524 for (i=0; i<100; ++i) {
531 flush_cache(offset, size);
533 printf("## Total Size = 0x%08x = %d Bytes\n", size, size);
534 env_set_hex("filesize", size);
539 static void send_pad(void)
541 int count = his_pad_count;
547 /* converts escaped kermit char to binary char */
548 static char ktrans(char in)
550 if ((in & 0x60) == 0x40) {
551 return (char) (in & ~0x40);
552 } else if ((in & 0x7f) == 0x3f) {
553 return (char) (in | 0x40);
558 static int chk1(char *buffer)
565 return (int) ((total + ((total >> 6) & 0x03)) & 0x3f);
568 static void s1_sendpacket(char *packet)
577 static void send_ack(int n)
584 a_b[4] = tochar(chk1(&a_b[1]));
590 static void send_nack(int n)
597 a_b[4] = tochar(chk1(&a_b[1]));
604 static void (*os_data_init)(void);
605 static void (*os_data_char)(char new_char);
606 static int os_data_state, os_data_state_saved;
607 static char *os_data_addr, *os_data_addr_saved;
608 static char *bin_start_address;
610 static void bin_data_init(void)
613 os_data_addr = bin_start_address;
616 static void os_data_save(void)
618 os_data_state_saved = os_data_state;
619 os_data_addr_saved = os_data_addr;
622 static void os_data_restore(void)
624 os_data_state = os_data_state_saved;
625 os_data_addr = os_data_addr_saved;
628 static void bin_data_char(char new_char)
630 switch (os_data_state) {
632 *os_data_addr++ = new_char;
637 static void set_kerm_bin_mode(unsigned long *addr)
639 bin_start_address = (char *) addr;
640 os_data_init = bin_data_init;
641 os_data_char = bin_data_char;
645 /* k_data_* simply handles the kermit escape translations */
646 static int k_data_escape, k_data_escape_saved;
647 static void k_data_init(void)
653 static void k_data_save(void)
655 k_data_escape_saved = k_data_escape;
659 static void k_data_restore(void)
661 k_data_escape = k_data_escape_saved;
665 static void k_data_char(char new_char)
668 /* last char was escape - translate this character */
669 os_data_char(ktrans(new_char));
672 if (new_char == his_quote) {
673 /* this char is escape - remember */
676 /* otherwise send this char as-is */
677 os_data_char(new_char);
682 #define SEND_DATA_SIZE 20
683 static char send_parms[SEND_DATA_SIZE];
684 static char *send_ptr;
686 /* handle_send_packet interprits the protocol info and builds and
687 sends an appropriate ack for what we can do */
688 static void handle_send_packet(int n)
693 /* initialize some protocol parameters */
694 his_eol = END_CHAR; /* default end of line character */
697 his_quote = K_ESCAPE;
699 /* ignore last character if it filled the buffer */
700 if (send_ptr == &send_parms[SEND_DATA_SIZE - 1])
702 bytes = send_ptr - send_parms; /* how many bytes we'll process */
706 /* handle MAXL - max length */
707 /* ignore what he says - most I'll take (here) is 94 */
708 a_b[++length] = tochar(94);
711 /* handle TIME - time you should wait for my packets */
712 /* ignore what he says - don't wait for my ack longer than 1 second */
713 a_b[++length] = tochar(1);
716 /* handle NPAD - number of pad chars I need */
717 /* remember what he says - I need none */
718 his_pad_count = untochar(send_parms[2]);
719 a_b[++length] = tochar(0);
722 /* handle PADC - pad chars I need */
723 /* remember what he says - I need none */
724 his_pad_char = ktrans(send_parms[3]);
725 a_b[++length] = 0x40; /* He should ignore this */
728 /* handle EOL - end of line he needs */
729 /* remember what he says - I need CR */
730 his_eol = untochar(send_parms[4]);
731 a_b[++length] = tochar(END_CHAR);
734 /* handle QCTL - quote control char he'll use */
735 /* remember what he says - I'll use '#' */
736 his_quote = send_parms[5];
740 /* handle QBIN - 8-th bit prefixing */
741 /* ignore what he says - I refuse */
745 /* handle CHKT - the clock check type */
746 /* ignore what he says - I do type 1 (for now) */
750 /* handle REPT - the repeat prefix */
751 /* ignore what he says - I refuse (for now) */
755 /* handle CAPAS - the capabilities mask */
756 /* ignore what he says - I only do long packets - I don't do windows */
757 a_b[++length] = tochar(2); /* only long packets */
758 a_b[++length] = tochar(0); /* no windows */
759 a_b[++length] = tochar(94); /* large packet msb */
760 a_b[++length] = tochar(94); /* large packet lsb */
764 a_b[1] = tochar(length);
767 a_b[++length] = '\0';
768 a_b[length] = tochar(chk1(&a_b[1]));
769 a_b[++length] = his_eol;
770 a_b[++length] = '\0';
774 /* k_recv receives a OS Open image file over kermit line */
775 static int k_recv(void)
778 char k_state, k_state_saved;
785 /* initialize some protocol parameters */
786 his_eol = END_CHAR; /* default end of line character */
789 his_quote = K_ESCAPE;
791 /* initialize the k_recv and k_data state machine */
795 k_state_saved = k_state;
797 n = 0; /* just to get rid of a warning */
800 /* expect this "type" sequence (but don't check):
805 B: break transmission
808 /* enter main loop */
810 /* set the send packet pointer to begining of send packet parms */
811 send_ptr = send_parms;
813 /* With each packet, start summing the bytes starting with the length.
814 Save the current sequence number.
815 Note the type of the packet.
816 If a character less than SPACE (0x20) is received - error.
820 /* OLD CODE, Prior to checking sequence numbers */
821 /* first have all state machines save current states */
822 k_state_saved = k_state;
827 /* wait for the starting character or ^C */
830 case START_CHAR: /* start packet */
832 case ETX_CHAR: /* ^C waiting for packet */
839 /* get length of packet */
842 if ((new_char & 0xE0) == 0)
844 sum += new_char & 0xff;
845 length = untochar(new_char);
846 /* get sequence number */
848 if ((new_char & 0xE0) == 0)
850 sum += new_char & 0xff;
851 n = untochar(new_char);
854 /* NEW CODE - check sequence numbers for retried packets */
855 /* Note - this new code assumes that the sequence number is correctly
856 * received. Handling an invalid sequence number adds another layer
857 * of complexity that may not be needed - yet! At this time, I'm hoping
858 * that I don't need to buffer the incoming data packets and can write
859 * the data into memory in real time.
862 /* same sequence number, restore the previous state */
863 k_state = k_state_saved;
866 /* new sequence number, checkpoint the download */
868 k_state_saved = k_state;
873 /* get packet type */
875 if ((new_char & 0xE0) == 0)
877 sum += new_char & 0xff;
880 /* check for extended length */
882 /* (length byte was 0, decremented twice) */
883 /* get the two length bytes */
885 if ((new_char & 0xE0) == 0)
887 sum += new_char & 0xff;
888 len_hi = untochar(new_char);
890 if ((new_char & 0xE0) == 0)
892 sum += new_char & 0xff;
893 len_lo = untochar(new_char);
894 length = len_hi * 95 + len_lo;
895 /* check header checksum */
897 if ((new_char & 0xE0) == 0)
899 if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
901 sum += new_char & 0xff;
902 /* --length; */ /* new length includes only data and block check to come */
904 /* bring in rest of packet */
907 if ((new_char & 0xE0) == 0)
909 sum += new_char & 0xff;
911 if (k_state == DATA_TYPE) {
912 /* pass on the data if this is a data packet */
913 k_data_char (new_char);
914 } else if (k_state == SEND_TYPE) {
915 /* save send pack in buffer as is */
916 *send_ptr++ = new_char;
917 /* if too much data, back off the pointer */
918 if (send_ptr >= &send_parms[SEND_DATA_SIZE])
922 /* get and validate checksum character */
924 if ((new_char & 0xE0) == 0)
926 if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f))
930 if (new_char != END_CHAR) {
932 /* restore state machines */
933 k_state = k_state_saved;
935 /* send a negative acknowledge packet in */
937 } else if (k_state == SEND_TYPE) {
938 /* crack the protocol parms, build an appropriate ack packet */
939 handle_send_packet(n);
941 /* send simple acknowledge packet in */
943 /* quit if end of transmission */
944 if (k_state == BREAK_TYPE)
948 return ((ulong) os_data_addr - (ulong) bin_start_address);
951 static int getcxmodem(void) {
956 static ulong load_serial_ymodem(ulong offset, int mode)
961 connection_info_t info;
962 char ymodemBuf[1024];
963 ulong store_addr = ~0;
968 res = xyzModem_stream_open(&info, &err);
972 xyzModem_stream_read(ymodemBuf, 1024, &err)) > 0) {
973 store_addr = addr + offset;
976 #ifdef CONFIG_MTD_NOR_FLASH
977 if (addr2info(store_addr)) {
980 rc = flash_write((char *) ymodemBuf,
989 memcpy((char *)(store_addr), ymodemBuf,
995 printf("%s\n", xyzModem_error(err));
998 xyzModem_stream_close(&err);
999 xyzModem_stream_terminate(false, &getcxmodem);
1002 flush_cache(offset, ALIGN(size, ARCH_DMA_MINALIGN));
1004 printf("## Total Size = 0x%08x = %d Bytes\n", size, size);
1005 env_set_hex("filesize", size);
1012 /* -------------------------------------------------------------------- */
1014 #if defined(CONFIG_CMD_LOADS)
1016 #ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
1018 loads, 3, 0, do_load_serial,
1019 "load S-Record file over serial line",
1020 "[ off ] [ baud ]\n"
1021 " - load S-Record file over serial line"
1022 " with offset 'off' and baudrate 'baud'"
1025 #else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
1027 loads, 2, 0, do_load_serial,
1028 "load S-Record file over serial line",
1030 " - load S-Record file over serial line with offset 'off'"
1032 #endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
1035 * SAVES always requires LOADS support, but not vice versa
1039 #if defined(CONFIG_CMD_SAVES)
1040 #ifdef CONFIG_SYS_LOADS_BAUD_CHANGE
1042 saves, 4, 0, do_save_serial,
1043 "save S-Record file over serial line",
1044 "[ off ] [size] [ baud ]\n"
1045 " - save S-Record file over serial line"
1046 " with offset 'off', size 'size' and baudrate 'baud'"
1048 #else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
1050 saves, 3, 0, do_save_serial,
1051 "save S-Record file over serial line",
1053 " - save S-Record file over serial line with offset 'off' and size 'size'"
1055 #endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */
1056 #endif /* CONFIG_CMD_SAVES */
1057 #endif /* CONFIG_CMD_LOADS */
1060 #if defined(CONFIG_CMD_LOADB)
1062 loadb, 3, 0, do_load_serial_bin,
1063 "load binary file over serial line (kermit mode)",
1064 "[ off ] [ baud ]\n"
1065 " - load binary file over serial line"
1066 " with offset 'off' and baudrate 'baud'"
1070 loadx, 3, 0, do_load_serial_bin,
1071 "load binary file over serial line (xmodem mode)",
1072 "[ off ] [ baud ]\n"
1073 " - load binary file over serial line"
1074 " with offset 'off' and baudrate 'baud'"
1078 loady, 3, 0, do_load_serial_bin,
1079 "load binary file over serial line (ymodem mode)",
1080 "[ off ] [ baud ]\n"
1081 " - load binary file over serial line"
1082 " with offset 'off' and baudrate 'baud'"
1085 #endif /* CONFIG_CMD_LOADB */