Example #1
0
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;
}
Example #2
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);
}