diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2017-01-05 00:25:15 +0100 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2017-01-07 15:52:33 +0100 |
commit | 950f4bdc027cb8c83fd2145590549fdcf5522078 (patch) | |
tree | fb2d845b03c31c3aee674b5f227b74c8216e622e /utils/imxtools/scsitools | |
parent | 07781847826d6901e047c3c55d227aae487a9f4c (diff) | |
download | rockbox-950f4bdc027cb8c83fd2145590549fdcf5522078.tar.gz rockbox-950f4bdc027cb8c83fd2145590549fdcf5522078.zip |
imxtools: move firmware read/write to library
Split the ugly firmware read/write into a API function and a much simplified code.
Also the code can now report progress.
Change-Id: I3f998eaf0c067c6da42b1d2dd9c5a5bf43c6915d
Diffstat (limited to 'utils/imxtools/scsitools')
-rw-r--r-- | utils/imxtools/scsitools/scsitool.c | 264 | ||||
-rw-r--r-- | utils/imxtools/scsitools/stmp_scsi.c | 175 | ||||
-rw-r--r-- | utils/imxtools/scsitools/stmp_scsi.h | 8 |
3 files changed, 259 insertions, 188 deletions
diff --git a/utils/imxtools/scsitools/scsitool.c b/utils/imxtools/scsitools/scsitool.c index a84b5919d7..8367af6939 100644 --- a/utils/imxtools/scsitools/scsitool.c +++ b/utils/imxtools/scsitools/scsitool.c | |||
@@ -31,6 +31,8 @@ | |||
31 | #include <sys/stat.h> | 31 | #include <sys/stat.h> |
32 | #include <fcntl.h> | 32 | #include <fcntl.h> |
33 | #include <unistd.h> | 33 | #include <unistd.h> |
34 | #include <inttypes.h> | ||
35 | #include <errno.h> | ||
34 | #include "rbscsi.h" | 36 | #include "rbscsi.h" |
35 | #include "misc.h" | 37 | #include "misc.h" |
36 | #include "stmp_scsi.h" | 38 | #include "stmp_scsi.h" |
@@ -147,7 +149,7 @@ static int do_info(void) | |||
147 | cprintf_field(" Number of drives:", " %u\n", info.nr_drives); | 149 | cprintf_field(" Number of drives:", " %u\n", info.nr_drives); |
148 | if(info.has.size) | 150 | if(info.has.size) |
149 | { | 151 | { |
150 | cprintf_field(" Media size:", " %llu ", (unsigned long long)info.size); | 152 | cprintf_field(" Media size:", " %" PRIu64 " ", info.size); |
151 | cprintf(RED, "(%.3f %s)\n", get_size_natural(info.size), get_size_suffix(info.size)); | 153 | cprintf(RED, "(%.3f %s)\n", get_size_natural(info.size), get_size_suffix(info.size)); |
152 | } | 154 | } |
153 | if(info.has.alloc_size) | 155 | if(info.has.alloc_size) |
@@ -226,17 +228,17 @@ static int do_info(void) | |||
226 | continue; | 228 | continue; |
227 | if(info.has.sector_size) | 229 | if(info.has.sector_size) |
228 | { | 230 | { |
229 | cprintf_field(" Sector size:", " %llu ", (unsigned long long)info.sector_size); | 231 | cprintf_field(" Sector size:", " %" PRIu32 " ", info.sector_size); |
230 | cprintf(RED, "(%.3f %s)\n", get_size_natural(info.sector_size), get_size_suffix(info.sector_size)); | 232 | cprintf(RED, "(%.3f %s)\n", get_size_natural(info.sector_size), get_size_suffix(info.sector_size)); |
231 | } | 233 | } |
232 | if(info.has.erase_size) | 234 | if(info.has.erase_size) |
233 | { | 235 | { |
234 | cprintf_field(" Erase size:", " %llu ", (unsigned long long)info.erase_size); | 236 | cprintf_field(" Erase size:", " %" PRIu32 " ", info.erase_size); |
235 | cprintf(RED, "(%.3f %s)\n", get_size_natural(info.erase_size), get_size_suffix(info.erase_size)); | 237 | cprintf(RED, "(%.3f %s)\n", get_size_natural(info.erase_size), get_size_suffix(info.erase_size)); |
236 | } | 238 | } |
237 | if(info.has.size) | 239 | if(info.has.size) |
238 | { | 240 | { |
239 | cprintf_field(" Drive size:", " %llu ", (unsigned long long)info.size); | 241 | cprintf_field(" Drive size:", " %" PRIu64 " ", info.size); |
240 | cprintf(RED, "(%.3f %s)\n", get_size_natural(info.size), get_size_suffix(info.size)); | 242 | cprintf(RED, "(%.3f %s)\n", get_size_natural(info.size), get_size_suffix(info.size)); |
241 | } | 243 | } |
242 | if(info.has.sector_count) | 244 | if(info.has.sector_count) |
@@ -282,92 +284,70 @@ static int do_info(void) | |||
282 | return 0; | 284 | return 0; |
283 | } | 285 | } |
284 | 286 | ||
285 | void do_extract(const char *file) | 287 | struct rw_fw_context_t |
286 | { | 288 | { |
287 | FILE *f = NULL; | 289 | int tot_size; |
288 | cprintf(BLUE, "Extracting firmware...\n"); | 290 | int cur_size; |
289 | 291 | int last_percent; | |
290 | struct stmp_logical_media_table_t *table = NULL; | 292 | FILE *f; |
291 | int ret = stmp_get_logical_media_table(g_dev_fd, &table); | 293 | bool read; |
292 | if(ret) | 294 | }; |
293 | { | 295 | |
294 | cprintf(GREY, "Cannot get logical table: %d\n", ret); | 296 | int rw_fw(void *user, void *buf, size_t size) |
295 | goto Lend; | 297 | { |
296 | } | 298 | struct rw_fw_context_t *ctx = user; |
297 | int entry = 0; | 299 | int this_percent = (ctx->cur_size * 100LLU) / ctx->tot_size; |
298 | while(entry < table->header.count) | 300 | if(this_percent != ctx->last_percent && (this_percent % 5) == 0) |
299 | if(table->entry[entry].type == SCSI_STMP_DRIVE_TYPE_SYSTEM && | ||
300 | table->entry[entry].tag == SCSI_STMP_DRIVE_TAG_SYSTEM_BOOT) | ||
301 | break; | ||
302 | else | ||
303 | entry++; | ||
304 | if(entry == table->header.count) | ||
305 | { | ||
306 | cprintf(GREY, "Cannot find firmware partition\n"); | ||
307 | goto Lend; | ||
308 | } | ||
309 | uint8_t drive_no = table->entry[entry].drive_no; | ||
310 | uint64_t drive_sz = table->entry[entry].size; | ||
311 | if(g_debug) | ||
312 | { | ||
313 | cprintf(RED, "* "); | ||
314 | cprintf_field("Drive: ", "%#x\n", drive_no); | ||
315 | cprintf(RED, "* "); | ||
316 | cprintf_field("Size: ", "%#llx\n", (unsigned long long)drive_sz); | ||
317 | } | ||
318 | struct stmp_logical_drive_info_t info; | ||
319 | ret = stmp_get_logical_drive_info(g_dev_fd, drive_no, &info); | ||
320 | if(ret || !info.has.sector_size) | ||
321 | { | ||
322 | cprintf(GREY, "Cannot get sector size\n"); | ||
323 | goto Lend; | ||
324 | } | ||
325 | unsigned sector_size = info.sector_size; | ||
326 | if(g_debug) | ||
327 | { | ||
328 | cprintf(RED, "* "); | ||
329 | cprintf_field("Sector size: ", "%lu\n", (unsigned long)sector_size); | ||
330 | } | ||
331 | uint8_t *sector = malloc(sector_size); | ||
332 | ret = stmp_read_logical_drive_sectors(g_dev_fd, drive_no, 0, 1, sector, sector_size); | ||
333 | if(ret) | ||
334 | { | ||
335 | cprintf(GREY, "Cannot read first sector: %d\n", ret); | ||
336 | goto Lend; | ||
337 | } | ||
338 | uint32_t fw_size = *(uint32_t *)(sector + 0x1c) * 16; | ||
339 | if(g_debug) | ||
340 | { | 301 | { |
341 | cprintf(RED, "* "); | 302 | cprintf(RED, "%d%%", this_percent); |
342 | cprintf_field("Firmware size: ", "%#x\n", fw_size); | 303 | cprintf(YELLOW, "..."); |
304 | fflush(stdout); | ||
343 | } | 305 | } |
306 | ctx->last_percent = this_percent; | ||
307 | int ret = -1; | ||
308 | if(ctx->read) | ||
309 | ret = fread(buf, size, 1, ctx->f); | ||
310 | else | ||
311 | ret = fwrite(buf, size, 1, ctx->f); | ||
312 | ctx->cur_size += size; | ||
313 | if(ret != 1) | ||
314 | return -1; | ||
315 | else | ||
316 | return size; | ||
317 | } | ||
344 | 318 | ||
345 | f = fopen(file, "wb"); | 319 | void rw_finish(struct rw_fw_context_t *ctx) |
320 | { | ||
321 | if(ctx->last_percent == 100) | ||
322 | return; | ||
323 | cprintf(RED, "100%%\n"); | ||
324 | } | ||
325 | |||
326 | void do_extract(const char *file) | ||
327 | { | ||
328 | FILE *f = fopen(file, "wb"); | ||
346 | if(f == NULL) | 329 | if(f == NULL) |
347 | { | 330 | { |
348 | cprintf(GREY, "Cannot open '%s' for writing: %m\n", file); | 331 | cprintf(GREY, "Cannot open output file: %s\n", strerror(errno)); |
349 | goto Lend; | 332 | return; |
350 | } | 333 | } |
351 | 334 | int ret = stmp_read_firmware(g_dev_fd, NULL, NULL); | |
352 | for(int sec = 0; sec * sector_size < fw_size; sec++) | 335 | if(ret < 0) |
353 | { | 336 | { |
354 | ret = stmp_read_logical_drive_sectors(g_dev_fd, drive_no, sec, 1, sector, sector_size); | 337 | cprintf(GREY, "Cannot get firmware size: %d\n", ret); |
355 | if(ret) | 338 | return; |
356 | { | ||
357 | cprintf(GREY, "Cannot read sector %d: %d\n", sec, ret); | ||
358 | goto Lend; | ||
359 | } | ||
360 | if(fwrite(sector, sector_size, 1, f) != 1) | ||
361 | { | ||
362 | cprintf(GREY, "Write failed: %m\n"); | ||
363 | goto Lend; | ||
364 | } | ||
365 | } | 339 | } |
366 | cprintf(BLUE, "Done\n"); | 340 | struct rw_fw_context_t ctx; |
367 | Lend: | 341 | ctx.tot_size = ret; |
368 | free(table); | 342 | ctx.cur_size = 0; |
369 | if(f) | 343 | ctx.f = f; |
370 | fclose(f); | 344 | ctx.last_percent = -1; |
345 | ctx.read = false; | ||
346 | ret = stmp_read_firmware(g_dev_fd, &ctx, &rw_fw); | ||
347 | if(ret < 0) | ||
348 | cprintf(GREY, "Cannot read firmware: %d\n", ret); | ||
349 | rw_finish(&ctx); | ||
350 | fclose(f); | ||
371 | } | 351 | } |
372 | 352 | ||
373 | void do_write(const char *file, int want_a_brick) | 353 | void do_write(const char *file, int want_a_brick) |
@@ -379,115 +359,25 @@ void do_write(const char *file, int want_a_brick) | |||
379 | cprintf(GREY, "option on the command line and do not complain if you end up with a brick ;)\n"); | 359 | cprintf(GREY, "option on the command line and do not complain if you end up with a brick ;)\n"); |
380 | return; | 360 | return; |
381 | } | 361 | } |
382 | FILE *f = NULL; | 362 | FILE *f = fopen(file, "rb"); |
383 | cprintf(BLUE, "Writing firmware...\n"); | ||
384 | |||
385 | struct stmp_logical_media_table_t *table = NULL; | ||
386 | int ret = stmp_get_logical_media_table(g_dev_fd, &table); | ||
387 | if(ret) | ||
388 | { | ||
389 | cprintf(GREY, "Cannot get logical table: %d\n", ret); | ||
390 | goto Lend; | ||
391 | } | ||
392 | int entry = 0; | ||
393 | while(entry < table->header.count) | ||
394 | if(table->entry[entry].type == SCSI_STMP_DRIVE_TYPE_SYSTEM && | ||
395 | table->entry[entry].tag == SCSI_STMP_DRIVE_TAG_SYSTEM_BOOT) | ||
396 | break; | ||
397 | else | ||
398 | entry++; | ||
399 | if(entry == table->header.count) | ||
400 | { | ||
401 | cprintf(GREY, "Cannot find firmware partition\n"); | ||
402 | goto Lend; | ||
403 | } | ||
404 | uint8_t drive_no = table->entry[entry].drive_no; | ||
405 | uint64_t drive_sz = table->entry[entry].size; | ||
406 | if(g_debug) | ||
407 | { | ||
408 | cprintf(RED, "* "); | ||
409 | cprintf_field("Drive: ", "%#x\n", drive_no); | ||
410 | cprintf(RED, "* "); | ||
411 | cprintf_field("Size: ", "%#llx\n", (unsigned long long)drive_sz); | ||
412 | } | ||
413 | struct stmp_logical_drive_info_t info; | ||
414 | ret = stmp_get_logical_drive_info(g_dev_fd, drive_no, &info); | ||
415 | if(ret || !info.has.sector_size) | ||
416 | { | ||
417 | cprintf(GREY, "Cannot get sector size\n"); | ||
418 | goto Lend; | ||
419 | } | ||
420 | unsigned sector_size = info.sector_size; | ||
421 | uint8_t *sector = malloc(sector_size); | ||
422 | |||
423 | /* sanity check by reading first sector */ | ||
424 | ret = stmp_read_logical_drive_sectors(g_dev_fd, drive_no, 0, 1, sector, sector_size); | ||
425 | if(ret) | ||
426 | { | ||
427 | cprintf(GREY, "Cannot read first sector: %d\n", ret); | ||
428 | return; | ||
429 | } | ||
430 | uint32_t sig = *(uint32_t *)(sector + 0x14); | ||
431 | if(sig != 0x504d5453) | ||
432 | { | ||
433 | cprintf(GREY, "There is something wrong: the first sector doesn't have the STMP signature. Bailing out...\n"); | ||
434 | return; | ||
435 | } | ||
436 | |||
437 | f = fopen(file, "rb"); | ||
438 | if(f == NULL) | 363 | if(f == NULL) |
439 | { | 364 | { |
440 | cprintf(GREY, "Cannot open '%s' for writing: %m\n", file); | 365 | cprintf(GREY, "Cannot open output file: %s\n", strerror(errno)); |
441 | goto Lend; | 366 | return; |
442 | } | 367 | } |
368 | struct rw_fw_context_t ctx; | ||
443 | fseek(f, 0, SEEK_END); | 369 | fseek(f, 0, SEEK_END); |
444 | int fw_size = ftell(f); | 370 | ctx.tot_size = ftell(f); |
445 | fseek(f, 0, SEEK_SET); | 371 | fseek(f, 0, SEEK_SET); |
446 | if(g_debug) | 372 | ctx.cur_size = 0; |
447 | { | 373 | ctx.f = f; |
448 | cprintf(RED, "* "); | 374 | ctx.last_percent = -1; |
449 | cprintf_field("Firmware size: ", "%#x\n", fw_size); | 375 | ctx.read = true; |
450 | } | 376 | int ret = stmp_write_firmware(g_dev_fd, &ctx, &rw_fw); |
451 | /* sanity check size */ | 377 | if(ret < 0) |
452 | if((uint64_t)fw_size > drive_sz) | 378 | cprintf(GREY, "Cannot write firmware: %d\n", ret); |
453 | { | 379 | rw_finish(&ctx); |
454 | cprintf(GREY, "You cannot write a firmware greater than the partition size.\n"); | 380 | fclose(f); |
455 | goto Lend; | ||
456 | } | ||
457 | |||
458 | int percent = -1; | ||
459 | for(int off = 0; off < fw_size; off += sector_size) | ||
460 | { | ||
461 | int sec = off / sector_size; | ||
462 | int this_percent = (sec * 100) / (fw_size / sector_size); | ||
463 | if(this_percent != percent && (this_percent % 5) == 0) | ||
464 | { | ||
465 | cprintf(RED, "%d%%", this_percent); | ||
466 | cprintf(YELLOW, "..."); | ||
467 | fflush(stdout); | ||
468 | } | ||
469 | percent = this_percent; | ||
470 | int xfer_len = MIN(fw_size - off, (int)sector_size); | ||
471 | if(fread(sector, xfer_len, 1, f) != 1) | ||
472 | { | ||
473 | cprintf(GREY, "Read failed: %m\n"); | ||
474 | goto Lend; | ||
475 | } | ||
476 | /* NOTE transfer a whole sector even if incomplete, the device won't access | ||
477 | * partial sectors */ | ||
478 | if(xfer_len < (int)sector_size) | ||
479 | memset(sector + xfer_len, 0, sector_size - xfer_len); | ||
480 | ret = stmp_write_logical_drive_sectors(g_dev_fd, drive_no, sec, 1, sector, sector_size); | ||
481 | if(ret) | ||
482 | { | ||
483 | cprintf(GREY, "Cannot write sector %d: %d\n", sec, ret); | ||
484 | goto Lend; | ||
485 | } | ||
486 | } | ||
487 | cprintf(BLUE, "Done\n"); | ||
488 | Lend: | ||
489 | if(f) | ||
490 | fclose(f); | ||
491 | } | 381 | } |
492 | 382 | ||
493 | static void usage(void) | 383 | static void usage(void) |
@@ -586,14 +476,14 @@ int main(int argc, char **argv) | |||
586 | rb_scsi_device_t scsi_dev = rb_scsi_open(argv[optind], g_debug ? RB_SCSI_DEBUG : 0, NULL, scsi_printf); | 476 | rb_scsi_device_t scsi_dev = rb_scsi_open(argv[optind], g_debug ? RB_SCSI_DEBUG : 0, NULL, scsi_printf); |
587 | if(scsi_dev == 0) | 477 | if(scsi_dev == 0) |
588 | { | 478 | { |
589 | cprintf(GREY, "Cannot open device: %m\n"); | 479 | cprintf(GREY, "Cannot open device\n"); |
590 | ret = 1; | 480 | ret = 1; |
591 | goto Lend; | 481 | goto Lend; |
592 | } | 482 | } |
593 | g_dev_fd = stmp_open(scsi_dev, g_debug ? STMP_DEBUG : 0, NULL, scsi_printf); | 483 | g_dev_fd = stmp_open(scsi_dev, g_debug ? STMP_DEBUG : 0, NULL, scsi_printf); |
594 | if(g_dev_fd == 0) | 484 | if(g_dev_fd == 0) |
595 | { | 485 | { |
596 | cprintf(GREY, "Cannot open stmp device: %m\n"); | 486 | cprintf(GREY, "Cannot open stmp device\n"); |
597 | ret = 2; | 487 | ret = 2; |
598 | goto Lend; | 488 | goto Lend; |
599 | } | 489 | } |
diff --git a/utils/imxtools/scsitools/stmp_scsi.c b/utils/imxtools/scsitools/stmp_scsi.c index dcda91e295..e1d0dc4c77 100644 --- a/utils/imxtools/scsitools/stmp_scsi.c +++ b/utils/imxtools/scsitools/stmp_scsi.c | |||
@@ -23,7 +23,6 @@ | |||
23 | #include <stdarg.h> | 23 | #include <stdarg.h> |
24 | #include <stdio.h> | 24 | #include <stdio.h> |
25 | #define _BSD_SOURCE | 25 | #define _BSD_SOURCE |
26 | #include <endian.h> | ||
27 | #include "stmp_scsi.h" | 26 | #include "stmp_scsi.h" |
28 | 27 | ||
29 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) | 28 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) |
@@ -131,6 +130,7 @@ int stmp_sense_analysis(stmp_device_t dev, int status, uint8_t *sense, int sense | |||
131 | for(int i = 0; i < sense_size; i++) | 130 | for(int i = 0; i < sense_size; i++) |
132 | stmp_printf(dev, " %02x", sense[i]); | 131 | stmp_printf(dev, " %02x", sense[i]); |
133 | stmp_printf(dev, "\n"); | 132 | stmp_printf(dev, "\n"); |
133 | rb_scsi_decode_sense(dev->dev, sense, sense_size); | ||
134 | } | 134 | } |
135 | return status; | 135 | return status; |
136 | } | 136 | } |
@@ -614,3 +614,176 @@ int stmp_get_logical_drive_info(stmp_device_t dev, uint8_t drive, struct stmp_lo | |||
614 | } | 614 | } |
615 | return 0; | 615 | return 0; |
616 | } | 616 | } |
617 | |||
618 | int stmp_read_firmware(stmp_device_t dev, void *user, stmp_fw_rw_fn_t fn) | ||
619 | { | ||
620 | /* read logicial table */ | ||
621 | uint8_t *sector = NULL; | ||
622 | struct stmp_logical_media_table_t *table = NULL; | ||
623 | int ret = stmp_get_logical_media_table(dev, &table); | ||
624 | if(ret) | ||
625 | { | ||
626 | stmp_printf(dev, "Cannot get logical table: %d\n", ret); | ||
627 | return -1; | ||
628 | } | ||
629 | /* locate firmware partition */ | ||
630 | int entry = 0; | ||
631 | while(entry < table->header.count) | ||
632 | if(table->entry[entry].type == SCSI_STMP_DRIVE_TYPE_SYSTEM && | ||
633 | table->entry[entry].tag == SCSI_STMP_DRIVE_TAG_SYSTEM_BOOT) | ||
634 | break; | ||
635 | else | ||
636 | entry++; | ||
637 | if(entry == table->header.count) | ||
638 | { | ||
639 | stmp_printf(dev, "Cannot find firmware partition\n"); | ||
640 | goto Lerr; | ||
641 | } | ||
642 | uint8_t drive_no = table->entry[entry].drive_no; | ||
643 | uint64_t drive_sz = table->entry[entry].size; | ||
644 | stmp_debugf(dev, "Firmware drive: %#x\n", drive_no); | ||
645 | stmp_debugf(dev, "Firmware max size: %#llx\n", (unsigned long long)drive_sz); | ||
646 | /* get drive info */ | ||
647 | struct stmp_logical_drive_info_t info; | ||
648 | ret = stmp_get_logical_drive_info(dev, drive_no, &info); | ||
649 | if(ret || !info.has.sector_size) | ||
650 | { | ||
651 | stmp_printf(dev, "Cannot get sector size\n"); | ||
652 | goto Lerr; | ||
653 | } | ||
654 | unsigned sector_size = info.sector_size; | ||
655 | stmp_debugf(dev, "Firmware sector size: %lu\n", (unsigned long)sector_size); | ||
656 | /* allocate a buffer for one sector */ | ||
657 | sector = malloc(sector_size); | ||
658 | /* read the first sector to check it is correct and get the total size */ | ||
659 | ret = stmp_read_logical_drive_sectors(dev, drive_no, 0, 1, sector, sector_size); | ||
660 | if(ret) | ||
661 | { | ||
662 | stmp_printf(dev, "Cannot read first sector: %d\n", ret); | ||
663 | goto Lerr; | ||
664 | } | ||
665 | uint32_t sig = *(uint32_t *)(sector + 0x14); | ||
666 | if(sig != 0x504d5453) | ||
667 | { | ||
668 | stmp_printf(dev, "There is something wrong: the first sector doesn't have the STMP signature.\n"); | ||
669 | goto Lerr; | ||
670 | } | ||
671 | uint32_t fw_size = *(uint32_t *)(sector + 0x1c) * 16; /* see SB file format */ | ||
672 | stmp_debugf(dev, "Firmware size: %#x\n", fw_size); | ||
673 | /* if fn is NULL, just return the size immediately */ | ||
674 | if(fn != NULL) | ||
675 | { | ||
676 | /* read all sectors one by one */ | ||
677 | for(int sec = 0; sec * sector_size < fw_size; sec++) | ||
678 | { | ||
679 | ret = stmp_read_logical_drive_sectors(dev, drive_no, sec, 1, sector, sector_size); | ||
680 | if(ret) | ||
681 | { | ||
682 | stmp_printf(dev, "Cannot read sector %d: %d\n", sec, ret); | ||
683 | goto Lerr; | ||
684 | } | ||
685 | int xfer_len = MIN(sector_size, fw_size - sec * sector_size); | ||
686 | ret = fn(user, sector, xfer_len); | ||
687 | if(ret != xfer_len) | ||
688 | { | ||
689 | stmp_printf(dev, "User write failed: %d\n", ret); | ||
690 | goto Lerr; | ||
691 | } | ||
692 | } | ||
693 | } | ||
694 | ret = fw_size; | ||
695 | Lend: | ||
696 | free(table); | ||
697 | if(sector) | ||
698 | free(sector); | ||
699 | return ret; | ||
700 | Lerr: | ||
701 | ret = -1; | ||
702 | goto Lend; | ||
703 | } | ||
704 | |||
705 | int stmp_write_firmware(stmp_device_t dev, void *user, stmp_fw_rw_fn_t fn) | ||
706 | { | ||
707 | /* read logicial table */ | ||
708 | struct stmp_logical_media_table_t *table = NULL; | ||
709 | int ret = stmp_get_logical_media_table(dev, &table); | ||
710 | if(ret) | ||
711 | { | ||
712 | stmp_printf(dev, "Cannot get logical table: %d\n", ret); | ||
713 | return -1; | ||
714 | } | ||
715 | /* locate firmware partition */ | ||
716 | int entry = 0; | ||
717 | while(entry < table->header.count) | ||
718 | if(table->entry[entry].type == SCSI_STMP_DRIVE_TYPE_SYSTEM && | ||
719 | table->entry[entry].tag == SCSI_STMP_DRIVE_TAG_SYSTEM_BOOT) | ||
720 | break; | ||
721 | else | ||
722 | entry++; | ||
723 | if(entry == table->header.count) | ||
724 | { | ||
725 | stmp_printf(dev, "Cannot find firmware partition\n"); | ||
726 | goto Lerr; | ||
727 | } | ||
728 | uint8_t drive_no = table->entry[entry].drive_no; | ||
729 | uint64_t drive_sz = table->entry[entry].size; | ||
730 | stmp_debugf(dev, "Firmware drive: %#x\n", drive_no); | ||
731 | stmp_debugf(dev, "Firmware max size: %#llx\n", (unsigned long long)drive_sz); | ||
732 | /* get drive info */ | ||
733 | struct stmp_logical_drive_info_t info; | ||
734 | ret = stmp_get_logical_drive_info(dev, drive_no, &info); | ||
735 | if(ret || !info.has.sector_size) | ||
736 | { | ||
737 | stmp_printf(dev, "Cannot get sector size\n"); | ||
738 | goto Lerr; | ||
739 | } | ||
740 | unsigned sector_size = info.sector_size; | ||
741 | stmp_debugf(dev, "Firmware sector size: %lu\n", (unsigned long)sector_size); | ||
742 | /* allocate a buffer for one sector */ | ||
743 | uint8_t *sector = malloc(sector_size); | ||
744 | /* read the first sector to check it is correct and get the total size */ | ||
745 | ret = fn(user, sector, sector_size); | ||
746 | /* the whole file could be smaller than one sector, but it must be greater | ||
747 | * then the header size */ | ||
748 | if(ret < 0x20) | ||
749 | { | ||
750 | stmp_printf(dev, "User read failed: %d\n", ret); | ||
751 | goto Lerr; | ||
752 | } | ||
753 | uint32_t sig = *(uint32_t *)(sector + 0x14); | ||
754 | if(sig != 0x504d5453) | ||
755 | { | ||
756 | stmp_printf(dev, "There is something wrong: the first sector doesn't have the STMP signature.\n"); | ||
757 | goto Lerr; | ||
758 | } | ||
759 | uint32_t fw_size = *(uint32_t *)(sector + 0x1c) * 16; /* see SB file format */ | ||
760 | stmp_debugf(dev, "Firmware size: %#x\n", fw_size); | ||
761 | /* write all sectors one by one */ | ||
762 | for(int sec = 0; sec * sector_size < fw_size; sec++) | ||
763 | { | ||
764 | int xfer_len = MIN(sector_size, fw_size - sec * sector_size); | ||
765 | /* avoid rereading the first sector */ | ||
766 | if(sec != 0) | ||
767 | ret = fn(user, sector, xfer_len); | ||
768 | if(ret != xfer_len) | ||
769 | { | ||
770 | stmp_printf(dev, "User read failed: %d\n", ret); | ||
771 | goto Lerr; | ||
772 | } | ||
773 | if(ret < (int)sector_size) | ||
774 | memset(sector + ret, 0, sector_size - ret); | ||
775 | ret = stmp_write_logical_drive_sectors(dev, drive_no, sec, 1, sector, sector_size); | ||
776 | if(ret) | ||
777 | { | ||
778 | stmp_printf(dev, "Cannot write sector %d: %d\n", sec, ret); | ||
779 | goto Lerr; | ||
780 | } | ||
781 | } | ||
782 | ret = fw_size; | ||
783 | Lend: | ||
784 | free(table); | ||
785 | return ret; | ||
786 | Lerr: | ||
787 | ret = -1; | ||
788 | goto Lend; | ||
789 | } | ||
diff --git a/utils/imxtools/scsitools/stmp_scsi.h b/utils/imxtools/scsitools/stmp_scsi.h index 68d77daeec..56068b5d4c 100644 --- a/utils/imxtools/scsitools/stmp_scsi.h +++ b/utils/imxtools/scsitools/stmp_scsi.h | |||
@@ -355,6 +355,14 @@ int stmp_read_logical_drive_sectors(stmp_device_t dev, uint8_t drive, uint64_t a | |||
355 | uint32_t count, void *buffer, int buffer_size); | 355 | uint32_t count, void *buffer, int buffer_size); |
356 | int stmp_write_logical_drive_sectors(stmp_device_t dev, uint8_t drive, uint64_t address, | 356 | int stmp_write_logical_drive_sectors(stmp_device_t dev, uint8_t drive, uint64_t address, |
357 | uint32_t count, void *buffer, int buffer_size); | 357 | uint32_t count, void *buffer, int buffer_size); |
358 | /* return <0 on error, or firmware size in bytes otherwise, | ||
359 | * if not NULL, the read/write function will be called as many times as needed to provide | ||
360 | * the entire firmware, it should return number of bytes read/written on success or -1 on error | ||
361 | * in all cases, the total size of the firmware is based on the header | ||
362 | * if NULL for read, return firmware size */ | ||
363 | typedef int (*stmp_fw_rw_fn_t)(void *user, void *buf, size_t size); | ||
364 | int stmp_read_firmware(stmp_device_t dev, void *user, stmp_fw_rw_fn_t fn); | ||
365 | int stmp_write_firmware(stmp_device_t dev, void *user, stmp_fw_rw_fn_t fn); | ||
358 | /* string helpers */ | 366 | /* string helpers */ |
359 | const char *stmp_get_logical_media_type_string(uint32_t type); | 367 | const char *stmp_get_logical_media_type_string(uint32_t type); |
360 | const char *stmp_get_logical_media_vendor_string(uint32_t type); | 368 | const char *stmp_get_logical_media_vendor_string(uint32_t type); |