Dfu::Dfu() { usb_dfu_func_descriptor func_dfu = {0}; struct libusb_device_descriptor desc; int ret; m_context = 0; m_transfer_size = 0; memset(&m_dif, 0, sizeof(m_dif)); ret = libusb_init(&m_context); if (ret) throw std::runtime_error("Cannot initialize USB."); #if 0 num_devs = count_dfu_devices(m_context, &m_dif); if (num_devs != 1) throw std::runtime_error("Cannot find Pixy DFU device."); if (!get_first_dfu_device(m_context, &m_dif)) throw std::runtime_error("Cannot find Pixy DFU device."); ret = libusb_open(m_dif.dev, &m_dif.dev_handle); if (ret || !m_dif.dev_handle) throw std::runtime_error("Cannot open Pixy DFU device."); #else m_dif.dev_handle = libusb_open_device_with_vid_pid(m_context, PIXY_DFU_VID, PIXY_DFU_DID); if (m_dif.dev_handle==NULL) throw std::runtime_error("Cannot open Pixy DFU device."); m_dif.dev = libusb_get_device(m_dif.dev_handle); #endif if (libusb_set_configuration(m_dif.dev_handle, 1) < 0) throw std::runtime_error("Cannot set configuration for Pixy DFU device."); if (libusb_claim_interface(m_dif.dev_handle, m_dif.interface) < 0) throw std::runtime_error("Cannot claim interface for Pixy DFU device."); if (libusb_set_interface_alt_setting(m_dif.dev_handle, m_dif.interface, m_dif.altsetting) < 0) throw std::runtime_error("Cannot set interface for Pixy DFU device."); ret = usb_get_any_descriptor(m_dif.dev_handle, USB_DT_DFU, 0, (unsigned char *) &func_dfu, sizeof(func_dfu)); m_transfer_size = libusb_le16_to_cpu(func_dfu.wTransferSize); if (!m_transfer_size) throw std::runtime_error("Cannot get transfer size for Pixy DFU device."); /* DFU specification */ if (libusb_get_device_descriptor(m_dif.dev, &desc)) throw std::runtime_error("Cannot get descriptor for Pixy DFU device."); if (m_transfer_size < desc.bMaxPacketSize0) m_transfer_size = desc.bMaxPacketSize0; }
void locate_dfu_device(struct dfu_if *dif,char * error_string) { int num_devs; num_devs = count_dfu_devices( dif ); if( num_devs == 0 ) { fprintf( stderr, "%s\n",error_string ); 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( dif ) ) exit( 3 ); }
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); }
int download(AbstractFile* file, unsigned int transfer_size, int final_reset) { struct dfu_if _rt_dif, _dif, *dif = &_dif; int num_devs; int num_ifs; enum mode mode = MODE_NONE; struct dfu_status status; struct usb_dfu_func_descriptor func_dfu; char *alt_name = NULL; /* query alt name if non-NULL */ int ret; printf("dfu-util - (C) 2007 by OpenMoko Inc. Hacked by the iPhone Dev Team\n" "This program is Free Software and has ABSOLUTELY NO WARRANTY\n\n"); memset(dif, 0, sizeof(*dif)); dif->flags = DFU_IFF_VENDOR | DFU_IFF_PRODUCT; dif->vendor = 0x05ac; dif->product = 0x1222; dif->product_mask = 0xfff0; usb_init(); //usb_set_debug(255); usb_find_busses(); usb_find_devices(); mode = MODE_DOWNLOAD; if (mode == MODE_NONE) { fprintf(stderr, "You need to specify one of -D or -U\n"); help(); exit(2); } if (!file) { fprintf(stderr, "You need to specify a filename to -D -r -U\n"); help(); exit(2); } dfu_init(5000); num_devs = count_dfu_devices(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(dif)) exit(3); /* We have exactly one device. It's usb_device is now in dif->dev */ printf("Opening USB Device 0x%04x:0x%04x...\n", dif->vendor, dif->product); dif->dev_handle = usb_open(dif->dev); if (!dif->dev_handle) { fprintf(stderr, "Cannot open device: %s\n", usb_strerror()); 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); 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; } print_dfu_if(dif, NULL); num_ifs = count_dfu_interfaces(dif->dev); if (num_ifs < 0) { fprintf(stderr, "No DFU Interface after RESET?!?\n"); exit(1); } else if (num_ifs == 1) { if (!get_first_dfu_if(dif)) { fprintf(stderr, "Can't find the single available DFU IF\n"); exit(1); } } else if (num_ifs > 1 && !dif->flags & (DFU_IFF_IFACE|DFU_IFF_ALT)) { fprintf(stderr, "We have %u DFU Interfaces/Altsettings, " "you have to specify one via --intf / --alt options\n", num_ifs); exit(1); } dif->configuration = 1; printf("Setting Configuration %u...\n", dif->configuration); if (usb_set_configuration(dif->dev_handle, dif->configuration) < 0) { fprintf(stderr, "Cannot set configuration: %s\n", usb_strerror()); } printf("Claiming USB DFU Interface...\n"); if (usb_claim_interface(dif->dev_handle, dif->interface) < 0) { fprintf(stderr, "Cannot claim interface: %s\n", usb_strerror()); } printf("Setting Alternate Setting ...\n"); if (usb_set_altinterface(dif->dev_handle, dif->altsetting) < 0) { fprintf(stderr, "Cannot set alternate interface: %s\n", usb_strerror()); } status_again: printf("Determining device status: "); if (dfu_get_status(dif->dev_handle, dif->interface, &status ) < 0) { fprintf(stderr, "error get_status: %s\n", usb_strerror()); exit(1); } printf("state = %s, status = %d\n", dfu_state_to_string(status.bState), status.bStatus); 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: %s\n", usb_strerror()); 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: %s\n", usb_strerror()); exit(1); } goto status_again; break; case DFU_STATE_dfuIDLE: printf("dfuIDLE, continuing\n"); break; } if (!transfer_size) { /* Obtain DFU functional descriptor */ ret = usb_get_descriptor(dif->dev_handle, 0x21, dif->interface, &func_dfu, sizeof(func_dfu)); if (ret < 0) { fprintf(stderr, "Error obtaining DFU functional " "descriptor: %s\n", usb_strerror()); transfer_size = 0x800; } else { func_dfu.wTransferSize = LE2CPU16(func_dfu.wTransferSize); transfer_size = func_dfu.wTransferSize; } } printf("Transfer Size = 0x%04x\n", transfer_size); 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); } } switch (mode) { case MODE_DOWNLOAD: if (sam7dfu_do_dnload(dif->dev_handle, dif->interface, transfer_size, file) < 0) { usb_release_interface(dif->dev_handle, dif->interface); return 1; } 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: %s\n", usb_strerror()); } printf("Resetting USB to switch back to runtime mode\n"); ret = usb_reset(dif->dev_handle); if (ret < 0 && ret != -ENODEV) { fprintf(stderr, "error resetting after download: %s\n", usb_strerror()); } } if(usb_release_interface(dif->dev_handle, dif->interface) < 0) { fprintf(stderr, "error: releasing %s\n", usb_strerror()); } if(usb_close(dif->dev_handle) < 0) { fprintf(stderr, "error: closing %s\n", usb_strerror()); } return 0; }
int main(int argc, char **argv) { struct usb_vendprod vendprod; struct dfu_if _rt_dif, _dif, *dif = &_dif; int num_devs; int num_ifs; unsigned int transfer_size = 0; enum mode mode = MODE_NONE; struct dfu_status status; int quirks_auto_detect = 1; dfu_quirks manual_quirks; dfu_handle handle; char *filename = NULL; char *alt_name = NULL; /* query alt name if non-NULL */ char *end; int final_reset = 0; int page_size = getpagesize(); int ret; printf("dfu-util - (C) 2007-2008 by OpenMoko Inc.\n" "This program is Free Software and has ABSOLUTELY NO WARRANTY\n\n"); dfu_quirks_clear(&manual_quirks); memset(dif, 0, sizeof(*dif)); usb_init(); //usb_set_debug(255); usb_find_busses(); usb_find_devices(); while (1) { int c, option_index = 0; c = getopt_long(argc, argv, "hVvld:p:c:i:a:t:U:D:C:S:RQNq:", opts, &option_index); if (c == -1) break; switch (c) { case 'h': help(); exit(0); break; case 'V': print_version(); exit(0); break; case 'v': verbose = 1; break; case 'l': list_dfu_interfaces(); exit(0); break; case 'd': /* Parse device */ if (parse_vendprod(&vendprod, optarg) < 0) { fprintf(stderr, "unable to parse `%s'\n", optarg); exit(2); } dif->vendor = vendprod.vendor; dif->product = vendprod.product; dif->flags |= (DFU_IFF_VENDOR | DFU_IFF_PRODUCT); 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; filename = optarg; break; case 'D': mode = MODE_DOWNLOAD; filename = optarg; break; case 'C': mode = MODE_COMPARE; /* TODO: verify firmware */ filename = optarg; break; case 'S': filename = optarg; add_file_suffix(filename); exit(0); break; case 'R': final_reset = 1; break; case 'Q': dfu_quirks_print(); exit(0); break; case 'N': quirks_auto_detect = 0; break; case 'q': quirks_auto_detect = 0; dfu_quirk_set(&manual_quirks, atoi(optarg)); break; default: help(); exit(2); } } if (mode == MODE_NONE) { fprintf(stderr, "You need to specify one of -D or -U\n"); help(); exit(2); } if (!filename) { fprintf(stderr, "You need to specify a filename to -D -r -U\n"); help(); exit(2); } dfu_init(&handle, 5000); num_devs = count_dfu_devices(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(dif)) exit(3); /* We have exactly one device. It's usb_device is now in dif->dev */ printf("Opening USB Device 0x%04x:0x%04x...\n", dif->vendor, dif->product); dif->dev_handle = usb_open(dif->dev); if (!dif->dev_handle) { fprintf(stderr, "Cannot open device: %s\n", usb_strerror()); 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); handle.device = _rt_dif.dev_handle; handle.interface = _rt_dif.interface; /* automatic quirk detection */ if(quirks_auto_detect) { /* TODO: let the detection be influenced by bcdDFU, bcdDevice */ handle.quirk_flags = dfu_quirks_detect(0, dif->vendor, dif->product, 0); } /* merge with manual quirks */ dfu_quirks_insert(&handle.quirk_flags, &manual_quirks); if(!dfu_quirks_is_empty(&handle.quirk_flags)) { printf("Selected quirks: "); dfu_quirks_print_set(&handle.quirk_flags); printf("\n"); } 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 %d...\n", _rt_dif.interface); if (usb_claim_interface(_rt_dif.dev_handle, _rt_dif.interface) < 0) { fprintf(stderr, "Cannot claim interface: %s\n", usb_strerror()); exit(1); } /* DFU 1.0, Table 4.1: in runtime-mode, alternate interface setting must be zero. therefore we can assume, '0' is correct. the reason we use usb_set_altinterface() here: switch devices to the interface set using usb_claim_interface() above - for some reason this isn't done there. is the only libusb API which issues the SET_INTERFACE USB standard request is usb_set_altinterface() */ if (usb_set_altinterface(_rt_dif.dev_handle, 0) < 0) { fprintf(stderr, "Cannot set alternate interface %d: %s\n", 0, usb_strerror()); exit(1); } printf("Determining device state: "); int state = -1; if ( (state = dfu_get_state(&handle)) < 0) { exit(1); } printf("state = %s\n", dfu_state_to_string(state)); dfu_sm_set_state_unchecked(&handle, state); printf("Determining device status: "); if (dfu_get_status(&handle, &status ) < 0) { exit(1); } printf("state = %s, status = %d = \"%s\"\n", dfu_state_to_string(status.bState), status.bStatus, dfu_status_to_string(status.bStatus) ); switch (status.bState) { case DFU_STATE_appIDLE: case DFU_STATE_appDETACH: printf("Device really in Runtime Mode, send DFU " "detach request...\n"); if(status.bState == DFU_STATE_appDETACH) { printf("Device is already in state %s, skipping DFU_DETACH request\n", dfu_state_to_string(status.bState)); } else { if (dfu_detach(&handle, 1000) < 0) { exit(1); break; } } /* handle bitWillDetach (DFU 1.1) */ if(handle.dfu_ver == DFU_VERSION_1_1 && handle.func_dfu.bmAttributes & USB_DFU_WILL_DETACH) { /* TODO: test this with a real DFU 1.1 device */ printf("Waiting for USB device's own detach (bitWillDetach=1)...\n"); dfu_sm_set_state_checked(&handle, DFU_STATE_dfuIDLE); } else { printf("Resetting USB...\n"); ret = dfu_usb_reset(&handle); if (ret < 0 && ret != -ENODEV) { /* do nothing; error msg is output in dfu_usb_reset. */ } } sleep(2); break; case DFU_STATE_dfuERROR: printf("dfuERROR, clearing status\n"); if (dfu_clear_status(&handle) < 0) { exit(1); break; } break; default: fprintf(stderr, "WARNING: Runtime device already " "in DFU state ?!?\n"); goto dfustate; break; } /* 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, "Can't resolve path after RESET?\n"); exit(1); } } num_devs = count_dfu_devices(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(dif)) exit(3); printf("Opening USB Device...\n"); dif->dev_handle = usb_open(dif->dev); if (!dif->dev_handle) { fprintf(stderr, "Cannot open device: %s\n", usb_strerror()); 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; } print_dfu_if(dif, NULL); num_ifs = count_dfu_interfaces(dif->dev); if (num_ifs < 0) { fprintf(stderr, "No DFU Interface after RESET?!?\n"); exit(1); } else if (num_ifs == 1) { if (!get_first_dfu_if(dif)) { fprintf(stderr, "Can't find the single available " "DFU IF\n"); exit(1); } } else if (num_ifs > 1 && (!dif->flags) & (DFU_IFF_IFACE|DFU_IFF_ALT)) { fprintf(stderr, "We have %u DFU Interfaces/Altsettings, " "you have to specify one via --intf / --alt options\n", num_ifs); exit(1); } #if 0 printf("Setting Configuration %u...\n", dif->configuration); if (usb_set_configuration(dif->dev_handle, dif->configuration) < 0) { fprintf(stderr, "Cannot set configuration: %s\n", usb_strerror()); exit(1); } #endif printf("Claiming USB DFU Interface...\n"); if (usb_claim_interface(dif->dev_handle, dif->interface) < 0) { fprintf(stderr, "Cannot claim interface: %s\n", usb_strerror()); exit(1); } printf("Setting Alternate Setting ...\n"); if (usb_set_altinterface(dif->dev_handle, dif->altsetting) < 0) { fprintf(stderr, "Cannot set alternate interface: %s\n", usb_strerror()); exit(1); } /* update the handle to point to the dfu-mode descriptor */ handle.device = dif->dev_handle; handle.interface = dif->interface; status_again: printf("Determining device status: "); if (dfu_get_status(&handle, &status ) < 0) { fprintf(stderr, "error get_status: %s\n", usb_strerror()); exit(1); } printf("state = %s, status = %d\n", dfu_state_to_string(status.bState), status.bStatus); /* force the statemachine into current status */ dfu_sm_set_state_unchecked(&handle, status.bState); 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(&handle) < 0) { fprintf(stderr, "error clear_status: %s\n", usb_strerror()); 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(&handle) < 0) { fprintf(stderr, "can't send DFU_ABORT: %s\n", usb_strerror()); exit(1); } goto status_again; break; case DFU_STATE_dfuIDLE: printf("dfuIDLE, continuing\n"); break; } /* Obtain DFU functional descriptor */ ret = usb_get_descriptor(dif->dev_handle, 0x21, dif->interface, &(handle.func_dfu), sizeof(handle.func_dfu)); if (ret < 0) { fprintf(stderr, "Error obtaining DFU functional " "descriptor: %s\n", usb_strerror()); if(dfu_quirk_is_set(&handle.quirk_flags, QUIRK_IGNORE_INVALID_FUNCTIONAL_DESCRIPTOR)) { handle.func_dfu.bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_CAN_UPLOAD | USB_DFU_MANIFEST_TOL; handle.func_dfu.wTransferSize = cpu_to_le16(transfer_size); if(!transfer_size) transfer_size = page_size; handle.func_dfu.bcdDFUVersion = USB_DFU_VER_1_0; fprintf(stderr, " Still, try to continue with default flags/manual settings.\n"); } else { exit(1); } } else { transfer_size = le16_to_cpu(handle.func_dfu.wTransferSize); } /* why is this limited to page_size, a host-dependent value? (sgiessl) */ if (transfer_size > page_size) transfer_size = page_size; /* quirk overwriting DFU version */ if(dfu_quirk_is_set(&handle.quirk_flags, QUIRK_FORCE_DFU_VERSION_1_0)) { handle.func_dfu.bcdDFUVersion = USB_DFU_VER_1_0; } else if(dfu_quirk_is_set(&handle.quirk_flags, QUIRK_FORCE_DFU_VERSION_1_1)) { handle.func_dfu.bcdDFUVersion = USB_DFU_VER_1_1; } /* read DFU version */ switch(handle.func_dfu.bcdDFUVersion) { case USB_DFU_VER_1_1: handle.dfu_ver = DFU_VERSION_1_1; break; default: printf("WARNING: device specifies unknown DFU version 0x%.2x, defaulting to DFU 1.0\n", handle.func_dfu.bcdDFUVersion); /* fall through intended */ case USB_DFU_VER_1_0: handle.dfu_ver = DFU_VERSION_1_0; break; } printf("Transfer Size = 0x%04x\n", transfer_size); printf("Device functional descriptor: %s\n", dfu_func_descriptor_to_string(&handle.func_dfu)); 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(&handle); dfu_get_status(&handle, &status); if (DFU_STATUS_OK != status.bStatus) { fprintf(stderr, "Error: %d\n", status.bStatus); exit(1); } } switch (mode) { case MODE_UPLOAD: if (sam7dfu_do_upload(&handle, transfer_size, filename) < 0) exit(1); break; case MODE_DOWNLOAD: if (sam7dfu_do_dnload(&handle, transfer_size, filename) < 0) exit(1); break; default: fprintf(stderr, "Unsupported mode: %u\n", mode); exit(1); } if (final_reset) { if(dfu_quirk_is_set(&handle.quirk_flags, QUIRK_OPENMOKO_DETACH_BEFORE_FINAL_RESET)) { /* DFU_DETACH is only allowed in appIDLE, so this is non-standard (as of DFU 1.0, and 1.1). */ printf("Initiating reset by sending DFU_DETACH (QUIRK_OPENMOKO_DETACH_BEFORE_FINAL_RESET)\n"); if (dfu_detach(&handle, 1000) < 0) { fprintf(stderr, "can't detach: %s\n", usb_strerror()); } } printf("Resetting USB to switch back to runtime mode\n"); ret = usb_reset(dif->dev_handle); if (ret < 0 && ret != -ENODEV) { fprintf(stderr, "error resetting after download: %s\n", usb_strerror()); } } exit(0); }