Support unconfigured devices
authorDaniel Drake <dsd@gentoo.org>
Sat, 10 May 2008 20:45:42 +0000 (21:45 +0100)
committerDaniel Drake <dsd@gentoo.org>
Sat, 10 May 2008 20:45:42 +0000 (21:45 +0100)
TODO
libusb/core.c
libusb/descriptor.c
libusb/libusb.h
libusb/os/linux_usbfs.c

diff --git a/TODO b/TODO
index f1e4345..84d3ab9 100644 (file)
--- 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
 =========================================
index d94d7b8..572e6a3 100644 (file)
@@ -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
index f545c7f..4a3be62 100644 (file)
@@ -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
index f946a27..a24fce3 100644 (file)
@@ -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);
index 4c0fb41..e69578c 100644 (file)
@@ -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;