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);
113 for (i = step; i <= total; i += step)
114 printf("%c", done >= i ? '=' : '-');
115 printf("| %3d%%", done * 100 / total);
122 static void dump_pkt(const uint8_t *buf, size_t size, const char *msg)
128 for (i = 0; i < size; ) {
131 printf(" %02x", buf[i]);
140 static uint8_t sum(const uint8_t *buf, size_t size)
145 for (i = 0; i < size; i++)
151 static int send_pkt(int fd, uint8_t cmd, uint32_t addr, uint8_t len,
152 const void *data, size_t data_size)
158 pkt[1] = 9 - 2 + data_size;
166 memcpy(&pkt[8], data, data_size);
167 pkt[8 + data_size] = sum(&pkt[1], 8 + data_size - 1);
169 // dump_pkt(pkt, 9 + data_size, "send:");
171 ret = write(fd, pkt, 9 + data_size);
172 if (ret != 9 + data_size) {
173 fprintf(stderr, "write: %d/%zd, cmd %02x, addr %08x: ",
174 ret, 9 + data_size, cmd, addr);
176 dump_pkt(pkt, 9 + data_size, "");
183 static int recv_pkt(int fd, uint8_t cmd, uint32_t addr,
184 void *data, size_t data_size)
186 size_t size = 9 + data_size;
192 for (got = 0; got < size; ) {
193 ret = read(fd, pkt + got, size);
195 fprintf(stderr, "read: %d, %zd/%zd, "
196 "cmd %02x, addr %08x: ",
197 ret, got, size, cmd, addr);
200 dump_pkt(pkt, got, "received data:");
206 csum = sum(&pkt[1], size - 2);
207 if (csum != pkt[size - 1]) {
208 fprintf(stderr, "read: %zd, cmd %02x, addr %08x: "
209 "checksum incorrect, expected %02x\n",
210 got, cmd, addr, csum);
211 dump_pkt(pkt, got, "");
215 if (pkt[0] != 0xa5 || pkt[1] != size - 2 || pkt[2] != 0xff) {
216 fprintf(stderr, "read: %zd, cmd %02x, addr %08x: "
217 "unexpected response header\n",
219 dump_pkt(pkt, got, "");
224 memcpy(data, &pkt[8], data_size);
226 // dump_pkt(pkt, got, "recv:");
231 static int do_rx(int fd, uint32_t addr, void *buf, size_t size)
233 size_t size_total = size;
234 int progress_bytes = 0;
239 /* must use at least 2 packets */
240 if (size <= MAX_DATA_SIZE)
245 ret = send_pkt(fd, 0x01, addr, len, NULL, 0);
249 ret = recv_pkt(fd, 0x01, addr, cbuf, len);
258 uint8_t cmd = size > MAX_DATA_SIZE ? 0x11 : 0x21;
261 if (len > MAX_DATA_SIZE)
264 ret = send_pkt(fd, cmd, addr, len, NULL, 0);
268 ret = recv_pkt(fd, 0x01, addr, cbuf, len);
276 progress_bytes -= len;
277 if (progress_bytes <= 0 || size == 0) {
278 print_progress(size_total - size, size_total);
279 progress_bytes = PROGRESS_STEP;
286 static int do_tx(int fd, uint32_t addr, void *buf, size_t size, int exe)
288 int progress_bytes = 0;
289 size_t size_total = size;
290 size_t size_last = size;
291 uint32_t addr_last = addr;
297 if (size > MAX_DATA_SIZE) {
298 /* skip first packet, we'll send it last */
299 size_last = MAX_DATA_SIZE;
306 if (len > MAX_DATA_SIZE)
309 ret = send_pkt(fd, cmd, addr, len, cbuf, len);
313 ret = recv_pkt(fd, cmd, addr, NULL, 0);
321 progress_bytes -= len;
322 if (progress_bytes <= 0) {
323 print_progress(size_total - size - size_last,
325 progress_bytes = PROGRESS_STEP;
333 ret = send_pkt(fd, cmd, addr_last, size_last, buf, size_last);
337 ret = recv_pkt(fd, cmd, addr_last, NULL, 0);
341 print_progress(size_total, size_total);
346 static void usage(const char *argv0)
349 "%s [dev <device>] command(s)\n"
351 " rx <addr> <size> <outfile>\n"
352 " tx <addr> <size> <infile> [x]\n"
353 " <outfile> can be \"/hd/\" for a hexdump\n"
354 " x instructs to execute uploaded data\n"
355 " for tx, size 0 asks to use filesize\n\n"
356 "useful regions (addr size):\n"
357 " 0x00000000 0x080000 - BIOS\n"
358 " 0x00180000 0x010000 - Backup-RAM\n"
359 " 0x00200000 0x100000 - Work-RAM-L\n"
360 " 0x05a00000 0x080000 - 68000-RAM\n"
361 " 0x05c00000 0x080000 - VDP1-VRAM\n"
362 " 0x05c80000 0x040000 - VDP1-FB\n"
363 " 0x05e00000 0x080000 - VDP2-VRAM\n"
364 " 0x05f00000 0x001000 - VDP2-CRAM\n"
365 " 0x06000000 0x100000 - Work-RAM-H\n"
370 static void invarg(int argc, char *argv[], int arg)
373 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
375 fprintf(stderr, "missing required argument %d\n", arg);
379 int main(int argc, char *argv[])
381 const char *portname = "/dev/ttyUSB0";
393 if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
396 if (!strcmp(argv[arg], "dev")) {
398 if (argv[arg] != NULL)
399 portname = argv[arg];
401 invarg(argc, argv, arg);
405 fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
407 fprintf(stderr, "open %s: ", portname);
418 if (!strcmp(argv[arg], "rx") ||
419 !strcmp(argv[arg], "tx"))
421 int tx = !strcmp(argv[arg], "tx");
425 invarg(argc, argv, argc);
428 addr = strtoul(argv[++arg], &endp, 0);
429 if (endp == NULL || *endp != 0)
430 invarg(argc, argv, arg);
433 size = strtoul(argv[++arg], &endp, 0);
434 if (endp == NULL || *endp != 0)
435 invarg(argc, argv, arg);
438 if (tx || strcmp(fname, "/hd/") != 0) {
439 f = fopen(fname, tx ? "rb" : "wb");
441 fprintf(stderr, "open %s: \n",
448 if (tx && size == 0) {
449 ret = fstat(fileno(f), &st);
451 fprintf(stderr, "fstat %s: \n",
460 fprintf(stderr, "size is 0\n");
464 buf = realloc(buf, size);
466 fprintf(stderr, "OOM\n");
471 ret = fread(buf, 1, size, f);
473 fprintf(stderr, "read %s: \n",
480 && !strcmp(argv[arg + 1], "x"))
486 ret = do_tx(fd, addr, buf, size, exe);
491 ret = do_rx(fd, addr, buf, size);
496 ret = fwrite(buf, 1, size, f);
506 dump_pkt(buf, size, "");
517 invarg(argc, argv, arg);