Last active
June 18, 2020 03:52
-
-
Save akarpenko/173aaa09604eed6d80020e0b53dfe350 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/cmd/send.c b/cmd/send.c | |
new file mode 100644 | |
index 0000000000..e533e5a65a | |
--- /dev/null | |
+++ b/cmd/send.c | |
@@ -0,0 +1,349 @@ | |
+#include <common.h> | |
+#include <command.h> | |
+#include <console.h> | |
+#include <cpu_func.h> | |
+#include <env.h> | |
+#include <flash.h> | |
+#include <image.h> | |
+#include <exports.h> | |
+#include <serial.h> | |
+#include <asm/cache.h> | |
+#include <linux/delay.h> | |
+#include <watchdog.h> | |
+ | |
+#define SEND_CHAR_TIMEOUT 10000 /* 10 seconds */ | |
+ | |
+#define X_SOH 0x01 | |
+#define X_STX 0x02 | |
+#define X_ACK 0x06 | |
+#define X_NAK 0x15 | |
+#define X_EOF 0x04 | |
+ | |
+struct xmodem_chunk { | |
+ uint8_t start; | |
+ uint8_t block; | |
+ uint8_t block_neg; | |
+ uint8_t payload[128]; | |
+ //uint8_t payload[1024]; | |
+ uint16_t crc; | |
+} __attribute__((packed)); | |
+ | |
+#define CRC_POLY 0x1021 | |
+ | |
+static uint16_t crc_update(uint16_t crc_in, int incr) | |
+{ | |
+ uint16_t xor = crc_in >> 15; | |
+ uint16_t out = crc_in << 1; | |
+ | |
+ if (incr) | |
+ out++; | |
+ | |
+ if (xor) | |
+ out ^= CRC_POLY; | |
+ | |
+ return out; | |
+} | |
+ | |
+static uint16_t crc16(const uint8_t *data, uint16_t size) | |
+{ | |
+ uint16_t crc, i; | |
+ | |
+ for (crc = 0; size > 0; size--, data++) | |
+ for (i = 0x80; i; i >>= 1) | |
+ crc = crc_update(crc, *data & i); | |
+ | |
+ for (i = 0; i < 16; i++) | |
+ crc = crc_update(crc, 0); | |
+ | |
+ return crc; | |
+} | |
+ | |
+static uint16_t swap16(uint16_t in) | |
+{ | |
+ return (in >> 8) | ((in & 0xff) << 8); | |
+} | |
+ | |
+enum { | |
+ PROTOCOL_XMODEM, | |
+ PROTOCOL_YMODEM, | |
+}; | |
+ | |
+static void putc3(char ch) { | |
+ int loop = 0; | |
+ while (*(volatile uint32_t*)(0x80074018) & 0x00000020) { | |
+ loop++; | |
+ if (loop > 1000000) | |
+ break; | |
+ } | |
+ *(volatile uint32_t*)(0x80074000) = ch; | |
+} | |
+ | |
+ | |
+int serial_write(void *buf, size_t len) { | |
+ for (size_t i = 0; i < len; i++) { | |
+ putc3(((uint8_t *)buf)[i]); | |
+ } | |
+ return len; | |
+} | |
+ | |
+int getc_timeout(uint8_t *c, unsigned long timeout) | |
+{ | |
+ ulong now = get_timer(0); | |
+ WATCHDOG_RESET(); | |
+ while (!tstc()) { | |
+ if (get_timer(now) > timeout) | |
+ break; | |
+ } | |
+ if (tstc()) { | |
+ *c = getc(); | |
+ return 1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+int serial_read(void *buf, size_t len) { | |
+ int ret; | |
+ for (size_t i = 0; i < len; i++) { | |
+ ret = getc_timeout(buf + i, SEND_CHAR_TIMEOUT); | |
+ if (!ret) { | |
+ return i; // timeout | |
+ } | |
+ } | |
+ return len; | |
+} | |
+ | |
+static int xymodem_send(const uint8_t *buf, size_t len, int protocol, int wait) | |
+{ | |
+ int ret; | |
+ uint8_t answer; | |
+ uint8_t eof = X_EOF; | |
+ struct xmodem_chunk chunk; | |
+ int skip_payload = 0; | |
+ | |
+ if (wait) { | |
+ //printf("Waiting for receiver ping ..."); | |
+ | |
+ do { | |
+ ret = getc_timeout(&answer, 10000); | |
+ if (!ret) { | |
+ //printf("timed out.\n"); | |
+ return -errno; | |
+ } | |
+ } while (answer != 'C'); | |
+ | |
+ //printf("done.\n"); | |
+ } | |
+ | |
+ //printf("Sending data"); | |
+ | |
+ if (protocol == PROTOCOL_YMODEM) { | |
+ char filename[] = "memory.bin"; | |
+ // write file name | |
+ strncpy((char *) chunk.payload, filename, sizeof(chunk.payload)); | |
+ // write file length | |
+ sprintf((char *) chunk.payload + strlen(filename) + 1, "%ul", len); | |
+ | |
+ chunk.block = 0; | |
+ skip_payload = 1; | |
+ } else { | |
+ chunk.block = 1; | |
+ } | |
+ | |
+ //chunk.start = X_STX; // 1024 byte chunks | |
+ chunk.start = X_SOH; // 128 byte chunks | |
+ | |
+ unsigned int errors = 0; | |
+ int crc16_set = 0; | |
+ while (len) { | |
+ size_t z = 0; | |
+ int next = 0; | |
+ char status; | |
+ | |
+ if (!skip_payload) { | |
+ z = min(len, sizeof(chunk.payload)); | |
+ memcpy(chunk.payload, buf, z); | |
+ memset(chunk.payload + z, 0xff, sizeof(chunk.payload) - z); | |
+ } else if (errors >= 10) { | |
+ printf("Too many errors\n"); | |
+ return -errno; | |
+ } | |
+ | |
+ chunk.crc = swap16(crc16(chunk.payload, sizeof(chunk.payload))); | |
+ chunk.block_neg = 0xff - chunk.block; | |
+ | |
+ ret = serial_write(&chunk, sizeof(chunk)); | |
+ if (ret != sizeof(chunk)) | |
+ return -errno; | |
+ | |
+ ret = serial_read(&answer, sizeof(answer)); | |
+ if (ret != sizeof(answer)) | |
+ return -errno; | |
+ switch (answer) { | |
+ case X_NAK: | |
+ status = 'N'; | |
+ errors++; | |
+ break; | |
+ case X_ACK: | |
+ status = '.'; | |
+ skip_payload = 0; | |
+ next = 1; | |
+ break; | |
+ default: | |
+ status = '?'; | |
+ errors++; | |
+ break; | |
+ } | |
+ | |
+ if (answer == X_ACK && !crc16_set) { | |
+ // get C (for CRC16 mode) right after ACK | |
+ getc_timeout(&answer, 2000); | |
+ crc16_set = 1; | |
+ } | |
+ | |
+ //printf("%c", status); | |
+ //fflush(stdout); | |
+ | |
+ if (next) { | |
+ chunk.block++; | |
+ len -= z; | |
+ buf += z; | |
+ } | |
+ } | |
+ | |
+ ret = serial_write(&eof, sizeof(eof)); | |
+ if (ret != sizeof(eof)) | |
+ return -errno; | |
+ | |
+ /* send EOTs again for YMODEM, if necessary */ | |
+ if (protocol == PROTOCOL_YMODEM) { | |
+ for (int i = 0; i < 10; i++) { | |
+ ret = serial_read(&answer, sizeof(answer)); | |
+ if (ret != sizeof(answer)) | |
+ return -errno; | |
+ | |
+ if (answer == X_ACK) { | |
+ getc_timeout(&answer, 2000); // 'C' | |
+ break; | |
+ } | |
+ | |
+ ret = serial_write(&eof, sizeof(eof)); | |
+ if (ret != sizeof(eof)) | |
+ return -errno; | |
+ } | |
+ /* send null block to signify no more files */ | |
+ memset(chunk.payload, 0, sizeof(chunk.payload)); | |
+ chunk.block = 0; | |
+ chunk.block_neg = 0xff; | |
+ chunk.crc = 0; | |
+ ret = serial_write(&chunk, sizeof(chunk)); | |
+ if (ret != sizeof(chunk)) | |
+ return -errno; | |
+ getc_timeout(&answer, 2000); // ACK | |
+ } | |
+ | |
+ //printf("done.\n"); | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+static int send_serial(ulong address, ulong count, int protocol) | |
+{ | |
+ return xymodem_send((const uint8_t *)address, count, protocol, 1); | |
+} | |
+ | |
+ | |
+int do_send_serial(struct cmd_tbl *cmdtp, int flag, int argc, | |
+ char *const argv[]) | |
+{ | |
+ ulong offset = 0; | |
+ ulong size = 0; | |
+ | |
+ /* need at least two arguments */ | |
+ if (argc < 2) | |
+ goto usage; | |
+ | |
+#ifdef CONFIG_SYS_SEND_BAUD_CHANGE | |
+ int save_baudrate, current_baudrate; | |
+ | |
+ save_baudrate = current_baudrate = gd->baudrate; | |
+#endif | |
+ | |
+ if (argc >= 2) { | |
+ offset = simple_strtoul(argv[1], NULL, 16); | |
+ } | |
+#ifdef CONFIG_SYS_SEND_BAUD_CHANGE | |
+ if (argc >= 3) { | |
+ size = simple_strtoul(argv[2], NULL, 16); | |
+ } | |
+ if (argc == 4) { | |
+ save_baudrate = (int)simple_strtoul(argv[3], NULL, 10); | |
+ | |
+ /* default to current baudrate */ | |
+ if (save_baudrate == 0) | |
+ save_baudrate = current_baudrate; | |
+ } | |
+ if (save_baudrate != current_baudrate) { | |
+ printf("## Switch baudrate to %d bps and press ENTER ...\n", | |
+ save_baudrate); | |
+ udelay(50000); | |
+ gd->baudrate = save_baudrate; | |
+ serial_setbrg(); | |
+ udelay(50000); | |
+ for (;;) { | |
+ if (getc() == '\r') | |
+ break; | |
+ } | |
+ } | |
+#else /* ! CONFIG_SYS_SEND_BAUD_CHANGE */ | |
+ if (argc == 3) { | |
+ size = simple_strtoul(argv[2], NULL, 16); | |
+ } | |
+#endif /* CONFIG_SYS_SEND_BAUD_CHANGE */ | |
+ | |
+ int protocol = strcmp(argv[0], "sendx") == 0 ? PROTOCOL_XMODEM : PROTOCOL_YMODEM; | |
+ | |
+ printf("## Ready to send binary...\n"); | |
+ if (send_serial(offset, size, protocol)) { | |
+ printf("## Binary send aborted\n"); | |
+ } else { | |
+ printf("## Binary send complete\n"); | |
+ } | |
+#ifdef CONFIG_SYS_SEND_BAUD_CHANGE | |
+ if (save_baudrate != current_baudrate) { | |
+ printf("## Switch baudrate to %d bps and press ESC ...\n", | |
+ (int)current_baudrate); | |
+ udelay(50000); | |
+ gd->baudrate = current_baudrate; | |
+ serial_setbrg(); | |
+ udelay(50000); | |
+ for (;;) { | |
+ if (getc() == 0x1B) /* ESC */ | |
+ break; | |
+ } | |
+ } | |
+#endif | |
+ return 0; | |
+ | |
+usage: | |
+ return CMD_RET_USAGE; | |
+} | |
+ | |
+ | |
+#if defined(CONFIG_CMD_SEND) | |
+U_BOOT_CMD( | |
+ sendx, 4, 0, do_send_serial, | |
+ "send binary over serial line (xmodem mode)", | |
+ "[ off ] [ size ] [ baud ]\n" | |
+ " - send binary over serial line" | |
+ " with offset 'off', size 'size' and baudrate 'baud'" | |
+); | |
+ | |
+U_BOOT_CMD( | |
+ sendy, 4, 0, do_send_serial, | |
+ "send binary over serial line (ymodem mode)", | |
+ "[ off ] [ size ] [ baud ]\n" | |
+ " - send binary over serial line" | |
+ " with offset 'off', size 'size' and baudrate 'baud'" | |
+); | |
+#endif /* CONFIG_CMD_SEND */ | |
\ No newline at end of file | |
diff --git a/cmd/Kconfig b/cmd/Kconfig | |
index 192b3b262f..831a1e481b 100644 | |
--- a/cmd/Kconfig | |
+++ b/cmd/Kconfig | |
@@ -1088,6 +1088,12 @@ config CMD_LOADS | |
help | |
Load an S-Record file over serial line | |
+config CMD_SEND | |
+ bool "sendb" | |
+ default y | |
+ help | |
+ Send a binary over serial line. | |
+ | |
config CMD_MMC | |
bool "mmc" | |
help | |
diff --git a/cmd/Makefile b/cmd/Makefile | |
index 974ad48b0a..e58288a863 100644 | |
--- a/cmd/Makefile | |
+++ b/cmd/Makefile | |
@@ -83,6 +83,7 @@ obj-$(CONFIG_LED_STATUS_CMD) += legacy_led.o | |
obj-$(CONFIG_CMD_LED) += led.o | |
obj-$(CONFIG_CMD_LICENSE) += license.o | |
obj-y += load.o | |
+obj-y += send.o | |
obj-$(CONFIG_CMD_LOG) += log.o | |
obj-$(CONFIG_ID_EEPROM) += mac.o | |
obj-$(CONFIG_CMD_MD5SUM) += md5sum.o |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment