From b50e1f74202e18ea10f1f2cd46e9e02a9d4f0e65 Mon Sep 17 00:00:00 2001 From: Thomas Albers Date: Sun, 6 Aug 2023 10:01:18 +0200 Subject: Rewrite for new serial protocol --- zup.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 zup.c (limited to 'zup.c') diff --git a/zup.c b/zup.c new file mode 100644 index 0000000..84d1cc1 --- /dev/null +++ b/zup.c @@ -0,0 +1,244 @@ +#include + +#include +#include +#include +#include + +#include +#include + +static void +help() +{ + const char *msg = + "Usage: zup [options] command...\n" + "Send commands to a computer running zbootloader. If no commands are\n" + "provided jump into REPL instead.\n" + "\n" + "Options:\n" + " -b, --baud=BAUD use BAUD as baud rate for serial port\n" + " -v, --verbose \n" + " -V, --version \n" + " -H, --human-readable \n" + " -r, --raw \n" + " -R, --repl ignore commands and go into REPL instead\n" + " -p, --port=FILE use FILE for serial comunication\n" + " -h, --help display this text and exit\n" + "\n" + "Available commands:\n" + " r:BANK:ADDR:LEN:FILE read LEN bytes from BANK and ADDR into FILE\n" + " w:BANK:ADDR:FILE write FILE into BANK and ADDR\n" + " b:ADDR exit zbootloader and boot from ADDR\n" + "\n" + "Report bugs to: thomas@thomaslabs.org\n" + "Home page: "; + puts(msg); + exit(EXIT_SUCCESS); +} + +static void +version() +{ + const char *fmt = + "zup %s\n" + "Copyright (C) 2022 Thomas Albers Raviola\n\n"; + printf(fmt, VERSION); + exit(EXIT_SUCCESS); +} + +static speed_t +parse_baud(const char *str) +{ + struct baud_pair { + const char *b; + speed_t c; + }; + + // Conversion table for baud rates + const struct baud_pair baud_rates[] = { + {"50", B50} + {"75", B75} + {"110", B110} + {"134", B134} + {"150", B150} + {"200", B200} + {"300", B300} + {"600", B600} + {"1200", B1200} + {"1800", B1800} + {"2400", B2400} + {"4800", B4800} + {"9600", B9600} + {"19200", B19200} + {"38400", B38400} + {"57600", B57600} + {"115200", B115200} + {"230400", B230400} + {"460800", B460800} + {"500000", B500000} + {"576000", B576000} + {"921600", B921600} + {"1000000", B1000000} + {"1152000", B1152000} + {"1500000", B1500000} + {"2000000", B2000000} + }; + + for (int i = 0; i < LEN(baud_rates); i++) { + if (!strcmp(baud_rates[i].b, str)) + return baud_rates[i].c; + } + + return 0; +} + + +static int +parse_options(int argc, char *const argv[], struct param *param) +{ + const char *sopts = "b:p:hHVRrv"; + const struct option lopts[] = { + { "baud", required_argument, 0, 'b'}, + { "verbose", no_argument, 0, 'v'}, + { "version", no_argument, 0, 'V'}, + { "raw", no_argument, 0, 'r'}, + { "repl", no_argument, 0, 'R'}, + { "port", required_argument, 0, 'p'}, + { "human-readable", no_argument, 0, 'H'}, + { "help", no_argument, 0, 'h'}, + { 0, 0, 0, 0 } + }; + + int c; + int i = 0; + + while ((c = getopt_long(argc, argv, sopts, lopts, &i)) != -1) { + switch (c) { + case 'b': + if (!(param->baud = parse_baud(optarg))) { + fprintf(stderr, "Error: Invalid baud rate '%s'\n", optarg); + exit(EXIT_FAILURE); + } + break; + + case 'v': + param->verbose = 1; + break; + + case 'p': + strncpy(param->port, optarg, LEN(param->port)); + break; + + case 'R': + param->repl = 1; + break; + + case 'H': + param->human_readable = 1; + break; + + case 'r': + param->human_readable = 0; + break; + + case 'V': + version(); + break; + + case 'h': + case '?': + help(); + break; + + default: + break; + } + } + + return optind; +} + +void +print_error(int error) +{ + int i = -error - 1; + const char *msgs[] = { + "ERR_TIMEOUT", + "ERR_READ", + "ERR_WRITE", + "ERR_NACK", + "ERR_ARGS" + }; + + if (0 <= i && i < LEN(msgs)) + fprintf(stderr, "%s\n", msgs[i]); + else + fprintf(stderr, "Unknown error. Code %d\n", error); +} + +void +hexdump(size_t start_address, size_t len, const uint8_t *buf) +{ + long offset = start_address - (start_address & ~0xF); + printf(" "); + for (int i = 0; i < 16; ++i) + printf(" %01X%01X", i, i); + puts("\n------------------------------------------------------" + "|-----------------|"); + for (long i = 0; i < (len + 15) / 16; ++i) { + printf("%04X:", (start_address & ~0xF) + 16 * i); + for (long j = 0; j < 16; ++j) { + long k = 16 * i + j - offset; + if (k < 0 || k >= len) + printf(" "); + else + printf(" %02X", buf[k]); + } + printf(" | "); + for (long j = 0; j < min(16, len - 16 * i); ++j) { + long k = 16 * i + j - offset; + if (k < 0 || k >= len) { + putchar(' '); + } else { + int c = buf[16 * i + j - offset]; + putchar(isprint(c) ? c : '.'); + } + } + putchar('|'); + putchar('\n'); + } +} + +int verbose = 0; + +// TODO: Support stdin/stdout + +int +main(int argc, char *argv[]) +{ + int suc = EXIT_SUCCESS; + + // Init defaults + struct param param = { + .port = "/dev/ttyS1", + .baud = B9600, + .verbose = 0, + .human_readable = 0, + .repl = 0 + + }; + + int ind = parse_options(argc, argv, ¶m); + verbose = param.verbose; + + int fd = open_tty(param.port, param.baud); + + if (ind == argc || param.repl) + repl(fd, ¶m); + else + suc = run_commands(fd, ¶m, argc - ind, &argv[ind]); + + close(fd); + exit(EXIT_SUCCESS); +} -- cgit v1.2.3