From d9502b8d6241a5886e08c32797f917e9d475bef0 Mon Sep 17 00:00:00 2001 From: notaz Date: Mon, 30 Oct 2017 00:16:50 +0200 Subject: [PATCH 01/11] initial version --- Makefile | 10 + flashkit.c | 596 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 606 insertions(+) create mode 100644 Makefile create mode 100644 flashkit.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..589fe24 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +CFLAGS += -Wall -O2 + +TARGET = flashkit + +all: $(TARGET) + +clean: + $(RM) $(TARGET) + +.PHONY: clean all diff --git a/flashkit.c b/flashkit.c new file mode 100644 index 0000000..3e687fc --- /dev/null +++ b/flashkit.c @@ -0,0 +1,596 @@ +/* + * Tool for USB serial communication with Krikzz's FlashKit-MD + * Copyright (c) 2017 notaz + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include // hton +#include +#include + +#ifndef min +#define min(x, y) ((x) < (y) ? (x) : (y)) +#define max(x, y) ((x) > (y) ? (x) : (y)) +#endif + +enum dev_cmd { + CMD_ADDR = 0, + CMD_LEN = 1, + CMD_RD = 2, + CMD_WR = 3, + CMD_RY = 4, + CMD_DELAY = 5, +}; +#define PAR_MODE8 (1 << 4) +#define PAR_DEV_ID (1 << 5) +#define PAR_SINGE (1 << 6) +#define PAR_INC (1 << 7) + +static int setup(int fd) +{ + struct termios tty; + int ret; + + memset(&tty, 0, sizeof(tty)); + + ret = tcgetattr(fd, &tty); + if (ret != 0) + { + perror("tcgetattr"); + return 1; + } + + tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP + | INLCR | IGNCR | ICRNL | IXON); + tty.c_oflag &= ~OPOST; + tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + tty.c_cflag &= ~(CSIZE | PARENB); + tty.c_cflag |= CS8; + + //tty.c_cc[VMIN] = 1; + //tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout + + ret = tcsetattr(fd, TCSANOW, &tty); + if (ret != 0) { + perror("tcsetattr"); + return ret; + } + + return 0; +} + +static int write_serial(int fd, const void *data, size_t size) +{ + int ret; + + ret = write(fd, data, size); + if (ret != size) { + fprintf(stderr, "write %d/%zd: ", ret, size); + perror(""); + exit(1); + } + + return 0; +} + +static int read_serial(int fd, void *data, size_t size) +{ + size_t got = 0; + int ret; + + while (got < size) { + ret = read(fd, (char *)data + got, size - got); + if (ret <= 0) { + fprintf(stderr, "read %d %zd/%zd: ", + ret, got, size); + perror(""); + exit(1); + } + got += ret; + } + + return 0; +} + +static void set_addr(int fd, uint32_t addr) +{ + uint8_t cmd[6] = { + CMD_ADDR, addr >> 17, + CMD_ADDR, addr >> 9, + CMD_ADDR, addr >> 1 + }; + write_serial(fd, cmd, sizeof(cmd)); +} + +static uint16_t read_word(int fd, uint32_t addr) +{ + uint8_t cmd[7] = { + CMD_ADDR, addr >> 17, + CMD_ADDR, addr >> 9, + CMD_ADDR, addr >> 1, + CMD_RD | PAR_SINGE + }; + uint16_t r; + + write_serial(fd, cmd, sizeof(cmd)); + read_serial(fd, &r, sizeof(r)); + return ntohs(r); +} + +static void write_word(int fd, uint32_t addr, uint16_t d) +{ + uint8_t cmd[9] = { + CMD_ADDR, addr >> 17, + CMD_ADDR, addr >> 9, + CMD_ADDR, addr >> 1, + CMD_WR | PAR_SINGE, + d >> 8, d + }; + + write_serial(fd, cmd, sizeof(cmd)); +} + +static void read_block(int fd, void *dst, uint32_t size) +{ + uint8_t cmd[5] = { + CMD_LEN, size >> 9, + CMD_LEN, size >> 1, + CMD_RD | PAR_INC + }; + + assert(size <= 0x10000); + write_serial(fd, cmd, sizeof(cmd)); + read_serial(fd, dst, size); +} + +static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr) +{ + // unlock + write_word(fd, 0xaaa, 0xaa); + write_word(fd, 0x555, 0x55); + + write_word(fd, 0xaaa, cmd); + return read_word(fd, addr); +} + +static void flash_seq_erase(int fd, uint32_t addr) +{ + // printf("erase %06x\n", addr); + write_word(fd, 0xaaa, 0xaa); + write_word(fd, 0x555, 0x55); + write_word(fd, 0xaaa, 0x80); + + write_word(fd, 0xaaa, 0xaa); + write_word(fd, 0x555, 0x55); + write_word(fd, addr, 0x30); +} + +static void flash_seq_write(int fd, uint32_t addr, uint8_t *d) +{ + uint8_t cmd[] = { + // unlock + CMD_ADDR, 0, + CMD_ADDR, 0x05, + CMD_ADDR, 0x55, + CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa, + CMD_ADDR, 0, + CMD_ADDR, 0x02, + CMD_ADDR, 0xaa, + CMD_WR | PAR_SINGE | PAR_MODE8, 0x55, + // program setup + CMD_ADDR, 0, + CMD_ADDR, 0x05, + CMD_ADDR, 0x55, + CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0, + // program data + CMD_ADDR, addr >> 17, + CMD_ADDR, addr >> 9, + CMD_ADDR, addr >> 1, + CMD_WR | PAR_SINGE, d[0], d[1], + CMD_RY + }; + + write_serial(fd, cmd, sizeof(cmd)); +} + +// status wait + dummy read to cause a wait? +static uint16_t ry_read(int fd) +{ + uint8_t cmd[2] = { CMD_RY, CMD_RD | PAR_SINGE }; + uint16_t rv = 0; + + write_serial(fd, cmd, sizeof(cmd)); + read_serial(fd, &rv, sizeof(rv)); + return ntohs(rv); +} + +static void set_delay(int fd, uint8_t delay) +{ + uint8_t cmd[2] = { CMD_DELAY, delay }; + + write_serial(fd, cmd, sizeof(cmd)); +} + +static struct flash_info { + uint16_t mid; + uint16_t did; + uint32_t size; + uint16_t region_cnt; + struct { + uint32_t block_size; + uint32_t block_count; + uint32_t start; + uint32_t size; + } region[4]; +} info; + +static void read_info(int fd) +{ + static const uint16_t qry[3] = { 'Q', 'R', 'Y' }; + uint32_t total = 0; + uint16_t resp[3]; + uint32_t i, a; + + info.mid = flash_seq_r(fd, 0x90, 0); // autoselect + info.did = read_word(fd, 2); + + // could enter CFI directly, but there seems to be a "stack" + // of modes, so 2 exits would be needed + write_word(fd, 0, 0xf0); + + write_word(fd, 0xaa, 0x98); // CFI Query + resp[0] = read_word(fd, 0x20); + resp[1] = read_word(fd, 0x22); + resp[2] = read_word(fd, 0x24); + if (memcmp(resp, qry, sizeof(resp))) { + fprintf(stderr, "unexpected CFI response: %04x %04x %04x\n", + resp[0], resp[1], resp[2]); + exit(1); + } + info.size = 1u << read_word(fd, 0x4e); + info.region_cnt = read_word(fd, 0x58); + assert(0 < info.region_cnt && info.region_cnt <= 4); + for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) { + info.region[i].block_count = read_word(fd, a + 0) + 1; + info.region[i].block_count += read_word(fd, a + 2) << 8; + info.region[i].block_size = read_word(fd, a + 4) << 8; + info.region[i].block_size |= read_word(fd, a + 6) << 16; + info.region[i].start = total; + info.region[i].size = + info.region[i].block_size * info.region[i].block_count; + assert(info.region[i].size); + total += info.region[i].size; + } + + write_word(fd, 0, 0xf0); // flash reset + + printf("Flash info:\n"); + printf("Manufacturer ID: %04x\n", info.mid); + printf("Device ID: %04x\n", info.did); + printf("size: %u\n", info.size); + printf("Erase Block Regions: %u\n", info.region_cnt); + for (i = 0; i < info.region_cnt; i++) + printf(" %5u x %u\n", info.region[i].block_size, + info.region[i].block_count); + if (info.size != total) + fprintf(stderr, "warning: total is %u, bad CFI?\n", total); +} + +static uint32_t get_block_addr(uint32_t addr, uint32_t blk_offset) +{ + uint32_t i; + + assert(info.region_cnt); + for (i = 0; i < info.region_cnt; i++) { + if (info.region[i].start <= addr + && addr < info.region[i].start + info.region[i].size) + { + uint32_t blk = (addr - info.region[i].start) + / info.region[i].block_size + + blk_offset; + return info.region[i].start + + blk * info.region[i].block_size; + } + } + + fprintf(stderr, "\naddress out of range: 0x%x\n", addr); + exit(1); +} + +static void print_progress(uint32_t done, uint32_t total) +{ + int i, step; + + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); /* 20 */ + printf("\b\b\b\b\b\b"); + printf("%06x/%06x |", done, total); + + step = (total + 19) / 20; + for (i = step; i <= total; i += step) + fputc(done >= i ? '=' : '-', stdout); + printf("| %3d%%", done * 100 / total); + fflush(stdout); + if (done >= total) + fputc('\n', stdout); +} + +static void usage(const char *argv0) +{ + printf("usage:\n" + "%s [options]\n" + " -d (default /dev/ttyUSB0)\n" + " -r [size] dump the cart (default 4MB)\n" + " -w [size] flash the cart (file size)\n" + " -e erase (rounds to block size)\n" + " -a cart start address (default 0)\n" + " -v verify written data\n" + " -i get info about the flash chip\n" + , argv0); + exit(1); +} + +static void invarg(int argc, char *argv[], int arg) +{ + if (arg < argc) + fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]); + else + fprintf(stderr, "missing required argument %d\n", arg); + exit(1); +} + +static void *getarg(int argc, char *argv[], int arg) +{ + if (arg >= argc) + invarg(argc, argv, arg); + return argv[arg]; +} + +static uint8_t g_block[0x10000]; +static uint8_t g_block2[0x10000]; + +int main(int argc, char *argv[]) +{ + const char *portname = "/dev/ttyUSB0"; + const char *fname_w = NULL; + const char *fname_r = NULL; + long size_w = 0; + long size_r = 0; + long size_e = 0; + long size_v = 0; + long len, address_in = 0; + long a, a_blk, end; + int do_info = 0; + int do_verify = 0; + FILE *f_w = NULL; + FILE *f_r = NULL; + uint8_t id[2] = { 0, 0 }; + uint8_t cmd; + uint16_t rv; + int arg = 1; + int fd; + + if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) + usage(argv[0]); + + for (arg = 1; arg < argc; arg++) { + if (!strcmp(argv[arg], "-d")) { + portname = getarg(argc, argv, ++arg); + continue; + } + if (!strcmp(argv[arg], "-r")) { + fname_r = getarg(argc, argv, ++arg); + if (arg + 1 < argc && argv[arg + 1][0] != '-') { + size_r = strtol(argv[++arg], NULL, 0); + if (size_r <= 0) + invarg(argc, argv, arg); + } + continue; + } + if (!strcmp(argv[arg], "-w")) { + fname_w = getarg(argc, argv, ++arg); + if (arg + 1 < argc && argv[arg + 1][0] != '-') { + size_w = strtol(argv[++arg], NULL, 0); + if (size_w <= 0) + invarg(argc, argv, arg); + } + continue; + } + if (!strcmp(argv[arg], "-a")) { + address_in = strtol(getarg(argc, argv, ++arg), NULL, 0); + if (address_in < 0 || (address_in & 1)) + invarg(argc, argv, arg); + continue; + } + if (!strcmp(argv[arg], "-e")) { + size_e = strtol(getarg(argc, argv, ++arg), NULL, 0); + if (size_e <= 0) + invarg(argc, argv, arg); + continue; + } + if (!strcmp(argv[arg], "-v")) { + do_verify = 1; + continue; + } + if (!strcmp(argv[arg], "-i")) { + do_info = 1; + continue; + } + invarg(argc, argv, arg); + } + + if (fname_r && size_r == 0) + size_r = 0x400000; + + if (fname_w) { + f_w = fopen(fname_w, "rb"); + if (!f_w) { + fprintf(stderr, "fopen %s: ", fname_w); + perror(""); + return 1; + } + if (size_w <= 0) { + fseek(f_w, 0, SEEK_END); + size_w = ftell(f_w); + fseek(f_w, 0, SEEK_SET); + } + if (size_w <= 0) { + fprintf(stderr, "size of %s is %ld\n", + fname_w, size_w); + return 1; + } + if (size_e < size_w) + size_e = size_w; + if (do_verify) + size_v = size_w; + } + + fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC); + if (fd < 0) { + fprintf(stderr, "open %s: ", portname); + perror(""); + return 1; + } + + setup(fd); + + cmd = CMD_RD | PAR_SINGE | PAR_DEV_ID; + write_serial(fd, &cmd, sizeof(cmd)); + read_serial(fd, id, sizeof(id)); + if (id[0] != id[1] || id[0] == 0) { + fprintf(stderr, "unexpected id: %02x %02x\n", id[0], id[1]); + return 1; + } + printf("flashkit id: %02x\n", id[0]); + + set_delay(fd, 1); + write_word(fd, 0, 0xf0); // flash reset + + if (do_info || size_e) + read_info(fd); + + if (size_e) { + // set_delay(fd, 0); // ? + a_blk = get_block_addr(address_in, 0); + end = address_in + size_e; + + printf("erasing %ld bytes:\n", size_e); + print_progress(0, size_e); + for (a = address_in; a < end; ) { + flash_seq_erase(fd, a_blk); + rv = ry_read(fd); + if (rv != 0xffff) { + fprintf(stderr, "\nerase error: %lx %04x\n", + a_blk, rv); + } + + a_blk = get_block_addr(a_blk, 1); + a += a_blk - a; + print_progress(a - address_in, size_e); + } + } + if (f_w != NULL) { + uint8_t b[2]; + set_delay(fd, 0); + printf("writing %ld bytes:\n", size_w); + for (a = 0; a < size_w; a += 2) { + ssize_t r; + + b[1] = 0xff; + len = min(size_w - a, 2); + r = fread(b, 1, len, f_w); + if (r != len) { + perror("\nfread"); + return 1; + } + flash_seq_write(fd, address_in + a, b); + + if (!(a & 0x3fe)) + print_progress(a, size_w); + } + print_progress(a, size_w); + rv = ry_read(fd); + if (rv != ((b[0] << 8) | b[1])) + fprintf(stderr, "warning: last bytes: %04x %02x%02x\n", + rv, b[0], b[1]); + rewind(f_w); + set_delay(fd, 1); + } + + if (fname_r || size_v) { + long blks, blks_v, done, verify_diff = 0; + + blks = (size_r + sizeof(g_block) - 1) / sizeof(g_block); + blks_v = (size_v + sizeof(g_block) - 1) / sizeof(g_block); + blks = max(blks, blks_v); + if (fname_r) { + f_r = fopen(fname_r, "wb"); + if (!f_r) { + fprintf(stderr, "fopen %s: ", fname_r); + perror(""); + return 1; + } + } + + printf("reading %ld bytes:\n", max(size_r, size_v)); + print_progress(0, blks * sizeof(g_block)); + set_addr(fd, address_in); + for (done = 0; done < size_r || done < size_v; ) { + read_block(fd, g_block, sizeof(g_block)); + if (f_r && done < size_r) { + len = min(size_r - done, sizeof(g_block)); + if (fwrite(g_block, 1, len, f_r) != len) { + perror("fwrite"); + return 1; + } + } + if (done < size_v) { + len = min(size_v - done, sizeof(g_block)); + if (fread(g_block2, 1, len, f_w) != len) { + perror("fread"); + return 1; + } + verify_diff |= memcmp(g_block, g_block2, len); + } + done += sizeof(g_block); + print_progress(done, blks * sizeof(g_block)); + } + if (verify_diff) { + fprintf(stderr, "verify FAILED\n"); + return 1; + } + } + if (f_r) + fclose(f_r); + if (f_w) + fclose(f_w); + + return 0; +} -- 2.39.2 From c097fb8900ff2b788e6d3969a4fa6a8615565e7c Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 2 Sep 2020 23:14:19 +0300 Subject: [PATCH 02/11] attempt to add 8bit flash support --- flashkit.c | 203 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 154 insertions(+), 49 deletions(-) diff --git a/flashkit.c b/flashkit.c index 3e687fc..5d8a587 100644 --- a/flashkit.c +++ b/flashkit.c @@ -119,17 +119,38 @@ static int read_serial(int fd, void *data, size_t size) return 0; } -static void set_addr(int fd, uint32_t addr) +/* addr arg is always byte address */ +static void set_addr8(int fd, uint32_t addr) { uint8_t cmd[6] = { - CMD_ADDR, addr >> 17, - CMD_ADDR, addr >> 9, - CMD_ADDR, addr >> 1 + CMD_ADDR, addr >> 16, + CMD_ADDR, addr >> 8, + CMD_ADDR, addr >> 0 + }; + write_serial(fd, cmd, sizeof(cmd)); +} + +static void set_addr16(int fd, uint32_t addr) +{ + set_addr8(fd, addr >> 1); +} + +static uint16_t read_bus8(int fd, uint32_t addr) +{ + uint8_t cmd[7] = { + CMD_ADDR, addr >> 16, + CMD_ADDR, addr >> 8, + CMD_ADDR, addr >> 0, + CMD_RD | PAR_SINGE | PAR_MODE8 }; + uint8_t r; + write_serial(fd, cmd, sizeof(cmd)); + read_serial(fd, &r, sizeof(r)); + return r; } -static uint16_t read_word(int fd, uint32_t addr) +static uint16_t read_bus16(int fd, uint32_t addr) { uint8_t cmd[7] = { CMD_ADDR, addr >> 17, @@ -144,7 +165,20 @@ static uint16_t read_word(int fd, uint32_t addr) return ntohs(r); } -static void write_word(int fd, uint32_t addr, uint16_t d) +static void write_bus8(int fd, uint32_t addr, uint16_t d) +{ + uint8_t cmd[8] = { + CMD_ADDR, addr >> 16, + CMD_ADDR, addr >> 8, + CMD_ADDR, addr >> 0, + CMD_WR | PAR_SINGE | PAR_MODE8, + d + }; + + write_serial(fd, cmd, sizeof(cmd)); +} + +static void write_bus16(int fd, uint32_t addr, uint16_t d) { uint8_t cmd[9] = { CMD_ADDR, addr >> 17, @@ -157,42 +191,71 @@ static void write_word(int fd, uint32_t addr, uint16_t d) write_serial(fd, cmd, sizeof(cmd)); } -static void read_block(int fd, void *dst, uint32_t size) +static void read_block8(int fd, void *dst, uint32_t size) { + // PAR_MODE8 does not work, so read as 16bit and throw away MSB + uint8_t tmp[0x10000], *d8 = dst; uint8_t cmd[5] = { - CMD_LEN, size >> 9, - CMD_LEN, size >> 1, + CMD_LEN, size >> 8, + CMD_LEN, size >> 0, CMD_RD | PAR_INC }; + int i; assert(size <= 0x10000); write_serial(fd, cmd, sizeof(cmd)); read_serial(fd, dst, size); + read_serial(fd, tmp, size); + + for (i = 0; i < size / 2; i++) + d8[i] = d8[i * 2 + 1]; + d8 += size / 2; + for (i = 0; i < size / 2; i++) + d8[i] = tmp[i * 2 + 1]; } -static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr) +static void read_block16(int fd, void *dst, uint32_t size) { - // unlock - write_word(fd, 0xaaa, 0xaa); - write_word(fd, 0x555, 0x55); + uint8_t cmd[5] = { + CMD_LEN, size >> 9, + CMD_LEN, size >> 1, + CMD_RD | PAR_INC + }; - write_word(fd, 0xaaa, cmd); - return read_word(fd, addr); + assert(size <= 0x10000); + write_serial(fd, cmd, sizeof(cmd)); + read_serial(fd, dst, size); } -static void flash_seq_erase(int fd, uint32_t addr) +static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d) { - // printf("erase %06x\n", addr); - write_word(fd, 0xaaa, 0xaa); - write_word(fd, 0x555, 0x55); - write_word(fd, 0xaaa, 0x80); + uint8_t cmd[] = { + // unlock + CMD_ADDR, 0, + CMD_ADDR, 0x0a, + CMD_ADDR, 0xaa, + CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa, + CMD_ADDR, 0, + CMD_ADDR, 0x05, + CMD_ADDR, 0x55, + CMD_WR | PAR_SINGE | PAR_MODE8, 0x55, + // program setup + CMD_ADDR, 0, + CMD_ADDR, 0x0a, + CMD_ADDR, 0xaa, + CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0, + // program data + CMD_ADDR, addr >> 16, + CMD_ADDR, addr >> 8, + CMD_ADDR, addr >> 0, + CMD_WR | PAR_SINGE | PAR_MODE8, *d, + CMD_RY + }; - write_word(fd, 0xaaa, 0xaa); - write_word(fd, 0x555, 0x55); - write_word(fd, addr, 0x30); + write_serial(fd, cmd, sizeof(cmd)); } -static void flash_seq_write(int fd, uint32_t addr, uint8_t *d) +static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d) { uint8_t cmd[] = { // unlock @@ -220,6 +283,44 @@ static void flash_seq_write(int fd, uint32_t addr, uint8_t *d) write_serial(fd, cmd, sizeof(cmd)); } +static const struct iof +{ + void (*set_addr)(int fd, uint32_t addr); + uint16_t (*read_bus)(int fd, uint32_t addr); + void (*write_bus)(int fd, uint32_t addr, uint16_t d); + void (*read_block)(int fd, void *dst, uint32_t size); + void (*flash_seq_write)(int fd, uint32_t addr, const uint8_t *d); +} +io_ops[] = +{ + { set_addr8, read_bus8, write_bus8, read_block8, flash_seq_write8 }, + { set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 }, +}; + +static const struct iof *io = &io_ops[1]; + +static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr) +{ + // unlock + io->write_bus(fd, 0xaaa, 0xaa); + io->write_bus(fd, 0x555, 0x55); + + io->write_bus(fd, 0xaaa, cmd); + return io->read_bus(fd, addr); +} + +static void flash_seq_erase(int fd, uint32_t addr) +{ + // printf("erase %06x\n", addr); + io->write_bus(fd, 0xaaa, 0xaa); + io->write_bus(fd, 0x555, 0x55); + io->write_bus(fd, 0xaaa, 0x80); + + io->write_bus(fd, 0xaaa, 0xaa); + io->write_bus(fd, 0x555, 0x55); + io->write_bus(fd, addr, 0x30); +} + // status wait + dummy read to cause a wait? static uint16_t ry_read(int fd) { @@ -259,29 +360,29 @@ static void read_info(int fd) uint32_t i, a; info.mid = flash_seq_r(fd, 0x90, 0); // autoselect - info.did = read_word(fd, 2); + info.did = io->read_bus(fd, 2); // could enter CFI directly, but there seems to be a "stack" // of modes, so 2 exits would be needed - write_word(fd, 0, 0xf0); + io->write_bus(fd, 0, 0xf0); - write_word(fd, 0xaa, 0x98); // CFI Query - resp[0] = read_word(fd, 0x20); - resp[1] = read_word(fd, 0x22); - resp[2] = read_word(fd, 0x24); + io->write_bus(fd, 0xaa, 0x98); // CFI Query + resp[0] = io->read_bus(fd, 0x20); + resp[1] = io->read_bus(fd, 0x22); + resp[2] = io->read_bus(fd, 0x24); if (memcmp(resp, qry, sizeof(resp))) { fprintf(stderr, "unexpected CFI response: %04x %04x %04x\n", resp[0], resp[1], resp[2]); exit(1); } - info.size = 1u << read_word(fd, 0x4e); - info.region_cnt = read_word(fd, 0x58); + info.size = 1u << io->read_bus(fd, 0x4e); + info.region_cnt = io->read_bus(fd, 0x58); assert(0 < info.region_cnt && info.region_cnt <= 4); for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) { - info.region[i].block_count = read_word(fd, a + 0) + 1; - info.region[i].block_count += read_word(fd, a + 2) << 8; - info.region[i].block_size = read_word(fd, a + 4) << 8; - info.region[i].block_size |= read_word(fd, a + 6) << 16; + info.region[i].block_count = io->read_bus(fd, a + 0) + 1; + info.region[i].block_count += io->read_bus(fd, a + 2) << 8; + info.region[i].block_size = io->read_bus(fd, a + 4) << 8; + info.region[i].block_size |= io->read_bus(fd, a + 6) << 16; info.region[i].start = total; info.region[i].size = info.region[i].block_size * info.region[i].block_count; @@ -289,7 +390,7 @@ static void read_info(int fd) total += info.region[i].size; } - write_word(fd, 0, 0xf0); // flash reset + io->write_bus(fd, 0, 0xf0); // flash reset printf("Flash info:\n"); printf("Manufacturer ID: %04x\n", info.mid); @@ -328,10 +429,7 @@ static void print_progress(uint32_t done, uint32_t total) { int i, step; - printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); - printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); /* 20 */ - printf("\b\b\b\b\b\b"); - printf("%06x/%06x |", done, total); + printf("\r%06x/%06x |", done, total); step = (total + 19) / 20; for (i = step; i <= total; i += step) @@ -351,6 +449,7 @@ static void usage(const char *argv0) " -w [size] flash the cart (file size)\n" " -e erase (rounds to block size)\n" " -a cart start address (default 0)\n" + " -8 8bit flash\n" " -v verify written data\n" " -i get info about the flash chip\n" , argv0); @@ -389,6 +488,7 @@ int main(int argc, char *argv[]) long a, a_blk, end; int do_info = 0; int do_verify = 0; + int write_step = 2; FILE *f_w = NULL; FILE *f_r = NULL; uint8_t id[2] = { 0, 0 }; @@ -435,6 +535,11 @@ int main(int argc, char *argv[]) invarg(argc, argv, arg); continue; } + if (!strcmp(argv[arg], "-8")) { + io = &io_ops[0]; + write_step = 1; + continue; + } if (!strcmp(argv[arg], "-v")) { do_verify = 1; continue; @@ -491,7 +596,7 @@ int main(int argc, char *argv[]) printf("flashkit id: %02x\n", id[0]); set_delay(fd, 1); - write_word(fd, 0, 0xf0); // flash reset + io->write_bus(fd, 0, 0xf0); // flash reset if (do_info || size_e) read_info(fd); @@ -520,24 +625,24 @@ int main(int argc, char *argv[]) uint8_t b[2]; set_delay(fd, 0); printf("writing %ld bytes:\n", size_w); - for (a = 0; a < size_w; a += 2) { + for (a = 0; a < size_w; a += write_step) { ssize_t r; b[1] = 0xff; - len = min(size_w - a, 2); + len = min(size_w - a, write_step); r = fread(b, 1, len, f_w); if (r != len) { perror("\nfread"); return 1; } - flash_seq_write(fd, address_in + a, b); + io->flash_seq_write(fd, address_in + a, b); - if (!(a & 0x3fe)) + if (!(a & 0x3ff)) print_progress(a, size_w); } print_progress(a, size_w); rv = ry_read(fd); - if (rv != ((b[0] << 8) | b[1])) + if (write_step == 2 && rv != ((b[0] << 8) | b[1])) fprintf(stderr, "warning: last bytes: %04x %02x%02x\n", rv, b[0], b[1]); rewind(f_w); @@ -561,9 +666,9 @@ int main(int argc, char *argv[]) printf("reading %ld bytes:\n", max(size_r, size_v)); print_progress(0, blks * sizeof(g_block)); - set_addr(fd, address_in); + io->set_addr(fd, address_in); for (done = 0; done < size_r || done < size_v; ) { - read_block(fd, g_block, sizeof(g_block)); + io->read_block(fd, g_block, sizeof(g_block)); if (f_r && done < size_r) { len = min(size_r - done, sizeof(g_block)); if (fwrite(g_block, 1, len, f_r) != len) { -- 2.39.2 From 78ad81397a40de75d279d6a17969d04a2503785c Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 2 Sep 2020 23:49:35 +0300 Subject: [PATCH 03/11] support simple writes --- flashkit.c | 80 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 19 deletions(-) diff --git a/flashkit.c b/flashkit.c index 5d8a587..1dc54d9 100644 --- a/flashkit.c +++ b/flashkit.c @@ -440,15 +440,36 @@ static void print_progress(uint32_t done, uint32_t total) fputc('\n', stdout); } +static FILE *open_prep_read(const char *fname, long *size) +{ + FILE *f = fopen(fname, "rb"); + if (!f) { + fprintf(stderr, "fopen %s: ", fname); + perror(""); + exit(1); + } + if (*size <= 0) { + fseek(f, 0, SEEK_END); + *size = ftell(f); + fseek(f, 0, SEEK_SET); + } + if (*size <= 0) { + fprintf(stderr, "size of %s is %ld\n", fname, *size); + exit(1); + } + return f; +} + static void usage(const char *argv0) { printf("usage:\n" "%s [options]\n" " -d (default /dev/ttyUSB0)\n" " -r [size] dump the cart (default 4MB)\n" - " -w [size] flash the cart (file size)\n" + " -w [size] program the flash (def. file size)\n" + " -s [size] simple write (SRAM, etc, def. file size)\n" " -e erase (rounds to block size)\n" - " -a cart start address (default 0)\n" + " -a read/write start address (default 0)\n" " -8 8bit flash\n" " -v verify written data\n" " -i get info about the flash chip\n" @@ -480,8 +501,10 @@ int main(int argc, char *argv[]) const char *portname = "/dev/ttyUSB0"; const char *fname_w = NULL; const char *fname_r = NULL; + const char *fname_ws = NULL; long size_w = 0; long size_r = 0; + long size_ws = 0; long size_e = 0; long size_v = 0; long len, address_in = 0; @@ -491,6 +514,7 @@ int main(int argc, char *argv[]) int write_step = 2; FILE *f_w = NULL; FILE *f_r = NULL; + FILE *f_ws = NULL; uint8_t id[2] = { 0, 0 }; uint8_t cmd; uint16_t rv; @@ -523,6 +547,15 @@ int main(int argc, char *argv[]) } continue; } + if (!strcmp(argv[arg], "-s")) { + fname_ws = getarg(argc, argv, ++arg); + if (arg + 1 < argc && argv[arg + 1][0] != '-') { + size_ws = strtol(argv[++arg], NULL, 0); + if (size_ws <= 0) + invarg(argc, argv, arg); + } + continue; + } if (!strcmp(argv[arg], "-a")) { address_in = strtol(getarg(argc, argv, ++arg), NULL, 0); if (address_in < 0 || (address_in & 1)) @@ -555,22 +588,7 @@ int main(int argc, char *argv[]) size_r = 0x400000; if (fname_w) { - f_w = fopen(fname_w, "rb"); - if (!f_w) { - fprintf(stderr, "fopen %s: ", fname_w); - perror(""); - return 1; - } - if (size_w <= 0) { - fseek(f_w, 0, SEEK_END); - size_w = ftell(f_w); - fseek(f_w, 0, SEEK_SET); - } - if (size_w <= 0) { - fprintf(stderr, "size of %s is %ld\n", - fname_w, size_w); - return 1; - } + f_w = open_prep_read(fname_w, &size_w); if (size_e < size_w) size_e = size_w; if (do_verify) @@ -584,6 +602,9 @@ int main(int argc, char *argv[]) return 1; } + if (fname_ws) + f_ws = open_prep_read(fname_ws, &size_ws); + setup(fd); cmd = CMD_RD | PAR_SINGE | PAR_DEV_ID; @@ -624,7 +645,7 @@ int main(int argc, char *argv[]) if (f_w != NULL) { uint8_t b[2]; set_delay(fd, 0); - printf("writing %ld bytes:\n", size_w); + printf("flashing %ld bytes:\n", size_w); for (a = 0; a < size_w; a += write_step) { ssize_t r; @@ -648,6 +669,27 @@ int main(int argc, char *argv[]) rewind(f_w); set_delay(fd, 1); } + if (f_ws != NULL) { + printf("writing %ld bytes:\n", size_ws); + for (a = 0; a < size_ws; a += write_step) { + uint16_t b = 0xffff; + ssize_t r; + + len = min(size_ws - a, write_step); + r = fread(&b, 1, len, f_ws); + if (r != len) { + perror("\nfread"); + return 1; + } + if (write_step == 2) + b = htons(b); + io->write_bus(fd, address_in + a, b); + + if (!(a & 0x3ff)) + print_progress(a, size_ws); + } + print_progress(a, size_ws); + } if (fname_r || size_v) { long blks, blks_v, done, verify_diff = 0; -- 2.39.2 From f34ac796daecb84b7228c4eb6aa971039cb4dba8 Mon Sep 17 00:00:00 2001 From: notaz Date: Mon, 7 Sep 2020 15:30:27 +0300 Subject: [PATCH 04/11] support erase on a cart with a mapper or through a mirror --- flashkit.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/flashkit.c b/flashkit.c index 1dc54d9..9884dd6 100644 --- a/flashkit.c +++ b/flashkit.c @@ -406,17 +406,23 @@ static void read_info(int fd) static uint32_t get_block_addr(uint32_t addr, uint32_t blk_offset) { - uint32_t i; + uint32_t i, base, faddr; assert(info.region_cnt); + assert(info.size); + + // get a flash address to allow mapper hardware + faddr = addr & (info.size - 1); + base = addr & ~(info.size - 1); + for (i = 0; i < info.region_cnt; i++) { - if (info.region[i].start <= addr - && addr < info.region[i].start + info.region[i].size) + if (info.region[i].start <= faddr + && faddr < info.region[i].start + info.region[i].size) { - uint32_t blk = (addr - info.region[i].start) + uint32_t blk = (faddr - info.region[i].start) / info.region[i].block_size + blk_offset; - return info.region[i].start + return base + info.region[i].start + blk * info.region[i].block_size; } } -- 2.39.2 From af236294d24fb1bff39ee38bb15dca6d732d0f95 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Fri, 4 Sep 2020 15:05:50 +0200 Subject: [PATCH 05/11] make default port name easier to configure and setup for my device on iMac Signed-off-by: H. Nikolaus Schaller --- flashkit.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/flashkit.c b/flashkit.c index 9884dd6..00819c4 100644 --- a/flashkit.c +++ b/flashkit.c @@ -466,11 +466,13 @@ static FILE *open_prep_read(const char *fname, long *size) return f; } +static const char *portname = "/dev/cu.usbserial-AL0254JM"; + static void usage(const char *argv0) { printf("usage:\n" "%s [options]\n" - " -d (default /dev/ttyUSB0)\n" + " -d (default %s)\n" " -r [size] dump the cart (default 4MB)\n" " -w [size] program the flash (def. file size)\n" " -s [size] simple write (SRAM, etc, def. file size)\n" @@ -479,7 +481,7 @@ static void usage(const char *argv0) " -8 8bit flash\n" " -v verify written data\n" " -i get info about the flash chip\n" - , argv0); + , argv0, portname); exit(1); } @@ -504,7 +506,6 @@ static uint8_t g_block2[0x10000]; int main(int argc, char *argv[]) { - const char *portname = "/dev/ttyUSB0"; const char *fname_w = NULL; const char *fname_r = NULL; const char *fname_ws = NULL; -- 2.39.2 From 11d3b79ec66be7131ce7a4a69a186a37c92563f9 Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 16 Sep 2020 20:43:48 +0300 Subject: [PATCH 06/11] restore reasonable default port for Linux --- flashkit.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/flashkit.c b/flashkit.c index 00819c4..e6d5785 100644 --- a/flashkit.c +++ b/flashkit.c @@ -48,7 +48,7 @@ enum dev_cmd { CMD_RY = 4, CMD_DELAY = 5, }; -#define PAR_MODE8 (1 << 4) +#define PAR_MODE8 (1 << 4) /* but still drives noth LWR and UWR */ #define PAR_DEV_ID (1 << 5) #define PAR_SINGE (1 << 6) #define PAR_INC (1 << 7) @@ -466,7 +466,12 @@ static FILE *open_prep_read(const char *fname, long *size) return f; } -static const char *portname = "/dev/cu.usbserial-AL0254JM"; +static const char *portname = +#ifdef __APPLE__ + "/dev/cu.usbserial-AL0254JM"; +#else + "/dev/ttyUSB0"; +#endif static void usage(const char *argv0) { -- 2.39.2 From c5e4a2c2daba0efb2029d2ebba1c9d4353484fcf Mon Sep 17 00:00:00 2001 From: notaz Date: Thu, 5 Nov 2020 01:53:35 +0200 Subject: [PATCH 07/11] support LoROM --- flashkit.c | 157 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 133 insertions(+), 24 deletions(-) diff --git a/flashkit.c b/flashkit.c index e6d5785..75f348e 100644 --- a/flashkit.c +++ b/flashkit.c @@ -135,6 +135,16 @@ static void set_addr16(int fd, uint32_t addr) set_addr8(fd, addr >> 1); } +static uint32_t lorom_addr(uint32_t a) +{ + return ((a & 0x7f8000) << 1) | 0x8000 | (a & 0x7fff); +} + +static void set_addr8l(int fd, uint32_t addr) +{ + set_addr8(fd, lorom_addr(addr)); +} + static uint16_t read_bus8(int fd, uint32_t addr) { uint8_t cmd[7] = { @@ -165,6 +175,11 @@ static uint16_t read_bus16(int fd, uint32_t addr) return ntohs(r); } +static uint16_t read_bus8l(int fd, uint32_t addr) +{ + return read_bus8(fd, lorom_addr(addr)); +} + static void write_bus8(int fd, uint32_t addr, uint16_t d) { uint8_t cmd[8] = { @@ -191,6 +206,11 @@ static void write_bus16(int fd, uint32_t addr, uint16_t d) write_serial(fd, cmd, sizeof(cmd)); } +static void write_bus8l(int fd, uint32_t addr, uint16_t d) +{ + write_bus8(fd, lorom_addr(addr), d); +} + static void read_block8(int fd, void *dst, uint32_t size) { // PAR_MODE8 does not work, so read as 16bit and throw away MSB @@ -283,8 +303,43 @@ static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d) write_serial(fd, cmd, sizeof(cmd)); } +static void flash_seq_write8l(int fd, uint32_t addr, const uint8_t *d) +{ + addr = lorom_addr(addr); + uint8_t cmd[] = { + // unlock + CMD_ADDR, 0, + CMD_ADDR, 0x8a, + CMD_ADDR, 0xaa, + CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa, + CMD_ADDR, 0, + CMD_ADDR, 0x85, + CMD_ADDR, 0x55, + CMD_WR | PAR_SINGE | PAR_MODE8, 0x55, + // program setup + CMD_ADDR, 0, + CMD_ADDR, 0x8a, + CMD_ADDR, 0xaa, + CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0, + // program data + CMD_ADDR, addr >> 16, + CMD_ADDR, addr >> 8, + CMD_ADDR, addr >> 0, + CMD_WR | PAR_SINGE | PAR_MODE8, *d, + CMD_RY + }; + + write_serial(fd, cmd, sizeof(cmd)); +} + +#define N0 "" +#define N1 "8bit" +#define N2 "8bit+LoROM" +#define N3 "8bit+LoROM+adapter" static const struct iof { + const char *name; + int addrs_remapped; void (*set_addr)(int fd, uint32_t addr); uint16_t (*read_bus)(int fd, uint32_t addr); void (*write_bus)(int fd, uint32_t addr, uint16_t d); @@ -293,11 +348,12 @@ static const struct iof } io_ops[] = { - { set_addr8, read_bus8, write_bus8, read_block8, flash_seq_write8 }, - { set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 }, + { N0, 0, set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 }, + { N1, 0, set_addr8, read_bus8, write_bus8, read_block8, flash_seq_write8 }, + { N2, 1, set_addr8l, read_bus8l, write_bus8l, read_block8, flash_seq_write8l }, }; -static const struct iof *io = &io_ops[1]; +static const struct iof *io = &io_ops[0]; static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr) { @@ -309,7 +365,7 @@ static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr) return io->read_bus(fd, addr); } -static void flash_seq_erase(int fd, uint32_t addr) +static void flash_seq_erase_d(int fd, uint32_t addr, uint8_t d) { // printf("erase %06x\n", addr); io->write_bus(fd, 0xaaa, 0xaa); @@ -318,7 +374,17 @@ static void flash_seq_erase(int fd, uint32_t addr) io->write_bus(fd, 0xaaa, 0xaa); io->write_bus(fd, 0x555, 0x55); - io->write_bus(fd, addr, 0x30); + io->write_bus(fd, addr, d); +} + +static void flash_seq_erase(int fd, uint32_t addr) +{ + flash_seq_erase_d(fd, addr, 0x30); +} + +static void flash_seq_erase_full(int fd) +{ + flash_seq_erase_d(fd, 0xaaa, 0x10); } // status wait + dummy read to cause a wait? @@ -475,18 +541,23 @@ static const char *portname = static void usage(const char *argv0) { + size_t i; + printf("usage:\n" "%s [options]\n" " -d (default %s)\n" " -r [size] dump the cart (default 4MB)\n" " -w [size] program the flash (def. file size)\n" " -s [size] simple write (SRAM, etc, def. file size)\n" - " -e erase (rounds to block size)\n" + " -e erase (rounds to block size); can specify 'full'\n" " -a read/write start address (default 0)\n" - " -8 8bit flash\n" - " -v verify written data\n" - " -i get info about the flash chip\n" + " -m use an address mapper n, one of:\n" , argv0, portname); + for (i = 1; i < sizeof(io_ops) / sizeof(io_ops[0]); i++) + printf( + " %zd: %s\n", i, io_ops[i].name); + printf( " -v verify written data\n" + " -i get info about the flash chip\n"); exit(1); } @@ -506,8 +577,22 @@ static void *getarg(int argc, char *argv[], int arg) return argv[arg]; } -static uint8_t g_block[0x10000]; -static uint8_t g_block2[0x10000]; +static long getarg_l(int argc, char *argv[], int arg) +{ + char *endp = NULL; + long r; + + if (arg >= argc) + invarg(argc, argv, arg); + r = strtol(argv[arg], &endp, 0); + if (endp == NULL || *endp != 0) + invarg(argc, argv, arg); + return r; +} + +// 32K to easily handle SNES LoROM +static uint8_t g_block[0x8000]; +static uint8_t g_block2[sizeof(g_block)]; int main(int argc, char *argv[]) { @@ -544,7 +629,7 @@ int main(int argc, char *argv[]) if (!strcmp(argv[arg], "-r")) { fname_r = getarg(argc, argv, ++arg); if (arg + 1 < argc && argv[arg + 1][0] != '-') { - size_r = strtol(argv[++arg], NULL, 0); + size_r = getarg_l(argc, argv, ++arg); if (size_r <= 0) invarg(argc, argv, arg); } @@ -553,7 +638,7 @@ int main(int argc, char *argv[]) if (!strcmp(argv[arg], "-w")) { fname_w = getarg(argc, argv, ++arg); if (arg + 1 < argc && argv[arg + 1][0] != '-') { - size_w = strtol(argv[++arg], NULL, 0); + size_w = getarg_l(argc, argv, ++arg); if (size_w <= 0) invarg(argc, argv, arg); } @@ -562,27 +647,36 @@ int main(int argc, char *argv[]) if (!strcmp(argv[arg], "-s")) { fname_ws = getarg(argc, argv, ++arg); if (arg + 1 < argc && argv[arg + 1][0] != '-') { - size_ws = strtol(argv[++arg], NULL, 0); + size_ws = getarg_l(argc, argv, ++arg); if (size_ws <= 0) invarg(argc, argv, arg); } continue; } if (!strcmp(argv[arg], "-a")) { - address_in = strtol(getarg(argc, argv, ++arg), NULL, 0); - if (address_in < 0 || (address_in & 1)) + address_in = getarg_l(argc, argv, ++arg); + if (address_in < 0) invarg(argc, argv, arg); continue; } if (!strcmp(argv[arg], "-e")) { - size_e = strtol(getarg(argc, argv, ++arg), NULL, 0); - if (size_e <= 0) - invarg(argc, argv, arg); + arg++; + if (!strcmp(getarg(argc, argv, arg), "full")) + size_e = -1; + else { + size_e = getarg_l(argc, argv, arg); + if (size_e <= 0) + invarg(argc, argv, arg); + } continue; } - if (!strcmp(argv[arg], "-8")) { - io = &io_ops[0]; - write_step = 1; + if (!strcmp(argv[arg], "-m")) { + long v = getarg_l(argc, argv, ++arg); + if ((size_t)v >= sizeof(io_ops) / sizeof(io_ops[0])) + invarg(argc, argv, arg); + io = &io_ops[v]; + if (v != 0) + write_step = 1; continue; } if (!strcmp(argv[arg], "-v")) { @@ -601,7 +695,9 @@ int main(int argc, char *argv[]) if (fname_w) { f_w = open_prep_read(fname_w, &size_w); - if (size_e < size_w) + if (size_e == 0 && io->addrs_remapped) + size_e = -1; + if (size_e != -1 && size_e < size_w) size_e = size_w; if (do_verify) size_v = size_w; @@ -634,7 +730,17 @@ int main(int argc, char *argv[]) if (do_info || size_e) read_info(fd); - if (size_e) { + if (size_e == -1) { + printf("performing full erase..."); fflush(stdout); + flash_seq_erase_full(fd); + rv = ry_read(fd); + if (rv != 0xffff) { + fprintf(stderr, "\nerase error: %04x\n", rv); + return 1; + } + printf(" done.\n"); + } + else if (size_e) { // set_delay(fd, 0); // ? a_blk = get_block_addr(address_in, 0); end = address_in + size_e; @@ -647,6 +753,7 @@ int main(int argc, char *argv[]) if (rv != 0xffff) { fprintf(stderr, "\nerase error: %lx %04x\n", a_blk, rv); + return 1; } a_blk = get_block_addr(a_blk, 1); @@ -722,6 +829,8 @@ int main(int argc, char *argv[]) print_progress(0, blks * sizeof(g_block)); io->set_addr(fd, address_in); for (done = 0; done < size_r || done < size_v; ) { + if (io->addrs_remapped) + io->set_addr(fd, address_in + done); io->read_block(fd, g_block, sizeof(g_block)); if (f_r && done < size_r) { len = min(size_r - done, sizeof(g_block)); -- 2.39.2 From 563a41af36ed16a7a61de9e35ac82ff405bc318b Mon Sep 17 00:00:00 2001 From: notaz Date: Thu, 5 Nov 2020 12:46:45 +0200 Subject: [PATCH 08/11] adapter mappers --- flashkit.c | 150 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 122 insertions(+), 28 deletions(-) diff --git a/flashkit.c b/flashkit.c index 75f348e..023d2d2 100644 --- a/flashkit.c +++ b/flashkit.c @@ -135,16 +135,6 @@ static void set_addr16(int fd, uint32_t addr) set_addr8(fd, addr >> 1); } -static uint32_t lorom_addr(uint32_t a) -{ - return ((a & 0x7f8000) << 1) | 0x8000 | (a & 0x7fff); -} - -static void set_addr8l(int fd, uint32_t addr) -{ - set_addr8(fd, lorom_addr(addr)); -} - static uint16_t read_bus8(int fd, uint32_t addr) { uint8_t cmd[7] = { @@ -175,11 +165,6 @@ static uint16_t read_bus16(int fd, uint32_t addr) return ntohs(r); } -static uint16_t read_bus8l(int fd, uint32_t addr) -{ - return read_bus8(fd, lorom_addr(addr)); -} - static void write_bus8(int fd, uint32_t addr, uint16_t d) { uint8_t cmd[8] = { @@ -206,11 +191,6 @@ static void write_bus16(int fd, uint32_t addr, uint16_t d) write_serial(fd, cmd, sizeof(cmd)); } -static void write_bus8l(int fd, uint32_t addr, uint16_t d) -{ - write_bus8(fd, lorom_addr(addr), d); -} - static void read_block8(int fd, void *dst, uint32_t size) { // PAR_MODE8 does not work, so read as 16bit and throw away MSB @@ -303,9 +283,31 @@ static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d) write_serial(fd, cmd, sizeof(cmd)); } -static void flash_seq_write8l(int fd, uint32_t addr, const uint8_t *d) +// -- 8bit+LoROM -- + +static uint32_t lorom_rom_addr(uint32_t a) { - addr = lorom_addr(addr); + return ((a & 0x7f8000) << 1) | 0x8000 | (a & 0x7fff); +} + +static void set_addr8l(int fd, uint32_t a) +{ + set_addr8(fd, lorom_rom_addr(a)); +} + +static uint16_t read_bus8l(int fd, uint32_t a) +{ + return read_bus8(fd, lorom_rom_addr(a)); +} + +static void write_bus8l(int fd, uint32_t a, uint16_t d) +{ + write_bus8(fd, lorom_rom_addr(a), d); +} + +static void flash_seq_write8l(int fd, uint32_t a, const uint8_t *d) +{ + a = lorom_rom_addr(a); uint8_t cmd[] = { // unlock CMD_ADDR, 0, @@ -322,9 +324,9 @@ static void flash_seq_write8l(int fd, uint32_t addr, const uint8_t *d) CMD_ADDR, 0xaa, CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0, // program data - CMD_ADDR, addr >> 16, - CMD_ADDR, addr >> 8, - CMD_ADDR, addr >> 0, + CMD_ADDR, a >> 16, + CMD_ADDR, a >> 8, + CMD_ADDR, a >> 0, CMD_WR | PAR_SINGE | PAR_MODE8, *d, CMD_RY }; @@ -332,10 +334,100 @@ static void flash_seq_write8l(int fd, uint32_t addr, const uint8_t *d) write_serial(fd, cmd, sizeof(cmd)); } +// -- 8bit+LoROM+adapter -- + +static uint32_t do_flipflops(int fd, uint32_t a) +{ + static uint32_t abits_now = ~0u; // A23, A22, A21 + uint32_t abits = (a >> 21) & 7; + + if (abits != abits_now) { + // printf("flipflops: %x->%x\n", abits_now, abits); + write_bus16(fd, 0xa13000, abits); + abits_now = abits; + } + return a & 0x1fffff; +} + +static void set_addr8la(int fd, uint32_t a) +{ + set_addr8(fd, do_flipflops(fd, lorom_rom_addr(a))); +} + +static uint16_t read_bus8la(int fd, uint32_t a) +{ + return read_bus8(fd, do_flipflops(fd, lorom_rom_addr(a))); +} + +static void write_bus8la(int fd, uint32_t a, uint16_t d) +{ + write_bus8(fd, do_flipflops(fd, lorom_rom_addr(a)), d); +} + +static void flash_seq_write8la(int fd, uint32_t a, const uint8_t *d) +{ + // we should clear flipflops for the flash commands, but this + // doesn't seem to be necessary as the flash chip seems to + // ignore the upper bits when looking for commands, and this + // extra clearing would slow things down + a = do_flipflops(fd, lorom_rom_addr(a)); + uint8_t cmd[] = { + // unlock + CMD_ADDR, 0, + CMD_ADDR, 0x8a, + CMD_ADDR, 0xaa, + CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa, + CMD_ADDR, 0, + CMD_ADDR, 0x85, + CMD_ADDR, 0x55, + CMD_WR | PAR_SINGE | PAR_MODE8, 0x55, + // program setup + CMD_ADDR, 0, + CMD_ADDR, 0x8a, + CMD_ADDR, 0xaa, + CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0, + // program data + CMD_ADDR, a >> 16, + CMD_ADDR, a >> 8, + CMD_ADDR, a >> 0, + CMD_WR | PAR_SINGE | PAR_MODE8, *d, + CMD_RY + }; + + write_serial(fd, cmd, sizeof(cmd)); +} + +// -- 8bit+LoROM+adapter+sram -- + +static uint32_t lorom_sram_addr(uint32_t a) +{ + return a | 0x600000; +} + +static void set_addr8las(int fd, uint32_t a) +{ + set_addr8(fd, do_flipflops(fd, lorom_sram_addr(a))); +} + +static uint16_t read_bus8las(int fd, uint32_t a) +{ + return read_bus8(fd, do_flipflops(fd, lorom_sram_addr(a))); +} + +static void write_bus8las(int fd, uint32_t a, uint16_t d) +{ + write_bus8(fd, do_flipflops(fd, lorom_sram_addr(a)), d); +} + +static void flash_seq_write8las(int fd, uint32_t a, const uint8_t *d) +{ +} + #define N0 "" #define N1 "8bit" #define N2 "8bit+LoROM" #define N3 "8bit+LoROM+adapter" +#define N4 "8bit+LoROM+adapter+sram" static const struct iof { const char *name; @@ -348,9 +440,11 @@ static const struct iof } io_ops[] = { - { N0, 0, set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 }, - { N1, 0, set_addr8, read_bus8, write_bus8, read_block8, flash_seq_write8 }, - { N2, 1, set_addr8l, read_bus8l, write_bus8l, read_block8, flash_seq_write8l }, + { N0, 0, set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 }, + { N1, 0, set_addr8, read_bus8, write_bus8, read_block8, flash_seq_write8 }, + { N2, 1, set_addr8l, read_bus8l, write_bus8l, read_block8, flash_seq_write8l }, + { N3, 1, set_addr8la, read_bus8la, write_bus8la, read_block8, flash_seq_write8la }, + { N4, 0, set_addr8las, read_bus8las, write_bus8las, read_block8, flash_seq_write8las }, }; static const struct iof *io = &io_ops[0]; -- 2.39.2 From 5a2c48a5be076f5d40c52d9053f113fff8639a72 Mon Sep 17 00:00:00 2001 From: notaz Date: Mon, 21 Dec 2020 18:52:28 +0200 Subject: [PATCH 09/11] don't try to reset flash unconditionally because we can read/write SRAM now --- flashkit.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flashkit.c b/flashkit.c index 023d2d2..a2ebdb8 100644 --- a/flashkit.c +++ b/flashkit.c @@ -819,7 +819,9 @@ int main(int argc, char *argv[]) printf("flashkit id: %02x\n", id[0]); set_delay(fd, 1); - io->write_bus(fd, 0, 0xf0); // flash reset + + if (do_info || size_e || f_w) + io->write_bus(fd, 0, 0xf0); // flash reset if (do_info || size_e) read_info(fd); -- 2.39.2 From bc4a856ae0ca1535670148dbea99ceead38983f4 Mon Sep 17 00:00:00 2001 From: notaz Date: Sun, 15 Aug 2021 01:23:09 +0300 Subject: [PATCH 10/11] support SST flash chips --- flashkit.c | 177 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 108 insertions(+), 69 deletions(-) diff --git a/flashkit.c b/flashkit.c index a2ebdb8..843decb 100644 --- a/flashkit.c +++ b/flashkit.c @@ -53,6 +53,20 @@ enum dev_cmd { #define PAR_SINGE (1 << 6) #define PAR_INC (1 << 7) +static struct flash_info { + uint32_t prog_addr; + uint16_t mid; + uint16_t did; + uint32_t size; + uint16_t region_cnt; + struct { + uint32_t block_size; + uint32_t block_count; + uint32_t start; + uint32_t size; + } region[4]; +} info; + static int setup(int fd) { struct termios tty; @@ -231,18 +245,18 @@ static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d) { uint8_t cmd[] = { // unlock - CMD_ADDR, 0, - CMD_ADDR, 0x0a, - CMD_ADDR, 0xaa, + CMD_ADDR, info.prog_addr >> 16, + CMD_ADDR, info.prog_addr >> 8, + CMD_ADDR, info.prog_addr >> 0, CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa, - CMD_ADDR, 0, - CMD_ADDR, 0x05, - CMD_ADDR, 0x55, + CMD_ADDR, info.prog_addr >> 17, + CMD_ADDR, info.prog_addr >> 9, + CMD_ADDR, info.prog_addr >> 1, CMD_WR | PAR_SINGE | PAR_MODE8, 0x55, // program setup - CMD_ADDR, 0, - CMD_ADDR, 0x0a, - CMD_ADDR, 0xaa, + CMD_ADDR, info.prog_addr >> 16, + CMD_ADDR, info.prog_addr >> 8, + CMD_ADDR, info.prog_addr >> 0, CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0, // program data CMD_ADDR, addr >> 16, @@ -252,6 +266,7 @@ static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d) CMD_RY }; + assert(info.prog_addr); write_serial(fd, cmd, sizeof(cmd)); } @@ -259,18 +274,18 @@ static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d) { uint8_t cmd[] = { // unlock - CMD_ADDR, 0, - CMD_ADDR, 0x05, - CMD_ADDR, 0x55, + CMD_ADDR, info.prog_addr >> 17, + CMD_ADDR, info.prog_addr >> 9, + CMD_ADDR, info.prog_addr >> 1, CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa, - CMD_ADDR, 0, - CMD_ADDR, 0x02, - CMD_ADDR, 0xaa, + CMD_ADDR, info.prog_addr >> 18, + CMD_ADDR, info.prog_addr >> 10, + CMD_ADDR, info.prog_addr >> 2, CMD_WR | PAR_SINGE | PAR_MODE8, 0x55, // program setup - CMD_ADDR, 0, - CMD_ADDR, 0x05, - CMD_ADDR, 0x55, + CMD_ADDR, info.prog_addr >> 17, + CMD_ADDR, info.prog_addr >> 9, + CMD_ADDR, info.prog_addr >> 1, CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0, // program data CMD_ADDR, addr >> 17, @@ -280,6 +295,7 @@ static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d) CMD_RY }; + assert(info.prog_addr); write_serial(fd, cmd, sizeof(cmd)); } @@ -452,22 +468,24 @@ static const struct iof *io = &io_ops[0]; static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr) { // unlock - io->write_bus(fd, 0xaaa, 0xaa); - io->write_bus(fd, 0x555, 0x55); + assert(info.prog_addr); + io->write_bus(fd, info.prog_addr >> 0, 0xaa); + io->write_bus(fd, info.prog_addr >> 1, 0x55); - io->write_bus(fd, 0xaaa, cmd); + io->write_bus(fd, info.prog_addr >> 0, cmd); return io->read_bus(fd, addr); } static void flash_seq_erase_d(int fd, uint32_t addr, uint8_t d) { // printf("erase %06x\n", addr); - io->write_bus(fd, 0xaaa, 0xaa); - io->write_bus(fd, 0x555, 0x55); - io->write_bus(fd, 0xaaa, 0x80); + assert(info.prog_addr); + io->write_bus(fd, info.prog_addr >> 0, 0xaa); + io->write_bus(fd, info.prog_addr >> 1, 0x55); + io->write_bus(fd, info.prog_addr >> 0, 0x80); - io->write_bus(fd, 0xaaa, 0xaa); - io->write_bus(fd, 0x555, 0x55); + io->write_bus(fd, info.prog_addr >> 0, 0xaa); + io->write_bus(fd, info.prog_addr >> 1, 0x55); io->write_bus(fd, addr, d); } @@ -478,7 +496,7 @@ static void flash_seq_erase(int fd, uint32_t addr) static void flash_seq_erase_full(int fd) { - flash_seq_erase_d(fd, 0xaaa, 0x10); + flash_seq_erase_d(fd, info.prog_addr, 0x10); } // status wait + dummy read to cause a wait? @@ -499,59 +517,82 @@ static void set_delay(int fd, uint8_t delay) write_serial(fd, cmd, sizeof(cmd)); } -static struct flash_info { - uint16_t mid; - uint16_t did; - uint32_t size; - uint16_t region_cnt; - struct { - uint32_t block_size; - uint32_t block_count; - uint32_t start; - uint32_t size; - } region[4]; -} info; - static void read_info(int fd) { static const uint16_t qry[3] = { 'Q', 'R', 'Y' }; + uint16_t resp_cfi[3], sst_mid = ~0; uint32_t total = 0; - uint16_t resp[3]; uint32_t i, a; - info.mid = flash_seq_r(fd, 0x90, 0); // autoselect - info.did = io->read_bus(fd, 2); - // could enter CFI directly, but there seems to be a "stack" - // of modes, so 2 exits would be needed - io->write_bus(fd, 0, 0xf0); + // see if this chip understands CFI (common flash interface) + io->write_bus(fd, 0xaa, 0x98); + resp_cfi[0] = io->read_bus(fd, 0x20); + resp_cfi[1] = io->read_bus(fd, 0x22); + resp_cfi[2] = io->read_bus(fd, 0x24); + if (memcmp(resp_cfi, qry, sizeof(resp_cfi)) == 0) + { + info.size = 1u << io->read_bus(fd, 0x4e); + info.region_cnt = io->read_bus(fd, 0x58); + assert(0 < info.region_cnt && info.region_cnt <= 4); + for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) { + info.region[i].block_count = io->read_bus(fd, a + 0) + 1; + info.region[i].block_count += io->read_bus(fd, a + 2) << 8; + info.region[i].block_size = io->read_bus(fd, a + 4) << 8; + info.region[i].block_size |= io->read_bus(fd, a + 6) << 16; + info.region[i].start = total; + info.region[i].size = + info.region[i].block_size * info.region[i].block_count; + assert(info.region[i].size); + total += info.region[i].size; + } + if (info.size != total) + fprintf(stderr, "warning: total is %u, bad CFI?\n", total); - io->write_bus(fd, 0xaa, 0x98); // CFI Query - resp[0] = io->read_bus(fd, 0x20); - resp[1] = io->read_bus(fd, 0x22); - resp[2] = io->read_bus(fd, 0x24); - if (memcmp(resp, qry, sizeof(resp))) { - fprintf(stderr, "unexpected CFI response: %04x %04x %04x\n", - resp[0], resp[1], resp[2]); - exit(1); + info.prog_addr = 0xaaa; + io->write_bus(fd, 0, 0xf0); + info.mid = flash_seq_r(fd, 0x90, 0); // autoselect + info.did = io->read_bus(fd, 2); } - info.size = 1u << io->read_bus(fd, 0x4e); - info.region_cnt = io->read_bus(fd, 0x58); - assert(0 < info.region_cnt && info.region_cnt <= 4); - for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) { - info.region[i].block_count = io->read_bus(fd, a + 0) + 1; - info.region[i].block_count += io->read_bus(fd, a + 2) << 8; - info.region[i].block_size = io->read_bus(fd, a + 4) << 8; - info.region[i].block_size |= io->read_bus(fd, a + 6) << 16; - info.region[i].start = total; - info.region[i].size = - info.region[i].block_size * info.region[i].block_count; - assert(info.region[i].size); - total += info.region[i].size; + else + { + // try the SST protocol + info.prog_addr = 0x5555; + sst_mid = flash_seq_r(fd, 0x90, 0); + if (sst_mid == 0xbf) + { + info.mid = sst_mid; + info.did = io->read_bus(fd, 0x01); + switch (info.did) { + case 0xb5: + case 0xb6: + case 0xb7: + info.size = 128 * 1024 << (info.did - 0xb5); + break; + default: + fprintf(stderr, "unrecognized SST device %02x\n", info.did); + exit(1); + } + info.region_cnt = 1; + info.region[0].block_count = info.size / 4096; + info.region[0].block_size = 4096; + info.region[0].start = 0; + info.region[0].size = info.size; + } + else + info.prog_addr = 0; } io->write_bus(fd, 0, 0xf0); // flash reset + if (info.prog_addr == 0) { + fprintf(stderr, "unable to identify the flash chip :(\n"); + fprintf(stderr, "CFI response: %02x %02x %02x\n", + resp_cfi[0], resp_cfi[1], resp_cfi[2]); + fprintf(stderr, "SST MID: %02x\n", sst_mid); + exit(1); + } + printf("Flash info:\n"); printf("Manufacturer ID: %04x\n", info.mid); printf("Device ID: %04x\n", info.did); @@ -560,8 +601,6 @@ static void read_info(int fd) for (i = 0; i < info.region_cnt; i++) printf(" %5u x %u\n", info.region[i].block_size, info.region[i].block_count); - if (info.size != total) - fprintf(stderr, "warning: total is %u, bad CFI?\n", total); } static uint32_t get_block_addr(uint32_t addr, uint32_t blk_offset) -- 2.39.2 From f4924e98567056ac3c1e9caef67783a7992d07b0 Mon Sep 17 00:00:00 2001 From: notaz Date: Sun, 26 Sep 2021 20:44:32 +0300 Subject: [PATCH 11/11] add adapter handling for HiROM --- flashkit.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/flashkit.c b/flashkit.c index 843decb..fb64293 100644 --- a/flashkit.c +++ b/flashkit.c @@ -439,11 +439,81 @@ static void flash_seq_write8las(int fd, uint32_t a, const uint8_t *d) { } +// -- 8bit+adapter -- + +static void set_addr8a(int fd, uint32_t a) +{ + set_addr8(fd, do_flipflops(fd, a)); +} + +static uint16_t read_bus8a(int fd, uint32_t a) +{ + return read_bus8(fd, do_flipflops(fd, a)); +} + +static void write_bus8a(int fd, uint32_t a, uint16_t d) +{ + write_bus8(fd, do_flipflops(fd, a), d); +} + +static void flash_seq_write8a(int fd, uint32_t a, const uint8_t *d) +{ + // no flipflop clearing, see flash_seq_write8la + a = do_flipflops(fd, a); + uint8_t cmd[] = { + // unlock + CMD_ADDR, 0, + CMD_ADDR, 0x8a, + CMD_ADDR, 0xaa, + CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa, + CMD_ADDR, 0, + CMD_ADDR, 0x85, + CMD_ADDR, 0x55, + CMD_WR | PAR_SINGE | PAR_MODE8, 0x55, + // program setup + CMD_ADDR, 0, + CMD_ADDR, 0x8a, + CMD_ADDR, 0xaa, + CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0, + // program data + CMD_ADDR, a >> 16, + CMD_ADDR, a >> 8, + CMD_ADDR, a >> 0, + CMD_WR | PAR_SINGE | PAR_MODE8, *d, + CMD_RY + }; + + write_serial(fd, cmd, sizeof(cmd)); +} + +// -- 8bit+adapter+nocart -- + +static void set_addr8an(int fd, uint32_t a) +{ + set_addr8(fd, do_flipflops(fd, a) | 0x200000); +} + +static uint16_t read_bus8an(int fd, uint32_t a) +{ + return read_bus8(fd, do_flipflops(fd, a) | 0x200000); +} + +static void write_bus8an(int fd, uint32_t a, uint16_t d) +{ + write_bus8(fd, do_flipflops(fd, a) | 0x200000, d); +} + +static void flash_seq_write8an(int fd, uint32_t a, const uint8_t *d) +{ +} + #define N0 "" #define N1 "8bit" #define N2 "8bit+LoROM" #define N3 "8bit+LoROM+adapter" #define N4 "8bit+LoROM+adapter+sram" +#define N5 "8bit+adapter (use '-a 0x400000' for HiROM)" +#define N6 "8bit+adapter+nocart" static const struct iof { const char *name; @@ -461,6 +531,8 @@ io_ops[] = { N2, 1, set_addr8l, read_bus8l, write_bus8l, read_block8, flash_seq_write8l }, { N3, 1, set_addr8la, read_bus8la, write_bus8la, read_block8, flash_seq_write8la }, { N4, 0, set_addr8las, read_bus8las, write_bus8las, read_block8, flash_seq_write8las }, + { N5, 0, set_addr8a, read_bus8a, write_bus8a, read_block8, flash_seq_write8a }, + { N6, 0, set_addr8an, read_bus8an, write_bus8an, read_block8, flash_seq_write8an }, }; static const struct iof *io = &io_ops[0]; @@ -962,10 +1034,13 @@ int main(int argc, char *argv[]) printf("reading %ld bytes:\n", max(size_r, size_v)); print_progress(0, blks * sizeof(g_block)); - io->set_addr(fd, address_in); + a_blk = -1; for (done = 0; done < size_r || done < size_v; ) { - if (io->addrs_remapped) - io->set_addr(fd, address_in + done); + a = address_in + done; + if (io->addrs_remapped || (a >> 21) != a_blk) { + a_blk = a >> 21; + io->set_addr(fd, a); + } io->read_block(fd, g_block, sizeof(g_block)); if (f_r && done < size_r) { len = min(size_r - done, sizeof(g_block)); -- 2.39.2