2 * Tool for Saturn USB DataLink device
3 * Copyright (c) 2014 notaz
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 #include <sys/types.h>
34 #include <sys/ioctl.h>
37 // ugh.. avoid conflicting winsize/termio from sys/ioctl.h
38 #define winsize winsize_
39 #define termio termio_
40 #include <linux/termios.h>
42 #define BAUD_RATE 375000
43 #define MAX_DATA_SIZE 191
45 static int setup(int fd)
51 memset(&tty, 0, sizeof(tty));
53 //ret = tcgetattr(fd, &tty);
54 ret = ioctl(fd, TCGETS2, &tty);
60 tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
61 | INLCR | IGNCR | ICRNL | IXON);
62 tty.c_oflag &= ~OPOST;
63 tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
64 tty.c_cflag &= ~(CSIZE | PARENB);
65 tty.c_cflag |= CS8 | CSTOPB | CREAD;
66 tty.c_cflag &= ~CBAUD;
67 tty.c_cflag |= BOTHER;
69 tty.c_ospeed = BAUD_RATE;
72 tty.c_cc[VTIME] = 0; // read timeout deciseconds
74 //ret = tcsetattr(fd, TCSANOW, &tty);
75 ret = ioctl(fd, TCSETS2, &tty);
81 memset(&tty, 0, sizeof(tty));
82 ret = ioctl(fd, TCGETS2, &tty);
88 if (tty.c_ispeed != BAUD_RATE || tty.c_ospeed != BAUD_RATE) {
89 fprintf(stderr, "warning: could not set %d baud, got:\n",
91 fprintf(stderr, "c_ispeed: %d\n", tty.c_ispeed);
92 fprintf(stderr, "c_ospeed: %d\n", tty.c_ospeed);
93 fprintf(stderr, "c_cflag: 0%o\n", tty.c_cflag);
99 static void dump_pkt(const uint8_t *buf, size_t size, const char *msg)
105 for (i = 0; i < size; ) {
108 printf(" %02x", buf[i]);
117 static uint8_t sum(const uint8_t *buf, size_t size)
122 for (i = 0; i < size; i++)
128 static int send_pkt(int fd, uint8_t cmd, uint32_t addr, uint8_t len,
129 const void *data, size_t data_size)
135 pkt[1] = 9 - 2 + data_size;
143 memcpy(&pkt[8], data, data_size);
144 pkt[8 + data_size] = sum(&pkt[1], 8 + data_size - 1);
146 // dump_pkt(pkt, 9 + data_size, "send:");
148 ret = write(fd, pkt, 9 + data_size);
149 if (ret != 9 + data_size) {
150 fprintf(stderr, "write: %d/%zd, cmd %02x, addr %08x: ",
151 ret, 9 + data_size, cmd, addr);
153 dump_pkt(pkt, 9 + data_size, "");
160 static int recv_pkt(int fd, uint8_t cmd, uint32_t addr,
161 void *data, size_t data_size)
163 size_t size = 9 + data_size;
169 for (got = 0; got < size; ) {
170 ret = read(fd, pkt + got, size);
172 fprintf(stderr, "read: %d, %zd/%zd, "
173 "cmd %02x, addr %08x: ",
174 ret, got, size, cmd, addr);
177 dump_pkt(pkt, got, "received data:");
183 csum = sum(&pkt[1], size - 2);
184 if (csum != pkt[size - 1]) {
185 fprintf(stderr, "read: %zd, cmd %02x, addr %08x: "
186 "checksum incorrect, expected %02x\n",
187 got, cmd, addr, csum);
188 dump_pkt(pkt, got, "");
192 if (pkt[0] != 0xa5 || pkt[1] != size - 2 || pkt[2] != 0xff) {
193 fprintf(stderr, "read: %zd, cmd %02x, addr %08x: "
194 "unexpected response header\n",
196 dump_pkt(pkt, got, "");
201 memcpy(data, &pkt[8], data_size);
203 // dump_pkt(pkt, got, "recv:");
208 static int do_rx(int fd, uint32_t addr, void *buf, size_t size)
214 /* must use at least 2 packets */
215 if (size <= MAX_DATA_SIZE)
220 ret = send_pkt(fd, 0x01, addr, len, NULL, 0);
224 ret = recv_pkt(fd, 0x01, addr, cbuf, len);
233 uint8_t cmd = size > MAX_DATA_SIZE ? 0x11 : 0x21;
236 if (len > MAX_DATA_SIZE)
239 ret = send_pkt(fd, cmd, addr, len, NULL, 0);
243 ret = recv_pkt(fd, 0x01, addr, cbuf, len);
255 static int do_tx(int fd, uint32_t addr, void *buf, size_t size, int exe)
257 size_t size_last = size;
258 uint32_t addr_last = addr;
264 if (size > MAX_DATA_SIZE) {
265 /* skip first packet, we'll send it last */
266 size_last = MAX_DATA_SIZE;
273 if (len > MAX_DATA_SIZE)
276 ret = send_pkt(fd, cmd, addr, len, cbuf, len);
280 ret = recv_pkt(fd, cmd, addr, NULL, 0);
293 ret = send_pkt(fd, cmd, addr_last, size_last, buf, size_last);
297 ret = recv_pkt(fd, cmd, addr_last, NULL, 0);
304 static void usage(const char *argv0)
307 "%s [dev <device>] command(s)\n"
309 " rx <addr> <size> <outfile>\n"
310 " tx <addr> <size> <infile> [x]\n"
311 " <outfile> can be \"/hd/\" for a hexdump\n"
312 " x instructs to execute uploaded data\n"
313 " for tx, size 0 asks to use filesize\n", argv0);
317 static void invarg(int argc, char *argv[], int arg)
320 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
322 fprintf(stderr, "missing required argument %d\n", arg);
326 int main(int argc, char *argv[])
328 const char *portname = "/dev/ttyUSB0";
340 if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
343 if (!strcmp(argv[arg], "dev")) {
345 if (argv[arg] != NULL)
346 portname = argv[arg];
348 invarg(argc, argv, arg);
352 fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
354 fprintf(stderr, "open %s: ", portname);
365 if (!strcmp(argv[arg], "rx") ||
366 !strcmp(argv[arg], "tx"))
368 int tx = !strcmp(argv[arg], "tx");
372 invarg(argc, argv, argc);
375 addr = strtoul(argv[++arg], &endp, 0);
376 if (endp == NULL || *endp != 0)
377 invarg(argc, argv, arg);
380 size = strtoul(argv[++arg], &endp, 0);
381 if (endp == NULL || *endp != 0)
382 invarg(argc, argv, arg);
385 if (tx || strcmp(fname, "/hd/") != 0) {
386 f = fopen(fname, tx ? "rb" : "wb");
388 fprintf(stderr, "open %s: \n",
395 if (tx && size == 0) {
396 ret = fstat(fileno(f), &st);
398 fprintf(stderr, "fstat %s: \n",
407 fprintf(stderr, "size is 0\n");
411 buf = realloc(buf, size);
413 fprintf(stderr, "OOM\n");
418 ret = fread(buf, 1, size, f);
420 fprintf(stderr, "read %s: \n",
427 && !strcmp(argv[arg + 1], "x"))
433 ret = do_tx(fd, addr, buf, size, exe);
438 ret = do_rx(fd, addr, buf, size);
443 ret = fwrite(buf, 1, size, f);
453 dump_pkt(buf, size, "");
464 invarg(argc, argv, arg);