Commit | Line | Data |
---|---|---|
d9502b8d | 1 | /* |
2 | * Tool for USB serial communication with Krikzz's FlashKit-MD | |
3 | * Copyright (c) 2017 notaz | |
4 | * | |
5 | * Permission is hereby granted, free of charge, to any person obtaining | |
6 | * a copy of this software and associated documentation files (the | |
7 | * "Software"), to deal in the Software without restriction, including | |
8 | * without limitation the rights to use, copy, modify, merge, publish, | |
9 | * distribute, sublicense, and/or sell copies of the Software, and to | |
10 | * permit persons to whom the Software is furnished to do so, subject to | |
11 | * the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be | |
14 | * included in all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
20 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
21 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
22 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
23 | * SOFTWARE. | |
24 | */ | |
25 | ||
26 | #include <stdio.h> | |
27 | #include <string.h> | |
28 | #include <stdlib.h> | |
29 | #include <stdint.h> | |
30 | #include <assert.h> | |
31 | #include <sys/types.h> | |
32 | #include <sys/stat.h> | |
33 | #include <fcntl.h> | |
34 | #include <arpa/inet.h> // hton | |
35 | #include <termios.h> | |
36 | #include <unistd.h> | |
37 | ||
38 | #ifndef min | |
39 | #define min(x, y) ((x) < (y) ? (x) : (y)) | |
40 | #define max(x, y) ((x) > (y) ? (x) : (y)) | |
41 | #endif | |
42 | ||
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 | ||
c097fb89 | 122 | /* addr arg is always byte address */ |
123 | static void set_addr8(int fd, uint32_t addr) | |
d9502b8d | 124 | { |
125 | uint8_t cmd[6] = { | |
c097fb89 | 126 | CMD_ADDR, addr >> 16, |
127 | CMD_ADDR, addr >> 8, | |
128 | CMD_ADDR, addr >> 0 | |
129 | }; | |
130 | write_serial(fd, cmd, sizeof(cmd)); | |
131 | } | |
132 | ||
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 | |
d9502b8d | 145 | }; |
c097fb89 | 146 | uint8_t r; |
147 | ||
d9502b8d | 148 | write_serial(fd, cmd, sizeof(cmd)); |
c097fb89 | 149 | read_serial(fd, &r, sizeof(r)); |
150 | return r; | |
d9502b8d | 151 | } |
152 | ||
c097fb89 | 153 | static uint16_t read_bus16(int fd, uint32_t addr) |
d9502b8d | 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 | ||
c097fb89 | 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) | |
d9502b8d | 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 | ||
c097fb89 | 194 | static void read_block8(int fd, void *dst, uint32_t size) |
d9502b8d | 195 | { |
c097fb89 | 196 | // PAR_MODE8 does not work, so read as 16bit and throw away MSB |
197 | uint8_t tmp[0x10000], *d8 = dst; | |
d9502b8d | 198 | uint8_t cmd[5] = { |
c097fb89 | 199 | CMD_LEN, size >> 8, |
200 | CMD_LEN, size >> 0, | |
d9502b8d | 201 | CMD_RD | PAR_INC |
202 | }; | |
c097fb89 | 203 | int i; |
d9502b8d | 204 | |
205 | assert(size <= 0x10000); | |
206 | write_serial(fd, cmd, sizeof(cmd)); | |
207 | read_serial(fd, dst, size); | |
c097fb89 | 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]; | |
d9502b8d | 215 | } |
216 | ||
c097fb89 | 217 | static void read_block16(int fd, void *dst, uint32_t size) |
d9502b8d | 218 | { |
c097fb89 | 219 | uint8_t cmd[5] = { |
220 | CMD_LEN, size >> 9, | |
221 | CMD_LEN, size >> 1, | |
222 | CMD_RD | PAR_INC | |
223 | }; | |
d9502b8d | 224 | |
c097fb89 | 225 | assert(size <= 0x10000); |
226 | write_serial(fd, cmd, sizeof(cmd)); | |
227 | read_serial(fd, dst, size); | |
d9502b8d | 228 | } |
229 | ||
c097fb89 | 230 | static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d) |
d9502b8d | 231 | { |
c097fb89 | 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 | }; | |
d9502b8d | 254 | |
c097fb89 | 255 | write_serial(fd, cmd, sizeof(cmd)); |
d9502b8d | 256 | } |
257 | ||
c097fb89 | 258 | static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d) |
d9502b8d | 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 | ||
c097fb89 | 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 | ||
d9502b8d | 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 | |
c097fb89 | 363 | info.did = io->read_bus(fd, 2); |
d9502b8d | 364 | |
365 | // could enter CFI directly, but there seems to be a "stack" | |
366 | // of modes, so 2 exits would be needed | |
c097fb89 | 367 | io->write_bus(fd, 0, 0xf0); |
d9502b8d | 368 | |
c097fb89 | 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); | |
d9502b8d | 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 | } | |
c097fb89 | 378 | info.size = 1u << io->read_bus(fd, 0x4e); |
379 | info.region_cnt = io->read_bus(fd, 0x58); | |
d9502b8d | 380 | assert(0 < info.region_cnt && info.region_cnt <= 4); |
381 | for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) { | |
c097fb89 | 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; | |
d9502b8d | 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 | ||
c097fb89 | 393 | io->write_bus(fd, 0, 0xf0); // flash reset |
d9502b8d | 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 | { | |
f34ac796 | 409 | uint32_t i, base, faddr; |
d9502b8d | 410 | |
411 | assert(info.region_cnt); | |
f34ac796 | 412 | assert(info.size); |
413 | ||
414 | // get a flash address to allow mapper hardware | |
415 | faddr = addr & (info.size - 1); | |
416 | base = addr & ~(info.size - 1); | |
417 | ||
d9502b8d | 418 | for (i = 0; i < info.region_cnt; i++) { |
f34ac796 | 419 | if (info.region[i].start <= faddr |
420 | && faddr < info.region[i].start + info.region[i].size) | |
d9502b8d | 421 | { |
f34ac796 | 422 | uint32_t blk = (faddr - info.region[i].start) |
d9502b8d | 423 | / info.region[i].block_size |
424 | + blk_offset; | |
f34ac796 | 425 | return base + info.region[i].start |
d9502b8d | 426 | + blk * info.region[i].block_size; |
427 | } | |
428 | } | |
429 | ||
430 | fprintf(stderr, "\naddress out of range: 0x%x\n", addr); | |
431 | exit(1); | |
432 | } | |
433 | ||
434 | static void print_progress(uint32_t done, uint32_t total) | |
435 | { | |
436 | int i, step; | |
437 | ||
c097fb89 | 438 | printf("\r%06x/%06x |", done, total); |
d9502b8d | 439 | |
440 | step = (total + 19) / 20; | |
441 | for (i = step; i <= total; i += step) | |
442 | fputc(done >= i ? '=' : '-', stdout); | |
443 | printf("| %3d%%", done * 100 / total); | |
444 | fflush(stdout); | |
445 | if (done >= total) | |
446 | fputc('\n', stdout); | |
447 | } | |
448 | ||
78ad8139 | 449 | static FILE *open_prep_read(const char *fname, long *size) |
450 | { | |
451 | FILE *f = fopen(fname, "rb"); | |
452 | if (!f) { | |
453 | fprintf(stderr, "fopen %s: ", fname); | |
454 | perror(""); | |
455 | exit(1); | |
456 | } | |
457 | if (*size <= 0) { | |
458 | fseek(f, 0, SEEK_END); | |
459 | *size = ftell(f); | |
460 | fseek(f, 0, SEEK_SET); | |
461 | } | |
462 | if (*size <= 0) { | |
463 | fprintf(stderr, "size of %s is %ld\n", fname, *size); | |
464 | exit(1); | |
465 | } | |
466 | return f; | |
467 | } | |
468 | ||
af236294 NS |
469 | static const char *portname = "/dev/cu.usbserial-AL0254JM"; |
470 | ||
d9502b8d | 471 | static void usage(const char *argv0) |
472 | { | |
473 | printf("usage:\n" | |
474 | "%s [options]\n" | |
af236294 | 475 | " -d <ttydevice> (default %s)\n" |
d9502b8d | 476 | " -r <file> [size] dump the cart (default 4MB)\n" |
78ad8139 | 477 | " -w <file> [size] program the flash (def. file size)\n" |
478 | " -s <file> [size] simple write (SRAM, etc, def. file size)\n" | |
d9502b8d | 479 | " -e <size> erase (rounds to block size)\n" |
78ad8139 | 480 | " -a <start_address> read/write start address (default 0)\n" |
c097fb89 | 481 | " -8 8bit flash\n" |
d9502b8d | 482 | " -v verify written data\n" |
483 | " -i get info about the flash chip\n" | |
af236294 | 484 | , argv0, portname); |
d9502b8d | 485 | exit(1); |
486 | } | |
487 | ||
488 | static void invarg(int argc, char *argv[], int arg) | |
489 | { | |
490 | if (arg < argc) | |
491 | fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]); | |
492 | else | |
493 | fprintf(stderr, "missing required argument %d\n", arg); | |
494 | exit(1); | |
495 | } | |
496 | ||
497 | static void *getarg(int argc, char *argv[], int arg) | |
498 | { | |
499 | if (arg >= argc) | |
500 | invarg(argc, argv, arg); | |
501 | return argv[arg]; | |
502 | } | |
503 | ||
504 | static uint8_t g_block[0x10000]; | |
505 | static uint8_t g_block2[0x10000]; | |
506 | ||
507 | int main(int argc, char *argv[]) | |
508 | { | |
d9502b8d | 509 | const char *fname_w = NULL; |
510 | const char *fname_r = NULL; | |
78ad8139 | 511 | const char *fname_ws = NULL; |
d9502b8d | 512 | long size_w = 0; |
513 | long size_r = 0; | |
78ad8139 | 514 | long size_ws = 0; |
d9502b8d | 515 | long size_e = 0; |
516 | long size_v = 0; | |
517 | long len, address_in = 0; | |
518 | long a, a_blk, end; | |
519 | int do_info = 0; | |
520 | int do_verify = 0; | |
c097fb89 | 521 | int write_step = 2; |
d9502b8d | 522 | FILE *f_w = NULL; |
523 | FILE *f_r = NULL; | |
78ad8139 | 524 | FILE *f_ws = NULL; |
d9502b8d | 525 | uint8_t id[2] = { 0, 0 }; |
526 | uint8_t cmd; | |
527 | uint16_t rv; | |
528 | int arg = 1; | |
529 | int fd; | |
530 | ||
531 | if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) | |
532 | usage(argv[0]); | |
533 | ||
534 | for (arg = 1; arg < argc; arg++) { | |
535 | if (!strcmp(argv[arg], "-d")) { | |
536 | portname = getarg(argc, argv, ++arg); | |
537 | continue; | |
538 | } | |
539 | if (!strcmp(argv[arg], "-r")) { | |
540 | fname_r = getarg(argc, argv, ++arg); | |
541 | if (arg + 1 < argc && argv[arg + 1][0] != '-') { | |
542 | size_r = strtol(argv[++arg], NULL, 0); | |
543 | if (size_r <= 0) | |
544 | invarg(argc, argv, arg); | |
545 | } | |
546 | continue; | |
547 | } | |
548 | if (!strcmp(argv[arg], "-w")) { | |
549 | fname_w = getarg(argc, argv, ++arg); | |
550 | if (arg + 1 < argc && argv[arg + 1][0] != '-') { | |
551 | size_w = strtol(argv[++arg], NULL, 0); | |
552 | if (size_w <= 0) | |
553 | invarg(argc, argv, arg); | |
554 | } | |
555 | continue; | |
556 | } | |
78ad8139 | 557 | if (!strcmp(argv[arg], "-s")) { |
558 | fname_ws = getarg(argc, argv, ++arg); | |
559 | if (arg + 1 < argc && argv[arg + 1][0] != '-') { | |
560 | size_ws = strtol(argv[++arg], NULL, 0); | |
561 | if (size_ws <= 0) | |
562 | invarg(argc, argv, arg); | |
563 | } | |
564 | continue; | |
565 | } | |
d9502b8d | 566 | if (!strcmp(argv[arg], "-a")) { |
567 | address_in = strtol(getarg(argc, argv, ++arg), NULL, 0); | |
568 | if (address_in < 0 || (address_in & 1)) | |
569 | invarg(argc, argv, arg); | |
570 | continue; | |
571 | } | |
572 | if (!strcmp(argv[arg], "-e")) { | |
573 | size_e = strtol(getarg(argc, argv, ++arg), NULL, 0); | |
574 | if (size_e <= 0) | |
575 | invarg(argc, argv, arg); | |
576 | continue; | |
577 | } | |
c097fb89 | 578 | if (!strcmp(argv[arg], "-8")) { |
579 | io = &io_ops[0]; | |
580 | write_step = 1; | |
581 | continue; | |
582 | } | |
d9502b8d | 583 | if (!strcmp(argv[arg], "-v")) { |
584 | do_verify = 1; | |
585 | continue; | |
586 | } | |
587 | if (!strcmp(argv[arg], "-i")) { | |
588 | do_info = 1; | |
589 | continue; | |
590 | } | |
591 | invarg(argc, argv, arg); | |
592 | } | |
593 | ||
594 | if (fname_r && size_r == 0) | |
595 | size_r = 0x400000; | |
596 | ||
597 | if (fname_w) { | |
78ad8139 | 598 | f_w = open_prep_read(fname_w, &size_w); |
d9502b8d | 599 | if (size_e < size_w) |
600 | size_e = size_w; | |
601 | if (do_verify) | |
602 | size_v = size_w; | |
603 | } | |
604 | ||
605 | fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC); | |
606 | if (fd < 0) { | |
607 | fprintf(stderr, "open %s: ", portname); | |
608 | perror(""); | |
609 | return 1; | |
610 | } | |
611 | ||
78ad8139 | 612 | if (fname_ws) |
613 | f_ws = open_prep_read(fname_ws, &size_ws); | |
614 | ||
d9502b8d | 615 | setup(fd); |
616 | ||
617 | cmd = CMD_RD | PAR_SINGE | PAR_DEV_ID; | |
618 | write_serial(fd, &cmd, sizeof(cmd)); | |
619 | read_serial(fd, id, sizeof(id)); | |
620 | if (id[0] != id[1] || id[0] == 0) { | |
621 | fprintf(stderr, "unexpected id: %02x %02x\n", id[0], id[1]); | |
622 | return 1; | |
623 | } | |
624 | printf("flashkit id: %02x\n", id[0]); | |
625 | ||
626 | set_delay(fd, 1); | |
c097fb89 | 627 | io->write_bus(fd, 0, 0xf0); // flash reset |
d9502b8d | 628 | |
629 | if (do_info || size_e) | |
630 | read_info(fd); | |
631 | ||
632 | if (size_e) { | |
633 | // set_delay(fd, 0); // ? | |
634 | a_blk = get_block_addr(address_in, 0); | |
635 | end = address_in + size_e; | |
636 | ||
637 | printf("erasing %ld bytes:\n", size_e); | |
638 | print_progress(0, size_e); | |
639 | for (a = address_in; a < end; ) { | |
640 | flash_seq_erase(fd, a_blk); | |
641 | rv = ry_read(fd); | |
642 | if (rv != 0xffff) { | |
643 | fprintf(stderr, "\nerase error: %lx %04x\n", | |
644 | a_blk, rv); | |
645 | } | |
646 | ||
647 | a_blk = get_block_addr(a_blk, 1); | |
648 | a += a_blk - a; | |
649 | print_progress(a - address_in, size_e); | |
650 | } | |
651 | } | |
652 | if (f_w != NULL) { | |
653 | uint8_t b[2]; | |
654 | set_delay(fd, 0); | |
78ad8139 | 655 | printf("flashing %ld bytes:\n", size_w); |
c097fb89 | 656 | for (a = 0; a < size_w; a += write_step) { |
d9502b8d | 657 | ssize_t r; |
658 | ||
659 | b[1] = 0xff; | |
c097fb89 | 660 | len = min(size_w - a, write_step); |
d9502b8d | 661 | r = fread(b, 1, len, f_w); |
662 | if (r != len) { | |
663 | perror("\nfread"); | |
664 | return 1; | |
665 | } | |
c097fb89 | 666 | io->flash_seq_write(fd, address_in + a, b); |
d9502b8d | 667 | |
c097fb89 | 668 | if (!(a & 0x3ff)) |
d9502b8d | 669 | print_progress(a, size_w); |
670 | } | |
671 | print_progress(a, size_w); | |
672 | rv = ry_read(fd); | |
c097fb89 | 673 | if (write_step == 2 && rv != ((b[0] << 8) | b[1])) |
d9502b8d | 674 | fprintf(stderr, "warning: last bytes: %04x %02x%02x\n", |
675 | rv, b[0], b[1]); | |
676 | rewind(f_w); | |
677 | set_delay(fd, 1); | |
678 | } | |
78ad8139 | 679 | if (f_ws != NULL) { |
680 | printf("writing %ld bytes:\n", size_ws); | |
681 | for (a = 0; a < size_ws; a += write_step) { | |
682 | uint16_t b = 0xffff; | |
683 | ssize_t r; | |
684 | ||
685 | len = min(size_ws - a, write_step); | |
686 | r = fread(&b, 1, len, f_ws); | |
687 | if (r != len) { | |
688 | perror("\nfread"); | |
689 | return 1; | |
690 | } | |
691 | if (write_step == 2) | |
692 | b = htons(b); | |
693 | io->write_bus(fd, address_in + a, b); | |
694 | ||
695 | if (!(a & 0x3ff)) | |
696 | print_progress(a, size_ws); | |
697 | } | |
698 | print_progress(a, size_ws); | |
699 | } | |
d9502b8d | 700 | |
701 | if (fname_r || size_v) { | |
702 | long blks, blks_v, done, verify_diff = 0; | |
703 | ||
704 | blks = (size_r + sizeof(g_block) - 1) / sizeof(g_block); | |
705 | blks_v = (size_v + sizeof(g_block) - 1) / sizeof(g_block); | |
706 | blks = max(blks, blks_v); | |
707 | if (fname_r) { | |
708 | f_r = fopen(fname_r, "wb"); | |
709 | if (!f_r) { | |
710 | fprintf(stderr, "fopen %s: ", fname_r); | |
711 | perror(""); | |
712 | return 1; | |
713 | } | |
714 | } | |
715 | ||
716 | printf("reading %ld bytes:\n", max(size_r, size_v)); | |
717 | print_progress(0, blks * sizeof(g_block)); | |
c097fb89 | 718 | io->set_addr(fd, address_in); |
d9502b8d | 719 | for (done = 0; done < size_r || done < size_v; ) { |
c097fb89 | 720 | io->read_block(fd, g_block, sizeof(g_block)); |
d9502b8d | 721 | if (f_r && done < size_r) { |
722 | len = min(size_r - done, sizeof(g_block)); | |
723 | if (fwrite(g_block, 1, len, f_r) != len) { | |
724 | perror("fwrite"); | |
725 | return 1; | |
726 | } | |
727 | } | |
728 | if (done < size_v) { | |
729 | len = min(size_v - done, sizeof(g_block)); | |
730 | if (fread(g_block2, 1, len, f_w) != len) { | |
731 | perror("fread"); | |
732 | return 1; | |
733 | } | |
734 | verify_diff |= memcmp(g_block, g_block2, len); | |
735 | } | |
736 | done += sizeof(g_block); | |
737 | print_progress(done, blks * sizeof(g_block)); | |
738 | } | |
739 | if (verify_diff) { | |
740 | fprintf(stderr, "verify FAILED\n"); | |
741 | return 1; | |
742 | } | |
743 | } | |
744 | if (f_r) | |
745 | fclose(f_r); | |
746 | if (f_w) | |
747 | fclose(f_w); | |
748 | ||
749 | return 0; | |
750 | } |