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