2 * Tool for USB serial communication with Krikzz's FlashKit-MD
3 * Copyright (c) 2017 notaz
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:
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
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
31 #include <sys/types.h>
34 #include <arpa/inet.h> // hton
39 #define min(x, y) ((x) < (y) ? (x) : (y))
40 #define max(x, y) ((x) > (y) ? (x) : (y))
51 #define PAR_MODE8 (1 << 4)
52 #define PAR_DEV_ID (1 << 5)
53 #define PAR_SINGE (1 << 6)
54 #define PAR_INC (1 << 7)
56 static int setup(int fd)
61 memset(&tty, 0, sizeof(tty));
63 ret = tcgetattr(fd, &tty);
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);
78 //tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
80 ret = tcsetattr(fd, TCSANOW, &tty);
89 static int write_serial(int fd, const void *data, size_t size)
93 ret = write(fd, data, size);
95 fprintf(stderr, "write %d/%zd: ", ret, size);
103 static int read_serial(int fd, void *data, size_t size)
109 ret = read(fd, (char *)data + got, size - got);
111 fprintf(stderr, "read %d %zd/%zd: ",
122 /* addr arg is always byte address */
123 static void set_addr8(int fd, uint32_t addr)
126 CMD_ADDR, addr >> 16,
130 write_serial(fd, cmd, sizeof(cmd));
133 static void set_addr16(int fd, uint32_t addr)
135 set_addr8(fd, addr >> 1);
138 static uint16_t read_bus8(int fd, uint32_t addr)
141 CMD_ADDR, addr >> 16,
144 CMD_RD | PAR_SINGE | PAR_MODE8
148 write_serial(fd, cmd, sizeof(cmd));
149 read_serial(fd, &r, sizeof(r));
153 static uint16_t read_bus16(int fd, uint32_t addr)
156 CMD_ADDR, addr >> 17,
163 write_serial(fd, cmd, sizeof(cmd));
164 read_serial(fd, &r, sizeof(r));
168 static void write_bus8(int fd, uint32_t addr, uint16_t d)
171 CMD_ADDR, addr >> 16,
174 CMD_WR | PAR_SINGE | PAR_MODE8,
178 write_serial(fd, cmd, sizeof(cmd));
181 static void write_bus16(int fd, uint32_t addr, uint16_t d)
184 CMD_ADDR, addr >> 17,
191 write_serial(fd, cmd, sizeof(cmd));
194 static void read_block8(int fd, void *dst, uint32_t size)
196 // PAR_MODE8 does not work, so read as 16bit and throw away MSB
197 uint8_t tmp[0x10000], *d8 = dst;
205 assert(size <= 0x10000);
206 write_serial(fd, cmd, sizeof(cmd));
207 read_serial(fd, dst, size);
208 read_serial(fd, tmp, size);
210 for (i = 0; i < size / 2; i++)
211 d8[i] = d8[i * 2 + 1];
213 for (i = 0; i < size / 2; i++)
214 d8[i] = tmp[i * 2 + 1];
217 static void read_block16(int fd, void *dst, uint32_t size)
225 assert(size <= 0x10000);
226 write_serial(fd, cmd, sizeof(cmd));
227 read_serial(fd, dst, size);
230 static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d)
237 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
241 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
246 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
248 CMD_ADDR, addr >> 16,
251 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
255 write_serial(fd, cmd, sizeof(cmd));
258 static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d)
265 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
269 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
274 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
276 CMD_ADDR, addr >> 17,
279 CMD_WR | PAR_SINGE, d[0], d[1],
283 write_serial(fd, cmd, sizeof(cmd));
286 static const struct iof
288 void (*set_addr)(int fd, uint32_t addr);
289 uint16_t (*read_bus)(int fd, uint32_t addr);
290 void (*write_bus)(int fd, uint32_t addr, uint16_t d);
291 void (*read_block)(int fd, void *dst, uint32_t size);
292 void (*flash_seq_write)(int fd, uint32_t addr, const uint8_t *d);
296 { set_addr8, read_bus8, write_bus8, read_block8, flash_seq_write8 },
297 { set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 },
300 static const struct iof *io = &io_ops[1];
302 static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr)
305 io->write_bus(fd, 0xaaa, 0xaa);
306 io->write_bus(fd, 0x555, 0x55);
308 io->write_bus(fd, 0xaaa, cmd);
309 return io->read_bus(fd, addr);
312 static void flash_seq_erase(int fd, uint32_t addr)
314 // printf("erase %06x\n", addr);
315 io->write_bus(fd, 0xaaa, 0xaa);
316 io->write_bus(fd, 0x555, 0x55);
317 io->write_bus(fd, 0xaaa, 0x80);
319 io->write_bus(fd, 0xaaa, 0xaa);
320 io->write_bus(fd, 0x555, 0x55);
321 io->write_bus(fd, addr, 0x30);
324 // status wait + dummy read to cause a wait?
325 static uint16_t ry_read(int fd)
327 uint8_t cmd[2] = { CMD_RY, CMD_RD | PAR_SINGE };
330 write_serial(fd, cmd, sizeof(cmd));
331 read_serial(fd, &rv, sizeof(rv));
335 static void set_delay(int fd, uint8_t delay)
337 uint8_t cmd[2] = { CMD_DELAY, delay };
339 write_serial(fd, cmd, sizeof(cmd));
342 static struct flash_info {
349 uint32_t block_count;
355 static void read_info(int fd)
357 static const uint16_t qry[3] = { 'Q', 'R', 'Y' };
362 info.mid = flash_seq_r(fd, 0x90, 0); // autoselect
363 info.did = io->read_bus(fd, 2);
365 // could enter CFI directly, but there seems to be a "stack"
366 // of modes, so 2 exits would be needed
367 io->write_bus(fd, 0, 0xf0);
369 io->write_bus(fd, 0xaa, 0x98); // CFI Query
370 resp[0] = io->read_bus(fd, 0x20);
371 resp[1] = io->read_bus(fd, 0x22);
372 resp[2] = io->read_bus(fd, 0x24);
373 if (memcmp(resp, qry, sizeof(resp))) {
374 fprintf(stderr, "unexpected CFI response: %04x %04x %04x\n",
375 resp[0], resp[1], resp[2]);
378 info.size = 1u << io->read_bus(fd, 0x4e);
379 info.region_cnt = io->read_bus(fd, 0x58);
380 assert(0 < info.region_cnt && info.region_cnt <= 4);
381 for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) {
382 info.region[i].block_count = io->read_bus(fd, a + 0) + 1;
383 info.region[i].block_count += io->read_bus(fd, a + 2) << 8;
384 info.region[i].block_size = io->read_bus(fd, a + 4) << 8;
385 info.region[i].block_size |= io->read_bus(fd, a + 6) << 16;
386 info.region[i].start = total;
387 info.region[i].size =
388 info.region[i].block_size * info.region[i].block_count;
389 assert(info.region[i].size);
390 total += info.region[i].size;
393 io->write_bus(fd, 0, 0xf0); // flash reset
395 printf("Flash info:\n");
396 printf("Manufacturer ID: %04x\n", info.mid);
397 printf("Device ID: %04x\n", info.did);
398 printf("size: %u\n", info.size);
399 printf("Erase Block Regions: %u\n", info.region_cnt);
400 for (i = 0; i < info.region_cnt; i++)
401 printf(" %5u x %u\n", info.region[i].block_size,
402 info.region[i].block_count);
403 if (info.size != total)
404 fprintf(stderr, "warning: total is %u, bad CFI?\n", total);
407 static uint32_t get_block_addr(uint32_t addr, uint32_t blk_offset)
411 assert(info.region_cnt);
412 for (i = 0; i < info.region_cnt; i++) {
413 if (info.region[i].start <= addr
414 && addr < info.region[i].start + info.region[i].size)
416 uint32_t blk = (addr - info.region[i].start)
417 / info.region[i].block_size
419 return info.region[i].start
420 + blk * info.region[i].block_size;
424 fprintf(stderr, "\naddress out of range: 0x%x\n", addr);
428 static void print_progress(uint32_t done, uint32_t total)
432 printf("\r%06x/%06x |", done, total);
434 step = (total + 19) / 20;
435 for (i = step; i <= total; i += step)
436 fputc(done >= i ? '=' : '-', stdout);
437 printf("| %3d%%", done * 100 / total);
443 static FILE *open_prep_read(const char *fname, long *size)
445 FILE *f = fopen(fname, "rb");
447 fprintf(stderr, "fopen %s: ", fname);
452 fseek(f, 0, SEEK_END);
454 fseek(f, 0, SEEK_SET);
457 fprintf(stderr, "size of %s is %ld\n", fname, *size);
463 static void usage(const char *argv0)
467 " -d <ttydevice> (default /dev/ttyUSB0)\n"
468 " -r <file> [size] dump the cart (default 4MB)\n"
469 " -w <file> [size] program the flash (def. file size)\n"
470 " -s <file> [size] simple write (SRAM, etc, def. file size)\n"
471 " -e <size> erase (rounds to block size)\n"
472 " -a <start_address> read/write start address (default 0)\n"
474 " -v verify written data\n"
475 " -i get info about the flash chip\n"
480 static void invarg(int argc, char *argv[], int arg)
483 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
485 fprintf(stderr, "missing required argument %d\n", arg);
489 static void *getarg(int argc, char *argv[], int arg)
492 invarg(argc, argv, arg);
496 static uint8_t g_block[0x10000];
497 static uint8_t g_block2[0x10000];
499 int main(int argc, char *argv[])
501 const char *portname = "/dev/ttyUSB0";
502 const char *fname_w = NULL;
503 const char *fname_r = NULL;
504 const char *fname_ws = NULL;
510 long len, address_in = 0;
518 uint8_t id[2] = { 0, 0 };
524 if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
527 for (arg = 1; arg < argc; arg++) {
528 if (!strcmp(argv[arg], "-d")) {
529 portname = getarg(argc, argv, ++arg);
532 if (!strcmp(argv[arg], "-r")) {
533 fname_r = getarg(argc, argv, ++arg);
534 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
535 size_r = strtol(argv[++arg], NULL, 0);
537 invarg(argc, argv, arg);
541 if (!strcmp(argv[arg], "-w")) {
542 fname_w = getarg(argc, argv, ++arg);
543 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
544 size_w = strtol(argv[++arg], NULL, 0);
546 invarg(argc, argv, arg);
550 if (!strcmp(argv[arg], "-s")) {
551 fname_ws = getarg(argc, argv, ++arg);
552 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
553 size_ws = strtol(argv[++arg], NULL, 0);
555 invarg(argc, argv, arg);
559 if (!strcmp(argv[arg], "-a")) {
560 address_in = strtol(getarg(argc, argv, ++arg), NULL, 0);
561 if (address_in < 0 || (address_in & 1))
562 invarg(argc, argv, arg);
565 if (!strcmp(argv[arg], "-e")) {
566 size_e = strtol(getarg(argc, argv, ++arg), NULL, 0);
568 invarg(argc, argv, arg);
571 if (!strcmp(argv[arg], "-8")) {
576 if (!strcmp(argv[arg], "-v")) {
580 if (!strcmp(argv[arg], "-i")) {
584 invarg(argc, argv, arg);
587 if (fname_r && size_r == 0)
591 f_w = open_prep_read(fname_w, &size_w);
598 fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
600 fprintf(stderr, "open %s: ", portname);
606 f_ws = open_prep_read(fname_ws, &size_ws);
610 cmd = CMD_RD | PAR_SINGE | PAR_DEV_ID;
611 write_serial(fd, &cmd, sizeof(cmd));
612 read_serial(fd, id, sizeof(id));
613 if (id[0] != id[1] || id[0] == 0) {
614 fprintf(stderr, "unexpected id: %02x %02x\n", id[0], id[1]);
617 printf("flashkit id: %02x\n", id[0]);
620 io->write_bus(fd, 0, 0xf0); // flash reset
622 if (do_info || size_e)
626 // set_delay(fd, 0); // ?
627 a_blk = get_block_addr(address_in, 0);
628 end = address_in + size_e;
630 printf("erasing %ld bytes:\n", size_e);
631 print_progress(0, size_e);
632 for (a = address_in; a < end; ) {
633 flash_seq_erase(fd, a_blk);
636 fprintf(stderr, "\nerase error: %lx %04x\n",
640 a_blk = get_block_addr(a_blk, 1);
642 print_progress(a - address_in, size_e);
648 printf("flashing %ld bytes:\n", size_w);
649 for (a = 0; a < size_w; a += write_step) {
653 len = min(size_w - a, write_step);
654 r = fread(b, 1, len, f_w);
659 io->flash_seq_write(fd, address_in + a, b);
662 print_progress(a, size_w);
664 print_progress(a, size_w);
666 if (write_step == 2 && rv != ((b[0] << 8) | b[1]))
667 fprintf(stderr, "warning: last bytes: %04x %02x%02x\n",
673 printf("writing %ld bytes:\n", size_ws);
674 for (a = 0; a < size_ws; a += write_step) {
678 len = min(size_ws - a, write_step);
679 r = fread(&b, 1, len, f_ws);
686 io->write_bus(fd, address_in + a, b);
689 print_progress(a, size_ws);
691 print_progress(a, size_ws);
694 if (fname_r || size_v) {
695 long blks, blks_v, done, verify_diff = 0;
697 blks = (size_r + sizeof(g_block) - 1) / sizeof(g_block);
698 blks_v = (size_v + sizeof(g_block) - 1) / sizeof(g_block);
699 blks = max(blks, blks_v);
701 f_r = fopen(fname_r, "wb");
703 fprintf(stderr, "fopen %s: ", fname_r);
709 printf("reading %ld bytes:\n", max(size_r, size_v));
710 print_progress(0, blks * sizeof(g_block));
711 io->set_addr(fd, address_in);
712 for (done = 0; done < size_r || done < size_v; ) {
713 io->read_block(fd, g_block, sizeof(g_block));
714 if (f_r && done < size_r) {
715 len = min(size_r - done, sizeof(g_block));
716 if (fwrite(g_block, 1, len, f_r) != len) {
722 len = min(size_v - done, sizeof(g_block));
723 if (fread(g_block2, 1, len, f_w) != len) {
727 verify_diff |= memcmp(g_block, g_block2, len);
729 done += sizeof(g_block);
730 print_progress(done, blks * sizeof(g_block));
733 fprintf(stderr, "verify FAILED\n");