From f2ede9876cd4f5cfa7751b975670fa449187fe3d Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sat, 10 May 2008 21:45:42 +0100 Subject: [PATCH] Support unconfigured devices --- TODO | 1 - libusb/core.c | 17 +++++--- libusb/descriptor.c | 103 +++++++++++++++++++++++++++--------------------- libusb/libusb.h | 12 +++--- libusb/os/linux_usbfs.c | 67 ++++++++++++++++++++++++------- 5 files changed, 128 insertions(+), 72 deletions(-) diff --git a/TODO b/TODO index f1e4345..84d3ab9 100644 --- a/TODO +++ b/TODO @@ -6,7 +6,6 @@ endianness of control setup, issues when resubmitting transfers serialization of handle_events internal docs for OS porters check which messages are sent during open, claim interface, close, release -unconfigured devices 1.0 API style/naming points to reconsider ========================================= diff --git a/libusb/core.c b/libusb/core.c index d94d7b8..572e6a3 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -460,15 +460,16 @@ API_EXPORTED int libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint) { int iface_idx; - struct libusb_config_descriptor *config = - libusb_get_active_config_descriptor(dev); - int r = LIBUSB_ERROR_NOT_FOUND; - - if (!config) { + struct libusb_config_descriptor *config; + int r; + + r = libusb_get_active_config_descriptor(dev, &config); + if (r < 0) { usbi_err("could not retrieve active config descriptor"); return LIBUSB_ERROR_OTHER; } + r = LIBUSB_ERROR_NOT_FOUND; for (iface_idx = 0; iface_idx < config->bNumInterfaces; iface_idx++) { const struct libusb_interface *iface = &config->interface[iface_idx]; int altsetting_idx; @@ -682,6 +683,10 @@ API_EXPORTED libusb_device *libusb_get_device(libusb_device_handle *dev_handle) * must release all claimed interfaces using libusb_release_interface() before * setting a new active configuration. * + * A configuration value of -1 will put the device in unconfigured state. + * The USB specifications state that a configuration value of 0 does this, + * however buggy devices exist which actually have a configuration 0. + * * You should always use this function rather than formulating your own * SET_CONFIGURATION control request. This is because the underlying operating * system needs to know when such changes happen. @@ -690,7 +695,7 @@ API_EXPORTED libusb_device *libusb_get_device(libusb_device_handle *dev_handle) * * \param dev a device handle * \param configuration the bConfigurationValue of the configuration you - * wish to activate + * wish to activate, or -1 if you wish to put the device in unconfigured state * \returns 0 on success * \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist * \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed diff --git a/libusb/descriptor.c b/libusb/descriptor.c index f545c7f..4a3be62 100644 --- a/libusb/descriptor.c +++ b/libusb/descriptor.c @@ -447,39 +447,43 @@ API_EXPORTED int libusb_get_device_descriptor(libusb_device *dev, * sent to the device. * * \param dev a device - * \returns the USB configuration descriptor which must be freed with - * libusb_free_config_descriptor() when done - * \returns NULL on error + * \param config output location for the USB configuration descriptor. Only + * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() + * after use. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * \returns another LIBUSB_ERROR code on error * \see libusb_get_config_descriptor */ -API_EXPORTED -struct libusb_config_descriptor *libusb_get_active_config_descriptor( - libusb_device *dev) +API_EXPORTED int libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config) { - struct libusb_config_descriptor *config = malloc(sizeof(*config)); + struct libusb_config_descriptor *_config = malloc(sizeof(*_config)); unsigned char tmp[8]; unsigned char *buf = NULL; int r; usbi_dbg(""); - if (!config) - return NULL; + if (!_config) + return LIBUSB_ERROR_NO_MEM; r = usbi_backend->get_active_config_descriptor(dev, tmp, sizeof(tmp)); if (r < 0) goto err; - usbi_parse_descriptor(tmp, "bbw", config); - buf = malloc(config->wTotalLength); - if (!buf) + usbi_parse_descriptor(tmp, "bbw", _config); + buf = malloc(_config->wTotalLength); + if (!buf) { + r = LIBUSB_ERROR_NO_MEM; goto err; + } r = usbi_backend->get_active_config_descriptor(dev, buf, - config->wTotalLength); + _config->wTotalLength); if (r < 0) goto err; - r = parse_configuration(config, buf); + r = parse_configuration(_config, buf); if (r < 0) { usbi_err("parse_configuration failed with error %d", r); goto err; @@ -487,13 +491,14 @@ struct libusb_config_descriptor *libusb_get_active_config_descriptor( usbi_warn("descriptor data still left"); } - return config; + *config = _config; + return 0; err: - free(config); + free(_config); if (buf) free(buf); - return NULL; + return r; } /** \ingroup desc @@ -503,44 +508,49 @@ err: * * \param dev a device * \param config_index the index of the configuration you wish to retrieve - * \returns the USB configuration descriptor which must be freed with - * libusb_free_config_descriptor() when done - * \returns NULL on error + * \param config output location for the USB configuration descriptor. Only + * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() + * after use. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns another LIBUSB_ERROR code on error * \see libusb_get_active_config_descriptor() * \see libusb_get_config_descriptor_by_value() */ -API_EXPORTED struct libusb_config_descriptor *libusb_get_config_descriptor( - libusb_device *dev, uint8_t config_index) +API_EXPORTED int libusb_get_config_descriptor(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config) { - struct libusb_config_descriptor *config; + struct libusb_config_descriptor *_config; unsigned char tmp[8]; unsigned char *buf = NULL; int r; usbi_dbg("index %d", config_index); if (config_index >= dev->num_configurations) - return NULL; + return LIBUSB_ERROR_NOT_FOUND; - config = malloc(sizeof(*config)); - if (!config) - return NULL; + _config = malloc(sizeof(*_config)); + if (!_config) + return LIBUSB_ERROR_NO_MEM; r = usbi_backend->get_config_descriptor(dev, config_index, tmp, sizeof(tmp)); if (r < 0) goto err; - usbi_parse_descriptor(tmp, "bbw", config); - buf = malloc(config->wTotalLength); - if (!buf) + usbi_parse_descriptor(tmp, "bbw", _config); + buf = malloc(_config->wTotalLength); + if (!buf) { + r = LIBUSB_ERROR_NO_MEM; goto err; + } r = usbi_backend->get_config_descriptor(dev, config_index, buf, - config->wTotalLength); + _config->wTotalLength); if (r < 0) goto err; - r = parse_configuration(config, buf); + r = parse_configuration(_config, buf); if (r < 0) { usbi_err("parse_configuration failed with error %d", r); goto err; @@ -548,13 +558,14 @@ API_EXPORTED struct libusb_config_descriptor *libusb_get_config_descriptor( usbi_warn("descriptor data still left"); } - return config; + *config = _config; + return 0; err: - free(config); + free(_config); if (buf) free(buf); - return NULL; + return r; } /* iterate through all configurations, returning the index of the configuration @@ -591,22 +602,26 @@ int usbi_get_config_index_by_value(struct libusb_device *dev, * \param dev a device * \param bConfigurationValue the bConfigurationValue of the configuration you * wish to retrieve - * \returns the USB configuration descriptor which must be freed with - * libusb_free_config_descriptor() when done - * \returns NULL on error + * \param config output location for the USB configuration descriptor. Only + * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() + * after use. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns another LIBUSB_ERROR code on error * \see libusb_get_active_config_descriptor() * \see libusb_get_config_descriptor() */ -API_EXPORTED -struct libusb_config_descriptor *libusb_get_config_descriptor_by_value( - libusb_device *dev, uint8_t bConfigurationValue) +API_EXPORTED int libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config) { int idx; int r = usbi_get_config_index_by_value(dev, bConfigurationValue, &idx); - if (r < 0 || idx == -1) - return NULL; + if (r < 0) + return r; + else if (idx == -1) + return LIBUSB_ERROR_NOT_FOUND; else - return libusb_get_config_descriptor(dev, idx); + return libusb_get_config_descriptor(dev, idx, config); } /** \ingroup desc diff --git a/libusb/libusb.h b/libusb/libusb.h index f946a27..a24fce3 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h @@ -667,12 +667,12 @@ void libusb_unref_device(libusb_device *dev); int libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc); -struct libusb_config_descriptor *libusb_get_active_config_descriptor( - libusb_device *dev); -struct libusb_config_descriptor *libusb_get_config_descriptor( - libusb_device *dev, uint8_t config_index); -struct libusb_config_descriptor *libusb_get_config_descriptor_by_value( - libusb_device *dev, uint8_t bConfigurationValue); +int libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config); +int libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, + struct libusb_config_descriptor **config); +int libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config); void libusb_free_config_descriptor(struct libusb_config_descriptor *config); uint8_t libusb_get_bus_number(libusb_device *dev); uint8_t libusb_get_device_address(libusb_device *dev); diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index 4c0fb41..e69578c 100644 --- a/libusb/os/linux_usbfs.c +++ b/libusb/os/linux_usbfs.c @@ -249,6 +249,9 @@ static int usbfs_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len) { struct linux_device_priv *priv = __device_priv(dev); + if (!priv->config_descriptor) + return LIBUSB_ERROR_NOT_FOUND; /* device is unconfigured */ + /* retrieve cached copy */ memcpy(buffer, priv->config_descriptor, len); return 0; @@ -280,6 +283,9 @@ static int sysfs_get_active_config_descriptor(struct libusb_device *dev, if (r < 0) { usbi_err("read failed, ret=%d errno=%d", fd, errno); return LIBUSB_ERROR_IO; + } else if (r == 0) { + usbi_dbg("device is unconfigured"); + return LIBUSB_ERROR_NOT_FOUND; } else if (r < len) { usbi_err("short read %d/%d", r, len); return LIBUSB_ERROR_IO; @@ -416,7 +422,7 @@ static int cache_active_config(struct libusb_device *dev, int fd, } /* read the bConfigurationValue for a device */ -static int sysfs_get_active_config(struct libusb_device *dev) +static int sysfs_get_active_config(struct libusb_device *dev, int *config) { char *endptr; char tmp[4] = {0, 0, 0, 0}; @@ -435,8 +441,9 @@ static int sysfs_get_active_config(struct libusb_device *dev) r, errno); return LIBUSB_ERROR_IO; } else if (r == 0) { - usbi_err("short bConfigurationValue read"); - return LIBUSB_ERROR_IO; + usbi_err("device unconfigured"); + *config = -1; + return 0; } if (tmp[sizeof(tmp) - 1] != 0) { @@ -453,7 +460,8 @@ static int sysfs_get_active_config(struct libusb_device *dev) return LIBUSB_ERROR_IO; } - return (int) num; + *config = (int) num; + return 0; } /* send a control message to retrieve active configuration */ @@ -489,6 +497,7 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum, char path[PATH_MAX + 1]; int fd; int active_config = 0; + int device_configured = 1; ssize_t r; dev->bus_number = busnum; @@ -507,9 +516,11 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum, priv->config_descriptor = NULL; if (sysfs_can_relate_devices) { - active_config = sysfs_get_active_config(dev); - if (active_config < 0) - return active_config; + int tmp = sysfs_get_active_config(dev, &active_config); + if (tmp < 0) + return tmp; + if (active_config == -1) + device_configured = 0; } __get_usbfs_path(dev, path); @@ -539,6 +550,14 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum, if (active_config < 0) { close(fd); return active_config; + } else if (active_config == 0) { + /* some buggy devices have a configuration 0, but we're + * reaching into the corner of a corner case here, so let's + * not support buggy devices in these circumstances. + * stick to the specs: a configuration value of 0 means + * unconfigured. */ + usbi_dbg("assuming unconfigured device"); + device_configured = 0; } } } @@ -562,13 +581,20 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum, return LIBUSB_ERROR_IO; } - r = cache_active_config(dev, fd, active_config); - close(fd); - if (r < 0) { - free(dev_buf); - return r; + /* bit of a hack: set num_configurations now because cache_active_config() + * calls usbi_get_config_index_by_value() which uses it */ + dev->num_configurations = dev_buf[DEVICE_DESC_LENGTH - 1]; + + if (device_configured) { + r = cache_active_config(dev, fd, active_config); + if (r < 0) { + close(fd); + free(dev_buf); + return r; + } } + close(fd); priv->dev_descriptor = dev_buf; return 0; } @@ -745,6 +771,8 @@ static int sysfs_scan_device(struct discovered_devs **_discdevs, return LIBUSB_ERROR_IO; } + sysfs_can_relate_devices = 1; + r = fscanf(fd, "%d", &busnum); fclose(fd); if (r != 1) { @@ -859,6 +887,7 @@ static void op_close(struct libusb_device_handle *dev_handle) static int op_set_configuration(struct libusb_device_handle *handle, int config) { + struct linux_device_priv *priv = __device_priv(handle->dev); int fd = __device_handle_priv(handle)->fd; int r = ioctl(fd, IOCTL_USBFS_SETCONFIG, &config); if (r) { @@ -873,9 +902,17 @@ static int op_set_configuration(struct libusb_device_handle *handle, int config) if (!sysfs_has_descriptors) { /* update our cached active config descriptor */ - r = cache_active_config(handle->dev, fd, config); - if (r < 0) - usbi_warn("failed to update cached config descriptor, error %d", r); + if (config == -1) { + if (priv->config_descriptor) { + free(priv->config_descriptor); + priv->config_descriptor = NULL; + } + } else { + r = cache_active_config(handle->dev, fd, config); + if (r < 0) + usbi_warn("failed to update cached config descriptor, " + "error %d", r); + } } return 0; -- 2.7.4