1 /*
2 * DfuSe specific functions
3 *
4 * This implements the ST Microsystems DFU extensions (DfuSe)
5 * as per the DfuSe 1.1a specification (ST documents AN3156, AN2606)
6 * The DfuSe file format is described in ST document UM0391.
7 *
8 * Copyright 2010-2018 Tormod Volden <debian.tormod@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <string.h>
33
34 #include "portable.h"
35 #include "dfu.h"
36 #include "usb_dfu.h"
37 #include "dfu_file.h"
38 #include "dfuse.h"
39 #include "dfuse_mem.h"
40 #include "quirks.h"
41
42 #define DFU_TIMEOUT 5000
43
44 extern int verbose;
45 static unsigned int last_erased_page = 1; /* non-aligned value, won't match */
46 static unsigned int dfuse_address = 0;
47 static unsigned int dfuse_address_present = 0;
48 static unsigned int dfuse_length = 0;
49 static int dfuse_force = 0;
50 static int dfuse_leave = 0;
51 static int dfuse_unprotect = 0;
52 static int dfuse_mass_erase = 0;
53 static int dfuse_will_reset = 0;
54
quad2uint(unsigned char * p)55 static unsigned int quad2uint(unsigned char *p)
56 {
57 return (*p + (*(p + 1) << 8) + (*(p + 2) << 16) + (*(p + 3) << 24));
58 }
59
dfuse_parse_options(const char * options)60 static void dfuse_parse_options(const char *options)
61 {
62 char *end;
63 const char *endword;
64 unsigned int number;
65
66 /* address, possibly empty, must be first */
67 if (*options != ':') {
68 endword = strchr(options, ':');
69 if (!endword)
70 endword = options + strlen(options); /* GNU strchrnul */
71
72 number = strtoul(options, &end, 0);
73 if (end == endword) {
74 dfuse_address = number;
75 dfuse_address_present = 1;
76 } else {
77 errx(EX_USAGE, "Invalid dfuse address: %s", options);
78 }
79 options = endword;
80 }
81
82 while (*options) {
83 if (*options == ':') {
84 options++;
85 continue;
86 }
87 endword = strchr(options, ':');
88 if (!endword)
89 endword = options + strlen(options);
90
91 if (!strncmp(options, "force", endword - options)) {
92 dfuse_force++;
93 options += 5;
94 continue;
95 }
96 if (!strncmp(options, "leave", endword - options)) {
97 dfuse_leave = 1;
98 options += 5;
99 continue;
100 }
101 if (!strncmp(options, "unprotect", endword - options)) {
102 dfuse_unprotect = 1;
103 options += 9;
104 continue;
105 }
106 if (!strncmp(options, "mass-erase", endword - options)) {
107 dfuse_mass_erase = 1;
108 options += 10;
109 continue;
110 }
111 if (!strncmp(options, "will-reset", endword - options)) {
112 dfuse_will_reset = 1;
113 options += 10;
114 continue;
115 }
116
117 /* any valid number is interpreted as upload length */
118 number = strtoul(options, &end, 0);
119 if (end == endword) {
120 dfuse_length = number;
121 } else {
122 errx(EX_USAGE, "Invalid dfuse modifier: %s", options);
123 }
124 options = endword;
125 }
126 }
127
128 /* DFU_UPLOAD request for DfuSe 1.1a */
dfuse_upload(struct dfu_if * dif,const unsigned short length,unsigned char * data,unsigned short transaction)129 static int dfuse_upload(struct dfu_if *dif, const unsigned short length,
130 unsigned char *data, unsigned short transaction)
131 {
132 int status;
133
134 status = libusb_control_transfer(dif->dev_handle,
135 /* bmRequestType */ LIBUSB_ENDPOINT_IN |
136 LIBUSB_REQUEST_TYPE_CLASS |
137 LIBUSB_RECIPIENT_INTERFACE,
138 /* bRequest */ DFU_UPLOAD,
139 /* wValue */ transaction,
140 /* wIndex */ dif->interface,
141 /* Data */ data,
142 /* wLength */ length,
143 DFU_TIMEOUT);
144 if (status < 0) {
145 warnx("dfuse_upload: libusb_control_transfer returned %d (%s)",
146 status, libusb_error_name(status));
147 }
148 return status;
149 }
150
151 /* DFU_DNLOAD request for DfuSe 1.1a */
dfuse_download(struct dfu_if * dif,const unsigned short length,unsigned char * data,unsigned short transaction)152 static int dfuse_download(struct dfu_if *dif, const unsigned short length,
153 unsigned char *data, unsigned short transaction)
154 {
155 int status;
156
157 status = libusb_control_transfer(dif->dev_handle,
158 /* bmRequestType */ LIBUSB_ENDPOINT_OUT |
159 LIBUSB_REQUEST_TYPE_CLASS |
160 LIBUSB_RECIPIENT_INTERFACE,
161 /* bRequest */ DFU_DNLOAD,
162 /* wValue */ transaction,
163 /* wIndex */ dif->interface,
164 /* Data */ data,
165 /* wLength */ length,
166 DFU_TIMEOUT);
167 if (status < 0) {
168 /* Silently fail on leave request on some unpredictable devices */
169 if ((dif->quirks & QUIRK_DFUSE_LEAVE) && !length && !data && transaction == 2)
170 return status;
171 warnx("dfuse_download: libusb_control_transfer returned %d (%s)",
172 status, libusb_error_name(status));
173 }
174 return status;
175 }
176
177 /* DfuSe only commands */
178 /* Leaves the device in dfuDNLOAD-IDLE state */
dfuse_special_command(struct dfu_if * dif,unsigned int address,enum dfuse_command command)179 static int dfuse_special_command(struct dfu_if *dif, unsigned int address,
180 enum dfuse_command command)
181 {
182 const char* dfuse_command_name[] = { "SET_ADDRESS" , "ERASE_PAGE",
183 "MASS_ERASE", "READ_UNPROTECT"};
184 unsigned char buf[5];
185 int length;
186 int ret;
187 struct dfu_status dst;
188 int firstpoll = 1;
189 int zerotimeouts = 0;
190 int polltimeout = 0;
191 int stalls = 0;
192
193 if (command == ERASE_PAGE) {
194 struct memsegment *segment;
195 int page_size;
196
197 segment = find_segment(dif->mem_layout, address);
198 if (!segment || !(segment->memtype & DFUSE_ERASABLE)) {
199 errx(EX_USAGE, "Page at 0x%08x can not be erased",
200 address);
201 }
202 page_size = segment->pagesize;
203 if (verbose)
204 fprintf(stderr, "Erasing page size %i at address 0x%08x, page "
205 "starting at 0x%08x\n", page_size, address,
206 address & ~(page_size - 1));
207 buf[0] = 0x41; /* Erase command */
208 length = 5;
209 last_erased_page = address & ~(page_size - 1);
210 } else if (command == SET_ADDRESS) {
211 if (verbose > 1)
212 fprintf(stderr, " Setting address pointer to 0x%08x\n",
213 address);
214 buf[0] = 0x21; /* Set Address Pointer command */
215 length = 5;
216 } else if (command == MASS_ERASE) {
217 buf[0] = 0x41; /* Mass erase command when length = 1 */
218 length = 1;
219 } else if (command == READ_UNPROTECT) {
220 buf[0] = 0x92;
221 length = 1;
222 } else {
223 errx(EX_SOFTWARE, "Non-supported special command %d", command);
224 }
225 buf[1] = address & 0xff;
226 buf[2] = (address >> 8) & 0xff;
227 buf[3] = (address >> 16) & 0xff;
228 buf[4] = (address >> 24) & 0xff;
229
230 ret = dfuse_download(dif, length, buf, 0);
231 if (ret < 0) {
232 errx(EX_IOERR, "Error during special command \"%s\" download",
233 dfuse_command_name[command]);
234 }
235 do {
236 ret = dfu_get_status(dif, &dst);
237 /* Workaround for some STM32L4 bootloaders that report a too
238 * short poll timeout and may stall the pipe when we poll */
239 if (ret == LIBUSB_ERROR_PIPE && polltimeout != 0 && stalls < 3) {
240 dst.bState = DFU_STATE_dfuDNBUSY;
241 stalls++;
242 if (verbose)
243 fprintf(stderr, "* Device stalled USB pipe, reusing last poll timeout\n");
244 } else if (ret < 0) {
245 errx(EX_IOERR, "Error during special command \"%s\" get_status",
246 dfuse_command_name[command]);
247 } else {
248 polltimeout = dst.bwPollTimeout;
249 }
250 if (firstpoll) {
251 firstpoll = 0;
252 if (dst.bState != DFU_STATE_dfuDNBUSY) {
253 fprintf(stderr, "DFU state(%u) = %s, status(%u) = %s\n", dst.bState,
254 dfu_state_to_string(dst.bState), dst.bStatus,
255 dfu_status_to_string(dst.bStatus));
256 errx(EX_PROTOCOL, "Wrong state after command \"%s\" download",
257 dfuse_command_name[command]);
258 }
259 /* STM32F405 lies about mass erase timeout */
260 if (command == MASS_ERASE && dst.bwPollTimeout == 100) {
261 polltimeout = 35000; /* Datasheet says up to 32 seconds */
262 printf("Setting timeout to 35 seconds\n");
263 }
264 }
265 /* wait while command is executed */
266 if (verbose > 1)
267 fprintf(stderr, " Poll timeout %i ms\n", polltimeout);
268 milli_sleep(polltimeout);
269 if (command == READ_UNPROTECT)
270 return ret;
271 /* Workaround for e.g. Black Magic Probe getting stuck */
272 if (dst.bwPollTimeout == 0) {
273 if (++zerotimeouts == 100)
274 errx(EX_IOERR, "Device stuck after special command request");
275 } else {
276 zerotimeouts = 0;
277 }
278 } while (dst.bState == DFU_STATE_dfuDNBUSY);
279
280 if (dst.bStatus != DFU_STATUS_OK) {
281 errx(EX_IOERR, "%s not correctly executed",
282 dfuse_command_name[command]);
283 }
284 return ret;
285 }
286
287 /* returns number of bytes sent */
dfuse_dnload_chunk(struct dfu_if * dif,unsigned char * data,int size,int transaction)288 static int dfuse_dnload_chunk(struct dfu_if *dif, unsigned char *data, int size,
289 int transaction)
290 {
291 int bytes_sent;
292 struct dfu_status dst;
293 int ret;
294
295 ret = dfuse_download(dif, size, size ? data : NULL, transaction);
296 if (ret < 0) {
297 errx(EX_IOERR, "Error during download");
298 return ret;
299 }
300 bytes_sent = ret;
301
302 do {
303 ret = dfu_get_status(dif, &dst);
304 if (ret < 0) {
305 errx(EX_IOERR, "Error during download get_status");
306 return ret;
307 }
308 milli_sleep(dst.bwPollTimeout);
309 } while (dst.bState != DFU_STATE_dfuDNLOAD_IDLE &&
310 dst.bState != DFU_STATE_dfuERROR &&
311 dst.bState != DFU_STATE_dfuMANIFEST &&
312 !(dfuse_will_reset && (dst.bState == DFU_STATE_dfuDNBUSY)));
313
314 if (dst.bState == DFU_STATE_dfuMANIFEST)
315 printf("Transitioning to dfuMANIFEST state\n");
316
317 if (dst.bStatus != DFU_STATUS_OK) {
318 printf(" failed!\n");
319 fprintf(stderr, "DFU state(%u) = %s, status(%u) = %s\n", dst.bState,
320 dfu_state_to_string(dst.bState), dst.bStatus,
321 dfu_status_to_string(dst.bStatus));
322 return -1;
323 }
324 return bytes_sent;
325 }
326
dfuse_do_leave(struct dfu_if * dif)327 static void dfuse_do_leave(struct dfu_if *dif)
328 {
329 if (dfuse_address_present)
330 dfuse_special_command(dif, dfuse_address, SET_ADDRESS);
331 printf("Submitting leave request...\n");
332 if (dif->quirks & QUIRK_DFUSE_LEAVE) {
333 struct dfu_status dst;
334 /* The device might leave after this request, with or without a response */
335 dfuse_download(dif, 0, NULL, 2);
336 /* Or it might leave after this request, with or without a response */
337 dfu_get_status(dif, &dst);
338 } else {
339 dfuse_dnload_chunk(dif, NULL, 0, 2);
340 }
341 }
342
dfuse_do_upload(struct dfu_if * dif,int xfer_size,int fd,const char * dfuse_options)343 int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd,
344 const char *dfuse_options)
345 {
346 int total_bytes = 0;
347 int upload_limit = 0;
348 unsigned char *buf;
349 int transaction;
350 int ret;
351
352 buf = dfu_malloc(xfer_size);
353
354 if (dfuse_options)
355 dfuse_parse_options(dfuse_options);
356 if (dfuse_length)
357 upload_limit = dfuse_length;
358 if (dfuse_address_present) {
359 struct memsegment *mem_layout, *segment;
360
361 mem_layout = parse_memory_layout((char *)dif->alt_name);
362 if (!mem_layout)
363 errx(EX_IOERR, "Failed to parse memory layout");
364 if (dif->quirks & QUIRK_DFUSE_LAYOUT)
365 fixup_dfuse_layout(dif, &mem_layout);
366
367 segment = find_segment(mem_layout, dfuse_address);
368 if (!dfuse_force &&
369 (!segment || !(segment->memtype & DFUSE_READABLE)))
370 errx(EX_USAGE, "Page at 0x%08x is not readable",
371 dfuse_address);
372
373 if (!upload_limit) {
374 if (segment) {
375 upload_limit = segment->end - dfuse_address + 1;
376 printf("Limiting upload to end of memory segment, "
377 "%i bytes\n", upload_limit);
378 } else {
379 /* unknown segment - i.e. "force" has been used */
380 upload_limit = 0x4000;
381 printf("Limiting upload to %i bytes\n", upload_limit);
382 }
383 }
384 dfuse_special_command(dif, dfuse_address, SET_ADDRESS);
385 dfu_abort_to_idle(dif);
386 } else {
387 /* Boot loader decides the start address, unknown to us */
388 /* Use a short length to lower risk of running out of bounds */
389 if (!upload_limit) {
390 warnx("Unbound upload not supported on DfuSe devices");
391 upload_limit = 0x4000;
392 }
393 printf("Limiting default upload to %i bytes\n", upload_limit);
394 }
395
396 dfu_progress_bar("Upload", 0, 1);
397
398 transaction = 2;
399 while (1) {
400 int rc;
401
402 /* last chunk can be smaller than original xfer_size */
403 if (upload_limit - total_bytes < xfer_size)
404 xfer_size = upload_limit - total_bytes;
405 rc = dfuse_upload(dif, xfer_size, buf, transaction++);
406 if (rc < 0) {
407 ret = rc;
408 goto out_free;
409 }
410
411 dfu_file_write_crc(fd, 0, buf, rc);
412 total_bytes += rc;
413
414 if (total_bytes < 0)
415 errx(EX_SOFTWARE, "Received too many bytes");
416
417 if (rc < xfer_size || total_bytes >= upload_limit) {
418 /* last block, return successfully */
419 ret = 0;
420 break;
421 }
422 dfu_progress_bar("Upload", total_bytes, upload_limit);
423 }
424
425 dfu_progress_bar("Upload", total_bytes, total_bytes);
426
427 dfu_abort_to_idle(dif);
428 if (dfuse_leave)
429 dfuse_do_leave(dif);
430
431 out_free:
432 free(buf);
433
434 return ret;
435 }
436
437 /* Writes an element of any size to the device, taking care of page erases */
438 /* returns 0 on success, otherwise -EINVAL */
dfuse_dnload_element(struct dfu_if * dif,unsigned int dwElementAddress,unsigned int dwElementSize,unsigned char * data,int xfer_size)439 static int dfuse_dnload_element(struct dfu_if *dif, unsigned int dwElementAddress,
440 unsigned int dwElementSize, unsigned char *data,
441 int xfer_size)
442 {
443 int p;
444 int ret;
445 struct memsegment *segment;
446
447 /* Check at least that we can write to the last address */
448 segment =
449 find_segment(dif->mem_layout, dwElementAddress + dwElementSize - 1);
450 if (!dfuse_force &&
451 (!segment || !(segment->memtype & DFUSE_WRITEABLE))) {
452 errx(EX_USAGE, "Last page at 0x%08x is not writeable",
453 dwElementAddress + dwElementSize - 1);
454 }
455
456 if (!verbose)
457 dfu_progress_bar("Erase ", 0, 1);
458
459 /* First pass: Erase involved pages if needed */
460 for (p = 0; p < (int)dwElementSize; p += xfer_size) {
461 int page_size;
462 unsigned int erase_address;
463 unsigned int address = dwElementAddress + p;
464 int chunk_size = xfer_size;
465
466 segment = find_segment(dif->mem_layout, address);
467 if (!dfuse_force &&
468 (!segment || !(segment->memtype & DFUSE_WRITEABLE))) {
469 errx(EX_USAGE, "Page at 0x%08x is not writeable",
470 address);
471 }
472 /* If the location is not in the memory map we skip erasing */
473 /* since we wouldn't know the correct page size for flash erase */
474 if (!segment)
475 continue;
476
477 page_size = segment->pagesize;
478
479 /* check if this is the last chunk */
480 if (p + chunk_size > (int)dwElementSize)
481 chunk_size = dwElementSize - p;
482
483 /* Erase only for flash memory downloads */
484 if ((segment->memtype & DFUSE_ERASABLE) && !dfuse_mass_erase) {
485 /* erase all involved pages */
486 for (erase_address = address;
487 erase_address < address + chunk_size;
488 erase_address += page_size)
489 if ((erase_address & ~(page_size - 1)) !=
490 last_erased_page)
491 dfuse_special_command(dif,
492 erase_address,
493 ERASE_PAGE);
494
495 if (((address + chunk_size - 1) & ~(page_size - 1)) !=
496 last_erased_page) {
497 if (verbose > 1)
498 fprintf(stderr, " Chunk extends into next page,"
499 " erase it as well\n");
500 dfuse_special_command(dif,
501 address + chunk_size - 1,
502 ERASE_PAGE);
503 }
504 if (!verbose)
505 dfu_progress_bar("Erase ", p, dwElementSize);
506 }
507 }
508 if (!verbose)
509 dfu_progress_bar("Erase ", dwElementSize, dwElementSize);
510 if (!verbose)
511 dfu_progress_bar("Download", 0, 1);
512
513 /* Second pass: Write data to (erased) pages */
514 for (p = 0; p < (int)dwElementSize; p += xfer_size) {
515 unsigned int address = dwElementAddress + p;
516 int chunk_size = xfer_size;
517
518 /* check if this is the last chunk */
519 if (p + chunk_size > (int)dwElementSize)
520 chunk_size = dwElementSize - p;
521
522 if (verbose) {
523 fprintf(stderr, " Download from image offset "
524 "%08x to memory %08x-%08x, size %i\n",
525 p, address, address + chunk_size - 1,
526 chunk_size);
527 } else {
528 dfu_progress_bar("Download", p, dwElementSize);
529 }
530
531 dfuse_special_command(dif, address, SET_ADDRESS);
532
533 /* transaction = 2 for no address offset */
534 ret = dfuse_dnload_chunk(dif, data + p, chunk_size, 2);
535 if (ret != chunk_size) {
536 errx(EX_IOERR, "Failed to write whole chunk: "
537 "%i of %i bytes", ret, chunk_size);
538 return -EINVAL;
539 }
540 }
541 if (!verbose)
542 dfu_progress_bar("Download", dwElementSize, dwElementSize);
543 return 0;
544 }
545
546 static void
dfuse_memcpy(unsigned char * dst,unsigned char ** src,int * rem,int size)547 dfuse_memcpy(unsigned char *dst, unsigned char **src, int *rem, int size)
548 {
549 if (size > *rem) {
550 errx(EX_NOINPUT, "Corrupt DfuSe file: "
551 "Cannot read %d bytes from %d bytes", size, *rem);
552 }
553 if (dst != NULL)
554 memcpy(dst, *src, size);
555 (*src) += size;
556 (*rem) -= size;
557 }
558
559 /* Download raw binary file to DfuSe device */
dfuse_do_bin_dnload(struct dfu_if * dif,int xfer_size,struct dfu_file * file,unsigned int start_address)560 static int dfuse_do_bin_dnload(struct dfu_if *dif, int xfer_size,
561 struct dfu_file *file, unsigned int start_address)
562 {
563 unsigned int dwElementAddress;
564 unsigned int dwElementSize;
565 unsigned char *data;
566 int ret;
567
568 dwElementAddress = start_address;
569 dwElementSize = file->size.total -
570 file->size.suffix - file->size.prefix;
571
572 printf("Downloading element to address = 0x%08x, size = %i\n",
573 dwElementAddress, dwElementSize);
574
575 data = file->firmware + file->size.prefix;
576
577 ret = dfuse_dnload_element(dif, dwElementAddress, dwElementSize, data,
578 xfer_size);
579 if (ret == 0)
580 printf("File downloaded successfully\n");
581
582 return ret;
583 }
584
585 /* Parse a DfuSe file and download contents to device */
dfuse_do_dfuse_dnload(struct dfu_if * dif,int xfer_size,struct dfu_file * file)586 static int dfuse_do_dfuse_dnload(struct dfu_if *dif, int xfer_size,
587 struct dfu_file *file)
588 {
589 uint8_t dfuprefix[11];
590 uint8_t targetprefix[274];
591 uint8_t elementheader[8];
592 int image;
593 int element;
594 int bTargets;
595 int bAlternateSetting;
596 struct dfu_if *adif;
597 int dwNbElements;
598 unsigned int dwElementAddress;
599 unsigned int dwElementSize;
600 uint8_t *data;
601 int ret;
602 int rem;
603 int bFirstAddressSaved = 0;
604
605 rem = file->size.total - file->size.prefix - file->size.suffix;
606 data = file->firmware + file->size.prefix;
607
608 /* Must be larger than a minimal DfuSe header and suffix */
609 if (rem < (int)(sizeof(dfuprefix) +
610 sizeof(targetprefix) + sizeof(elementheader))) {
611 errx(EX_DATAERR, "File too small for a DfuSe file");
612 }
613
614 dfuse_memcpy(dfuprefix, &data, &rem, sizeof(dfuprefix));
615
616 if (strncmp((char *)dfuprefix, "DfuSe", 5)) {
617 errx(EX_DATAERR, "No valid DfuSe signature");
618 return -EINVAL;
619 }
620 if (dfuprefix[5] != 0x01) {
621 errx(EX_DATAERR, "DFU format revision %i not supported",
622 dfuprefix[5]);
623 return -EINVAL;
624 }
625 bTargets = dfuprefix[10];
626 printf("File contains %i DFU images\n", bTargets);
627
628 for (image = 1; image <= bTargets; image++) {
629 printf("Parsing DFU image %i\n", image);
630 dfuse_memcpy(targetprefix, &data, &rem, sizeof(targetprefix));
631 if (strncmp((char *)targetprefix, "Target", 6)) {
632 errx(EX_DATAERR, "No valid target signature");
633 return -EINVAL;
634 }
635 bAlternateSetting = targetprefix[6];
636 if (targetprefix[7])
637 printf("Target name: %s\n", &targetprefix[11]);
638 else
639 printf("No target name\n");
640 dwNbElements = quad2uint((unsigned char *)targetprefix + 270);
641 printf("Image for alternate setting %i, ", bAlternateSetting);
642 printf("(%i elements, ", dwNbElements);
643 printf("total size = %i)\n",
644 quad2uint((unsigned char *)targetprefix + 266));
645
646 adif = dif;
647 while (adif) {
648 if (bAlternateSetting == adif->altsetting) {
649 adif->dev_handle = dif->dev_handle;
650 printf("Setting Alternate Interface #%d ...\n",
651 adif->altsetting);
652 ret = libusb_set_interface_alt_setting(
653 adif->dev_handle,
654 adif->interface, adif->altsetting);
655 if (ret < 0) {
656 errx(EX_IOERR,
657 "Cannot set alternate interface: %s",
658 libusb_error_name(ret));
659 }
660 break;
661 }
662 adif = adif->next;
663 }
664 if (!adif)
665 warnx("No alternate setting %d (skipping elements)",
666 bAlternateSetting);
667
668 for (element = 1; element <= dwNbElements; element++) {
669 printf("Parsing element %i, ", element);
670 dfuse_memcpy(elementheader, &data, &rem, sizeof(elementheader));
671 dwElementAddress =
672 quad2uint((unsigned char *)elementheader);
673 dwElementSize =
674 quad2uint((unsigned char *)elementheader + 4);
675 printf("address = 0x%08x, ", dwElementAddress);
676 printf("size = %i\n", dwElementSize);
677
678 if (!bFirstAddressSaved) {
679 bFirstAddressSaved = 1;
680 dfuse_address = dwElementAddress;
681 }
682 /* sanity check */
683 if ((int)dwElementSize > rem)
684 errx(EX_DATAERR, "File too small for element size");
685
686 if (adif)
687 ret = dfuse_dnload_element(adif, dwElementAddress,
688 dwElementSize, data, xfer_size);
689 else
690 ret = 0;
691
692 /* advance read pointer */
693 dfuse_memcpy(NULL, &data, &rem, dwElementSize);
694
695 if (ret != 0)
696 return ret;
697 }
698 }
699
700 if (rem != 0)
701 warnx("%d bytes leftover", rem);
702
703 printf("Done parsing DfuSe file\n");
704
705 return 0;
706 }
707
dfuse_do_dnload(struct dfu_if * dif,int xfer_size,struct dfu_file * file,const char * dfuse_options)708 int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file,
709 const char *dfuse_options)
710 {
711 int ret;
712 struct dfu_if *adif;
713
714 if (dfuse_options)
715 dfuse_parse_options(dfuse_options);
716
717 adif = dif;
718 while (adif) {
719 adif->mem_layout = parse_memory_layout((char *)adif->alt_name);
720 if (!adif->mem_layout)
721 errx(EX_IOERR,
722 "Failed to parse memory layout for alternate interface %i",
723 adif->altsetting);
724 if (adif->quirks & QUIRK_DFUSE_LAYOUT)
725 fixup_dfuse_layout(adif, &(adif->mem_layout));
726 adif = adif->next;
727 }
728
729 if (dfuse_unprotect) {
730 if (!dfuse_force) {
731 errx(EX_USAGE, "The read unprotect command "
732 "will erase the flash memory"
733 "and can only be used with force\n");
734 }
735 ret = dfuse_special_command(dif, 0, READ_UNPROTECT);
736 printf("Device disconnects, erases flash and resets now\n");
737 return ret;
738 }
739 if (dfuse_mass_erase) {
740 if (!dfuse_force) {
741 errx(EX_USAGE, "The mass erase command "
742 "can only be used with force");
743 }
744 printf("Performing mass erase, this can take a moment\n");
745 ret = dfuse_special_command(dif, 0, MASS_ERASE);
746 }
747 if (!file->name) {
748 printf("DfuSe command mode\n");
749 ret = 0;
750 } else if (dfuse_address_present) {
751 if (file->bcdDFU == 0x11a) {
752 errx(EX_USAGE, "This is a DfuSe file, not "
753 "meant for raw download");
754 }
755 ret = dfuse_do_bin_dnload(dif, xfer_size, file, dfuse_address);
756 } else {
757 if (file->bcdDFU != 0x11a) {
758 warnx("Only DfuSe file version 1.1a is supported");
759 errx(EX_USAGE, "(for raw binary download, use the "
760 "--dfuse-address option)");
761 }
762 ret = dfuse_do_dfuse_dnload(dif, xfer_size, file);
763 }
764
765 adif = dif;
766 while (adif) {
767 free_segment_list(adif->mem_layout);
768 adif = adif->next;
769 }
770
771 if (!dfuse_will_reset) {
772 dfu_abort_to_idle(dif);
773 }
774
775 if (dfuse_leave)
776 dfuse_do_leave(dif);
777
778 return ret;
779 }
780
781 /* Check if we have one interface, possibly multiple alternate interfaces */
dfuse_multiple_alt(struct dfu_if * dfu_root)782 int dfuse_multiple_alt(struct dfu_if *dfu_root)
783 {
784 libusb_device *dev = dfu_root->dev;
785 uint8_t configuration = dfu_root->configuration;
786 uint8_t interface = dfu_root->interface;
787 struct dfu_if *dif = dfu_root->next;
788
789 while (dif) {
790 if (dev != dif->dev ||
791 configuration != dif->configuration ||
792 interface != dif->interface)
793 return 0;
794 dif = dif->next;
795 }
796 return 1;
797 }
798