emurom client program
authorJohn Tsiombikas <nuclear@member.fsf.org>
Tue, 13 Oct 2020 03:49:48 +0000 (06:49 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Tue, 13 Oct 2020 03:49:48 +0000 (06:49 +0300)
.gitignore
emurom/Makefile [new file with mode: 0644]
emurom/src/emurom.c [new file with mode: 0644]

index 2d76d46..3f1b0a9 100644 (file)
@@ -13,3 +13,4 @@
 *.net
 gerber
 fw/romdev
+emurom/emurom
diff --git a/emurom/Makefile b/emurom/Makefile
new file mode 100644 (file)
index 0000000..8a0e82f
--- /dev/null
@@ -0,0 +1,23 @@
+PREFIX ?= /usr/local
+
+src = $(wildcard src/*.c)
+obj = $(src:.c=.o)
+bin = emurom
+
+CFLAGS = -pedantic -Wall -g
+
+$(bin): $(obj)
+       $(CC) -o $@ $(obj) $(LDFLAGS)
+
+.PHONY: clean
+clean:
+       rm -f $(obj) $(bin)
+
+.PHONY: install
+install:
+       mkdir $(DESTDIR)$(PREFIX)/bin
+       cp $(bin) $(DESTDIR)$(PREFIX)/bin/$(bin)
+
+.PHONY: uninstall
+uninstall:
+       rm -f $(DESTDIR)$(PREFIX)/bin/$(bin)
diff --git a/emurom/src/emurom.c b/emurom/src/emurom.c
new file mode 100644 (file)
index 0000000..3f83126
--- /dev/null
@@ -0,0 +1,384 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+
+enum { FMT_BIN, FMT_IHEX, FMT_SREC };
+
+int send_bin(FILE *infile, int dev);
+int send_ihex(FILE *infile, int dev);
+int send_srec(FILE *infile, int dev);
+void print_usage(const char *argv0);
+
+FILE *infile;
+int sdev;
+int base_addr;
+
+int main(int argc, char **argv)
+{
+       int i;
+       char *endp;
+       const char *serdev = "/dev/ttyACM0";
+       const char *fname = 0;
+       struct termios term;
+       int (*send)(FILE*, int) = send_bin;
+
+       for(i=1; i<argc; i++) {
+               if(argv[i][0] == '-') {
+                       if(argv[i][2] == 0) {
+                               switch(argv[i][1]) {
+                               case 'd':
+                                       if(!argv[++i]) {
+                                               fprintf(stderr, "-d must be followed by a device file\n");
+                                               return 1;
+                                       }
+                                       serdev = argv[i];
+                                       break;
+
+                               case 'f':
+                                       if(strcmp(argv[++i], "bin") == 0) {
+                                               send = send_bin;
+                                       } else if(strcmp(argv[i], "ihex") == 0 || strcmp(argv[i], "hex") == 0) {
+                                               send = send_ihex;
+                                       } else if(strcmp(argv[i], "srec") == 0) {
+                                               send = send_srec;
+                                       } else {
+                                               fprintf(stderr, "invalid input format: %s\n", argv[i]);
+                                               return 1;
+                                       }
+                                       break;
+
+                               case 'a':
+                                       base_addr = strtol(argv[++i], &endp, 0);
+                                       if(endp == argv[i]) {
+                                               fprintf(stderr, "-a must be followed by a base address offset\n");
+                                               return 1;
+                                       }
+                                       break;
+
+                               case 'h':
+                                       print_usage(argv[0]);
+                                       return 0;
+
+                               default:
+                                       fprintf(stderr, "invalid option: %s\n", argv[i]);
+                                       print_usage(argv[0]);
+                                       return 1;
+                               }
+                       } else {
+                               fprintf(stderr, "invalid option: %s\n", argv[i]);
+                               print_usage(argv[0]);
+                               return 1;
+                       }
+               } else {
+                       if(fname) {
+                               fprintf(stderr, "unexpected argument: %s\n", argv[i]);
+                               print_usage(argv[0]);
+                               return 1;
+                       }
+                       fname = argv[i];
+               }
+       }
+
+       if(fname) {
+               if(!(infile = fopen(fname, "rb"))) {
+                       fprintf(stderr, "failed to open input file: %s: %s\n", fname, strerror(errno));
+                       return 1;
+               }
+       } else {
+               infile = stdin;
+       }
+
+       if((sdev = open(serdev, O_RDWR | O_NOCTTY)) == -1) {
+               fprintf(stderr, "failed to connect to devrom board (%s): %s\n", serdev, strerror(errno));
+               return 1;
+       }
+       tcgetattr(sdev, &term);
+
+       term.c_oflag = 0;
+       term.c_lflag = 0;
+       term.c_cc[VMIN] = 1;
+       term.c_cc[VTIME] = 0;
+
+       term.c_cflag = CLOCAL | CREAD | CS8 | HUPCL;
+       term.c_iflag = IGNBRK | IGNPAR;
+
+       cfsetispeed(&term, B38400);
+       cfsetospeed(&term, B38400);
+
+       if(tcsetattr(sdev, TCSANOW, &term) < 0) {
+               fprintf(stderr, "failed to set terminal attributes\n");
+               return 1;
+       }
+
+       if(send(infile, sdev) == -1) {
+               return 1;
+       }
+
+       fclose(infile);
+       return 0;
+}
+
+int read_line(int fd, char *buf, int bufsz)
+{
+       int i, num;
+       bufsz--;        /* leave space for the terminator */
+
+       while(bufsz > 0) {
+               if((num = read(fd, buf, bufsz)) <= 0) {
+                       break;
+               }
+
+               for(i=0; i<num; i++) {
+                       if(*buf == '\r' || *buf == '\n') {
+                               *buf = 0;
+                               return 0;
+                       }
+                       buf++;
+               }
+
+               bufsz -= num;
+               *buf = 0;
+       }
+
+       return -1;
+}
+
+int read_status(int dev)
+{
+       char buf[128];
+
+       if(read_line(dev, buf, sizeof buf) == -1) {
+               fprintf(stderr, "failed to read status\n");
+               return -1;
+       }
+       buf[127] = 0;
+
+       if(memcmp(buf, "ERR", 3) == 0) {
+               fprintf(stderr, "error:%s\n", buf + 3);
+               return -1;
+       }
+       return 0;
+}
+
+int cmd(int dev, const char *cmd)
+{
+       write(dev, cmd, strlen(cmd));
+       if(read_status(dev) != 0) {
+               return -1;
+       }
+       return 0;
+}
+
+int cmd_addr(int dev, int addr)
+{
+       char buf[32];
+       sprintf(buf, "a %d\n", addr);
+       return cmd(dev, buf);
+}
+
+int cmd_write(int dev, int val)
+{
+       char buf[32];
+
+       if(val < 0 || val >= 256) {
+               fprintf(stderr, "cmd_write: invalid byte value: %d\n", val);
+               return -1;
+       }
+
+       sprintf(buf, "w %d\n", val);
+       return cmd(dev, buf);
+}
+
+int send_bin(FILE *infile, int dev)
+{
+       int c, res = -1;
+       long count = 0, fsz = -1;
+
+       if(fseek(infile, 0, SEEK_END) != -1) {
+               fsz = ftell(infile);
+               rewind(infile);
+       }
+
+       if(cmd(dev, "p\n") == -1 || cmd_addr(dev, 0) == -1) {
+               return -1;
+       }
+
+       while((c = fgetc(infile)) != -1) {
+               if(cmd_write(dev, c) == -1) {
+                       goto end;
+               }
+
+               if(fsz > 0) {
+                       printf("\r%ld/%ld            ", ++count, fsz);
+                       fflush(stdout);
+               }
+       }
+       res = 0;
+
+end:
+       if(fsz > 0) putchar('\n');
+       cmd(dev, "b\n");
+       return res;
+}
+
+long hexval(const char *s, int digits)
+{
+       int i;
+       long val = 0;
+
+       for(i=0; i<digits; i++) {
+               if(!isxdigit(*s)) {
+                       return -1;
+               }
+               val <<= 4;
+
+               if(*s >= 'a') {
+                       val |= *s - 'a' + 10;
+               } else if(*s >= 'A') {
+                       val |= *s - 'A' + 10;
+               } else {
+                       val |= *s - '0';
+               }
+               s++;
+       }
+       return val;
+}
+
+const char *substr(const char *s, int len)
+{
+       char *tmp;
+       static char *buf;
+       static int buflen;
+
+       if(len > buflen) {
+               if(!(tmp = malloc(len + 1))) {
+                       perror("failed to allocate substring buffer");
+                       return 0;
+               }
+               free(buf);
+               buf = tmp;
+               buflen = len;
+       }
+       memcpy(buf, s, len);
+       buf[len] = 0;
+       return buf;
+}
+
+enum {
+       TYPE_DATA               = 0,
+       TYPE_EOF                = 1,
+       TYPE_EXT16              = 2,
+       TYPE_START16    = 3,
+       TYPE_EXT32              = 4,
+       TYPE_START32    = 5
+};
+
+int send_ihex(FILE *infile, int dev)
+{
+       char line[512], *ptr;
+       int res = -1, count, addr, type, val;
+       int cur_addr = -1, offs = 0;
+
+       if(cmd(dev, "p\n") == -1) {
+               return -1;
+       }
+
+       while(fgets(line, sizeof line, infile)) {
+               if(line[0] != ':') continue;
+
+               ptr = line + 1;
+
+               if((count = hexval(ptr, 2)) == -1) {
+                       fprintf(stderr, "ihex: invalid count field: %s\n", substr(ptr, 2));
+                       goto end;
+               }
+               ptr += 2;
+               if((addr = hexval(ptr, 4)) == -1) {
+                       fprintf(stderr, "ihex: invalid address field: %s\n", substr(ptr, 4));
+                       goto end;
+               }
+               ptr += 4;
+               if((type = hexval(ptr, 2)) == -1 || type > 5) {
+                       fprintf(stderr, "ihex: invalid type field: %s\n", substr(ptr, 2));
+                       goto end;
+               }
+               ptr += 2;
+
+               switch(type) {
+               case TYPE_DATA:
+                       addr += base_addr + offs;
+                       if(cur_addr != addr) {
+                               cur_addr = addr;
+                               if(cmd_addr(dev, addr) == -1) {
+                                       goto end;
+                               }
+                       }
+
+                       cur_addr += count;
+                       while(count-- > 0) {
+                               if((val = hexval(ptr, 2)) == -1) {
+                                       fprintf(stderr, "ihex: invalid value: %s\n", substr(ptr, 2));
+                                       goto end;
+                               }
+                               if(cmd_write(dev, val) == -1) {
+                                       goto end;
+                               }
+                               ptr += 2;
+                       }
+                       break;
+
+               case TYPE_EOF:
+                       res = 0;
+                       goto end;
+
+               case TYPE_EXT16:
+                       if((val = hexval(ptr, 4)) == -1) {
+                               fprintf(stderr, "ihex: invalid extended segment address: %s\n", substr(ptr, 4));
+                               goto end;
+                       }
+                       offs = val << 4;
+                       ptr += 4;
+                       break;
+
+               case TYPE_EXT32:
+                       if((val = hexval(ptr, 8)) == -1) {
+                               fprintf(stderr, "ihex: invalid extended linear address: %s\n", substr(ptr, 8));
+                               goto end;
+                       }
+                       offs = val;
+                       ptr += 8;
+                       break;
+
+               default:
+                       break;
+               }
+
+       }
+       res = 0;
+
+end:
+       cmd(dev, "b\n");
+       return res;
+}
+
+int send_srec(FILE *infile, int dev)
+{
+       fprintf(stderr, "TODO: SREC format not implemented yet\n");
+       return -1;
+}
+
+void print_usage(const char *argv0)
+{
+       printf("Usage: %s [options] [input file]\n", argv0);
+       printf("If no input file is specified, defaults to reading standard input\n");
+       printf("Options:\n");
+       printf("  -d <device file> devrom board USB serial port (default: /dev/ttyACM0)\n");
+       printf("  -f <input format> format of the input file [bin/ihex/srec] (default: bin)\n");
+       printf("  -a <base address> address offset (default: 0)\n");
+       printf("  -h print usage and exit\n");
+}