Skip to content

Instantly share code, notes, and snippets.

@akarpenko
Last active June 18, 2020 03:52
Show Gist options
  • Save akarpenko/173aaa09604eed6d80020e0b53dfe350 to your computer and use it in GitHub Desktop.
Save akarpenko/173aaa09604eed6d80020e0b53dfe350 to your computer and use it in GitHub Desktop.
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