support LoROM
[flashkit-mdc.git] / flashkit.c
1 /*
2  * Tool for USB serial communication with Krikzz's FlashKit-MD
3  * Copyright (c) 2017 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 <stdint.h>
30 #include <assert.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <arpa/inet.h> // hton
35 #include <termios.h>
36 #include <unistd.h>
37
38 #ifndef min
39 #define min(x, y) ((x) < (y) ? (x) : (y))
40 #define max(x, y) ((x) > (y) ? (x) : (y))
41 #endif
42
43 enum dev_cmd {
44         CMD_ADDR = 0,
45         CMD_LEN = 1,
46         CMD_RD = 2,
47         CMD_WR = 3,
48         CMD_RY = 4,
49         CMD_DELAY = 5,
50 };
51 #define PAR_MODE8  (1 << 4)     /* but still drives noth LWR and UWR */
52 #define PAR_DEV_ID (1 << 5)
53 #define PAR_SINGE  (1 << 6)
54 #define PAR_INC    (1 << 7)
55
56 static int setup(int fd)
57 {
58         struct termios tty;
59         int ret;
60
61         memset(&tty, 0, sizeof(tty));
62
63         ret = tcgetattr(fd, &tty);
64         if (ret != 0)
65         {
66                 perror("tcgetattr");
67                 return 1;
68         }
69
70         tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
71                         | INLCR | IGNCR | ICRNL | IXON);
72         tty.c_oflag &= ~OPOST;
73         tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
74         tty.c_cflag &= ~(CSIZE | PARENB);
75         tty.c_cflag |= CS8;
76
77         //tty.c_cc[VMIN] = 1;
78         //tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout
79
80         ret = tcsetattr(fd, TCSANOW, &tty);
81         if (ret != 0) {
82                 perror("tcsetattr");
83                 return ret;
84         }
85
86         return 0;
87 }
88
89 static int write_serial(int fd, const void *data, size_t size)
90 {
91         int ret;
92
93         ret = write(fd, data, size);
94         if (ret != size) {
95                 fprintf(stderr, "write %d/%zd: ", ret, size);
96                 perror("");
97                 exit(1);
98         }
99
100         return 0;
101 }
102
103 static int read_serial(int fd, void *data, size_t size)
104 {
105         size_t got = 0;
106         int ret;
107
108         while (got < size) {
109                 ret = read(fd, (char *)data + got, size - got);
110                 if (ret <= 0) {
111                         fprintf(stderr, "read %d %zd/%zd: ",
112                                 ret, got, size);
113                         perror("");
114                         exit(1);
115                 }
116                 got += ret;
117         }
118
119         return 0;
120 }
121
122 /* addr arg is always byte address */
123 static void set_addr8(int fd, uint32_t addr)
124 {
125         uint8_t cmd[6] = {
126                 CMD_ADDR, addr >> 16,
127                 CMD_ADDR, addr >> 8,
128                 CMD_ADDR, addr >> 0
129         };
130         write_serial(fd, cmd, sizeof(cmd));
131 }
132
133 static void set_addr16(int fd, uint32_t addr)
134 {
135         set_addr8(fd, addr >> 1);
136 }
137
138 static uint32_t lorom_addr(uint32_t a)
139 {
140         return ((a & 0x7f8000) << 1) | 0x8000 | (a & 0x7fff);
141 }
142
143 static void set_addr8l(int fd, uint32_t addr)
144 {
145         set_addr8(fd, lorom_addr(addr));
146 }
147
148 static uint16_t read_bus8(int fd, uint32_t addr)
149 {
150         uint8_t cmd[7] = {
151                 CMD_ADDR, addr >> 16,
152                 CMD_ADDR, addr >> 8,
153                 CMD_ADDR, addr >> 0,
154                 CMD_RD | PAR_SINGE | PAR_MODE8
155         };
156         uint8_t r;
157
158         write_serial(fd, cmd, sizeof(cmd));
159         read_serial(fd, &r, sizeof(r));
160         return r;
161 }
162
163 static uint16_t read_bus16(int fd, uint32_t addr)
164 {
165         uint8_t cmd[7] = {
166                 CMD_ADDR, addr >> 17,
167                 CMD_ADDR, addr >> 9,
168                 CMD_ADDR, addr >> 1,
169                 CMD_RD | PAR_SINGE
170         };
171         uint16_t r;
172
173         write_serial(fd, cmd, sizeof(cmd));
174         read_serial(fd, &r, sizeof(r));
175         return ntohs(r);
176 }
177
178 static uint16_t read_bus8l(int fd, uint32_t addr)
179 {
180         return read_bus8(fd, lorom_addr(addr));
181 }
182
183 static void write_bus8(int fd, uint32_t addr, uint16_t d)
184 {
185         uint8_t cmd[8] = {
186                 CMD_ADDR, addr >> 16,
187                 CMD_ADDR, addr >> 8,
188                 CMD_ADDR, addr >> 0,
189                 CMD_WR | PAR_SINGE | PAR_MODE8,
190                 d
191         };
192
193         write_serial(fd, cmd, sizeof(cmd));
194 }
195
196 static void write_bus16(int fd, uint32_t addr, uint16_t d)
197 {
198         uint8_t cmd[9] = {
199                 CMD_ADDR, addr >> 17,
200                 CMD_ADDR, addr >> 9,
201                 CMD_ADDR, addr >> 1,
202                 CMD_WR | PAR_SINGE,
203                 d >> 8, d
204         };
205
206         write_serial(fd, cmd, sizeof(cmd));
207 }
208
209 static void write_bus8l(int fd, uint32_t addr, uint16_t d)
210 {
211         write_bus8(fd, lorom_addr(addr), d);
212 }
213
214 static void read_block8(int fd, void *dst, uint32_t size)
215 {
216         // PAR_MODE8 does not work, so read as 16bit and throw away MSB
217         uint8_t tmp[0x10000], *d8 = dst;
218         uint8_t cmd[5] = {
219                 CMD_LEN, size >> 8,
220                 CMD_LEN, size >> 0,
221                 CMD_RD | PAR_INC
222         };
223         int i;
224
225         assert(size <= 0x10000);
226         write_serial(fd, cmd, sizeof(cmd));
227         read_serial(fd, dst, size);
228         read_serial(fd, tmp, size);
229
230         for (i = 0; i < size / 2; i++)
231                 d8[i] = d8[i * 2 + 1];
232         d8 += size / 2;
233         for (i = 0; i < size / 2; i++)
234                 d8[i] = tmp[i * 2 + 1];
235 }
236
237 static void read_block16(int fd, void *dst, uint32_t size)
238 {
239         uint8_t cmd[5] = {
240                 CMD_LEN, size >> 9,
241                 CMD_LEN, size >> 1,
242                 CMD_RD | PAR_INC
243         };
244
245         assert(size <= 0x10000);
246         write_serial(fd, cmd, sizeof(cmd));
247         read_serial(fd, dst, size);
248 }
249
250 static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d)
251 {
252         uint8_t cmd[] = {
253                 // unlock
254                 CMD_ADDR, 0,
255                 CMD_ADDR, 0x0a,
256                 CMD_ADDR, 0xaa,
257                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
258                 CMD_ADDR, 0,
259                 CMD_ADDR, 0x05,
260                 CMD_ADDR, 0x55,
261                 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
262                 // program setup
263                 CMD_ADDR, 0,
264                 CMD_ADDR, 0x0a,
265                 CMD_ADDR, 0xaa,
266                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
267                 // program data
268                 CMD_ADDR, addr >> 16,
269                 CMD_ADDR, addr >> 8,
270                 CMD_ADDR, addr >> 0,
271                 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
272                 CMD_RY
273         };
274
275         write_serial(fd, cmd, sizeof(cmd));
276 }
277
278 static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d)
279 {
280         uint8_t cmd[] = {
281                 // unlock
282                 CMD_ADDR, 0,
283                 CMD_ADDR, 0x05,
284                 CMD_ADDR, 0x55,
285                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
286                 CMD_ADDR, 0,
287                 CMD_ADDR, 0x02,
288                 CMD_ADDR, 0xaa,
289                 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
290                 // program setup
291                 CMD_ADDR, 0,
292                 CMD_ADDR, 0x05,
293                 CMD_ADDR, 0x55,
294                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
295                 // program data
296                 CMD_ADDR, addr >> 17,
297                 CMD_ADDR, addr >> 9,
298                 CMD_ADDR, addr >> 1,
299                 CMD_WR | PAR_SINGE, d[0], d[1],
300                 CMD_RY
301         };
302
303         write_serial(fd, cmd, sizeof(cmd));
304 }
305
306 static void flash_seq_write8l(int fd, uint32_t addr, const uint8_t *d)
307 {
308         addr = lorom_addr(addr);
309         uint8_t cmd[] = {
310                 // unlock
311                 CMD_ADDR, 0,
312                 CMD_ADDR, 0x8a,
313                 CMD_ADDR, 0xaa,
314                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
315                 CMD_ADDR, 0,
316                 CMD_ADDR, 0x85,
317                 CMD_ADDR, 0x55,
318                 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
319                 // program setup
320                 CMD_ADDR, 0,
321                 CMD_ADDR, 0x8a,
322                 CMD_ADDR, 0xaa,
323                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
324                 // program data
325                 CMD_ADDR, addr >> 16,
326                 CMD_ADDR, addr >> 8,
327                 CMD_ADDR, addr >> 0,
328                 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
329                 CMD_RY
330         };
331
332         write_serial(fd, cmd, sizeof(cmd));
333 }
334
335 #define N0 ""
336 #define N1 "8bit"
337 #define N2 "8bit+LoROM"
338 #define N3 "8bit+LoROM+adapter"
339 static const struct iof
340 {
341         const char *name;
342         int      addrs_remapped;
343         void     (*set_addr)(int fd, uint32_t addr);
344         uint16_t (*read_bus)(int fd, uint32_t addr);
345         void     (*write_bus)(int fd, uint32_t addr, uint16_t d);
346         void     (*read_block)(int fd, void *dst, uint32_t size);
347         void     (*flash_seq_write)(int fd, uint32_t addr, const uint8_t *d);
348 }
349 io_ops[] =
350 {
351         { N0, 0, set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 },
352         { N1, 0, set_addr8,  read_bus8,  write_bus8,  read_block8,  flash_seq_write8  },
353         { N2, 1, set_addr8l, read_bus8l, write_bus8l, read_block8,  flash_seq_write8l },
354 };
355
356 static const struct iof *io = &io_ops[0];
357
358 static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr)
359 {
360         // unlock
361         io->write_bus(fd, 0xaaa, 0xaa);
362         io->write_bus(fd, 0x555, 0x55);
363
364         io->write_bus(fd, 0xaaa, cmd);
365         return io->read_bus(fd, addr);
366 }
367
368 static void flash_seq_erase_d(int fd, uint32_t addr, uint8_t d)
369 {
370         // printf("erase %06x\n", addr);
371         io->write_bus(fd, 0xaaa, 0xaa);
372         io->write_bus(fd, 0x555, 0x55);
373         io->write_bus(fd, 0xaaa, 0x80);
374
375         io->write_bus(fd, 0xaaa, 0xaa);
376         io->write_bus(fd, 0x555, 0x55);
377         io->write_bus(fd, addr, d);
378 }
379
380 static void flash_seq_erase(int fd, uint32_t addr)
381 {
382         flash_seq_erase_d(fd, addr, 0x30);
383 }
384
385 static void flash_seq_erase_full(int fd)
386 {
387         flash_seq_erase_d(fd, 0xaaa, 0x10);
388 }
389
390 // status wait + dummy read to cause a wait?
391 static uint16_t ry_read(int fd)
392 {
393         uint8_t cmd[2] = { CMD_RY, CMD_RD | PAR_SINGE };
394         uint16_t rv = 0;
395
396         write_serial(fd, cmd, sizeof(cmd));
397         read_serial(fd, &rv, sizeof(rv));
398         return ntohs(rv);
399 }
400
401 static void set_delay(int fd, uint8_t delay)
402 {
403         uint8_t cmd[2] = { CMD_DELAY, delay };
404
405         write_serial(fd, cmd, sizeof(cmd));
406 }
407
408 static struct flash_info {
409         uint16_t mid;
410         uint16_t did;
411         uint32_t size;
412         uint16_t region_cnt;
413         struct {
414                 uint32_t block_size;
415                 uint32_t block_count;
416                 uint32_t start;
417                 uint32_t size;
418         } region[4];
419 } info;
420
421 static void read_info(int fd)
422 {
423         static const uint16_t qry[3] = { 'Q', 'R', 'Y' };
424         uint32_t total = 0;
425         uint16_t resp[3];
426         uint32_t i, a;
427
428         info.mid = flash_seq_r(fd, 0x90, 0); // autoselect
429         info.did = io->read_bus(fd, 2);
430
431         // could enter CFI directly, but there seems to be a "stack"
432         // of modes, so 2 exits would be needed
433         io->write_bus(fd, 0, 0xf0);
434
435         io->write_bus(fd, 0xaa, 0x98); // CFI Query
436         resp[0] = io->read_bus(fd, 0x20);
437         resp[1] = io->read_bus(fd, 0x22);
438         resp[2] = io->read_bus(fd, 0x24);
439         if (memcmp(resp, qry, sizeof(resp))) {
440                 fprintf(stderr, "unexpected CFI response: %04x %04x %04x\n",
441                         resp[0], resp[1], resp[2]);
442                 exit(1);
443         }
444         info.size = 1u << io->read_bus(fd, 0x4e);
445         info.region_cnt = io->read_bus(fd, 0x58);
446         assert(0 < info.region_cnt && info.region_cnt <= 4);
447         for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) {
448                 info.region[i].block_count = io->read_bus(fd, a + 0) + 1;
449                 info.region[i].block_count += io->read_bus(fd, a + 2) << 8;
450                 info.region[i].block_size = io->read_bus(fd, a + 4) << 8;
451                 info.region[i].block_size |= io->read_bus(fd, a + 6) << 16;
452                 info.region[i].start = total;
453                 info.region[i].size =
454                         info.region[i].block_size * info.region[i].block_count;
455                 assert(info.region[i].size);
456                 total += info.region[i].size;
457         }
458
459         io->write_bus(fd, 0, 0xf0); // flash reset
460
461         printf("Flash info:\n");
462         printf("Manufacturer ID: %04x\n", info.mid);
463         printf("Device ID: %04x\n", info.did);
464         printf("size: %u\n", info.size);
465         printf("Erase Block Regions: %u\n", info.region_cnt);
466         for (i = 0; i < info.region_cnt; i++)
467                 printf("  %5u x %u\n", info.region[i].block_size,
468                         info.region[i].block_count);
469         if (info.size != total)
470                 fprintf(stderr, "warning: total is %u, bad CFI?\n", total);
471 }
472
473 static uint32_t get_block_addr(uint32_t addr, uint32_t blk_offset)
474 {
475         uint32_t i, base, faddr;
476
477         assert(info.region_cnt);
478         assert(info.size);
479
480         // get a flash address to allow mapper hardware
481         faddr = addr & (info.size - 1);
482         base = addr & ~(info.size - 1);
483
484         for (i = 0; i < info.region_cnt; i++) {
485                 if (info.region[i].start <= faddr
486                     && faddr < info.region[i].start + info.region[i].size)
487                 {
488                         uint32_t blk = (faddr - info.region[i].start)
489                                         / info.region[i].block_size
490                                         + blk_offset;
491                         return base + info.region[i].start
492                                 + blk * info.region[i].block_size;
493                 }
494         }
495
496         fprintf(stderr, "\naddress out of range: 0x%x\n", addr);
497         exit(1);
498 }
499
500 static void print_progress(uint32_t done, uint32_t total)
501 {
502         int i, step;
503
504         printf("\r%06x/%06x |", done, total);
505
506         step = (total + 19) / 20;
507         for (i = step; i <= total; i += step)
508                 fputc(done >= i ? '=' : '-', stdout);
509         printf("| %3d%%", done * 100 / total);
510         fflush(stdout);
511         if (done >= total)
512                 fputc('\n', stdout);
513 }
514
515 static FILE *open_prep_read(const char *fname, long *size)
516 {
517         FILE *f = fopen(fname, "rb");
518         if (!f) {
519                 fprintf(stderr, "fopen %s: ", fname);
520                 perror("");
521                 exit(1);
522         }
523         if (*size <= 0) {
524                 fseek(f, 0, SEEK_END);
525                 *size = ftell(f);
526                 fseek(f, 0, SEEK_SET);
527         }
528         if (*size <= 0) {
529                 fprintf(stderr, "size of %s is %ld\n", fname, *size);
530                 exit(1);
531         }
532         return f;
533 }
534
535 static const char *portname =
536 #ifdef __APPLE__
537         "/dev/cu.usbserial-AL0254JM";
538 #else
539         "/dev/ttyUSB0";
540 #endif
541
542 static void usage(const char *argv0)
543 {
544         size_t i;
545
546         printf("usage:\n"
547                 "%s [options]\n"
548                 "  -d <ttydevice>      (default %s)\n"
549                 "  -r <file> [size]    dump the cart (default 4MB)\n"
550                 "  -w <file> [size]    program the flash (def. file size)\n"
551                 "  -s <file> [size]    simple write (SRAM, etc, def. file size)\n"
552                 "  -e <size>           erase (rounds to block size); can specify 'full'\n"
553                 "  -a <start_address>  read/write start address (default 0)\n"
554                 "  -m <n>              use an address mapper n, one of:\n"
555                 , argv0, portname);
556         for (i = 1; i < sizeof(io_ops) / sizeof(io_ops[0]); i++)
557                 printf(
558                 "                       %zd: %s\n", i, io_ops[i].name);
559         printf( "  -v                  verify written data\n"
560                 "  -i                  get info about the flash chip\n");
561         exit(1);
562 }
563
564 static void invarg(int argc, char *argv[], int arg)
565 {
566         if (arg < argc)
567                 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
568         else
569                 fprintf(stderr, "missing required argument %d\n", arg);
570         exit(1);
571 }
572
573 static void *getarg(int argc, char *argv[], int arg)
574 {
575         if (arg >= argc)
576                 invarg(argc, argv, arg);
577         return argv[arg];
578 }
579
580 static long getarg_l(int argc, char *argv[], int arg)
581 {
582         char *endp = NULL;
583         long r;
584
585         if (arg >= argc)
586                 invarg(argc, argv, arg);
587         r = strtol(argv[arg], &endp, 0);
588         if (endp == NULL || *endp != 0)
589                 invarg(argc, argv, arg);
590         return r;
591 }
592
593 // 32K to easily handle SNES LoROM
594 static uint8_t g_block[0x8000];
595 static uint8_t g_block2[sizeof(g_block)];
596
597 int main(int argc, char *argv[])
598 {
599         const char *fname_w = NULL;
600         const char *fname_r = NULL;
601         const char *fname_ws = NULL;
602         long size_w = 0;
603         long size_r = 0;
604         long size_ws = 0;
605         long size_e = 0;
606         long size_v = 0;
607         long len, address_in = 0;
608         long a, a_blk, end;
609         int do_info = 0;
610         int do_verify = 0;
611         int write_step = 2;
612         FILE *f_w = NULL;
613         FILE *f_r = NULL;
614         FILE *f_ws = NULL;
615         uint8_t id[2] = { 0, 0 };
616         uint8_t cmd;
617         uint16_t rv;
618         int arg = 1;
619         int fd;
620
621         if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
622                 usage(argv[0]);
623
624         for (arg = 1; arg < argc; arg++) {
625                 if (!strcmp(argv[arg], "-d")) {
626                         portname = getarg(argc, argv, ++arg);
627                         continue;
628                 }
629                 if (!strcmp(argv[arg], "-r")) {
630                         fname_r = getarg(argc, argv, ++arg);
631                         if (arg + 1 < argc && argv[arg + 1][0] != '-') {
632                                 size_r = getarg_l(argc, argv, ++arg);
633                                 if (size_r <= 0)
634                                         invarg(argc, argv, arg);
635                         }
636                         continue;
637                 }
638                 if (!strcmp(argv[arg], "-w")) {
639                         fname_w = getarg(argc, argv, ++arg);
640                         if (arg + 1 < argc && argv[arg + 1][0] != '-') {
641                                 size_w = getarg_l(argc, argv, ++arg);
642                                 if (size_w <= 0)
643                                         invarg(argc, argv, arg);
644                         }
645                         continue;
646                 }
647                 if (!strcmp(argv[arg], "-s")) {
648                         fname_ws = getarg(argc, argv, ++arg);
649                         if (arg + 1 < argc && argv[arg + 1][0] != '-') {
650                                 size_ws = getarg_l(argc, argv, ++arg);
651                                 if (size_ws <= 0)
652                                         invarg(argc, argv, arg);
653                         }
654                         continue;
655                 }
656                 if (!strcmp(argv[arg], "-a")) {
657                         address_in = getarg_l(argc, argv, ++arg);
658                         if (address_in < 0)
659                                 invarg(argc, argv, arg);
660                         continue;
661                 }
662                 if (!strcmp(argv[arg], "-e")) {
663                         arg++;
664                         if (!strcmp(getarg(argc, argv, arg), "full"))
665                                 size_e = -1;
666                         else {
667                                 size_e = getarg_l(argc, argv, arg);
668                                 if (size_e <= 0)
669                                         invarg(argc, argv, arg);
670                         }
671                         continue;
672                 }
673                 if (!strcmp(argv[arg], "-m")) {
674                         long v = getarg_l(argc, argv, ++arg);
675                         if ((size_t)v >= sizeof(io_ops) / sizeof(io_ops[0]))
676                                 invarg(argc, argv, arg);
677                         io = &io_ops[v];
678                         if (v != 0)
679                                 write_step = 1;
680                         continue;
681                 }
682                 if (!strcmp(argv[arg], "-v")) {
683                         do_verify = 1;
684                         continue;
685                 }
686                 if (!strcmp(argv[arg], "-i")) {
687                         do_info = 1;
688                         continue;
689                 }
690                 invarg(argc, argv, arg);
691         }
692
693         if (fname_r && size_r == 0)
694                 size_r = 0x400000;
695
696         if (fname_w) {
697                 f_w = open_prep_read(fname_w, &size_w);
698                 if (size_e == 0 && io->addrs_remapped)
699                         size_e = -1;
700                 if (size_e != -1 && size_e < size_w)
701                         size_e = size_w;
702                 if (do_verify)
703                         size_v = size_w;
704         }
705
706         fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
707         if (fd < 0) {
708                 fprintf(stderr, "open %s: ", portname);
709                 perror("");
710                 return 1;
711         }
712
713         if (fname_ws)
714                 f_ws = open_prep_read(fname_ws, &size_ws);
715
716         setup(fd);
717
718         cmd = CMD_RD | PAR_SINGE | PAR_DEV_ID;
719         write_serial(fd, &cmd, sizeof(cmd));
720         read_serial(fd, id, sizeof(id));
721         if (id[0] != id[1] || id[0] == 0) {
722                 fprintf(stderr, "unexpected id: %02x %02x\n", id[0], id[1]);
723                 return 1;
724         }
725         printf("flashkit id: %02x\n", id[0]);
726
727         set_delay(fd, 1);
728         io->write_bus(fd, 0, 0xf0); // flash reset
729
730         if (do_info || size_e)
731                 read_info(fd);
732
733         if (size_e == -1) {
734                 printf("performing full erase..."); fflush(stdout);
735                 flash_seq_erase_full(fd);
736                 rv = ry_read(fd);
737                 if (rv != 0xffff) {
738                         fprintf(stderr, "\nerase error: %04x\n", rv);
739                         return 1;
740                 }
741                 printf(" done.\n");
742         }
743         else if (size_e) {
744                 // set_delay(fd, 0); // ?
745                 a_blk = get_block_addr(address_in, 0);
746                 end = address_in + size_e;
747
748                 printf("erasing %ld bytes:\n", size_e);
749                 print_progress(0, size_e);
750                 for (a = address_in; a < end; ) {
751                         flash_seq_erase(fd, a_blk);
752                         rv = ry_read(fd);
753                         if (rv != 0xffff) {
754                                 fprintf(stderr, "\nerase error: %lx %04x\n",
755                                         a_blk, rv);
756                                 return 1;
757                         }
758
759                         a_blk = get_block_addr(a_blk, 1);
760                         a += a_blk - a;
761                         print_progress(a - address_in, size_e);
762                 }
763         }
764         if (f_w != NULL) {
765                 uint8_t b[2];
766                 set_delay(fd, 0);
767                 printf("flashing %ld bytes:\n", size_w);
768                 for (a = 0; a < size_w; a += write_step) {
769                         ssize_t r;
770
771                         b[1] = 0xff;
772                         len = min(size_w - a, write_step);
773                         r = fread(b, 1, len, f_w);
774                         if (r != len) {
775                                 perror("\nfread");
776                                 return 1;
777                         }
778                         io->flash_seq_write(fd, address_in + a, b);
779
780                         if (!(a & 0x3ff))
781                                 print_progress(a, size_w);
782                 }
783                 print_progress(a, size_w);
784                 rv = ry_read(fd);
785                 if (write_step == 2 && rv != ((b[0] << 8) | b[1]))
786                         fprintf(stderr, "warning: last bytes: %04x %02x%02x\n",
787                                 rv, b[0], b[1]);
788                 rewind(f_w);
789                 set_delay(fd, 1);
790         }
791         if (f_ws != NULL) {
792                 printf("writing %ld bytes:\n", size_ws);
793                 for (a = 0; a < size_ws; a += write_step) {
794                         uint16_t b = 0xffff;
795                         ssize_t r;
796
797                         len = min(size_ws - a, write_step);
798                         r = fread(&b, 1, len, f_ws);
799                         if (r != len) {
800                                 perror("\nfread");
801                                 return 1;
802                         }
803                         if (write_step == 2)
804                                 b = htons(b);
805                         io->write_bus(fd, address_in + a, b);
806
807                         if (!(a & 0x3ff))
808                                 print_progress(a, size_ws);
809                 }
810                 print_progress(a, size_ws);
811         }
812
813         if (fname_r || size_v) {
814                 long blks, blks_v, done, verify_diff = 0;
815
816                 blks = (size_r + sizeof(g_block) - 1) / sizeof(g_block);
817                 blks_v = (size_v + sizeof(g_block) - 1) / sizeof(g_block);
818                 blks = max(blks, blks_v);
819                 if (fname_r) {
820                         f_r = fopen(fname_r, "wb");
821                         if (!f_r) {
822                                 fprintf(stderr, "fopen %s: ", fname_r);
823                                 perror("");
824                                 return 1;
825                         }
826                 }
827
828                 printf("reading %ld bytes:\n", max(size_r, size_v));
829                 print_progress(0, blks * sizeof(g_block));
830                 io->set_addr(fd, address_in);
831                 for (done = 0; done < size_r || done < size_v; ) {
832                         if (io->addrs_remapped)
833                                 io->set_addr(fd, address_in + done);
834                         io->read_block(fd, g_block, sizeof(g_block));
835                         if (f_r && done < size_r) {
836                                 len = min(size_r - done, sizeof(g_block));
837                                 if (fwrite(g_block, 1, len, f_r) != len) {
838                                         perror("fwrite");
839                                         return 1;
840                                 }
841                         }
842                         if (done < size_v) {
843                                 len = min(size_v - done, sizeof(g_block));
844                                 if (fread(g_block2, 1, len, f_w) != len) {
845                                         perror("fread");
846                                         return 1;
847                                 }
848                                 verify_diff |= memcmp(g_block, g_block2, len);
849                         }
850                         done += sizeof(g_block);
851                         print_progress(done, blks * sizeof(g_block));
852                 }
853                 if (verify_diff) {
854                         fprintf(stderr, "verify FAILED\n");
855                         return 1;
856                 }
857         }
858         if (f_r)
859                 fclose(f_r);
860         if (f_w)
861                 fclose(f_w);
862
863         return 0;
864 }