fix rgb order
[saturn.git] / datalink.c
CommitLineData
01698af3 1/*
2 * Tool for Saturn USB DataLink device
3 * Copyright (c) 2014 notaz
4 *
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:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
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
23 * SOFTWARE.
24 */
25
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <string.h>
30#include <stdint.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <sys/ioctl.h>
35#include <unistd.h>
36
37// ugh.. avoid conflicting winsize/termio from sys/ioctl.h
38#define winsize winsize_
39#define termio termio_
40#include <linux/termios.h>
41
42#define BAUD_RATE 375000
43#define MAX_DATA_SIZE 191
d6a44ec7 44#define PROGRESS_STEP (4 * 1024)
01698af3 45
46static int setup(int fd)
47{
48 struct termios2 tty;
49 //struct termios tty;
50 int ret;
51
52 memset(&tty, 0, sizeof(tty));
53
54 //ret = tcgetattr(fd, &tty);
55 ret = ioctl(fd, TCGETS2, &tty);
56 if (ret != 0) {
57 perror("TCGETS2");
58 return ret;
59 }
60
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;
69 tty.c_ispeed =
70 tty.c_ospeed = BAUD_RATE;
71
72 tty.c_cc[VMIN] = 1;
73 tty.c_cc[VTIME] = 0; // read timeout deciseconds
74
75 //ret = tcsetattr(fd, TCSANOW, &tty);
76 ret = ioctl(fd, TCSETS2, &tty);
77 if (ret != 0) {
78 perror("TCSETS2");
79 return ret;
80 }
81
82 memset(&tty, 0, sizeof(tty));
83 ret = ioctl(fd, TCGETS2, &tty);
84 if (ret != 0) {
85 perror("TCGETS2");
86 return ret;
87 }
88
89 if (tty.c_ispeed != BAUD_RATE || tty.c_ospeed != BAUD_RATE) {
90 fprintf(stderr, "warning: could not set %d baud, got:\n",
91 BAUD_RATE);
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);
95 }
96
97 return 0;
98}
99
d6a44ec7 100static void print_progress(int done, int total)
101{
102 int i, step;
103
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);
108
109 step = total / 20;
5491df41 110 if (step <= 0)
111 step = 1;
112
d6a44ec7 113 for (i = step; i <= total; i += step)
114 printf("%c", done >= i ? '=' : '-');
115 printf("| %3d%%", done * 100 / total);
116 fflush(stdout);
117
118 if (done == total)
119 printf("\n");
120}
121
01698af3 122static void dump_pkt(const uint8_t *buf, size_t size, const char *msg)
123{
124 size_t i;
125
126 printf("%s\n", msg);
127
128 for (i = 0; i < size; ) {
129 printf("%02zx:", i);
130 while (i < size) {
131 printf(" %02x", buf[i]);
132 i++;
133 if ((i & 15) == 0)
134 break;
135 }
136 printf("\n");
137 }
138}
139
140static uint8_t sum(const uint8_t *buf, size_t size)
141{
142 uint8_t ret = 0;
143 size_t i;
144
145 for (i = 0; i < size; i++)
146 ret += buf[i];
147
148 return ret;
149}
150
151static int send_pkt(int fd, uint8_t cmd, uint32_t addr, uint8_t len,
152 const void *data, size_t data_size)
153{
154 uint8_t pkt[200];
155 int ret;
156
157 pkt[0] = 0x5a;
158 pkt[1] = 9 - 2 + data_size;
159 pkt[2] = cmd;
160 pkt[3] = addr >> 24;
161 pkt[4] = addr >> 16;
162 pkt[5] = addr >> 8;
163 pkt[6] = addr;
164 pkt[7] = len;
165 if (data_size > 0)
166 memcpy(&pkt[8], data, data_size);
167 pkt[8 + data_size] = sum(&pkt[1], 8 + data_size - 1);
168
169 // dump_pkt(pkt, 9 + data_size, "send:");
170
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);
175 perror("");
176 dump_pkt(pkt, 9 + data_size, "");
177 return -1;
178 }
179
180 return 0;
181}
182
183static int recv_pkt(int fd, uint8_t cmd, uint32_t addr,
184 void *data, size_t data_size)
185{
186 size_t size = 9 + data_size;
187 size_t got;
188 uint8_t pkt[200];
189 uint8_t csum;
190 int ret;
191
192 for (got = 0; got < size; ) {
193 ret = read(fd, pkt + got, size);
194 if (ret <= 0) {
195 fprintf(stderr, "read: %d, %zd/%zd, "
196 "cmd %02x, addr %08x: ",
197 ret, got, size, cmd, addr);
198 perror("");
199 if (got > 0)
200 dump_pkt(pkt, got, "received data:");
201 return -1;
202 }
203 got += ret;
204 }
205
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, "");
212 return -2;
213 }
214
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",
218 got, cmd, addr);
219 dump_pkt(pkt, got, "");
220 return -2;
221 }
222
223 if (data_size != 0)
224 memcpy(data, &pkt[8], data_size);
225
226 // dump_pkt(pkt, got, "recv:");
227
228 return 0;
229}
230
231static int do_rx(int fd, uint32_t addr, void *buf, size_t size)
232{
d6a44ec7 233 size_t size_total = size;
234 int progress_bytes = 0;
01698af3 235 char *cbuf = buf;
236 int ret;
237 int len;
238
239 /* must use at least 2 packets */
240 if (size <= MAX_DATA_SIZE)
241 len = size / 2;
242 else
243 len = MAX_DATA_SIZE;
244
245 ret = send_pkt(fd, 0x01, addr, len, NULL, 0);
246 if (ret != 0)
247 return ret;
248
249 ret = recv_pkt(fd, 0x01, addr, cbuf, len);
250 if (ret != 0)
251 return ret;
252
253 addr += len;
254 cbuf += len;
255 size -= len;
256
257 while (size > 0) {
258 uint8_t cmd = size > MAX_DATA_SIZE ? 0x11 : 0x21;
259
260 len = size;
261 if (len > MAX_DATA_SIZE)
262 len = MAX_DATA_SIZE;
263
264 ret = send_pkt(fd, cmd, addr, len, NULL, 0);
265 if (ret != 0)
266 return ret;
267
268 ret = recv_pkt(fd, 0x01, addr, cbuf, len);
269 if (ret != 0)
270 return ret;
271
272 addr += len;
273 cbuf += len;
274 size -= len;
d6a44ec7 275
276 progress_bytes -= len;
277 if (progress_bytes <= 0 || size == 0) {
278 print_progress(size_total - size, size_total);
279 progress_bytes = PROGRESS_STEP;
280 }
01698af3 281 }
282
283 return 0;
284}
285
286static int do_tx(int fd, uint32_t addr, void *buf, size_t size, int exe)
287{
d6a44ec7 288 int progress_bytes = 0;
289 size_t size_total = size;
01698af3 290 size_t size_last = size;
291 uint32_t addr_last = addr;
292 char *cbuf = buf;
293 uint8_t cmd = 0x09;
294 int ret;
295 int len;
296
297 if (size > MAX_DATA_SIZE) {
298 /* skip first packet, we'll send it last */
299 size_last = MAX_DATA_SIZE;
300 addr += size_last;
301 cbuf += size_last;
302 size -= size_last;
303
304 while (size > 0) {
305 len = size;
306 if (len > MAX_DATA_SIZE)
307 len = MAX_DATA_SIZE;
308
309 ret = send_pkt(fd, cmd, addr, len, cbuf, len);
310 if (ret != 0)
311 return ret;
312
313 ret = recv_pkt(fd, cmd, addr, NULL, 0);
314 if (ret != 0)
315 return ret;
316
317 addr += len;
318 cbuf += len;
319 size -= len;
d6a44ec7 320
321 progress_bytes -= len;
322 if (progress_bytes <= 0) {
323 print_progress(size_total - size - size_last,
324 size_total);
325 progress_bytes = PROGRESS_STEP;
326 }
01698af3 327 }
328 }
329
330 if (exe)
331 cmd = 0x19;
332
333 ret = send_pkt(fd, cmd, addr_last, size_last, buf, size_last);
334 if (ret != 0)
335 return ret;
336
337 ret = recv_pkt(fd, cmd, addr_last, NULL, 0);
338 if (ret != 0)
339 return ret;
340
d6a44ec7 341 print_progress(size_total, size_total);
342
01698af3 343 return 0;
344}
345
346static void usage(const char *argv0)
347{
348 printf("usage:\n"
349 "%s [dev <device>] command(s)\n"
350 "commands:\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"
d6a44ec7 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"
366 , argv0);
01698af3 367 exit(1);
368}
369
370static void invarg(int argc, char *argv[], int arg)
371{
372 if (arg < argc)
373 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
374 else
375 fprintf(stderr, "missing required argument %d\n", arg);
376 exit(1);
377}
378
379int main(int argc, char *argv[])
380{
381 const char *portname = "/dev/ttyUSB0";
382 const char *fname;
383 struct stat st;
384 void *buf = NULL;
385 FILE *f = NULL;
386 uint32_t addr;
387 size_t size;
388 char *endp;
389 int arg = 1;
390 int ret;
391 int fd;
392
393 if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
394 usage(argv[0]);
395
396 if (!strcmp(argv[arg], "dev")) {
397 arg++;
398 if (argv[arg] != NULL)
399 portname = argv[arg];
400 else
401 invarg(argc, argv, arg);
402 arg++;
403 }
404
405 fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
406 if (fd < 0) {
407 fprintf(stderr, "open %s: ", portname);
408 perror("");
409 return 1;
410 }
411
412 ret = setup(fd);
413 if (ret)
414 return ret;
415
416 while (arg < argc)
417 {
418 if (!strcmp(argv[arg], "rx") ||
419 !strcmp(argv[arg], "tx"))
420 {
421 int tx = !strcmp(argv[arg], "tx");
422 int exe = 0;
423
424 if (argc - arg < 4)
425 invarg(argc, argv, argc);
426
427 endp = NULL;
428 addr = strtoul(argv[++arg], &endp, 0);
429 if (endp == NULL || *endp != 0)
430 invarg(argc, argv, arg);
431
432 endp = NULL;
433 size = strtoul(argv[++arg], &endp, 0);
434 if (endp == NULL || *endp != 0)
435 invarg(argc, argv, arg);
436
437 fname = argv[++arg];
438 if (tx || strcmp(fname, "/hd/") != 0) {
439 f = fopen(fname, tx ? "rb" : "wb");
440 if (f == NULL) {
441 fprintf(stderr, "open %s: \n",
442 fname);
443 perror("");
444 return 1;
445 }
446 }
447
448 if (tx && size == 0) {
449 ret = fstat(fileno(f), &st);
450 if (ret != 0) {
451 fprintf(stderr, "fstat %s: \n",
452 fname);
453 perror("stat");
454 return 1;
455 }
456 size = st.st_size;
457 }
458
459 if (size == 0) {
460 fprintf(stderr, "size is 0\n");
461 return 1;
462 }
463
464 buf = realloc(buf, size);
465 if (buf == NULL) {
466 fprintf(stderr, "OOM\n");
467 return 1;
468 }
469
470 if (tx) {
471 ret = fread(buf, 1, size, f);
472 if (ret != size) {
473 fprintf(stderr, "read %s: \n",
474 fname);
475 perror("");
476 return 1;
477 }
478
479 if (arg + 1 < argc
480 && !strcmp(argv[arg + 1], "x"))
481 {
482 exe = 1;
483 arg++;
484 }
485
486 ret = do_tx(fd, addr, buf, size, exe);
487 if (ret != 0)
488 return ret;
489 }
490 else {
491 ret = do_rx(fd, addr, buf, size);
492 if (ret != 0)
493 return ret;
494
495 if (f != NULL) {
496 ret = fwrite(buf, 1, size, f);
497 if (ret != size) {
498 fprintf(stderr,
499 "write %s: \n",
500 fname);
501 perror("");
502 return 1;
503 }
504 }
505 else {
506 dump_pkt(buf, size, "");
507 }
508 }
509
510 if (f != NULL) {
511 fclose(f);
512 f = NULL;
513 }
514 arg++;
515 }
516 else {
517 invarg(argc, argv, arg);
518 }
519 }
520
521 return 0;
522}