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
44 #define PROGRESS_STEP (4 * 1024)
46 static int setup(int fd)
52 memset(&tty, 0, sizeof(tty));
54 //ret = tcgetattr(fd, &tty);
55 ret = ioctl(fd, TCGETS2, &tty);
61 tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
62 | INLCR | IGNCR | ICRNL | IXON);
63 tty.c_oflag &= ~OPOST;
64 tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
65 tty.c_cflag &= ~(CSIZE | PARENB);
66 tty.c_cflag |= CS8 | CSTOPB | CREAD;
67 tty.c_cflag &= ~CBAUD;
68 tty.c_cflag |= BOTHER;
70 tty.c_ospeed = BAUD_RATE;
73 tty.c_cc[VTIME] = 0; // read timeout deciseconds
75 //ret = tcsetattr(fd, TCSANOW, &tty);
76 ret = ioctl(fd, TCSETS2, &tty);
82 memset(&tty, 0, sizeof(tty));
83 ret = ioctl(fd, TCGETS2, &tty);
89 if (tty.c_ispeed != BAUD_RATE || tty.c_ospeed != BAUD_RATE) {
90 fprintf(stderr, "warning: could not set %d baud, got:\n",
92 fprintf(stderr, "c_ispeed: %d\n", tty.c_ispeed);
93 fprintf(stderr, "c_ospeed: %d\n", tty.c_ospeed);
94 fprintf(stderr, "c_cflag: 0%o\n", tty.c_cflag);
100 static void print_progress(int done, int total)
104 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
105 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); /* 20 */
106 printf("\b\b\b\b\b\b");
107 printf("%06x/%06x |", done, total);
110 for (i = step; i <= total; i += step)
111 printf("%c", done >= i ? '=' : '-');
112 printf("| %3d%%", done * 100 / total);
119 static void dump_pkt(const uint8_t *buf, size_t size, const char *msg)
125 for (i = 0; i < size; ) {
128 printf(" %02x", buf[i]);
137 static uint8_t sum(const uint8_t *buf, size_t size)
142 for (i = 0; i < size; i++)
148 static int send_pkt(int fd, uint8_t cmd, uint32_t addr, uint8_t len,
149 const void *data, size_t data_size)
155 pkt[1] = 9 - 2 + data_size;
163 memcpy(&pkt[8], data, data_size);
164 pkt[8 + data_size] = sum(&pkt[1], 8 + data_size - 1);
166 // dump_pkt(pkt, 9 + data_size, "send:");
168 ret = write(fd, pkt, 9 + data_size);
169 if (ret != 9 + data_size) {
170 fprintf(stderr, "write: %d/%zd, cmd %02x, addr %08x: ",
171 ret, 9 + data_size, cmd, addr);
173 dump_pkt(pkt, 9 + data_size, "");
180 static int recv_pkt(int fd, uint8_t cmd, uint32_t addr,
181 void *data, size_t data_size)
183 size_t size = 9 + data_size;
189 for (got = 0; got < size; ) {
190 ret = read(fd, pkt + got, size);
192 fprintf(stderr, "read: %d, %zd/%zd, "
193 "cmd %02x, addr %08x: ",
194 ret, got, size, cmd, addr);
197 dump_pkt(pkt, got, "received data:");
203 csum = sum(&pkt[1], size - 2);
204 if (csum != pkt[size - 1]) {
205 fprintf(stderr, "read: %zd, cmd %02x, addr %08x: "
206 "checksum incorrect, expected %02x\n",
207 got, cmd, addr, csum);
208 dump_pkt(pkt, got, "");
212 if (pkt[0] != 0xa5 || pkt[1] != size - 2 || pkt[2] != 0xff) {
213 fprintf(stderr, "read: %zd, cmd %02x, addr %08x: "
214 "unexpected response header\n",
216 dump_pkt(pkt, got, "");
221 memcpy(data, &pkt[8], data_size);
223 // dump_pkt(pkt, got, "recv:");
228 static int do_rx(int fd, uint32_t addr, void *buf, size_t size)
230 size_t size_total = size;
231 int progress_bytes = 0;
236 /* must use at least 2 packets */
237 if (size <= MAX_DATA_SIZE)
242 ret = send_pkt(fd, 0x01, addr, len, NULL, 0);
246 ret = recv_pkt(fd, 0x01, addr, cbuf, len);
255 uint8_t cmd = size > MAX_DATA_SIZE ? 0x11 : 0x21;
258 if (len > MAX_DATA_SIZE)
261 ret = send_pkt(fd, cmd, addr, len, NULL, 0);
265 ret = recv_pkt(fd, 0x01, addr, cbuf, len);
273 progress_bytes -= len;
274 if (progress_bytes <= 0 || size == 0) {
275 print_progress(size_total - size, size_total);
276 progress_bytes = PROGRESS_STEP;
283 static int do_tx(int fd, uint32_t addr, void *buf, size_t size, int exe)
285 int progress_bytes = 0;
286 size_t size_total = size;
287 size_t size_last = size;
288 uint32_t addr_last = addr;
294 if (size > MAX_DATA_SIZE) {
295 /* skip first packet, we'll send it last */
296 size_last = MAX_DATA_SIZE;
303 if (len > MAX_DATA_SIZE)
306 ret = send_pkt(fd, cmd, addr, len, cbuf, len);
310 ret = recv_pkt(fd, cmd, addr, NULL, 0);
318 progress_bytes -= len;
319 if (progress_bytes <= 0) {
320 print_progress(size_total - size - size_last,
322 progress_bytes = PROGRESS_STEP;
330 ret = send_pkt(fd, cmd, addr_last, size_last, buf, size_last);
334 ret = recv_pkt(fd, cmd, addr_last, NULL, 0);
338 print_progress(size_total, size_total);
343 static void usage(const char *argv0)
346 "%s [dev <device>] command(s)\n"
348 " rx <addr> <size> <outfile>\n"
349 " tx <addr> <size> <infile> [x]\n"
350 " <outfile> can be \"/hd/\" for a hexdump\n"
351 " x instructs to execute uploaded data\n"
352 " for tx, size 0 asks to use filesize\n\n"
353 "useful regions (addr size):\n"
354 " 0x00000000 0x080000 - BIOS\n"
355 " 0x00180000 0x010000 - Backup-RAM\n"
356 " 0x00200000 0x100000 - Work-RAM-L\n"
357 " 0x05a00000 0x080000 - 68000-RAM\n"
358 " 0x05c00000 0x080000 - VDP1-VRAM\n"
359 " 0x05c80000 0x040000 - VDP1-FB\n"
360 " 0x05e00000 0x080000 - VDP2-VRAM\n"
361 " 0x05f00000 0x001000 - VDP2-CRAM\n"
362 " 0x06000000 0x100000 - Work-RAM-H\n"
367 static void invarg(int argc, char *argv[], int arg)
370 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
372 fprintf(stderr, "missing required argument %d\n", arg);
376 int main(int argc, char *argv[])
378 const char *portname = "/dev/ttyUSB0";
390 if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
393 if (!strcmp(argv[arg], "dev")) {
395 if (argv[arg] != NULL)
396 portname = argv[arg];
398 invarg(argc, argv, arg);
402 fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
404 fprintf(stderr, "open %s: ", portname);
415 if (!strcmp(argv[arg], "rx") ||
416 !strcmp(argv[arg], "tx"))
418 int tx = !strcmp(argv[arg], "tx");
422 invarg(argc, argv, argc);
425 addr = strtoul(argv[++arg], &endp, 0);
426 if (endp == NULL || *endp != 0)
427 invarg(argc, argv, arg);
430 size = strtoul(argv[++arg], &endp, 0);
431 if (endp == NULL || *endp != 0)
432 invarg(argc, argv, arg);
435 if (tx || strcmp(fname, "/hd/") != 0) {
436 f = fopen(fname, tx ? "rb" : "wb");
438 fprintf(stderr, "open %s: \n",
445 if (tx && size == 0) {
446 ret = fstat(fileno(f), &st);
448 fprintf(stderr, "fstat %s: \n",
457 fprintf(stderr, "size is 0\n");
461 buf = realloc(buf, size);
463 fprintf(stderr, "OOM\n");
468 ret = fread(buf, 1, size, f);
470 fprintf(stderr, "read %s: \n",
477 && !strcmp(argv[arg + 1], "x"))
483 ret = do_tx(fd, addr, buf, size, exe);
488 ret = do_rx(fd, addr, buf, size);
493 ret = fwrite(buf, 1, size, f);
503 dump_pkt(buf, size, "");
514 invarg(argc, argv, arg);