fix rgb order
[saturn.git] / datalink.c
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
44 #define PROGRESS_STEP (4 * 1024)
45
46 static 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
100 static 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;
110         if (step <= 0)
111                 step = 1;
112
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
122 static 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
140 static 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
151 static 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
183 static 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
231 static int do_rx(int fd, uint32_t addr, void *buf, size_t size)
232 {
233         size_t size_total = size;
234         int progress_bytes = 0;
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;
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                 }
281         }
282
283         return 0;
284 }
285
286 static int do_tx(int fd, uint32_t addr, void *buf, size_t size, int exe)
287 {
288         int progress_bytes = 0;
289         size_t size_total = size;
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;
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                         }
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
341         print_progress(size_total, size_total);
342
343         return 0;
344 }
345
346 static 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"
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);
367         exit(1);
368 }
369
370 static 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
379 int 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 }