summaryrefslogtreecommitdiff
path: root/zup.c
diff options
context:
space:
mode:
authorThomas Albers <thomas@thomaslabs.org>2023-08-06 10:01:18 +0200
committerThomas Albers <thomas@thomaslabs.org>2023-08-06 10:01:18 +0200
commitb50e1f74202e18ea10f1f2cd46e9e02a9d4f0e65 (patch)
tree3b408615595bf82c2ac4f6ec62009ab2f05a6bd5 /zup.c
parent56157d2cc9385e5df65ba310abef2873429c8af5 (diff)
Rewrite for new serial protocol
Diffstat (limited to 'zup.c')
-rw-r--r--zup.c244
1 files changed, 244 insertions, 0 deletions
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 <zup.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <getopt.h>
+#include <unistd.h>
+
+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: <https://thomaslabs.org>";
+ 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, &param);
+ verbose = param.verbose;
+
+ int fd = open_tty(param.port, param.baud);
+
+ if (ind == argc || param.repl)
+ repl(fd, &param);
+ else
+ suc = run_commands(fd, &param, argc - ind, &argv[ind]);
+
+ close(fd);
+ exit(EXIT_SUCCESS);
+}