Example #1
0
int dfu_abort_to_idle(struct dfu_if *dif)
{
	int ret;
	struct dfu_status dst;

	ret = dfu_abort(dif->dev_handle, dif->interface);
	if (ret < 0) {
		errx(EX_IOERR, "Error sending dfu abort request");
		exit(1);
	}
	ret = dfu_get_status(dif, &dst);
	if (ret < 0) {
		errx(EX_IOERR, "Error during abort get_status");
		exit(1);
	}
	if (dst.bState != DFU_STATE_dfuIDLE) {
		errx(EX_IOERR, "Failed to enter idle state on abort");
		exit(1);
	}
	milli_sleep(dst.bwPollTimeout);
	return ret;
}
Example #2
0
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);
}
Example #3
0
int Dfu::download(const QString &filename)
{
    int bytes_sent = 0;
    unsigned char *buf;
    struct dfu_status dst;
    int ret;
    int fsize;
    unsigned int bytes_left;
    int chunk_size;
    const char *exception = NULL;

    FILE *filep = fopen(filename.toUtf8(), "rb");
    if (filep == NULL)
        throw std::runtime_error("Cannot open file.");

    buf = (unsigned char *)malloc(m_transfer_size);
    fseek(filep, 0, SEEK_END);
    fsize = ftell(filep);
    rewind(filep);

    while (bytes_sent < fsize)
    {
        bytes_left = fsize - bytes_sent;
        if (bytes_left < m_transfer_size)
            chunk_size = bytes_left;
        else
            chunk_size = m_transfer_size;
        ret = fread(buf, 1, chunk_size, filep);
        if (ret < 0)
        {
            exception = "Error while reading file.";
            goto exit;
        }
        ret = dfu_download(m_dif.dev_handle, m_dif.interface, ret, ret ? buf : NULL);
        if (ret < 0)
        {
            exception = "Error while downloading DFU file.";
            goto exit;
        }
        bytes_sent += ret;

        do {
            ret = dfu_get_status(m_dif.dev_handle, m_dif.interface, &dst);
            if (ret < 0)
            {
                exception = "Error while getting status.";
                goto exit;
            }

            if (dst.bState == DFU_STATE_dfuDNLOAD_IDLE ||
                    dst.bState == DFU_STATE_dfuERROR)
                break;

            milli_sleep(dst.bwPollTimeout);

        } while (1);

        if (dst.bStatus != DFU_STATUS_OK)
        {
            exception = "Bad status during DFU download.";
            goto exit;
        }
    }

    /* send one zero sized download request to signalize end */
    ret = dfu_download(m_dif.dev_handle, m_dif.interface, 0, NULL);
    if (ret < 0)
    {
        exception = "Error while downloading DFU file.";
        goto exit;
    }

    /* Transition to MANIFEST_SYNC state */
    ret = dfu_get_status(m_dif.dev_handle, m_dif.interface, &dst);

exit:
    free(buf);
    fclose(filep);
    if (exception)
        throw std::runtime_error(exception);

    return bytes_sent;
}
Example #4
0
bool uploadFirmware(IOUSBDeviceInterface300** device)
{
    IOUSBInterfaceInterface300** interface = getDFUInterface(device);
    
    if (interface != NULL)
    {
        IOUSBDFUDescriptor* descriptor = getDFUDescriptor(interface);
        
        if (descriptor != NULL)
        {
            if (descriptor->bmAttributes & USB_DFU_CAN_DOWNLOAD)
            {
                (*interface)->USBInterfaceOpen(interface);
                
                unsigned char intfIndex;
                (*interface)->GetInterfaceNumber(interface, &intfIndex);
                
                struct dfu_status status;
                int firmware_size = firmware.size.total - firmware.size.suffix;
                int remaining = firmware_size;
                unsigned short transaction = 1;
                int sent = 0;

                printf("[i] Initiating firmware upload (%d bytes, %d bytes transfer size).\n", remaining, descriptor->wTransferSize);
                
                while (remaining > 0)
                {
                    int size = descriptor->wTransferSize < remaining ? descriptor->wTransferSize : remaining;
                    
                    printf("[i] Downloading firmware: Chunk %d (%d bytes) - %d / %d bytes.\n", transaction, size, sent, firmware_size);
                    
                    if (dfu_download(interface,
                                     intfIndex,
                                     size,
                                     transaction,
                                     firmware.firmware + sent) != kIOReturnSuccess)
                        break;
                    
                    sent += size;
                    remaining -= size;
                                    
                    // Wrap transaction around if required
                    transaction++;
                    transaction %= USHRT_MAX;
                    
                    if (dfu_get_status(interface, intfIndex, &status) != kIOReturnSuccess)
                        break;
                    
                    if (status.bStatus != DFU_STATUS_OK)
                    {
                        fprintf(stderr, "[!] Firmware download aborting (state %s, status %s).\n",
                                dfu_state_to_string(status.bState),
                                dfu_status_to_string(status.bStatus));
                        
                        break;
                    }
                }
                
                dfu_get_status(interface, intfIndex, &status);
                printf("[i] Device State %s, Status %s, String %d\n", dfu_state_to_string(status.bState), dfu_status_to_string(status.bStatus), status.iString);
                                
                usleep(status.bwPollTimeout * 1000);
                
                // Signal firmware upload finished
                dfu_download(interface, intfIndex, 0, transaction, NULL);
                
                dfu_get_status(interface, intfIndex, &status);
                printf("[i] Device State %s, Status %s, String %d\n", dfu_state_to_string(status.bState), dfu_status_to_string(status.bStatus), status.iString);
                
                // Firmware upload done resetting device
                if (remaining > 0)
                    fprintf(stderr, "[!] Error while flashing: \"%s\", %d / %d bytes remaining.\n",
                            dfu_status_to_string(status.bStatus), remaining, firmware_size);
                else if (status.bStatus != DFU_STATUS_OK)
                    fprintf(stderr, "[!] Error while flashing, %s.\n", dfu_status_to_string(status.bStatus));
                else
                {
                    printf("[i] Firmware upload complete, resetting device.\n");
                    
                    (*device)->ResetDevice(device);
                    (*interface)->USBInterfaceClose(interface);
                    (*interface)->Release(interface);
                    
                    (*device)->USBDeviceClose(device);
                    
                    return true;
                }

                (*interface)->USBInterfaceClose(interface);
            }
            else
                fprintf(stderr, "[!] Device is not capable to download firmware.\n");
        }
        else
            fprintf(stderr, "[!] Failed to locate DFU descriptor for interface.\n");
        
        (*interface)->Release(interface);
    }
    else
        fprintf(stderr, "[!] Failed to locate DFU interface.\n");
    
    (*device)->USBDeviceClose(device);
    
    return false;
}
/*
 *  Gets the device into the dfuIDLE state if possible.
 *
 *  device    - the dfu device to commmunicate with
 *
 *  returns 0 on success, 1 if device was reset, error otherwise
 */
static int32_t dfu_make_idle( dfu_device_t *device,
                              const dfu_bool initial_abort )
{
    dfu_status_t status;
    int32_t retries = 4;

    if( true == initial_abort ) {
        dfu_abort( device );
    }

    while( 0 < retries ) {
        if( 0 != dfu_get_status(device, &status) ) {
            dfu_clear_status( device );
            continue;
        }

        DEBUG( "State: %s (%d)\n", dfu_state_to_string(status.bState), status.bState );

        switch( status.bState ) {
            case STATE_DFU_IDLE:
                if( DFU_STATUS_OK == status.bStatus ) {
                    return 0;
                }

                /* We need the device to have the DFU_STATUS_OK status. */
                dfu_clear_status( device );
                break;

            case STATE_DFU_DOWNLOAD_SYNC:   /* abort -> idle */
            case STATE_DFU_DOWNLOAD_IDLE:   /* abort -> idle */
            case STATE_DFU_MANIFEST_SYNC:   /* abort -> idle */
            case STATE_DFU_UPLOAD_IDLE:     /* abort -> idle */
            case STATE_DFU_DOWNLOAD_BUSY:   /* abort -> error */
            case STATE_DFU_MANIFEST:        /* abort -> error */
                dfu_abort( device );
                break;

            case STATE_DFU_ERROR:
                dfu_clear_status( device );
                break;

            case STATE_APP_IDLE:
                dfu_detach( device, DFU_DETACH_TIMEOUT );
                break;

            case STATE_APP_DETACH:
            case STATE_DFU_MANIFEST_WAIT_RESET:
                DEBUG( "Resetting the device\n" );
#ifdef HAVE_LIBUSB_1_0
                libusb_reset_device( device->handle );
#else
                usb_reset( device->handle );
#endif
                return 1;
        }

        retries--;
    }

    DEBUG( "Not able to transition the device into the dfuIDLE state.\n" );
    return -2;
}
Example #6
0
void check_runtime_interface( struct dfu_if *_rt_dif, struct dfu_if *dif,
    struct dfu_status *status )
{
  if( !_rt_dif->flags & DFU_IFF_DFU ) {

    while(1) { 

      /* 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 */

      claim_interface(_rt_dif);

      printf( "Determining device status: " );
      if( dfu_get_status
          ( _rt_dif->dev_handle, _rt_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:
          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: %s\n",
                usb_strerror(  ) );
            exit( 1 );
            break;
          }
          printf( "Resetting USB...\n" );
          int ret = usb_reset( _rt_dif->dev_handle );
          if( ret < 0 && ret != -ENODEV )
            fprintf( stderr,
                "error resetting after detach: %s\n",
                usb_strerror(  ) );
          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: %s\n",
                usb_strerror(  ) );
            exit( 1 );
            break;
          }
          break;
        default:
          fprintf( stderr, "WARNING: Runtime device already "
              "in DFU state ?!?\n" );
          break;
      }

      if( in_array( status->bState, ( unsigned char[3] ) {
            DFU_STATE_appIDLE, DFU_STATE_appDETACH,
            DFU_STATE_dfuERROR}, 3 ) >= 0 ) {

        /* now we need to re-scan the bus and locate our device */
        int ret = 0;
        if( ( ret = usb_find_devices(  ) ) < 2 )
          printf( "not at least 2 device changes found ?!?\n" );

        locate_dfu_device(dif,"Lost device after RESET?");

        open_usb_device(dif);
      } else break;

      sleep(1);
    }
Example #7
0
File: main.c Project: 1butler1/xpwn
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;
}
Example #8
0
int dfuload_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file)
{
	int bytes_sent;
	int expected_size;
	unsigned char *buf;
	unsigned short transaction = 0;
	struct dfu_status dst;
	int ret;

	printf("Copying data from PC to DFU device\n");

	buf = file->firmware;
	expected_size = file->size.total - file->size.suffix;
	bytes_sent = 0;

	dfu_progress_bar("Download", 0, 1);
	while (bytes_sent < expected_size) {
		int bytes_left;
		int chunk_size;

		bytes_left = expected_size - bytes_sent;
		if (bytes_left < xfer_size)
			chunk_size = bytes_left;
		else
			chunk_size = xfer_size;

		ret = dfu_download(dif->dev_handle, dif->interface,
		    chunk_size, transaction++, chunk_size ? buf : NULL);
		if (ret < 0) {
			warnx("Error during download");
			goto out;
		}
		bytes_sent += chunk_size;
		buf += chunk_size;

		do {
			ret = dfu_get_status(dif, &dst);
			if (ret < 0) {
				errx(EX_IOERR, "Error during download get_status");
				goto out;
			}

			if (dst.bState == DFU_STATE_dfuDNLOAD_IDLE ||
					dst.bState == DFU_STATE_dfuERROR)
				break;

			/* Wait while device executes flashing */
			milli_sleep(dst.bwPollTimeout);

		} while (1);
		if (dst.bStatus != DFU_STATUS_OK) {
			printf(" failed!\n");
			printf("state(%u) = %s, status(%u) = %s\n", dst.bState,
				dfu_state_to_string(dst.bState), dst.bStatus,
				dfu_status_to_string(dst.bStatus));
			ret = -1;
			goto out;
		}
		dfu_progress_bar("Download", bytes_sent, bytes_sent + bytes_left);
	}

	/* send one zero sized download request to signalize end */
	ret = dfu_download(dif->dev_handle, dif->interface,
	    0, transaction, NULL);
	if (ret < 0) {
		errx(EX_IOERR, "Error sending completion packet");
		goto out;
	}

	dfu_progress_bar("Download", bytes_sent, bytes_sent);

	if (verbose)
		printf("Sent a total of %i bytes\n", bytes_sent);

get_status:
	/* Transition to MANIFEST_SYNC state */
	ret = dfu_get_status(dif, &dst);
	if (ret < 0) {
		warnx("unable to read DFU status after completion");
		goto out;
	}
	printf("state(%u) = %s, status(%u) = %s\n", dst.bState,
		dfu_state_to_string(dst.bState), dst.bStatus,
		dfu_status_to_string(dst.bStatus));

	milli_sleep(dst.bwPollTimeout);

	/* FIXME: deal correctly with ManifestationTolerant=0 / WillDetach bits */
	switch (dst.bState) {
	case DFU_STATE_dfuMANIFEST_SYNC:
	case DFU_STATE_dfuMANIFEST:
		/* some devices (e.g. TAS1020b) need some time before we
		 * can obtain the status */
		milli_sleep(1000);
		goto get_status;
		break;
	case DFU_STATE_dfuIDLE:
		break;
	}
	printf("Done!\n");

out:
	return bytes_sent;
}
Example #9
0
/**
 * Process QDA packet.
 *
 * Parse, process, and reply to an incoming QDA packet.
 *
 * @param[in] data The buffer containing the packet.
 * @param[in] len  The length of packet or its upper bound (since XMODEM may
 * 		   add some padding bytes).
 */
static void qda_process_pkt(uint8_t *data, size_t len)
{
	qda_pkt_t *pkt;
	qda_dnl_req_payload_t *dnload_req;
	qda_upl_req_payload_t *upload_req;
	qda_set_alt_setting_payload_t *altset_req;
	size_t expected_len;

	dfu_dev_state_t state;
	dfu_dev_status_t status;
	uint32_t poll_timeout;

	pkt = (qda_pkt_t *)data;
	expected_len = sizeof(*pkt);

	switch (pkt->type) {
	case QDA_PKT_DEV_DESC_REQ:
		qda_dev_dsc_rsp();
		break;
	case QDA_PKT_DFU_DESC_REQ:
		qda_dfu_dsc_rsp();
		break;
	case QDA_PKT_DFU_SET_ALT_SETTING:
		altset_req = (qda_set_alt_setting_payload_t *)pkt->payload;
		STALL_AND_BREAK_ON_ERR(
		    dfu_set_alt_setting(altset_req->alt_setting));
		qda_ack();
		break;
	case QDA_PKT_DFU_DNLOAD_REQ:
		dnload_req = (qda_dnl_req_payload_t *)pkt->payload;
		expected_len += sizeof(*dnload_req) + dnload_req->data_len;
		if ((len < expected_len) ||
		    dfu_process_dnload(dnload_req->block_num, dnload_req->data,
				       dnload_req->data_len)) {
			qda_stall();
			break;
		}
		qda_ack();
		break;
	case QDA_PKT_DFU_UPLOAD_REQ:
		upload_req = (qda_upl_req_payload_t *)pkt->payload;
		/* UPLOAD requests are handled differently from the others in
		 * order to reuse qda_buf */
		handle_upload_req(upload_req);
		break;
	case QDA_PKT_DFU_GETSTATUS_REQ:
		STALL_AND_BREAK_ON_ERR(
		    dfu_get_status(&status, &state, &poll_timeout));
		qda_dfu_get_status_rsp(state, status, poll_timeout);
		break;
	case QDA_PKT_DFU_CLRSTATUS:
		STALL_AND_BREAK_ON_ERR(dfu_clr_status());
		qda_ack();
		break;
	case QDA_PKT_DFU_GETSTATE_REQ:
		STALL_AND_BREAK_ON_ERR(dfu_get_state(&state));
		qda_dfu_get_state_rsp(state);
		break;
	case QDA_PKT_DFU_ABORT:
		STALL_AND_BREAK_ON_ERR(dfu_abort());
		qda_ack();
		break;
	case QDA_PKT_RESET:
		qda_ack();
		qm_soc_reset(QM_COLD_RESET);
		break;
	/* QDA_PKT_DFU_DETACH should not be received */
	default:
		/* NOTE: how to handle bad QDA requests? Send a QDA_STALL
		 * message for now */
		qda_stall();
		break;
	}
}
Example #10
0
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);
}
Example #11
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);
}