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