int main(int argc, char **argv) { int expected_size = 0; unsigned int transfer_size = 0; enum mode mode = MODE_NONE; struct dfu_status status; libusb_context *ctx; struct dfu_file file; char *end; int final_reset = 0; int detach_unsupported = 0; int ret; int dfuse_device = 0; int fd; const char *dfuse_options = NULL; int detach_delay = 5; uint16_t runtime_vendor; uint16_t runtime_product; memset(&file, 0, sizeof(file)); /* make sure all prints are flushed */ setvbuf(stdout, NULL, _IONBF, 0); while (1) { int c, option_index = 0; c = getopt_long(argc, argv, "hVvlenE:d:p:c:i:a:S:t:U:D:Rs:Z:", opts, &option_index); if (c == -1) break; switch (c) { case 'h': help(); break; case 'V': mode = MODE_VERSION; break; case 'v': verbose++; break; case 'l': mode = MODE_LIST; break; case 'e': mode = MODE_DETACH; match_iface_alt_index = 0; match_iface_index = 0; break; case 'E': detach_delay = atoi(optarg); break; case 'n': detach_unsupported = 1; break; case 'd': parse_vendprod(optarg); break; case 'p': /* Parse device path */ ret = resolve_device_path(optarg); if (ret < 0) errx(EX_SOFTWARE, "Unable to parse '%s'", optarg); if (!ret) errx(EX_SOFTWARE, "Cannot find '%s'", optarg); break; case 'c': /* Configuration */ match_config_index = atoi(optarg); break; case 'i': /* Interface */ match_iface_index = atoi(optarg); break; case 'a': /* Interface Alternate Setting */ match_iface_alt_index = strtoul(optarg, &end, 0); if (*end) { match_iface_alt_name = optarg; match_iface_alt_index = -1; } break; case 'S': parse_serial(optarg); break; case 't': transfer_size = atoi(optarg); break; case 'U': mode = MODE_UPLOAD; file.name = optarg; break; case 'Z': expected_size = atoi(optarg); break; case 'D': mode = MODE_DOWNLOAD; file.name = optarg; break; case 'R': final_reset = 1; break; case 's': dfuse_options = optarg; break; default: help(); break; } } print_version(); if (mode == MODE_VERSION) { exit(0); } if (mode == MODE_NONE) { fprintf(stderr, "You need to specify one of -D or -U\n"); help(); } if (match_config_index == 0) { /* Handle "-c 0" (unconfigured device) as don't care */ match_config_index = -1; } if (mode == MODE_DOWNLOAD) { dfu_load_file(&file, MAYBE_SUFFIX, MAYBE_PREFIX); /* If the user didn't specify product and/or vendor IDs to match, * use any IDs from the file suffix for device matching */ if (match_vendor < 0 && file.idVendor != 0xffff) { match_vendor = file.idVendor; printf("Match vendor ID from file: %04x\n", match_vendor); } if (match_product < 0 && file.idProduct != 0xffff) { match_product = file.idProduct; printf("Match product ID from file: %04x\n", match_product); } } ret = libusb_init(&ctx); if (ret) errx(EX_IOERR, "unable to initialize libusb: %i", ret); if (verbose > 2) { libusb_set_debug(ctx, 255); } probe_devices(ctx); if (mode == MODE_LIST) { list_dfu_interfaces(); exit(0); } if (dfu_root == NULL) { errx(EX_IOERR, "No DFU capable USB device available"); } else if (dfu_root->next != NULL) { /* We cannot safely support more than one DFU capable device * with same vendor/product ID, since during DFU we need to do * a USB bus reset, after which the target device will get a * new address */ errx(EX_IOERR, "More than one DFU capable USB device found! " "Try `--list' and specify the serial number " "or disconnect all but one device\n"); } /* We have exactly one device. Its libusb_device is now in dfu_root->dev */ printf("Opening DFU capable USB device...\n"); ret = libusb_open(dfu_root->dev, &dfu_root->dev_handle); if (ret || !dfu_root->dev_handle) errx(EX_IOERR, "Cannot open device"); printf("ID %04x:%04x\n", dfu_root->vendor, dfu_root->product); printf("Run-time device DFU version %04x\n", libusb_le16_to_cpu(dfu_root->func_dfu.bcdDFUVersion)); /* Transition from run-Time mode to DFU mode */ if (!(dfu_root->flags & DFU_IFF_DFU)) { int err; /* In the 'first round' during runtime mode, there can only be one * DFU Interface descriptor according to the DFU Spec. */ /* FIXME: check if the selected device really has only one */ runtime_vendor = dfu_root->vendor; runtime_product = dfu_root->product; printf("Claiming USB DFU Runtime Interface...\n"); if (libusb_claim_interface(dfu_root->dev_handle, dfu_root->interface) < 0) { errx(EX_IOERR, "Cannot claim interface %d", dfu_root->interface); } if (libusb_set_interface_alt_setting(dfu_root->dev_handle, dfu_root->interface, 0) < 0) { errx(EX_IOERR, "Cannot set alt interface zero"); } printf("Determining device status: "); err = dfu_get_status(dfu_root, &status); if (err == LIBUSB_ERROR_PIPE) { printf("Device does not implement get_status, assuming appIDLE\n"); status.bStatus = DFU_STATUS_OK; status.bwPollTimeout = 0; status.bState = DFU_STATE_appIDLE; status.iString = 0; } else if (err < 0) { errx(EX_IOERR, "error get_status"); } else { printf("state = %s, status = %d\n", dfu_state_to_string(status.bState), status.bStatus); } milli_sleep(status.bwPollTimeout); switch (status.bState) { case DFU_STATE_appIDLE: case DFU_STATE_appDETACH: if (!detach_unsupported) { printf("Device really in Runtime Mode, send DFU " "detach request...\n"); if (dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000) < 0) { warnx("error detaching"); } } if (dfu_root->func_dfu.bmAttributes & USB_DFU_WILL_DETACH) { printf("Device will detach and reattach...\n"); } else { printf("Resetting USB...\n"); ret = libusb_reset_device(dfu_root->dev_handle); if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) errx(EX_IOERR, "error resetting " "after detach"); } break; case DFU_STATE_dfuERROR: printf("dfuERROR, clearing status\n"); if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) { errx(EX_IOERR, "error clear_status"); } /* fall through */ default: warnx("WARNING: Runtime device already in DFU state ?!?"); libusb_release_interface(dfu_root->dev_handle, dfu_root->interface); goto dfustate; } libusb_release_interface(dfu_root->dev_handle, dfu_root->interface); libusb_close(dfu_root->dev_handle); dfu_root->dev_handle = NULL; if (mode == MODE_DETACH) { libusb_exit(ctx); exit(0); } /* keeping handles open might prevent re-enumeration */ disconnect_devices(); milli_sleep(detach_delay * 1000); /* Change match vendor and product to impossible values to force * only DFU mode matches in the following probe */ match_vendor = match_product = 0x10000; probe_devices(ctx); if (dfu_root == NULL) { errx(EX_IOERR, "Lost device after RESET?"); } else if (dfu_root->next != NULL) { errx(EX_IOERR, "More than one DFU capable USB device found! " "Try `--list' and specify the serial number " "or disconnect all but one device"); } /* Check for DFU mode device */ if (!(dfu_root->flags | DFU_IFF_DFU)) errx(EX_SOFTWARE, "Device is not in DFU mode"); printf("Opening DFU USB Device...\n"); ret = libusb_open(dfu_root->dev, &dfu_root->dev_handle); if (ret || !dfu_root->dev_handle) { errx(EX_IOERR, "Cannot open device"); } } else { /* we're already in DFU mode, so we can skip the detach/reset * procedure */ /* If a match vendor/product was specified, use that as the runtime * vendor/product, otherwise use the DFU mode vendor/product */ runtime_vendor = match_vendor < 0 ? dfu_root->vendor : match_vendor; runtime_product = match_product < 0 ? dfu_root->product : match_product; } dfustate: #if 0 printf("Setting Configuration %u...\n", dfu_root->configuration); if (libusb_set_configuration(dfu_root->dev_handle, dfu_root->configuration) < 0) { errx(EX_IOERR, "Cannot set configuration"); } #endif printf("Claiming USB DFU Interface...\n"); if (libusb_claim_interface(dfu_root->dev_handle, dfu_root->interface) < 0) { errx(EX_IOERR, "Cannot claim interface"); } printf("Setting Alternate Setting #%d ...\n", dfu_root->altsetting); if (libusb_set_interface_alt_setting(dfu_root->dev_handle, dfu_root->interface, dfu_root->altsetting) < 0) { errx(EX_IOERR, "Cannot set alternate interface"); } status_again: printf("Determining device status: "); if (dfu_get_status(dfu_root, &status ) < 0) { errx(EX_IOERR, "error get_status"); } printf("state = %s, status = %d\n", dfu_state_to_string(status.bState), status.bStatus); milli_sleep(status.bwPollTimeout); switch (status.bState) { case DFU_STATE_appIDLE: case DFU_STATE_appDETACH: errx(EX_IOERR, "Device still in Runtime Mode!"); break; case DFU_STATE_dfuERROR: printf("dfuERROR, clearing status\n"); if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) { errx(EX_IOERR, "error clear_status"); } goto status_again; break; case DFU_STATE_dfuDNLOAD_IDLE: case DFU_STATE_dfuUPLOAD_IDLE: printf("aborting previous incomplete transfer\n"); if (dfu_abort(dfu_root->dev_handle, dfu_root->interface) < 0) { errx(EX_IOERR, "can't send DFU_ABORT"); } goto status_again; break; case DFU_STATE_dfuIDLE: printf("dfuIDLE, continuing\n"); break; default: break; } if (DFU_STATUS_OK != status.bStatus ) { printf("WARNING: DFU Status: '%s'\n", dfu_status_to_string(status.bStatus)); /* Clear our status & try again. */ if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) errx(EX_IOERR, "USB communication error"); if (dfu_get_status(dfu_root, &status) < 0) errx(EX_IOERR, "USB communication error"); if (DFU_STATUS_OK != status.bStatus) errx(EX_SOFTWARE, "Status is not OK: %d", status.bStatus); milli_sleep(status.bwPollTimeout); } printf("DFU mode device DFU version %04x\n", libusb_le16_to_cpu(dfu_root->func_dfu.bcdDFUVersion)); if (dfu_root->func_dfu.bcdDFUVersion == libusb_cpu_to_le16(0x11a)) dfuse_device = 1; /* If not overridden by the user */ if (!transfer_size) { transfer_size = libusb_le16_to_cpu( dfu_root->func_dfu.wTransferSize); if (transfer_size) { printf("Device returned transfer size %i\n", transfer_size); } else { errx(EX_IOERR, "Transfer size must be specified"); } } #ifdef HAVE_GETPAGESIZE /* autotools lie when cross-compiling for Windows using mingw32/64 */ #ifndef __MINGW32__ /* limitation of Linux usbdevio */ if ((int)transfer_size > getpagesize()) { transfer_size = getpagesize(); printf("Limited transfer size to %i\n", transfer_size); } #endif /* __MINGW32__ */ #endif /* HAVE_GETPAGESIZE */ if (transfer_size < dfu_root->bMaxPacketSize0) { transfer_size = dfu_root->bMaxPacketSize0; printf("Adjusted transfer size to %i\n", transfer_size); } switch (mode) { case MODE_UPLOAD: /* open for "exclusive" writing */ fd = open(file.name, O_WRONLY | O_BINARY | O_CREAT | O_EXCL | O_TRUNC, 0666); if (fd < 0) err(EX_IOERR, "Cannot open file %s for writing", file.name); if (dfuse_device || dfuse_options) { if (dfuse_do_upload(dfu_root, transfer_size, fd, dfuse_options) < 0) exit(1); } else { if (dfuload_do_upload(dfu_root, transfer_size, expected_size, fd) < 0) { exit(1); } } close(fd); break; case MODE_DOWNLOAD: if (((file.idVendor != 0xffff && file.idVendor != runtime_vendor) || (file.idProduct != 0xffff && file.idProduct != runtime_product)) && ((file.idVendor != 0xffff && file.idVendor != dfu_root->vendor) || (file.idProduct != 0xffff && file.idProduct != dfu_root->product))) { errx(EX_IOERR, "Error: File ID %04x:%04x does " "not match device (%04x:%04x or %04x:%04x)", file.idVendor, file.idProduct, runtime_vendor, runtime_product, dfu_root->vendor, dfu_root->product); } if (dfuse_device || dfuse_options || file.bcdDFU == 0x11a) { if (dfuse_do_dnload(dfu_root, transfer_size, &file, dfuse_options) < 0) exit(1); } else { if (dfuload_do_dnload(dfu_root, transfer_size, &file) < 0) exit(1); } break; case MODE_DETACH: if (detach_unsupported) break; ret = dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000); if (ret < 0) { warnx("can't detach"); } break; default: errx(EX_IOERR, "Unsupported mode: %u", mode); break; } if (final_reset) { if (detach_unsupported) { // STM32 DFU devices dont support DFU_DETACH // Instead, force the device into STATE_DFU_MANIFEST_WAIT_RESET // by sending a download request of size 0 and checking the state if (dfu_download(dfu_root->dev_handle, dfu_root->interface, 0x0, 0x0, NULL)) { warnx("Failure forcing a manifest"); } else { if (dfu_get_status(dfu_root, &status) < 0) { warnx("Unable to check status after manifest"); } else { printf("state = %s, status = %d\n", dfu_state_to_string(status.bState), status.bStatus); if (status.bState != STATE_DFU_MANIFEST) { warnx("Device should be in manifest state"); } } } } else { if (dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000) < 0) { /* Even if detach failed, just carry on to leave the device in a known state */ warnx("can't detach"); } } printf("Resetting USB to switch back to runtime mode\n"); ret = libusb_reset_device(dfu_root->dev_handle); if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) { errx(EX_IOERR, "error resetting after download"); } } libusb_close(dfu_root->dev_handle); dfu_root->dev_handle = NULL; libusb_exit(ctx); return (0); }
static void probe_configuration(libusb_device *dev, struct libusb_device_descriptor *desc) { struct usb_dfu_func_descriptor func_dfu; libusb_device_handle *devh; struct dfu_if *pdfu; struct libusb_config_descriptor *cfg; const struct libusb_interface_descriptor *intf; const struct libusb_interface *uif; char alt_name[MAX_DESC_STR_LEN + 1]; char serial_name[MAX_DESC_STR_LEN + 1]; int cfg_idx; int intf_idx; int alt_idx; int ret; int has_dfu; for (cfg_idx = 0; cfg_idx != desc->bNumConfigurations; cfg_idx++) { memset(&func_dfu, 0, sizeof(func_dfu)); has_dfu = 0; ret = libusb_get_config_descriptor(dev, cfg_idx, &cfg); if (ret != 0) return; if (match_config_index > -1 && match_config_index != cfg->bConfigurationValue) { libusb_free_config_descriptor(cfg); continue; } /* * In some cases, noticably FreeBSD if uid != 0, * the configuration descriptors are empty */ if (!cfg) return; ret = find_descriptor(cfg->extra, cfg->extra_length, USB_DT_DFU, &func_dfu, sizeof(func_dfu)); if (ret > -1) goto found_dfu; for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; intf_idx++) { uif = &cfg->interface[intf_idx]; if (!uif) break; for (alt_idx = 0; alt_idx < cfg->interface[intf_idx].num_altsetting; alt_idx++) { intf = &uif->altsetting[alt_idx]; if (intf->bInterfaceClass != 0xfe || intf->bInterfaceSubClass != 1) continue; ret = find_descriptor(intf->extra, intf->extra_length, USB_DT_DFU, &func_dfu, sizeof(func_dfu)); if (ret > -1) goto found_dfu; has_dfu = 1; } } if (has_dfu) { /* * Finally try to retrieve it requesting the * device directly This is not supported on * all devices for non-standard types */ if (libusb_open(dev, &devh) == 0) { ret = libusb_get_descriptor(devh, USB_DT_DFU, 0, (void *)&func_dfu, sizeof(func_dfu)); libusb_close(devh); if (ret > -1) goto found_dfu; } warnx("Device has DFU interface, " "but has no DFU functional descriptor"); /* fake version 1.0 */ func_dfu.bLength = 7; func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); goto found_dfu; } libusb_free_config_descriptor(cfg); continue; found_dfu: if (func_dfu.bLength == 7) { printf("Deducing device DFU version from functional descriptor " "length\n"); func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); } else if (func_dfu.bLength < 9) { printf("Error obtaining DFU functional descriptor\n"); printf("Please report this as a bug!\n"); printf("Warning: Assuming DFU version 1.0\n"); func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); printf("Warning: Transfer size can not be detected\n"); func_dfu.wTransferSize = 0; } for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; intf_idx++) { if (match_iface_index > -1 && match_iface_index != intf_idx) continue; uif = &cfg->interface[intf_idx]; if (!uif) break; for (alt_idx = 0; alt_idx < uif->num_altsetting; alt_idx++) { int dfu_mode; intf = &uif->altsetting[alt_idx]; if (intf->bInterfaceClass != 0xfe || intf->bInterfaceSubClass != 1) continue; dfu_mode = (intf->bInterfaceProtocol == 2); /* e.g. DSO Nano has bInterfaceProtocol 0 instead of 2 */ if (func_dfu.bcdDFUVersion == 0x011a && intf->bInterfaceProtocol == 0) dfu_mode = 1; /* LPC DFU bootloader has bInterfaceProtocol 1 (Runtime) instead of 2 */ if (desc->idVendor == 0x1fc9 && desc->idProduct == 0x000c && intf->bInterfaceProtocol == 1) dfu_mode = 1; if (dfu_mode && match_iface_alt_index > -1 && match_iface_alt_index != alt_idx) continue; if (dfu_mode) { if ((match_vendor_dfu >= 0 && match_vendor_dfu != desc->idVendor) || (match_product_dfu >= 0 && match_product_dfu != desc->idProduct)) { continue; } } else { if ((match_vendor >= 0 && match_vendor != desc->idVendor) || (match_product >= 0 && match_product != desc->idProduct)) { continue; } } if (libusb_open(dev, &devh)) { warnx("Cannot open DFU device %04x:%04x", desc->idVendor, desc->idProduct); break; } if (intf->iInterface != 0) ret = libusb_get_string_descriptor_ascii(devh, intf->iInterface, (void *)alt_name, MAX_DESC_STR_LEN); else ret = -1; if (ret < 1) strcpy(alt_name, "UNKNOWN"); if (desc->iSerialNumber != 0) ret = libusb_get_string_descriptor_ascii(devh, desc->iSerialNumber, (void *)serial_name, MAX_DESC_STR_LEN); else ret = -1; if (ret < 1) strcpy(serial_name, "UNKNOWN"); libusb_close(devh); if (dfu_mode && match_iface_alt_name != NULL && strcmp(alt_name, match_iface_alt_name)) continue; if (dfu_mode) { if (match_serial_dfu != NULL && strcmp(match_serial_dfu, serial_name)) continue; } else { if (match_serial != NULL && strcmp(match_serial, serial_name)) continue; } pdfu = dfu_malloc(sizeof(*pdfu)); memset(pdfu, 0, sizeof(*pdfu)); pdfu->func_dfu = func_dfu; pdfu->dev = libusb_ref_device(dev); pdfu->quirks = get_quirks(desc->idVendor, desc->idProduct, desc->bcdDevice); pdfu->vendor = desc->idVendor; pdfu->product = desc->idProduct; pdfu->bcdDevice = desc->bcdDevice; pdfu->configuration = cfg->bConfigurationValue; pdfu->interface = intf->bInterfaceNumber; pdfu->altsetting = intf->bAlternateSetting; pdfu->devnum = libusb_get_device_address(dev); pdfu->busnum = libusb_get_bus_number(dev); pdfu->alt_name = strdup(alt_name); if (pdfu->alt_name == NULL) errx(EX_SOFTWARE, "Out of memory"); pdfu->serial_name = strdup(serial_name); if (pdfu->serial_name == NULL) errx(EX_SOFTWARE, "Out of memory"); if (dfu_mode) pdfu->flags |= DFU_IFF_DFU; if (pdfu->quirks & QUIRK_FORCE_DFU11) { pdfu->func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0110); } pdfu->bMaxPacketSize0 = desc->bMaxPacketSize0; /* queue into list */ pdfu->next = dfu_root; dfu_root = pdfu; } } libusb_free_config_descriptor(cfg); } }
static int sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev) { int proplen; int n, *addr, *port_prop; char *phypath; uint8_t *rdata; struct libusb_device_descriptor *descr; sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv; /* Device descriptors */ proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, "usb-dev-descriptor", &rdata); if (proplen <= 0) { return (LIBUSB_ERROR_IO); } descr = (struct libusb_device_descriptor *)rdata; bcopy(descr, &dpriv->dev_descr, LIBUSB_DT_DEVICE_SIZE); dpriv->dev_descr.bcdUSB = libusb_cpu_to_le16(descr->bcdUSB); dpriv->dev_descr.idVendor = libusb_cpu_to_le16(descr->idVendor); dpriv->dev_descr.idProduct = libusb_cpu_to_le16(descr->idProduct); dpriv->dev_descr.bcdDevice = libusb_cpu_to_le16(descr->bcdDevice); /* Raw configuration descriptors */ proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, "usb-raw-cfg-descriptors", &rdata); if (proplen <= 0) { usbi_dbg("can't find raw config descriptors"); return (LIBUSB_ERROR_IO); } dpriv->raw_cfgdescr = calloc(1, proplen); if (dpriv->raw_cfgdescr == NULL) { return (LIBUSB_ERROR_NO_MEM); } else { bcopy(rdata, dpriv->raw_cfgdescr, proplen); dpriv->cfgvalue = ((struct libusb_config_descriptor *) rdata)->bConfigurationValue; } n = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &port_prop); if ((n != 1) || (*port_prop <= 0)) { return (LIBUSB_ERROR_IO); } dev->port_number = *port_prop; /* device physical path */ phypath = di_devfs_path(node); if (phypath) { dpriv->phypath = strdup(phypath); di_devfs_path_free(phypath); } else { free(dpriv->raw_cfgdescr); return (LIBUSB_ERROR_IO); } /* address */ n = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "assigned-address", &addr); if (n != 1 || *addr == 0) { usbi_dbg("can't get address"); } else { dev->device_address = *addr; } /* speed */ if (di_prop_exists(DDI_DEV_T_ANY, node, "low-speed") == 1) { dev->speed = LIBUSB_SPEED_LOW; } else if (di_prop_exists(DDI_DEV_T_ANY, node, "high-speed") == 1) { dev->speed = LIBUSB_SPEED_HIGH; } else if (di_prop_exists(DDI_DEV_T_ANY, node, "full-speed") == 1) { dev->speed = LIBUSB_SPEED_FULL; } else if (di_prop_exists(DDI_DEV_T_ANY, node, "super-speed") == 1) { dev->speed = LIBUSB_SPEED_SUPER; } usbi_dbg("vid=%x pid=%x, path=%s, bus_nmber=0x%x, port_number=%d, " "speed=%d", dpriv->dev_descr.idVendor, dpriv->dev_descr.idProduct, dpriv->phypath, dev->bus_number, dev->port_number, dev->speed); return (LIBUSB_SUCCESS); }
int main(int argc, char **argv) { struct dfu_if _rt_dif, _dif, *dif = &_dif; int num_devs; int num_ifs; unsigned int transfer_size = 0; unsigned int host_page_size; enum mode mode = MODE_DOWNLOAD; //MODE_NONE; struct dfu_status status; struct usb_dfu_func_descriptor func_dfu = {0}, func_dfu_rt = {0}; libusb_context *ctx; struct dfu_file file; char *alt_name = NULL; /* query alt name if non-NULL */ char *device_id_filter = NULL; unsigned char active_alt_name[MAX_DESC_STR_LEN+1]; char *end; int final_reset = 0; int ret; int dfuse = 0; unsigned int dfuse_address = 0; /* FIXME allow address to be zero? */ struct libusb_device_descriptor desc; host_page_size = getpagesize(); memset(dif, 0, sizeof(*dif)); /*if ( argc < 2 ) { fprintf( stderr, "unable to get file name from command line\n"); exit( 1 ); } file.name = argv[1];*/ while (1) { int c, option_index = 0; c = getopt_long(argc, argv, "hVvled:p:c:i:a:t:U:D:Rs:", opts, &option_index); if (c == -1) break; switch (c) { case 'h': help(); exit(0); break; case 'V': mode = MODE_VERSION; break; case 'v': verbose++; break; case 'l': mode = MODE_LIST; break; case 'e': mode = MODE_DETACH; break; case 'd': device_id_filter = optarg; break; case 'p': // Parse device path dif->path = optarg; dif->flags |= DFU_IFF_PATH; ret = resolve_device_path(dif); if (ret < 0) { fprintf(stderr, "unable to parse `%s'\n", optarg); exit(2); } if (!ret) { fprintf(stderr, "cannot find `%s'\n", optarg); exit(1); } break; case 'c': // Configuration dif->configuration = atoi(optarg); dif->flags |= DFU_IFF_CONFIG; break; case 'i': // Interface dif->interface = atoi(optarg); dif->flags |= DFU_IFF_IFACE; break; case 'a': // Interface Alternate Setting dif->altsetting = strtoul(optarg, &end, 0); if (*end) alt_name = optarg; dif->flags |= DFU_IFF_ALT; break; case 't': transfer_size = atoi(optarg); break; case 'U': mode = MODE_UPLOAD; file.name = optarg; break; case 'D': mode = MODE_DOWNLOAD; file.name = optarg; break; case 'R': final_reset = 1; break; case 's': dfuse = 1; if (strcmp(optarg, "default")) { dfuse_address = strtoul(optarg, &end, 0); if (!dfuse_address || (*end)) { fprintf(stderr, "invalid dfuse address: %s\n", optarg); exit(2); } } break; default: help(); exit(2); } } print_version(); if (mode == MODE_VERSION) { exit(0); } if (mode == MODE_NONE) { fprintf(stderr, "Error: You need to specify one of -D or -U\n\n"); help(); exit(2); } if (device_id_filter) { // Parse device ID parse_vendprod(&dif->vendor, &dif->product, device_id_filter); printf("Filter on vendor = 0x%04x product = 0x%04x\n", dif->vendor, dif->product); if (dif->vendor) dif->flags |= DFU_IFF_VENDOR; if (dif->product) dif->flags |= DFU_IFF_PRODUCT; } ret = libusb_init(&ctx); if (ret) { fprintf(stderr, "unable to initialize libusb: %i\n", ret); return EXIT_FAILURE; } /* if (verbose > 1) { libusb_set_debug(ctx, 255); } if (mode == MODE_LIST) { list_dfu_interfaces(ctx); exit(0); }*/ dfu_init(5000); num_devs = count_dfu_devices(ctx, dif); if (num_devs == 0) { fprintf(stderr, "No DFU capable USB device found\n"); exit(1); } else if (num_devs > 1) { /* We cannot safely support more than one DFU capable device * with same vendor/product ID, since during DFU we need to do * a USB bus reset, after which the target device will get a * new address */ fprintf(stderr, "More than one DFU capable USB device found, " "you might try `--list' and then disconnect all but one " "device\n"); exit(3); } if (!get_first_dfu_device(ctx, dif)) exit(3); /* We have exactly one device. Its libusb_device is now in dif->dev */ printf("Opening DFU capable USB device... "); ret = libusb_open(dif->dev, &dif->dev_handle); if (ret || !dif->dev_handle) { fprintf(stderr, "Cannot open device\n"); exit(1); } /* try to find first DFU interface of device */ memcpy(&_rt_dif, dif, sizeof(_rt_dif)); if (!get_first_dfu_if(&_rt_dif)) exit(1); printf("ID %04x:%04x\n", _rt_dif.vendor, _rt_dif.product); /* find set of quirks for this device */ set_quirks(_rt_dif.vendor, _rt_dif.product); /* Obtain run-time DFU functional descriptor without asking device * E.g. Freerunner does not like to be requested at this point */ ret = get_cached_extra_descriptor(_rt_dif.dev, _rt_dif.configuration, _rt_dif.interface, USB_DT_DFU, 0, (unsigned char *) &func_dfu_rt, sizeof(func_dfu_rt)); if (ret == 7) { /* DFU 1.0 does not have this field */ printf("Deducing device DFU version from functional descriptor " "length\n"); func_dfu_rt.bcdDFUVersion = libusb_cpu_to_le16(0x0100); } else if (ret < 9) { fprintf(stderr, "WARNING: Can not find cached DFU functional " "descriptor\n"); printf("Warning: Assuming DFU version 1.0\n"); func_dfu_rt.bcdDFUVersion = libusb_cpu_to_le16(0x0100); } printf("Run-time device DFU version %04x\n", libusb_le16_to_cpu(func_dfu_rt.bcdDFUVersion)); /* Transition from run-Time mode to DFU mode */ if (!(_rt_dif.flags & DFU_IFF_DFU)) { /* In the 'first round' during runtime mode, there can only be one * DFU Interface descriptor according to the DFU Spec. */ /* FIXME: check if the selected device really has only one */ printf("Claiming USB DFU Runtime Interface...\n"); if (libusb_claim_interface(_rt_dif.dev_handle, _rt_dif.interface) < 0) { fprintf(stderr, "Cannot claim interface %d\n", _rt_dif.interface); exit(1); } if (libusb_set_interface_alt_setting(_rt_dif.dev_handle, _rt_dif.interface, 0) < 0) { fprintf(stderr, "Cannot set alt interface zero\n"); exit(1); } printf("Determining device status: "); if (dfu_get_status(_rt_dif.dev_handle, _rt_dif.interface, &status ) < 0) { fprintf(stderr, "error get_status\n"); exit(1); } printf("state = %s, status = %d\n", dfu_state_to_string(status.bState), status.bStatus); if (!(quirks & QUIRK_POLLTIMEOUT)) usleep(status.bwPollTimeout * 1000); switch (status.bState) { case DFU_STATE_appIDLE: case DFU_STATE_appDETACH: printf("Device really in Runtime Mode, send DFU " "detach request...\n"); if (dfu_detach(_rt_dif.dev_handle, _rt_dif.interface, 1000) < 0) { fprintf(stderr, "error detaching\n"); exit(1); break; } libusb_release_interface(_rt_dif.dev_handle, _rt_dif.interface); if (func_dfu_rt.bmAttributes & USB_DFU_WILL_DETACH) { printf("Device will detach and reattach...\n"); } else { printf("Resetting USB...\n"); ret = libusb_reset_device(_rt_dif.dev_handle); if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) fprintf(stderr, "error resetting " "after detach\n"); } sleep(2); break; case DFU_STATE_dfuERROR: printf("dfuERROR, clearing status\n"); if (dfu_clear_status(_rt_dif.dev_handle, _rt_dif.interface) < 0) { fprintf(stderr, "error clear_status\n"); exit(1); break; } break; default: fprintf(stderr, "WARNING: Runtime device already " "in DFU state ?!?\n"); goto dfustate; break; } libusb_release_interface(_rt_dif.dev_handle, _rt_dif.interface); libusb_close(_rt_dif.dev_handle); if (mode == MODE_DETACH) { libusb_exit(ctx); exit(0); } /* now we need to re-scan the bus and locate our device */ // if (usb_find_devices() < 2) // printf("not at least 2 device changes found ?!?\n"); if (dif->flags & DFU_IFF_PATH) { ret = resolve_device_path(dif); if (ret < 0) { fprintf(stderr, "internal error: cannot re-parse `%s'\n", dif->path); abort(); } if (!ret) { fprintf(stderr, "Cannot resolve path after RESET?\n"); exit(1); } } num_devs = count_dfu_devices(ctx, dif); if (num_devs == 0) { fprintf(stderr, "Lost device after RESET?\n"); exit(1); } else if (num_devs > 1) { fprintf(stderr, "More than one DFU capable USB " "device found, you might try `--list' and " "then disconnect all but one device\n"); exit(1); } if (!get_first_dfu_device(ctx, dif)) exit(3); printf("Opening DFU USB Device...\n"); ret = libusb_open(dif->dev, &dif->dev_handle); if (ret || !dif->dev_handle) { fprintf(stderr, "Cannot open device\n"); exit(1); } } else { /* we're already in DFU mode, so we can skip the detach/reset * procedure */ } dfustate: if (alt_name) { int n; n = find_dfu_if(dif->dev, &alt_by_name, alt_name); if (!n) { fprintf(stderr, "No such Alternate Setting: \"%s\"\n", alt_name); exit(1); } if (n < 0) { fprintf(stderr, "Error %d in name lookup\n", n); exit(1); } dif->altsetting = n-1; } num_ifs = count_matching_dfu_if(dif); if (num_ifs < 0) { fprintf(stderr, "No matching DFU Interface after RESET?!?\n"); exit(1); } else if (num_ifs > 1 ) { printf("Detected interfaces after DFU transition\n"); list_dfu_interfaces(ctx); fprintf(stderr, "We have %u DFU Interfaces/Altsettings," " you have to specify one via --intf / --alt" " options\n", num_ifs); exit(1); } if (!get_matching_dfu_if(dif)) { fprintf(stderr, "Can't find the matching DFU interface/" "altsetting\n"); exit(1); } print_dfu_if(dif, NULL); if (get_alt_name(dif, active_alt_name) > 0) dif->alt_name = active_alt_name; else dif->alt_name = NULL; #if 0 printf("Setting Configuration %u...\n", dif->configuration); if (libusb_set_configuration(dif->dev_handle, dif->configuration) < 0) { fprintf(stderr, "Cannot set configuration\n"); exit(1); } #endif printf("Claiming USB DFU Interface...\n"); if (libusb_claim_interface(dif->dev_handle, dif->interface) < 0) { fprintf(stderr, "Cannot claim interface\n"); exit(1); } printf("Setting Alternate Setting #%d ...\n", dif->altsetting); if (libusb_set_interface_alt_setting(dif->dev_handle, dif->interface, dif->altsetting) < 0) { fprintf(stderr, "Cannot set alternate interface\n"); exit(1); } status_again: printf("Determining device status: "); if (dfu_get_status(dif->dev_handle, dif->interface, &status ) < 0) { fprintf(stderr, "error get_status\n"); exit(1); } printf("state = %s, status = %d\n", dfu_state_to_string(status.bState), status.bStatus); if (!(quirks & QUIRK_POLLTIMEOUT)) usleep(status.bwPollTimeout * 1000); switch (status.bState) { case DFU_STATE_appIDLE: case DFU_STATE_appDETACH: fprintf(stderr, "Device still in Runtime Mode!\n"); exit(1); break; case DFU_STATE_dfuERROR: printf("dfuERROR, clearing status\n"); if (dfu_clear_status(dif->dev_handle, dif->interface) < 0) { fprintf(stderr, "error clear_status\n"); exit(1); } goto status_again; break; case DFU_STATE_dfuDNLOAD_IDLE: case DFU_STATE_dfuUPLOAD_IDLE: printf("aborting previous incomplete transfer\n"); if (dfu_abort(dif->dev_handle, dif->interface) < 0) { fprintf(stderr, "can't send DFU_ABORT\n"); exit(1); } goto status_again; break; case DFU_STATE_dfuIDLE: printf("dfuIDLE, continuing\n"); break; } if (DFU_STATUS_OK != status.bStatus ) { printf("WARNING: DFU Status: '%s'\n", dfu_status_to_string(status.bStatus)); /* Clear our status & try again. */ dfu_clear_status(dif->dev_handle, dif->interface); dfu_get_status(dif->dev_handle, dif->interface, &status); if (DFU_STATUS_OK != status.bStatus) { fprintf(stderr, "Error: %d\n", status.bStatus); exit(1); } if (!(quirks & QUIRK_POLLTIMEOUT)) usleep(status.bwPollTimeout * 1000); } /* Get the DFU mode DFU functional descriptor * If it is not found cached, we will request it from the device */ ret = get_cached_extra_descriptor(dif->dev, dif->configuration, dif->interface, USB_DT_DFU, 0, (unsigned char *) &func_dfu, sizeof(func_dfu)); if (ret < 7) { fprintf(stderr, "Error obtaining cached DFU functional " "descriptor\n"); ret = usb_get_any_descriptor(dif->dev_handle, USB_DT_DFU, 0, (unsigned char *) &func_dfu, sizeof(func_dfu)); } if (ret == 7) { printf("Deducing device DFU version from functional descriptor " "length\n"); func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); } else if (ret < 9) { printf("Error obtaining DFU functional descriptor\n"); printf("Warning: Assuming DFU version 1.0\n"); func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); printf("Warning: Transfer size can not be detected\n"); func_dfu.wTransferSize = 0; } printf("DFU mode device DFU version %04x\n", libusb_le16_to_cpu(func_dfu.bcdDFUVersion)); if (func_dfu.bcdDFUVersion == 0x11a) dfuse = 1; if (!transfer_size) { transfer_size = libusb_le16_to_cpu(func_dfu.wTransferSize); printf("Device returned transfer size %i\n", transfer_size); } /* if returned zero or not detected (and not user specified) */ if (!transfer_size) { transfer_size = DEFAULT_TRANSFER_SIZE; printf("Warning: Trying default transfer size %i\n", transfer_size); } /* limitation of Linux usbdevio */ if (transfer_size > host_page_size) { transfer_size = host_page_size; printf("Limited transfer size to %i\n", transfer_size); } /* DFU specification */ //struct libusb_device_descriptor desc; if (libusb_get_device_descriptor(dif->dev, &desc)) { fprintf(stderr, "Error: Failed to get device descriptor\n"); exit(1); } if (transfer_size < desc.bMaxPacketSize0) { transfer_size = desc.bMaxPacketSize0; printf("Adjusted transfer size to %i\n", transfer_size); } switch (mode) { case MODE_UPLOAD: file.fd = open(file.name, O_WRONLY|O_CREAT|O_EXCL, 0644); if (file.fd < 0) { perror(file.name); exit(1); } if (dfuse) { if (dfuse_do_upload(dif, transfer_size, file, dfuse_address) < 0) exit(1); } else { if (dfuload_do_upload(dif, transfer_size, file) < 0) exit(1); } close(file.fd); break; case MODE_DOWNLOAD: file.fd = open(file.name, O_RDONLY|O_BINARY); if (file.fd < 0) { perror(file.name); exit(1); } ret = parse_dfu_suffix(&file); if (ret < 0) exit(1); if (ret == 0) { fprintf(stderr, "Warning: File has no DFU suffix\n"); } else if (file.bcdDFU != 0x0100 && file.bcdDFU != 0x11a) { fprintf(stderr, "Unsupported DFU file revision " "%04x\n", file.bcdDFU); exit(1); } if (file.idVendor != 0xffff && dif->vendor != file.idVendor) { fprintf(stderr, "Warning: File vendor ID %04x does " "not match device %04x\n", file.idVendor, dif->vendor); } if (file.idProduct != 0xffff && dif->product != file.idProduct) { fprintf(stderr, "Warning: File product ID %04x does " "not match device %04x\n", file.idProduct, dif->product); } if (dfuse || file.bcdDFU == 0x11a) { if (dfuse_do_dnload(dif, transfer_size, file, dfuse_address) < 0) exit(1); } else { if (dfuload_do_dnload(dif, transfer_size, file) < 0) exit(1); } close(file.fd); break; default: fprintf(stderr, "Unsupported mode: %u\n", mode); exit(1); } if (final_reset) { if (dfu_detach(dif->dev_handle, dif->interface, 1000) < 0) { fprintf(stderr, "can't detach\n"); } printf("Resetting USB to switch back to runtime mode\n"); ret = libusb_reset_device(dif->dev_handle); if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) { fprintf(stderr, "error resetting after download\n"); } } libusb_close(dif->dev_handle); libusb_exit(ctx); exit(0); }