support LoROM
[flashkit-mdc.git] / flashkit.c
CommitLineData
d9502b8d 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
43enum 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};
11d3b79e 51#define PAR_MODE8 (1 << 4) /* but still drives noth LWR and UWR */
d9502b8d 52#define PAR_DEV_ID (1 << 5)
53#define PAR_SINGE (1 << 6)
54#define PAR_INC (1 << 7)
55
56static 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
89static 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
103static 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
c097fb89 122/* addr arg is always byte address */
123static void set_addr8(int fd, uint32_t addr)
d9502b8d 124{
125 uint8_t cmd[6] = {
c097fb89 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
133static void set_addr16(int fd, uint32_t addr)
134{
135 set_addr8(fd, addr >> 1);
136}
137
c5e4a2c2 138static uint32_t lorom_addr(uint32_t a)
139{
140 return ((a & 0x7f8000) << 1) | 0x8000 | (a & 0x7fff);
141}
142
143static void set_addr8l(int fd, uint32_t addr)
144{
145 set_addr8(fd, lorom_addr(addr));
146}
147
c097fb89 148static 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
d9502b8d 155 };
c097fb89 156 uint8_t r;
157
d9502b8d 158 write_serial(fd, cmd, sizeof(cmd));
c097fb89 159 read_serial(fd, &r, sizeof(r));
160 return r;
d9502b8d 161}
162
c097fb89 163static uint16_t read_bus16(int fd, uint32_t addr)
d9502b8d 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
c5e4a2c2 178static uint16_t read_bus8l(int fd, uint32_t addr)
179{
180 return read_bus8(fd, lorom_addr(addr));
181}
182
c097fb89 183static 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
196static void write_bus16(int fd, uint32_t addr, uint16_t d)
d9502b8d 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
c5e4a2c2 209static void write_bus8l(int fd, uint32_t addr, uint16_t d)
210{
211 write_bus8(fd, lorom_addr(addr), d);
212}
213
c097fb89 214static void read_block8(int fd, void *dst, uint32_t size)
d9502b8d 215{
c097fb89 216 // PAR_MODE8 does not work, so read as 16bit and throw away MSB
217 uint8_t tmp[0x10000], *d8 = dst;
d9502b8d 218 uint8_t cmd[5] = {
c097fb89 219 CMD_LEN, size >> 8,
220 CMD_LEN, size >> 0,
d9502b8d 221 CMD_RD | PAR_INC
222 };
c097fb89 223 int i;
d9502b8d 224
225 assert(size <= 0x10000);
226 write_serial(fd, cmd, sizeof(cmd));
227 read_serial(fd, dst, size);
c097fb89 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];
d9502b8d 235}
236
c097fb89 237static void read_block16(int fd, void *dst, uint32_t size)
d9502b8d 238{
c097fb89 239 uint8_t cmd[5] = {
240 CMD_LEN, size >> 9,
241 CMD_LEN, size >> 1,
242 CMD_RD | PAR_INC
243 };
d9502b8d 244
c097fb89 245 assert(size <= 0x10000);
246 write_serial(fd, cmd, sizeof(cmd));
247 read_serial(fd, dst, size);
d9502b8d 248}
249
c097fb89 250static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d)
d9502b8d 251{
c097fb89 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 };
d9502b8d 274
c097fb89 275 write_serial(fd, cmd, sizeof(cmd));
d9502b8d 276}
277
c097fb89 278static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d)
d9502b8d 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
c5e4a2c2 306static 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"
c097fb89 339static const struct iof
340{
c5e4a2c2 341 const char *name;
342 int addrs_remapped;
c097fb89 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}
349io_ops[] =
350{
c5e4a2c2 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 },
c097fb89 354};
355
c5e4a2c2 356static const struct iof *io = &io_ops[0];
c097fb89 357
358static 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
c5e4a2c2 368static void flash_seq_erase_d(int fd, uint32_t addr, uint8_t d)
c097fb89 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);
c5e4a2c2 377 io->write_bus(fd, addr, d);
378}
379
380static void flash_seq_erase(int fd, uint32_t addr)
381{
382 flash_seq_erase_d(fd, addr, 0x30);
383}
384
385static void flash_seq_erase_full(int fd)
386{
387 flash_seq_erase_d(fd, 0xaaa, 0x10);
c097fb89 388}
389
d9502b8d 390// status wait + dummy read to cause a wait?
391static 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
401static 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
408static 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
421static 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
c097fb89 429 info.did = io->read_bus(fd, 2);
d9502b8d 430
431 // could enter CFI directly, but there seems to be a "stack"
432 // of modes, so 2 exits would be needed
c097fb89 433 io->write_bus(fd, 0, 0xf0);
d9502b8d 434
c097fb89 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);
d9502b8d 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 }
c097fb89 444 info.size = 1u << io->read_bus(fd, 0x4e);
445 info.region_cnt = io->read_bus(fd, 0x58);
d9502b8d 446 assert(0 < info.region_cnt && info.region_cnt <= 4);
447 for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) {
c097fb89 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;
d9502b8d 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
c097fb89 459 io->write_bus(fd, 0, 0xf0); // flash reset
d9502b8d 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
473static uint32_t get_block_addr(uint32_t addr, uint32_t blk_offset)
474{
f34ac796 475 uint32_t i, base, faddr;
d9502b8d 476
477 assert(info.region_cnt);
f34ac796 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
d9502b8d 484 for (i = 0; i < info.region_cnt; i++) {
f34ac796 485 if (info.region[i].start <= faddr
486 && faddr < info.region[i].start + info.region[i].size)
d9502b8d 487 {
f34ac796 488 uint32_t blk = (faddr - info.region[i].start)
d9502b8d 489 / info.region[i].block_size
490 + blk_offset;
f34ac796 491 return base + info.region[i].start
d9502b8d 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
500static void print_progress(uint32_t done, uint32_t total)
501{
502 int i, step;
503
c097fb89 504 printf("\r%06x/%06x |", done, total);
d9502b8d 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
78ad8139 515static 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
11d3b79e 535static const char *portname =
536#ifdef __APPLE__
537 "/dev/cu.usbserial-AL0254JM";
538#else
539 "/dev/ttyUSB0";
540#endif
af236294 541
d9502b8d 542static void usage(const char *argv0)
543{
c5e4a2c2 544 size_t i;
545
d9502b8d 546 printf("usage:\n"
547 "%s [options]\n"
af236294 548 " -d <ttydevice> (default %s)\n"
d9502b8d 549 " -r <file> [size] dump the cart (default 4MB)\n"
78ad8139 550 " -w <file> [size] program the flash (def. file size)\n"
551 " -s <file> [size] simple write (SRAM, etc, def. file size)\n"
c5e4a2c2 552 " -e <size> erase (rounds to block size); can specify 'full'\n"
78ad8139 553 " -a <start_address> read/write start address (default 0)\n"
c5e4a2c2 554 " -m <n> use an address mapper n, one of:\n"
af236294 555 , argv0, portname);
c5e4a2c2 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");
d9502b8d 561 exit(1);
562}
563
564static 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
573static void *getarg(int argc, char *argv[], int arg)
574{
575 if (arg >= argc)
576 invarg(argc, argv, arg);
577 return argv[arg];
578}
579
c5e4a2c2 580static 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
594static uint8_t g_block[0x8000];
595static uint8_t g_block2[sizeof(g_block)];
d9502b8d 596
597int main(int argc, char *argv[])
598{
d9502b8d 599 const char *fname_w = NULL;
600 const char *fname_r = NULL;
78ad8139 601 const char *fname_ws = NULL;
d9502b8d 602 long size_w = 0;
603 long size_r = 0;
78ad8139 604 long size_ws = 0;
d9502b8d 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;
c097fb89 611 int write_step = 2;
d9502b8d 612 FILE *f_w = NULL;
613 FILE *f_r = NULL;
78ad8139 614 FILE *f_ws = NULL;
d9502b8d 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] != '-') {
c5e4a2c2 632 size_r = getarg_l(argc, argv, ++arg);
d9502b8d 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] != '-') {
c5e4a2c2 641 size_w = getarg_l(argc, argv, ++arg);
d9502b8d 642 if (size_w <= 0)
643 invarg(argc, argv, arg);
644 }
645 continue;
646 }
78ad8139 647 if (!strcmp(argv[arg], "-s")) {
648 fname_ws = getarg(argc, argv, ++arg);
649 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
c5e4a2c2 650 size_ws = getarg_l(argc, argv, ++arg);
78ad8139 651 if (size_ws <= 0)
652 invarg(argc, argv, arg);
653 }
654 continue;
655 }
d9502b8d 656 if (!strcmp(argv[arg], "-a")) {
c5e4a2c2 657 address_in = getarg_l(argc, argv, ++arg);
658 if (address_in < 0)
d9502b8d 659 invarg(argc, argv, arg);
660 continue;
661 }
662 if (!strcmp(argv[arg], "-e")) {
c5e4a2c2 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 }
d9502b8d 671 continue;
672 }
c5e4a2c2 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;
c097fb89 680 continue;
681 }
d9502b8d 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) {
78ad8139 697 f_w = open_prep_read(fname_w, &size_w);
c5e4a2c2 698 if (size_e == 0 && io->addrs_remapped)
699 size_e = -1;
700 if (size_e != -1 && size_e < size_w)
d9502b8d 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
78ad8139 713 if (fname_ws)
714 f_ws = open_prep_read(fname_ws, &size_ws);
715
d9502b8d 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);
c097fb89 728 io->write_bus(fd, 0, 0xf0); // flash reset
d9502b8d 729
730 if (do_info || size_e)
731 read_info(fd);
732
c5e4a2c2 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) {
d9502b8d 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);
c5e4a2c2 756 return 1;
d9502b8d 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);
78ad8139 767 printf("flashing %ld bytes:\n", size_w);
c097fb89 768 for (a = 0; a < size_w; a += write_step) {
d9502b8d 769 ssize_t r;
770
771 b[1] = 0xff;
c097fb89 772 len = min(size_w - a, write_step);
d9502b8d 773 r = fread(b, 1, len, f_w);
774 if (r != len) {
775 perror("\nfread");
776 return 1;
777 }
c097fb89 778 io->flash_seq_write(fd, address_in + a, b);
d9502b8d 779
c097fb89 780 if (!(a & 0x3ff))
d9502b8d 781 print_progress(a, size_w);
782 }
783 print_progress(a, size_w);
784 rv = ry_read(fd);
c097fb89 785 if (write_step == 2 && rv != ((b[0] << 8) | b[1]))
d9502b8d 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 }
78ad8139 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 }
d9502b8d 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));
c097fb89 830 io->set_addr(fd, address_in);
d9502b8d 831 for (done = 0; done < size_r || done < size_v; ) {
c5e4a2c2 832 if (io->addrs_remapped)
833 io->set_addr(fd, address_in + done);
c097fb89 834 io->read_block(fd, g_block, sizeof(g_block));
d9502b8d 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}