From: notaz Date: Sun, 7 Sep 2014 18:07:20 +0000 (+0300) Subject: initial commit X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=01698af38065bd6602995bff9e8823d52a7847c3;p=saturn.git initial commit download and upload works --- 01698af38065bd6602995bff9e8823d52a7847c3 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..60c8312 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +CFLAGS += -Wall -ggdb +ifndef DEBUG +CFLAGS += -O2 +endif + +TARGET = datalink + +all: $(TARGET) + +clean: + $(RM) $(TARGET) diff --git a/datalink.c b/datalink.c new file mode 100644 index 0000000..94e2c6e --- /dev/null +++ b/datalink.c @@ -0,0 +1,469 @@ +/* + * Tool for Saturn USB DataLink device + * Copyright (c) 2014 notaz + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ugh.. avoid conflicting winsize/termio from sys/ioctl.h +#define winsize winsize_ +#define termio termio_ +#include + +#define BAUD_RATE 375000 +#define MAX_DATA_SIZE 191 + +static int setup(int fd) +{ + struct termios2 tty; + //struct termios tty; + int ret; + + memset(&tty, 0, sizeof(tty)); + + //ret = tcgetattr(fd, &tty); + ret = ioctl(fd, TCGETS2, &tty); + if (ret != 0) { + perror("TCGETS2"); + return ret; + } + + tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP + | INLCR | IGNCR | ICRNL | IXON); + tty.c_oflag &= ~OPOST; + tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + tty.c_cflag &= ~(CSIZE | PARENB); + tty.c_cflag |= CS8 | CSTOPB | CREAD; + tty.c_cflag &= ~CBAUD; + tty.c_cflag |= BOTHER; + tty.c_ispeed = + tty.c_ospeed = BAUD_RATE; + + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; // read timeout deciseconds + + //ret = tcsetattr(fd, TCSANOW, &tty); + ret = ioctl(fd, TCSETS2, &tty); + if (ret != 0) { + perror("TCSETS2"); + return ret; + } + + memset(&tty, 0, sizeof(tty)); + ret = ioctl(fd, TCGETS2, &tty); + if (ret != 0) { + perror("TCGETS2"); + return ret; + } + + if (tty.c_ispeed != BAUD_RATE || tty.c_ospeed != BAUD_RATE) { + fprintf(stderr, "warning: could not set %d baud, got:\n", + BAUD_RATE); + fprintf(stderr, "c_ispeed: %d\n", tty.c_ispeed); + fprintf(stderr, "c_ospeed: %d\n", tty.c_ospeed); + fprintf(stderr, "c_cflag: 0%o\n", tty.c_cflag); + } + + return 0; +} + +static void dump_pkt(const uint8_t *buf, size_t size, const char *msg) +{ + size_t i; + + printf("%s\n", msg); + + for (i = 0; i < size; ) { + printf("%02zx:", i); + while (i < size) { + printf(" %02x", buf[i]); + i++; + if ((i & 15) == 0) + break; + } + printf("\n"); + } +} + +static uint8_t sum(const uint8_t *buf, size_t size) +{ + uint8_t ret = 0; + size_t i; + + for (i = 0; i < size; i++) + ret += buf[i]; + + return ret; +} + +static int send_pkt(int fd, uint8_t cmd, uint32_t addr, uint8_t len, + const void *data, size_t data_size) +{ + uint8_t pkt[200]; + int ret; + + pkt[0] = 0x5a; + pkt[1] = 9 - 2 + data_size; + pkt[2] = cmd; + pkt[3] = addr >> 24; + pkt[4] = addr >> 16; + pkt[5] = addr >> 8; + pkt[6] = addr; + pkt[7] = len; + if (data_size > 0) + memcpy(&pkt[8], data, data_size); + pkt[8 + data_size] = sum(&pkt[1], 8 + data_size - 1); + + // dump_pkt(pkt, 9 + data_size, "send:"); + + ret = write(fd, pkt, 9 + data_size); + if (ret != 9 + data_size) { + fprintf(stderr, "write: %d/%zd, cmd %02x, addr %08x: ", + ret, 9 + data_size, cmd, addr); + perror(""); + dump_pkt(pkt, 9 + data_size, ""); + return -1; + } + + return 0; +} + +static int recv_pkt(int fd, uint8_t cmd, uint32_t addr, + void *data, size_t data_size) +{ + size_t size = 9 + data_size; + size_t got; + uint8_t pkt[200]; + uint8_t csum; + int ret; + + for (got = 0; got < size; ) { + ret = read(fd, pkt + got, size); + if (ret <= 0) { + fprintf(stderr, "read: %d, %zd/%zd, " + "cmd %02x, addr %08x: ", + ret, got, size, cmd, addr); + perror(""); + if (got > 0) + dump_pkt(pkt, got, "received data:"); + return -1; + } + got += ret; + } + + csum = sum(&pkt[1], size - 2); + if (csum != pkt[size - 1]) { + fprintf(stderr, "read: %zd, cmd %02x, addr %08x: " + "checksum incorrect, expected %02x\n", + got, cmd, addr, csum); + dump_pkt(pkt, got, ""); + return -2; + } + + if (pkt[0] != 0xa5 || pkt[1] != size - 2 || pkt[2] != 0xff) { + fprintf(stderr, "read: %zd, cmd %02x, addr %08x: " + "unexpected response header\n", + got, cmd, addr); + dump_pkt(pkt, got, ""); + return -2; + } + + if (data_size != 0) + memcpy(data, &pkt[8], data_size); + + // dump_pkt(pkt, got, "recv:"); + + return 0; +} + +static int do_rx(int fd, uint32_t addr, void *buf, size_t size) +{ + char *cbuf = buf; + int ret; + int len; + + /* must use at least 2 packets */ + if (size <= MAX_DATA_SIZE) + len = size / 2; + else + len = MAX_DATA_SIZE; + + ret = send_pkt(fd, 0x01, addr, len, NULL, 0); + if (ret != 0) + return ret; + + ret = recv_pkt(fd, 0x01, addr, cbuf, len); + if (ret != 0) + return ret; + + addr += len; + cbuf += len; + size -= len; + + while (size > 0) { + uint8_t cmd = size > MAX_DATA_SIZE ? 0x11 : 0x21; + + len = size; + if (len > MAX_DATA_SIZE) + len = MAX_DATA_SIZE; + + ret = send_pkt(fd, cmd, addr, len, NULL, 0); + if (ret != 0) + return ret; + + ret = recv_pkt(fd, 0x01, addr, cbuf, len); + if (ret != 0) + return ret; + + addr += len; + cbuf += len; + size -= len; + } + + return 0; +} + +static int do_tx(int fd, uint32_t addr, void *buf, size_t size, int exe) +{ + size_t size_last = size; + uint32_t addr_last = addr; + char *cbuf = buf; + uint8_t cmd = 0x09; + int ret; + int len; + + if (size > MAX_DATA_SIZE) { + /* skip first packet, we'll send it last */ + size_last = MAX_DATA_SIZE; + addr += size_last; + cbuf += size_last; + size -= size_last; + + while (size > 0) { + len = size; + if (len > MAX_DATA_SIZE) + len = MAX_DATA_SIZE; + + ret = send_pkt(fd, cmd, addr, len, cbuf, len); + if (ret != 0) + return ret; + + ret = recv_pkt(fd, cmd, addr, NULL, 0); + if (ret != 0) + return ret; + + addr += len; + cbuf += len; + size -= len; + } + } + + if (exe) + cmd = 0x19; + + ret = send_pkt(fd, cmd, addr_last, size_last, buf, size_last); + if (ret != 0) + return ret; + + ret = recv_pkt(fd, cmd, addr_last, NULL, 0); + if (ret != 0) + return ret; + + return 0; +} + +static void usage(const char *argv0) +{ + printf("usage:\n" + "%s [dev ] command(s)\n" + "commands:\n" + " rx \n" + " tx [x]\n" + " can be \"/hd/\" for a hexdump\n" + " x instructs to execute uploaded data\n" + " for tx, size 0 asks to use filesize\n", argv0); + exit(1); +} + +static void invarg(int argc, char *argv[], int arg) +{ + if (arg < argc) + fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]); + else + fprintf(stderr, "missing required argument %d\n", arg); + exit(1); +} + +int main(int argc, char *argv[]) +{ + const char *portname = "/dev/ttyUSB0"; + const char *fname; + struct stat st; + void *buf = NULL; + FILE *f = NULL; + uint32_t addr; + size_t size; + char *endp; + int arg = 1; + int ret; + int fd; + + if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) + usage(argv[0]); + + if (!strcmp(argv[arg], "dev")) { + arg++; + if (argv[arg] != NULL) + portname = argv[arg]; + else + invarg(argc, argv, arg); + arg++; + } + + fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC); + if (fd < 0) { + fprintf(stderr, "open %s: ", portname); + perror(""); + return 1; + } + + ret = setup(fd); + if (ret) + return ret; + + while (arg < argc) + { + if (!strcmp(argv[arg], "rx") || + !strcmp(argv[arg], "tx")) + { + int tx = !strcmp(argv[arg], "tx"); + int exe = 0; + + if (argc - arg < 4) + invarg(argc, argv, argc); + + endp = NULL; + addr = strtoul(argv[++arg], &endp, 0); + if (endp == NULL || *endp != 0) + invarg(argc, argv, arg); + + endp = NULL; + size = strtoul(argv[++arg], &endp, 0); + if (endp == NULL || *endp != 0) + invarg(argc, argv, arg); + + fname = argv[++arg]; + if (tx || strcmp(fname, "/hd/") != 0) { + f = fopen(fname, tx ? "rb" : "wb"); + if (f == NULL) { + fprintf(stderr, "open %s: \n", + fname); + perror(""); + return 1; + } + } + + if (tx && size == 0) { + ret = fstat(fileno(f), &st); + if (ret != 0) { + fprintf(stderr, "fstat %s: \n", + fname); + perror("stat"); + return 1; + } + size = st.st_size; + } + + if (size == 0) { + fprintf(stderr, "size is 0\n"); + return 1; + } + + buf = realloc(buf, size); + if (buf == NULL) { + fprintf(stderr, "OOM\n"); + return 1; + } + + if (tx) { + ret = fread(buf, 1, size, f); + if (ret != size) { + fprintf(stderr, "read %s: \n", + fname); + perror(""); + return 1; + } + + if (arg + 1 < argc + && !strcmp(argv[arg + 1], "x")) + { + exe = 1; + arg++; + } + + ret = do_tx(fd, addr, buf, size, exe); + if (ret != 0) + return ret; + } + else { + ret = do_rx(fd, addr, buf, size); + if (ret != 0) + return ret; + + if (f != NULL) { + ret = fwrite(buf, 1, size, f); + if (ret != size) { + fprintf(stderr, + "write %s: \n", + fname); + perror(""); + return 1; + } + } + else { + dump_pkt(buf, size, ""); + } + } + + if (f != NULL) { + fclose(f); + f = NULL; + } + arg++; + } + else { + invarg(argc, argv, arg); + } + } + + return 0; +}