support simple writes
[flashkit-mdc.git] / flashkit.c
1 /*
2  * Tool for USB serial communication with Krikzz's FlashKit-MD
3  * Copyright (c) 2017 notaz
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be 
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <assert.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <arpa/inet.h> // hton
35 #include <termios.h>
36 #include <unistd.h>
37
38 #ifndef min
39 #define min(x, y) ((x) < (y) ? (x) : (y))
40 #define max(x, y) ((x) > (y) ? (x) : (y))
41 #endif
42
43 enum dev_cmd {
44         CMD_ADDR = 0,
45         CMD_LEN = 1,
46         CMD_RD = 2,
47         CMD_WR = 3,
48         CMD_RY = 4,
49         CMD_DELAY = 5,
50 };
51 #define PAR_MODE8  (1 << 4)
52 #define PAR_DEV_ID (1 << 5)
53 #define PAR_SINGE  (1 << 6)
54 #define PAR_INC    (1 << 7)
55
56 static int setup(int fd)
57 {
58         struct termios tty;
59         int ret;
60
61         memset(&tty, 0, sizeof(tty));
62
63         ret = tcgetattr(fd, &tty);
64         if (ret != 0)
65         {
66                 perror("tcgetattr");
67                 return 1;
68         }
69
70         tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
71                         | INLCR | IGNCR | ICRNL | IXON);
72         tty.c_oflag &= ~OPOST;
73         tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
74         tty.c_cflag &= ~(CSIZE | PARENB);
75         tty.c_cflag |= CS8;
76
77         //tty.c_cc[VMIN] = 1;
78         //tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout
79
80         ret = tcsetattr(fd, TCSANOW, &tty);
81         if (ret != 0) {
82                 perror("tcsetattr");
83                 return ret;
84         }
85
86         return 0;
87 }
88
89 static int write_serial(int fd, const void *data, size_t size)
90 {
91         int ret;
92
93         ret = write(fd, data, size);
94         if (ret != size) {
95                 fprintf(stderr, "write %d/%zd: ", ret, size);
96                 perror("");
97                 exit(1);
98         }
99
100         return 0;
101 }
102
103 static int read_serial(int fd, void *data, size_t size)
104 {
105         size_t got = 0;
106         int ret;
107
108         while (got < size) {
109                 ret = read(fd, (char *)data + got, size - got);
110                 if (ret <= 0) {
111                         fprintf(stderr, "read %d %zd/%zd: ",
112                                 ret, got, size);
113                         perror("");
114                         exit(1);
115                 }
116                 got += ret;
117         }
118
119         return 0;
120 }
121
122 /* addr arg is always byte address */
123 static void set_addr8(int fd, uint32_t addr)
124 {
125         uint8_t cmd[6] = {
126                 CMD_ADDR, addr >> 16,
127                 CMD_ADDR, addr >> 8,
128                 CMD_ADDR, addr >> 0
129         };
130         write_serial(fd, cmd, sizeof(cmd));
131 }
132
133 static void set_addr16(int fd, uint32_t addr)
134 {
135         set_addr8(fd, addr >> 1);
136 }
137
138 static uint16_t read_bus8(int fd, uint32_t addr)
139 {
140         uint8_t cmd[7] = {
141                 CMD_ADDR, addr >> 16,
142                 CMD_ADDR, addr >> 8,
143                 CMD_ADDR, addr >> 0,
144                 CMD_RD | PAR_SINGE | PAR_MODE8
145         };
146         uint8_t r;
147
148         write_serial(fd, cmd, sizeof(cmd));
149         read_serial(fd, &r, sizeof(r));
150         return r;
151 }
152
153 static uint16_t read_bus16(int fd, uint32_t addr)
154 {
155         uint8_t cmd[7] = {
156                 CMD_ADDR, addr >> 17,
157                 CMD_ADDR, addr >> 9,
158                 CMD_ADDR, addr >> 1,
159                 CMD_RD | PAR_SINGE
160         };
161         uint16_t r;
162
163         write_serial(fd, cmd, sizeof(cmd));
164         read_serial(fd, &r, sizeof(r));
165         return ntohs(r);
166 }
167
168 static void write_bus8(int fd, uint32_t addr, uint16_t d)
169 {
170         uint8_t cmd[8] = {
171                 CMD_ADDR, addr >> 16,
172                 CMD_ADDR, addr >> 8,
173                 CMD_ADDR, addr >> 0,
174                 CMD_WR | PAR_SINGE | PAR_MODE8,
175                 d
176         };
177
178         write_serial(fd, cmd, sizeof(cmd));
179 }
180
181 static void write_bus16(int fd, uint32_t addr, uint16_t d)
182 {
183         uint8_t cmd[9] = {
184                 CMD_ADDR, addr >> 17,
185                 CMD_ADDR, addr >> 9,
186                 CMD_ADDR, addr >> 1,
187                 CMD_WR | PAR_SINGE,
188                 d >> 8, d
189         };
190
191         write_serial(fd, cmd, sizeof(cmd));
192 }
193
194 static void read_block8(int fd, void *dst, uint32_t size)
195 {
196         // PAR_MODE8 does not work, so read as 16bit and throw away MSB
197         uint8_t tmp[0x10000], *d8 = dst;
198         uint8_t cmd[5] = {
199                 CMD_LEN, size >> 8,
200                 CMD_LEN, size >> 0,
201                 CMD_RD | PAR_INC
202         };
203         int i;
204
205         assert(size <= 0x10000);
206         write_serial(fd, cmd, sizeof(cmd));
207         read_serial(fd, dst, size);
208         read_serial(fd, tmp, size);
209
210         for (i = 0; i < size / 2; i++)
211                 d8[i] = d8[i * 2 + 1];
212         d8 += size / 2;
213         for (i = 0; i < size / 2; i++)
214                 d8[i] = tmp[i * 2 + 1];
215 }
216
217 static void read_block16(int fd, void *dst, uint32_t size)
218 {
219         uint8_t cmd[5] = {
220                 CMD_LEN, size >> 9,
221                 CMD_LEN, size >> 1,
222                 CMD_RD | PAR_INC
223         };
224
225         assert(size <= 0x10000);
226         write_serial(fd, cmd, sizeof(cmd));
227         read_serial(fd, dst, size);
228 }
229
230 static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d)
231 {
232         uint8_t cmd[] = {
233                 // unlock
234                 CMD_ADDR, 0,
235                 CMD_ADDR, 0x0a,
236                 CMD_ADDR, 0xaa,
237                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
238                 CMD_ADDR, 0,
239                 CMD_ADDR, 0x05,
240                 CMD_ADDR, 0x55,
241                 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
242                 // program setup
243                 CMD_ADDR, 0,
244                 CMD_ADDR, 0x0a,
245                 CMD_ADDR, 0xaa,
246                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
247                 // program data
248                 CMD_ADDR, addr >> 16,
249                 CMD_ADDR, addr >> 8,
250                 CMD_ADDR, addr >> 0,
251                 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
252                 CMD_RY
253         };
254
255         write_serial(fd, cmd, sizeof(cmd));
256 }
257
258 static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d)
259 {
260         uint8_t cmd[] = {
261                 // unlock
262                 CMD_ADDR, 0,
263                 CMD_ADDR, 0x05,
264                 CMD_ADDR, 0x55,
265                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
266                 CMD_ADDR, 0,
267                 CMD_ADDR, 0x02,
268                 CMD_ADDR, 0xaa,
269                 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
270                 // program setup
271                 CMD_ADDR, 0,
272                 CMD_ADDR, 0x05,
273                 CMD_ADDR, 0x55,
274                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
275                 // program data
276                 CMD_ADDR, addr >> 17,
277                 CMD_ADDR, addr >> 9,
278                 CMD_ADDR, addr >> 1,
279                 CMD_WR | PAR_SINGE, d[0], d[1],
280                 CMD_RY
281         };
282
283         write_serial(fd, cmd, sizeof(cmd));
284 }
285
286 static const struct iof
287 {
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);
293 }
294 io_ops[] =
295 {
296         { set_addr8,  read_bus8,  write_bus8,  read_block8,  flash_seq_write8  },
297         { set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 },
298 };
299
300 static const struct iof *io = &io_ops[1];
301
302 static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr)
303 {
304         // unlock
305         io->write_bus(fd, 0xaaa, 0xaa);
306         io->write_bus(fd, 0x555, 0x55);
307
308         io->write_bus(fd, 0xaaa, cmd);
309         return io->read_bus(fd, addr);
310 }
311
312 static void flash_seq_erase(int fd, uint32_t addr)
313 {
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);
318
319         io->write_bus(fd, 0xaaa, 0xaa);
320         io->write_bus(fd, 0x555, 0x55);
321         io->write_bus(fd, addr, 0x30);
322 }
323
324 // status wait + dummy read to cause a wait?
325 static uint16_t ry_read(int fd)
326 {
327         uint8_t cmd[2] = { CMD_RY, CMD_RD | PAR_SINGE };
328         uint16_t rv = 0;
329
330         write_serial(fd, cmd, sizeof(cmd));
331         read_serial(fd, &rv, sizeof(rv));
332         return ntohs(rv);
333 }
334
335 static void set_delay(int fd, uint8_t delay)
336 {
337         uint8_t cmd[2] = { CMD_DELAY, delay };
338
339         write_serial(fd, cmd, sizeof(cmd));
340 }
341
342 static struct flash_info {
343         uint16_t mid;
344         uint16_t did;
345         uint32_t size;
346         uint16_t region_cnt;
347         struct {
348                 uint32_t block_size;
349                 uint32_t block_count;
350                 uint32_t start;
351                 uint32_t size;
352         } region[4];
353 } info;
354
355 static void read_info(int fd)
356 {
357         static const uint16_t qry[3] = { 'Q', 'R', 'Y' };
358         uint32_t total = 0;
359         uint16_t resp[3];
360         uint32_t i, a;
361
362         info.mid = flash_seq_r(fd, 0x90, 0); // autoselect
363         info.did = io->read_bus(fd, 2);
364
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);
368
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]);
376                 exit(1);
377         }
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;
391         }
392
393         io->write_bus(fd, 0, 0xf0); // flash reset
394
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);
405 }
406
407 static uint32_t get_block_addr(uint32_t addr, uint32_t blk_offset)
408 {
409         uint32_t i;
410
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)
415                 {
416                         uint32_t blk = (addr - info.region[i].start)
417                                         / info.region[i].block_size
418                                         + blk_offset;
419                         return info.region[i].start
420                                 + blk * info.region[i].block_size;
421                 }
422         }
423
424         fprintf(stderr, "\naddress out of range: 0x%x\n", addr);
425         exit(1);
426 }
427
428 static void print_progress(uint32_t done, uint32_t total)
429 {
430         int i, step;
431
432         printf("\r%06x/%06x |", done, total);
433
434         step = (total + 19) / 20;
435         for (i = step; i <= total; i += step)
436                 fputc(done >= i ? '=' : '-', stdout);
437         printf("| %3d%%", done * 100 / total);
438         fflush(stdout);
439         if (done >= total)
440                 fputc('\n', stdout);
441 }
442
443 static FILE *open_prep_read(const char *fname, long *size)
444 {
445         FILE *f = fopen(fname, "rb");
446         if (!f) {
447                 fprintf(stderr, "fopen %s: ", fname);
448                 perror("");
449                 exit(1);
450         }
451         if (*size <= 0) {
452                 fseek(f, 0, SEEK_END);
453                 *size = ftell(f);
454                 fseek(f, 0, SEEK_SET);
455         }
456         if (*size <= 0) {
457                 fprintf(stderr, "size of %s is %ld\n", fname, *size);
458                 exit(1);
459         }
460         return f;
461 }
462
463 static void usage(const char *argv0)
464 {
465         printf("usage:\n"
466                 "%s [options]\n"
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"
473                 "  -8                  8bit flash\n"
474                 "  -v                  verify written data\n"
475                 "  -i                  get info about the flash chip\n"
476                 , argv0);
477         exit(1);
478 }
479
480 static void invarg(int argc, char *argv[], int arg)
481 {
482         if (arg < argc)
483                 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
484         else
485                 fprintf(stderr, "missing required argument %d\n", arg);
486         exit(1);
487 }
488
489 static void *getarg(int argc, char *argv[], int arg)
490 {
491         if (arg >= argc)
492                 invarg(argc, argv, arg);
493         return argv[arg];
494 }
495
496 static uint8_t g_block[0x10000];
497 static uint8_t g_block2[0x10000];
498
499 int main(int argc, char *argv[])
500 {
501         const char *portname = "/dev/ttyUSB0";
502         const char *fname_w = NULL;
503         const char *fname_r = NULL;
504         const char *fname_ws = NULL;
505         long size_w = 0;
506         long size_r = 0;
507         long size_ws = 0;
508         long size_e = 0;
509         long size_v = 0;
510         long len, address_in = 0;
511         long a, a_blk, end;
512         int do_info = 0;
513         int do_verify = 0;
514         int write_step = 2;
515         FILE *f_w = NULL;
516         FILE *f_r = NULL;
517         FILE *f_ws = NULL;
518         uint8_t id[2] = { 0, 0 };
519         uint8_t cmd;
520         uint16_t rv;
521         int arg = 1;
522         int fd;
523
524         if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
525                 usage(argv[0]);
526
527         for (arg = 1; arg < argc; arg++) {
528                 if (!strcmp(argv[arg], "-d")) {
529                         portname = getarg(argc, argv, ++arg);
530                         continue;
531                 }
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);
536                                 if (size_r <= 0)
537                                         invarg(argc, argv, arg);
538                         }
539                         continue;
540                 }
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);
545                                 if (size_w <= 0)
546                                         invarg(argc, argv, arg);
547                         }
548                         continue;
549                 }
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);
554                                 if (size_ws <= 0)
555                                         invarg(argc, argv, arg);
556                         }
557                         continue;
558                 }
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);
563                         continue;
564                 }
565                 if (!strcmp(argv[arg], "-e")) {
566                         size_e = strtol(getarg(argc, argv, ++arg), NULL, 0);
567                         if (size_e <= 0)
568                                 invarg(argc, argv, arg);
569                         continue;
570                 }
571                 if (!strcmp(argv[arg], "-8")) {
572                         io = &io_ops[0];
573                         write_step = 1;
574                         continue;
575                 }
576                 if (!strcmp(argv[arg], "-v")) {
577                         do_verify = 1;
578                         continue;
579                 }
580                 if (!strcmp(argv[arg], "-i")) {
581                         do_info = 1;
582                         continue;
583                 }
584                 invarg(argc, argv, arg);
585         }
586
587         if (fname_r && size_r == 0)
588                 size_r = 0x400000;
589
590         if (fname_w) {
591                 f_w = open_prep_read(fname_w, &size_w);
592                 if (size_e < size_w)
593                         size_e = size_w;
594                 if (do_verify)
595                         size_v = size_w;
596         }
597
598         fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
599         if (fd < 0) {
600                 fprintf(stderr, "open %s: ", portname);
601                 perror("");
602                 return 1;
603         }
604
605         if (fname_ws)
606                 f_ws = open_prep_read(fname_ws, &size_ws);
607
608         setup(fd);
609
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]);
615                 return 1;
616         }
617         printf("flashkit id: %02x\n", id[0]);
618
619         set_delay(fd, 1);
620         io->write_bus(fd, 0, 0xf0); // flash reset
621
622         if (do_info || size_e)
623                 read_info(fd);
624
625         if (size_e) {
626                 // set_delay(fd, 0); // ?
627                 a_blk = get_block_addr(address_in, 0);
628                 end = address_in + size_e;
629
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);
634                         rv = ry_read(fd);
635                         if (rv != 0xffff) {
636                                 fprintf(stderr, "\nerase error: %lx %04x\n",
637                                         a_blk, rv);
638                         }
639
640                         a_blk = get_block_addr(a_blk, 1);
641                         a += a_blk - a;
642                         print_progress(a - address_in, size_e);
643                 }
644         }
645         if (f_w != NULL) {
646                 uint8_t b[2];
647                 set_delay(fd, 0);
648                 printf("flashing %ld bytes:\n", size_w);
649                 for (a = 0; a < size_w; a += write_step) {
650                         ssize_t r;
651
652                         b[1] = 0xff;
653                         len = min(size_w - a, write_step);
654                         r = fread(b, 1, len, f_w);
655                         if (r != len) {
656                                 perror("\nfread");
657                                 return 1;
658                         }
659                         io->flash_seq_write(fd, address_in + a, b);
660
661                         if (!(a & 0x3ff))
662                                 print_progress(a, size_w);
663                 }
664                 print_progress(a, size_w);
665                 rv = ry_read(fd);
666                 if (write_step == 2 && rv != ((b[0] << 8) | b[1]))
667                         fprintf(stderr, "warning: last bytes: %04x %02x%02x\n",
668                                 rv, b[0], b[1]);
669                 rewind(f_w);
670                 set_delay(fd, 1);
671         }
672         if (f_ws != NULL) {
673                 printf("writing %ld bytes:\n", size_ws);
674                 for (a = 0; a < size_ws; a += write_step) {
675                         uint16_t b = 0xffff;
676                         ssize_t r;
677
678                         len = min(size_ws - a, write_step);
679                         r = fread(&b, 1, len, f_ws);
680                         if (r != len) {
681                                 perror("\nfread");
682                                 return 1;
683                         }
684                         if (write_step == 2)
685                                 b = htons(b);
686                         io->write_bus(fd, address_in + a, b);
687
688                         if (!(a & 0x3ff))
689                                 print_progress(a, size_ws);
690                 }
691                 print_progress(a, size_ws);
692         }
693
694         if (fname_r || size_v) {
695                 long blks, blks_v, done, verify_diff = 0;
696
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);
700                 if (fname_r) {
701                         f_r = fopen(fname_r, "wb");
702                         if (!f_r) {
703                                 fprintf(stderr, "fopen %s: ", fname_r);
704                                 perror("");
705                                 return 1;
706                         }
707                 }
708
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) {
717                                         perror("fwrite");
718                                         return 1;
719                                 }
720                         }
721                         if (done < size_v) {
722                                 len = min(size_v - done, sizeof(g_block));
723                                 if (fread(g_block2, 1, len, f_w) != len) {
724                                         perror("fread");
725                                         return 1;
726                                 }
727                                 verify_diff |= memcmp(g_block, g_block2, len);
728                         }
729                         done += sizeof(g_block);
730                         print_progress(done, blks * sizeof(g_block));
731                 }
732                 if (verify_diff) {
733                         fprintf(stderr, "verify FAILED\n");
734                         return 1;
735                 }
736         }
737         if (f_r)
738                 fclose(f_r);
739         if (f_w)
740                 fclose(f_w);
741
742         return 0;
743 }