diff options
Diffstat (limited to 'src/dfuse.c')
-rw-r--r-- | src/dfuse.c | 338 |
1 files changed, 239 insertions, 99 deletions
diff --git a/src/dfuse.c b/src/dfuse.c index 4a02511..17a62f6 100644 --- a/src/dfuse.c +++ b/src/dfuse.c @@ -5,7 +5,7 @@ * as per the DfuSe 1.1a specification (ST documents AN3156, AN2606) * The DfuSe file format is described in ST document UM0391. * - * Copyright 2010-2014 Tormod Volden <debian.tormod@gmail.com> + * Copyright 2010-2018 Tormod Volden <debian.tormod@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include <stdio.h> #include <stdlib.h> #include <errno.h> @@ -33,25 +37,27 @@ #include "dfu_file.h" #include "dfuse.h" #include "dfuse_mem.h" +#include "quirks.h" #define DFU_TIMEOUT 5000 extern int verbose; static unsigned int last_erased_page = 1; /* non-aligned value, won't match */ -static struct memsegment *mem_layout; static unsigned int dfuse_address = 0; +static unsigned int dfuse_address_present = 0; static unsigned int dfuse_length = 0; static int dfuse_force = 0; static int dfuse_leave = 0; static int dfuse_unprotect = 0; static int dfuse_mass_erase = 0; +static int dfuse_will_reset = 0; -unsigned int quad2uint(unsigned char *p) +static unsigned int quad2uint(unsigned char *p) { return (*p + (*(p + 1) << 8) + (*(p + 2) << 16) + (*(p + 3) << 24)); } -void dfuse_parse_options(const char *options) +static void dfuse_parse_options(const char *options) { char *end; const char *endword; @@ -66,8 +72,9 @@ void dfuse_parse_options(const char *options) number = strtoul(options, &end, 0); if (end == endword) { dfuse_address = number; + dfuse_address_present = 1; } else { - errx(EX_IOERR, "Invalid dfuse address: %s", options); + errx(EX_USAGE, "Invalid dfuse address: %s", options); } options = endword; } @@ -101,20 +108,25 @@ void dfuse_parse_options(const char *options) options += 10; continue; } + if (!strncmp(options, "will-reset", endword - options)) { + dfuse_will_reset = 1; + options += 10; + continue; + } /* any valid number is interpreted as upload length */ number = strtoul(options, &end, 0); if (end == endword) { dfuse_length = number; } else { - errx(EX_IOERR, "Invalid dfuse modifier: %s", options); + errx(EX_USAGE, "Invalid dfuse modifier: %s", options); } options = endword; } } /* DFU_UPLOAD request for DfuSe 1.1a */ -int dfuse_upload(struct dfu_if *dif, const unsigned short length, +static int dfuse_upload(struct dfu_if *dif, const unsigned short length, unsigned char *data, unsigned short transaction) { int status; @@ -130,14 +142,14 @@ int dfuse_upload(struct dfu_if *dif, const unsigned short length, /* wLength */ length, DFU_TIMEOUT); if (status < 0) { - errx(EX_IOERR, "%s: libusb_control_msg returned %d", - __FUNCTION__, status); + warnx("dfuse_upload: libusb_control_transfer returned %d (%s)", + status, libusb_error_name(status)); } return status; } /* DFU_DNLOAD request for DfuSe 1.1a */ -int dfuse_download(struct dfu_if *dif, const unsigned short length, +static int dfuse_download(struct dfu_if *dif, const unsigned short length, unsigned char *data, unsigned short transaction) { int status; @@ -153,15 +165,18 @@ int dfuse_download(struct dfu_if *dif, const unsigned short length, /* wLength */ length, DFU_TIMEOUT); if (status < 0) { - errx(EX_IOERR, "%s: libusb_control_transfer returned %d", - __FUNCTION__, status); + /* Silently fail on leave request on some unpredictable devices */ + if ((dif->quirks & QUIRK_DFUSE_LEAVE) && !length && !data && transaction == 2) + return status; + warnx("dfuse_download: libusb_control_transfer returned %d (%s)", + status, libusb_error_name(status)); } return status; } /* DfuSe only commands */ /* Leaves the device in dfuDNLOAD-IDLE state */ -int dfuse_special_command(struct dfu_if *dif, unsigned int address, +static int dfuse_special_command(struct dfu_if *dif, unsigned int address, enum dfuse_command command) { const char* dfuse_command_name[] = { "SET_ADDRESS" , "ERASE_PAGE", @@ -171,27 +186,30 @@ int dfuse_special_command(struct dfu_if *dif, unsigned int address, int ret; struct dfu_status dst; int firstpoll = 1; + int zerotimeouts = 0; + int polltimeout = 0; + int stalls = 0; if (command == ERASE_PAGE) { struct memsegment *segment; int page_size; - segment = find_segment(mem_layout, address); + segment = find_segment(dif->mem_layout, address); if (!segment || !(segment->memtype & DFUSE_ERASABLE)) { - errx(EX_IOERR, "Page at 0x%08x can not be erased", + errx(EX_USAGE, "Page at 0x%08x can not be erased", address); } page_size = segment->pagesize; - if (verbose > 1) - printf("Erasing page size %i at address 0x%08x, page " + if (verbose) + fprintf(stderr, "Erasing page size %i at address 0x%08x, page " "starting at 0x%08x\n", page_size, address, address & ~(page_size - 1)); buf[0] = 0x41; /* Erase command */ length = 5; last_erased_page = address & ~(page_size - 1); } else if (command == SET_ADDRESS) { - if (verbose > 2) - printf(" Setting address pointer to 0x%08x\n", + if (verbose > 1) + fprintf(stderr, " Setting address pointer to 0x%08x\n", address); buf[0] = 0x21; /* Set Address Pointer command */ length = 5; @@ -202,7 +220,7 @@ int dfuse_special_command(struct dfu_if *dif, unsigned int address, buf[0] = 0x92; length = 1; } else { - errx(EX_IOERR, "Non-supported special command %d", command); + errx(EX_SOFTWARE, "Non-supported special command %d", command); } buf[1] = address & 0xff; buf[2] = (address >> 8) & 0xff; @@ -216,31 +234,47 @@ int dfuse_special_command(struct dfu_if *dif, unsigned int address, } do { ret = dfu_get_status(dif, &dst); - if (ret < 0) { + /* Workaround for some STM32L4 bootloaders that report a too + * short poll timeout and may stall the pipe when we poll */ + if (ret == LIBUSB_ERROR_PIPE && polltimeout != 0 && stalls < 3) { + dst.bState = DFU_STATE_dfuDNBUSY; + stalls++; + if (verbose) + fprintf(stderr, "* Device stalled USB pipe, reusing last poll timeout\n"); + } else if (ret < 0) { errx(EX_IOERR, "Error during special command \"%s\" get_status", dfuse_command_name[command]); + } else { + polltimeout = dst.bwPollTimeout; } if (firstpoll) { firstpoll = 0; if (dst.bState != DFU_STATE_dfuDNBUSY) { - printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + fprintf(stderr, "DFU state(%u) = %s, status(%u) = %s\n", dst.bState, dfu_state_to_string(dst.bState), dst.bStatus, dfu_status_to_string(dst.bStatus)); - errx(EX_IOERR, "Wrong state after command \"%s\" download", + errx(EX_PROTOCOL, "Wrong state after command \"%s\" download", dfuse_command_name[command]); } /* STM32F405 lies about mass erase timeout */ if (command == MASS_ERASE && dst.bwPollTimeout == 100) { - dst.bwPollTimeout = 35000; + polltimeout = 35000; /* Datasheet says up to 32 seconds */ printf("Setting timeout to 35 seconds\n"); } } /* wait while command is executed */ - if (verbose) - printf(" Poll timeout %i ms\n", dst.bwPollTimeout); - milli_sleep(dst.bwPollTimeout); + if (verbose > 1) + fprintf(stderr, " Poll timeout %i ms\n", polltimeout); + milli_sleep(polltimeout); if (command == READ_UNPROTECT) return ret; + /* Workaround for e.g. Black Magic Probe getting stuck */ + if (dst.bwPollTimeout == 0) { + if (++zerotimeouts == 100) + errx(EX_IOERR, "Device stuck after special command request"); + } else { + zerotimeouts = 0; + } } while (dst.bState == DFU_STATE_dfuDNBUSY); if (dst.bStatus != DFU_STATUS_OK) { @@ -250,7 +284,8 @@ int dfuse_special_command(struct dfu_if *dif, unsigned int address, return ret; } -int dfuse_dnload_chunk(struct dfu_if *dif, unsigned char *data, int size, +/* returns number of bytes sent */ +static int dfuse_dnload_chunk(struct dfu_if *dif, unsigned char *data, int size, int transaction) { int bytes_sent; @@ -273,14 +308,15 @@ int dfuse_dnload_chunk(struct dfu_if *dif, unsigned char *data, int size, milli_sleep(dst.bwPollTimeout); } while (dst.bState != DFU_STATE_dfuDNLOAD_IDLE && dst.bState != DFU_STATE_dfuERROR && - dst.bState != DFU_STATE_dfuMANIFEST); + dst.bState != DFU_STATE_dfuMANIFEST && + !(dfuse_will_reset && (dst.bState == DFU_STATE_dfuDNBUSY))); if (dst.bState == DFU_STATE_dfuMANIFEST) printf("Transitioning to dfuMANIFEST state\n"); if (dst.bStatus != DFU_STATUS_OK) { printf(" failed!\n"); - printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + fprintf(stderr, "DFU state(%u) = %s, status(%u) = %s\n", dst.bState, dfu_state_to_string(dst.bState), dst.bStatus, dfu_status_to_string(dst.bStatus)); return -1; @@ -288,6 +324,22 @@ int dfuse_dnload_chunk(struct dfu_if *dif, unsigned char *data, int size, return bytes_sent; } +static void dfuse_do_leave(struct dfu_if *dif) +{ + if (dfuse_address_present) + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + printf("Submitting leave request...\n"); + if (dif->quirks & QUIRK_DFUSE_LEAVE) { + struct dfu_status dst; + /* The device might leave after this request, with or without a response */ + dfuse_download(dif, 0, NULL, 2); + /* Or it might leave after this request, with or without a response */ + dfu_get_status(dif, &dst); + } else { + dfuse_dnload_chunk(dif, NULL, 0, 2); + } +} + int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, const char *dfuse_options) { @@ -303,31 +355,41 @@ int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, dfuse_parse_options(dfuse_options); if (dfuse_length) upload_limit = dfuse_length; - if (dfuse_address) { - struct memsegment *segment; + if (dfuse_address_present) { + struct memsegment *mem_layout, *segment; mem_layout = parse_memory_layout((char *)dif->alt_name); if (!mem_layout) errx(EX_IOERR, "Failed to parse memory layout"); + if (dif->quirks & QUIRK_DFUSE_LAYOUT) + fixup_dfuse_layout(dif, &mem_layout); segment = find_segment(mem_layout, dfuse_address); if (!dfuse_force && (!segment || !(segment->memtype & DFUSE_READABLE))) - errx(EX_IOERR, "Page at 0x%08x is not readable", + errx(EX_USAGE, "Page at 0x%08x is not readable", dfuse_address); if (!upload_limit) { - upload_limit = segment->end - dfuse_address + 1; - printf("Limiting upload to end of memory segment, " - "%i bytes\n", upload_limit); + if (segment) { + upload_limit = segment->end - dfuse_address + 1; + printf("Limiting upload to end of memory segment, " + "%i bytes\n", upload_limit); + } else { + /* unknown segment - i.e. "force" has been used */ + upload_limit = 0x4000; + printf("Limiting upload to %i bytes\n", upload_limit); + } } dfuse_special_command(dif, dfuse_address, SET_ADDRESS); dfu_abort_to_idle(dif); } else { /* Boot loader decides the start address, unknown to us */ /* Use a short length to lower risk of running out of bounds */ - if (!upload_limit) + if (!upload_limit) { + warnx("Unbound upload not supported on DfuSe devices"); upload_limit = 0x4000; + } printf("Limiting default upload to %i bytes\n", upload_limit); } @@ -354,7 +416,7 @@ int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, if (rc < xfer_size || total_bytes >= upload_limit) { /* last block, return successfully */ - ret = total_bytes; + ret = 0; break; } dfu_progress_bar("Upload", total_bytes, upload_limit); @@ -363,10 +425,8 @@ int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, dfu_progress_bar("Upload", total_bytes, total_bytes); dfu_abort_to_idle(dif); - if (dfuse_leave) { - dfuse_special_command(dif, dfuse_address, SET_ADDRESS); - dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */ - } + if (dfuse_leave) + dfuse_do_leave(dif); out_free: free(buf); @@ -376,7 +436,7 @@ int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, /* Writes an element of any size to the device, taking care of page erases */ /* returns 0 on success, otherwise -EINVAL */ -int dfuse_dnload_element(struct dfu_if *dif, unsigned int dwElementAddress, +static int dfuse_dnload_element(struct dfu_if *dif, unsigned int dwElementAddress, unsigned int dwElementSize, unsigned char *data, int xfer_size) { @@ -386,25 +446,34 @@ int dfuse_dnload_element(struct dfu_if *dif, unsigned int dwElementAddress, /* Check at least that we can write to the last address */ segment = - find_segment(mem_layout, dwElementAddress + dwElementSize - 1); - if (!segment || !(segment->memtype & DFUSE_WRITEABLE)) { - errx(EX_IOERR, "Last page at 0x%08x is not writeable", + find_segment(dif->mem_layout, dwElementAddress + dwElementSize - 1); + if (!dfuse_force && + (!segment || !(segment->memtype & DFUSE_WRITEABLE))) { + errx(EX_USAGE, "Last page at 0x%08x is not writeable", dwElementAddress + dwElementSize - 1); } - dfu_progress_bar("Download", 0, 1); + if (!verbose) + dfu_progress_bar("Erase ", 0, 1); + /* First pass: Erase involved pages if needed */ for (p = 0; p < (int)dwElementSize; p += xfer_size) { int page_size; unsigned int erase_address; unsigned int address = dwElementAddress + p; int chunk_size = xfer_size; - segment = find_segment(mem_layout, address); - if (!segment || !(segment->memtype & DFUSE_WRITEABLE)) { - errx(EX_IOERR, "Page at 0x%08x is not writeable", + segment = find_segment(dif->mem_layout, address); + if (!dfuse_force && + (!segment || !(segment->memtype & DFUSE_WRITEABLE))) { + errx(EX_USAGE, "Page at 0x%08x is not writeable", address); } + /* If the location is not in the memory map we skip erasing */ + /* since we wouldn't know the correct page size for flash erase */ + if (!segment) + continue; + page_size = segment->pagesize; /* check if this is the last chunk */ @@ -425,17 +494,33 @@ int dfuse_dnload_element(struct dfu_if *dif, unsigned int dwElementAddress, if (((address + chunk_size - 1) & ~(page_size - 1)) != last_erased_page) { - if (verbose > 2) - printf(" Chunk extends into next page," + if (verbose > 1) + fprintf(stderr, " Chunk extends into next page," " erase it as well\n"); dfuse_special_command(dif, address + chunk_size - 1, ERASE_PAGE); } + if (!verbose) + dfu_progress_bar("Erase ", p, dwElementSize); } + } + if (!verbose) + dfu_progress_bar("Erase ", dwElementSize, dwElementSize); + if (!verbose) + dfu_progress_bar("Download", 0, 1); + + /* Second pass: Write data to (erased) pages */ + for (p = 0; p < (int)dwElementSize; p += xfer_size) { + unsigned int address = dwElementAddress + p; + int chunk_size = xfer_size; + + /* check if this is the last chunk */ + if (p + chunk_size > (int)dwElementSize) + chunk_size = dwElementSize - p; if (verbose) { - printf(" Download from image offset " + fprintf(stderr, " Download from image offset " "%08x to memory %08x-%08x, size %i\n", p, address, address + chunk_size - 1, chunk_size); @@ -462,7 +547,7 @@ static void dfuse_memcpy(unsigned char *dst, unsigned char **src, int *rem, int size) { if (size > *rem) { - errx(EX_IOERR, "Corrupt DfuSe file: " + errx(EX_NOINPUT, "Corrupt DfuSe file: " "Cannot read %d bytes from %d bytes", size, *rem); } if (dst != NULL) @@ -472,7 +557,7 @@ dfuse_memcpy(unsigned char *dst, unsigned char **src, int *rem, int size) } /* Download raw binary file to DfuSe device */ -int dfuse_do_bin_dnload(struct dfu_if *dif, int xfer_size, +static int dfuse_do_bin_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file, unsigned int start_address) { unsigned int dwElementAddress; @@ -484,25 +569,21 @@ int dfuse_do_bin_dnload(struct dfu_if *dif, int xfer_size, dwElementSize = file->size.total - file->size.suffix - file->size.prefix; - printf("Downloading to address = 0x%08x, size = %i\n", + printf("Downloading element to address = 0x%08x, size = %i\n", dwElementAddress, dwElementSize); data = file->firmware + file->size.prefix; ret = dfuse_dnload_element(dif, dwElementAddress, dwElementSize, data, xfer_size); - if (ret != 0) - goto out_free; + if (ret == 0) + printf("File downloaded successfully\n"); - printf("File downloaded successfully\n"); - ret = dwElementSize; - - out_free: return ret; } /* Parse a DfuSe file and download contents to device */ -int dfuse_do_dfuse_dnload(struct dfu_if *dif, int xfer_size, +static int dfuse_do_dfuse_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file) { uint8_t dfuprefix[11]; @@ -512,6 +593,7 @@ int dfuse_do_dfuse_dnload(struct dfu_if *dif, int xfer_size, int element; int bTargets; int bAlternateSetting; + struct dfu_if *adif; int dwNbElements; unsigned int dwElementAddress; unsigned int dwElementSize; @@ -526,43 +608,65 @@ int dfuse_do_dfuse_dnload(struct dfu_if *dif, int xfer_size, /* Must be larger than a minimal DfuSe header and suffix */ if (rem < (int)(sizeof(dfuprefix) + sizeof(targetprefix) + sizeof(elementheader))) { - errx(EX_SOFTWARE, "File too small for a DfuSe file"); + errx(EX_DATAERR, "File too small for a DfuSe file"); } dfuse_memcpy(dfuprefix, &data, &rem, sizeof(dfuprefix)); if (strncmp((char *)dfuprefix, "DfuSe", 5)) { - errx(EX_IOERR, "No valid DfuSe signature"); + errx(EX_DATAERR, "No valid DfuSe signature"); return -EINVAL; } if (dfuprefix[5] != 0x01) { - errx(EX_IOERR, "DFU format revision %i not supported", + errx(EX_DATAERR, "DFU format revision %i not supported", dfuprefix[5]); return -EINVAL; } bTargets = dfuprefix[10]; - printf("file contains %i DFU images\n", bTargets); + printf("File contains %i DFU images\n", bTargets); for (image = 1; image <= bTargets; image++) { - printf("parsing DFU image %i\n", image); + printf("Parsing DFU image %i\n", image); dfuse_memcpy(targetprefix, &data, &rem, sizeof(targetprefix)); if (strncmp((char *)targetprefix, "Target", 6)) { - errx(EX_IOERR, "No valid target signature"); + errx(EX_DATAERR, "No valid target signature"); return -EINVAL; } bAlternateSetting = targetprefix[6]; + if (targetprefix[7]) + printf("Target name: %s\n", &targetprefix[11]); + else + printf("No target name\n"); dwNbElements = quad2uint((unsigned char *)targetprefix + 270); - printf("image for alternate setting %i, ", bAlternateSetting); + printf("Image for alternate setting %i, ", bAlternateSetting); printf("(%i elements, ", dwNbElements); printf("total size = %i)\n", quad2uint((unsigned char *)targetprefix + 266)); - if (bAlternateSetting != dif->altsetting) - printf("Warning: Image does not match current alternate" - " setting.\n" - "Please rerun with the correct -a option setting" - " to download this image!\n"); + + adif = dif; + while (adif) { + if (bAlternateSetting == adif->altsetting) { + adif->dev_handle = dif->dev_handle; + printf("Setting Alternate Interface #%d ...\n", + adif->altsetting); + ret = libusb_set_interface_alt_setting( + adif->dev_handle, + adif->interface, adif->altsetting); + if (ret < 0) { + errx(EX_IOERR, + "Cannot set alternate interface: %s", + libusb_error_name(ret)); + } + break; + } + adif = adif->next; + } + if (!adif) + warnx("No alternate setting %d (skipping elements)", + bAlternateSetting); + for (element = 1; element <= dwNbElements; element++) { - printf("parsing element %i, ", element); + printf("Parsing element %i, ", element); dfuse_memcpy(elementheader, &data, &rem, sizeof(elementheader)); dwElementAddress = quad2uint((unsigned char *)elementheader); @@ -577,14 +681,13 @@ int dfuse_do_dfuse_dnload(struct dfu_if *dif, int xfer_size, } /* sanity check */ if ((int)dwElementSize > rem) - errx(EX_SOFTWARE, "File too small for element size"); + errx(EX_DATAERR, "File too small for element size"); - if (bAlternateSetting == dif->altsetting) { - ret = dfuse_dnload_element(dif, dwElementAddress, - dwElementSize, data, xfer_size); - } else { + if (adif) + ret = dfuse_dnload_element(adif, dwElementAddress, + dwElementSize, data, xfer_size); + else ret = 0; - } /* advance read pointer */ dfuse_memcpy(NULL, &data, &rem, dwElementSize); @@ -597,7 +700,7 @@ int dfuse_do_dfuse_dnload(struct dfu_if *dif, int xfer_size, if (rem != 0) warnx("%d bytes leftover", rem); - printf("done parsing DfuSe file\n"); + printf("Done parsing DfuSe file\n"); return 0; } @@ -606,52 +709,89 @@ int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file, const char *dfuse_options) { int ret; + struct dfu_if *adif; if (dfuse_options) dfuse_parse_options(dfuse_options); - mem_layout = parse_memory_layout((char *)dif->alt_name); - if (!mem_layout) { - errx(EX_IOERR, "Failed to parse memory layout"); + + adif = dif; + while (adif) { + adif->mem_layout = parse_memory_layout((char *)adif->alt_name); + if (!adif->mem_layout) + errx(EX_IOERR, + "Failed to parse memory layout for alternate interface %i", + adif->altsetting); + if (adif->quirks & QUIRK_DFUSE_LAYOUT) + fixup_dfuse_layout(adif, &(adif->mem_layout)); + adif = adif->next; } + if (dfuse_unprotect) { if (!dfuse_force) { - errx(EX_IOERR, "The read unprotect command " + errx(EX_USAGE, "The read unprotect command " "will erase the flash memory" "and can only be used with force\n"); } - dfuse_special_command(dif, 0, READ_UNPROTECT); + ret = dfuse_special_command(dif, 0, READ_UNPROTECT); printf("Device disconnects, erases flash and resets now\n"); - exit(0); + return ret; } if (dfuse_mass_erase) { if (!dfuse_force) { - errx(EX_IOERR, "The mass erase command " + errx(EX_USAGE, "The mass erase command " "can only be used with force"); } printf("Performing mass erase, this can take a moment\n"); - dfuse_special_command(dif, 0, MASS_ERASE); + ret = dfuse_special_command(dif, 0, MASS_ERASE); } - if (dfuse_address) { + if (!file->name) { + printf("DfuSe command mode\n"); + ret = 0; + } else if (dfuse_address_present) { if (file->bcdDFU == 0x11a) { - errx(EX_IOERR, "This is a DfuSe file, not " + errx(EX_USAGE, "This is a DfuSe file, not " "meant for raw download"); } ret = dfuse_do_bin_dnload(dif, xfer_size, file, dfuse_address); } else { if (file->bcdDFU != 0x11a) { warnx("Only DfuSe file version 1.1a is supported"); - errx(EX_IOERR, "(for raw binary download, use the " + errx(EX_USAGE, "(for raw binary download, use the " "--dfuse-address option)"); } ret = dfuse_do_dfuse_dnload(dif, xfer_size, file); } - free_segment_list(mem_layout); - dfu_abort_to_idle(dif); + adif = dif; + while (adif) { + free_segment_list(adif->mem_layout); + adif = adif->next; + } - if (dfuse_leave) { - dfuse_special_command(dif, dfuse_address, SET_ADDRESS); - dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */ + if (!dfuse_will_reset) { + dfu_abort_to_idle(dif); } + + if (dfuse_leave) + dfuse_do_leave(dif); + return ret; } + +/* Check if we have one interface, possibly multiple alternate interfaces */ +int dfuse_multiple_alt(struct dfu_if *dfu_root) +{ + libusb_device *dev = dfu_root->dev; + uint8_t configuration = dfu_root->configuration; + uint8_t interface = dfu_root->interface; + struct dfu_if *dif = dfu_root->next; + + while (dif) { + if (dev != dif->dev || + configuration != dif->configuration || + interface != dif->interface) + return 0; + dif = dif->next; + } + return 1; +} |