summaryrefslogtreecommitdiff
path: root/src/dfuse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dfuse.c')
-rw-r--r--src/dfuse.c338
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;
+}