summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Drake <dsd@gentoo.org>2008-05-09 14:34:31 +0100
committerDaniel Drake <dsd@gentoo.org>2008-05-09 19:33:33 +0100
commitfe4adcc99e30115204ab832ad3e0170c9aca7629 (patch)
treec439121d3e729bb87f239c4d5b0da205b741174d
parent5741bfe01a2481b8c3830c80edc3637bf62a7e16 (diff)
downloadlibusb-fe4adcc99e30115204ab832ad3e0170c9aca7629.tar.gz
Rework configuration handling
libusb no longer caches descriptors in libusb_device but backends are intended to be able to provide copies from memory. In the common linux case we can use sysfs.
-rw-r--r--TODO1
-rw-r--r--examples/lsusb.c11
-rw-r--r--libusb/core.c133
-rw-r--r--libusb/descriptor.c153
-rw-r--r--libusb/libusb.h12
-rw-r--r--libusb/libusbi.h17
-rw-r--r--libusb/os/linux_usbfs.c369
7 files changed, 513 insertions, 183 deletions
diff --git a/TODO b/TODO
index 478e43e..2b53246 100644
--- a/TODO
+++ b/TODO
@@ -7,6 +7,7 @@ serialization of handle_events
internal docs for OS porters
configuration handling
check which messages are sent during open, claim interface, close, release
+unconfigured devices
1.0 API style/naming points to reconsider
=========================================
diff --git a/examples/lsusb.c b/examples/lsusb.c
index 509e0bd..5a83020 100644
--- a/examples/lsusb.c
+++ b/examples/lsusb.c
@@ -28,10 +28,15 @@ void print_devs(libusb_device **devs)
int i = 0;
while ((dev = devs[i++]) != NULL) {
- const struct libusb_device_descriptor *desc =
- libusb_get_device_descriptor(dev);
+ struct libusb_device_descriptor desc;
+ int r = libusb_get_device_descriptor(dev, &desc);
+ if (r < 0) {
+ fprintf(stderr, "failed to get device descriptor");
+ return;
+ }
+
printf("%04x:%04x (bus %d, device %d)\n",
- desc->idVendor, desc->idProduct,
+ desc.idVendor, desc.idProduct,
libusb_get_bus_number(dev), libusb_get_device_address(dev));
}
}
diff --git a/libusb/core.c b/libusb/core.c
index f83305e..43491ad 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -304,95 +304,28 @@ struct libusb_device *usbi_alloc_device(unsigned long session_id)
return dev;
}
-/* call the OS discovery routines to populate descriptors etc */
-int usbi_discover_device(struct libusb_device *dev)
+/* to be called by OS implementations when a new device is ready for final
+ * sanitization and checking before being returned in a device list. */
+int usbi_sanitize_device(struct libusb_device *dev)
{
int r;
- int i;
- void *user_data;
unsigned char raw_desc[DEVICE_DESC_LENGTH];
- size_t alloc_size;
+ uint8_t num_configurations;
- dev->config = NULL;
-
- r = usbi_backend->begin_discovery(dev, &user_data);
+ r = usbi_backend->get_device_descriptor(dev, raw_desc);
if (r < 0)
return r;
-
- r = usbi_backend->get_device_descriptor(dev, raw_desc, user_data);
- if (r < 0)
- goto err;
- usbi_parse_descriptor(raw_desc, "bbWbbbbWWWbbbb", &dev->desc);
-
- if (dev->desc.bNumConfigurations > USB_MAXCONFIG) {
+ num_configurations = raw_desc[DEVICE_DESC_LENGTH - 1];
+ if (num_configurations > USB_MAXCONFIG) {
usbi_err("too many configurations");
- r = LIBUSB_ERROR_IO;
- goto err;
- }
-
- if (dev->desc.bNumConfigurations < 1) {
+ return LIBUSB_ERROR_IO;
+ } else if (num_configurations < 1) {
usbi_dbg("no configurations?");
- r = LIBUSB_ERROR_IO;
- goto err;
- }
-
- alloc_size = dev->desc.bNumConfigurations
- * sizeof(struct libusb_config_descriptor);
- dev->config = malloc(alloc_size);
- if (!dev->config) {
- r = LIBUSB_ERROR_NO_MEM;
- goto err;
- }
-
- memset(dev->config, 0, alloc_size);
- for (i = 0; i < dev->desc.bNumConfigurations; i++) {
- unsigned char tmp[8];
- unsigned char *bigbuffer;
- struct libusb_config_descriptor config;
-
- r = usbi_backend->get_config_descriptor(dev, i, tmp, sizeof(tmp),
- user_data);
- if (r < 0)
- goto err;
-
- usbi_parse_descriptor(tmp, "bbw", &config);
-
- bigbuffer = malloc(config.wTotalLength);
- if (!bigbuffer) {
- r = LIBUSB_ERROR_NO_MEM;
- goto err;
- }
-
- r = usbi_backend->get_config_descriptor(dev, i, bigbuffer,
- config.wTotalLength, user_data);
- if (r < 0) {
- free(bigbuffer);
- goto err;
- }
-
- r = usbi_parse_configuration(&dev->config[i], bigbuffer);
- free(bigbuffer);
- if (r < 0) {
- usbi_err("parse_configuration failed with code %d", r);
- goto err;
- } else if (r > 0) {
- usbi_warn("descriptor data still left\n");
- }
+ return LIBUSB_ERROR_IO;
}
- usbi_backend->end_discovery(dev, user_data);
return 0;
-
-err:
- if (dev->config) {
- usbi_clear_configurations(dev);
- free(dev->config);
- dev->config = NULL;
- }
-
- usbi_backend->end_discovery(dev, user_data);
- return r;
}
struct libusb_device *usbi_get_device_by_session_id(unsigned long session_id)
@@ -513,19 +446,27 @@ API_EXPORTED uint8_t libusb_get_device_address(libusb_device *dev)
/** \ingroup dev
* Convenience function to retrieve the wMaxPacketSize value for a particular
- * endpoint. This is useful for setting up isochronous transfers.
+ * endpoint in the active device configuration. This is useful for setting up
+ * isochronous transfers.
*
* \param dev a device
* \param endpoint address of the endpoint in question
- * \returns the wMaxPacketSize value, or LIBUSB_ERROR_NOT_FOUND if the endpoint
- * does not exist.
+ * \returns the wMaxPacketSize value
+ * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns LIBUSB_ERROR_OTHER on other failure
*/
API_EXPORTED int libusb_get_max_packet_size(libusb_device *dev,
unsigned char endpoint)
{
int iface_idx;
- /* FIXME: active config considerations? */
- struct libusb_config_descriptor *config = dev->config;
+ struct libusb_config_descriptor *config =
+ libusb_get_active_config_descriptor(dev);
+ int r = LIBUSB_ERROR_NOT_FOUND;
+
+ if (!config) {
+ usbi_err("could not retrieve active config descriptor");
+ return LIBUSB_ERROR_OTHER;
+ }
for (iface_idx = 0; iface_idx < config->bNumInterfaces; iface_idx++) {
const struct libusb_interface *iface = &config->interface[iface_idx];
@@ -540,13 +481,17 @@ API_EXPORTED int libusb_get_max_packet_size(libusb_device *dev,
for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) {
const struct libusb_endpoint_descriptor *ep =
&altsetting->endpoint[ep_idx];
- if (ep->bEndpointAddress == endpoint)
- return ep->wMaxPacketSize;
+ if (ep->bEndpointAddress == endpoint) {
+ r = ep->wMaxPacketSize;
+ goto out;
+ }
}
}
}
- return LIBUSB_ERROR_NOT_FOUND;
+out:
+ libusb_free_config_descriptor(config);
+ return r;
}
/** \ingroup dev
@@ -579,8 +524,7 @@ API_EXPORTED void libusb_unref_device(libusb_device *dev)
pthread_mutex_unlock(&dev->lock);
if (refcnt == 0) {
- usbi_dbg("destroy device %04x:%04x", dev->desc.idVendor,
- dev->desc.idProduct);
+ usbi_dbg("destroy device %d.%d", dev->bus_number, dev->device_address);
if (usbi_backend->destroy_device)
usbi_backend->destroy_device(dev);
@@ -589,10 +533,6 @@ API_EXPORTED void libusb_unref_device(libusb_device *dev)
list_del(&dev->list);
pthread_mutex_unlock(&usb_devs_lock);
- if (dev->config) {
- usbi_clear_configurations(dev);
- free(dev->config);
- }
free(dev);
}
}
@@ -615,7 +555,7 @@ API_EXPORTED libusb_device_handle *libusb_open(libusb_device *dev)
struct libusb_device_handle *handle;
size_t priv_size = usbi_backend->device_handle_priv_size;
int r;
- usbi_dbg("open %04x:%04x", dev->desc.idVendor, dev->desc.idProduct);
+ usbi_dbg("open %d.%d", dev->bus_number, dev->device_address);
handle = malloc(sizeof(*handle) + priv_size);
if (!handle)
@@ -670,9 +610,11 @@ API_EXPORTED libusb_device_handle *libusb_open_device_with_vid_pid(
return NULL;
while ((dev = devs[i++]) != NULL) {
- const struct libusb_device_descriptor *desc =
- libusb_get_device_descriptor(dev);
- if (desc->idVendor == vendor_id && desc->idProduct == product_id) {
+ struct libusb_device_descriptor desc;
+ int r = libusb_get_device_descriptor(dev, &desc);
+ if (r < 0)
+ goto out;
+ if (desc.idVendor == vendor_id && desc.idProduct == product_id) {
found = dev;
break;
}
@@ -681,6 +623,7 @@ API_EXPORTED libusb_device_handle *libusb_open_device_with_vid_pid(
if (found)
handle = libusb_open(found);
+out:
libusb_free_device_list(devs, 1);
return handle;
}
diff --git a/libusb/descriptor.c b/libusb/descriptor.c
index 822306d..e07711a 100644
--- a/libusb/descriptor.c
+++ b/libusb/descriptor.c
@@ -325,14 +325,7 @@ static void clear_configuration(struct libusb_config_descriptor *config)
free((void *) config->extra);
}
-void usbi_clear_configurations(struct libusb_device *dev)
-{
- int i;
- for (i = 0; i < dev->desc.bNumConfigurations; i++)
- clear_configuration(dev->config + i);
-}
-
-int usbi_parse_configuration(struct libusb_config_descriptor *config,
+static int parse_configuration(struct libusb_config_descriptor *config,
unsigned char *buffer)
{
int i;
@@ -430,23 +423,149 @@ err:
* This is a non-blocking function; the device descriptor is cached in memory.
*
* \param dev the device
- * \returns the USB device descriptor
+ * \param desc output location for the descriptor data
+ * \returns 0 on success or a LIBUSB_ERROR code on failure
*/
-API_EXPORTED const struct libusb_device_descriptor *libusb_get_device_descriptor(
- libusb_device *dev)
+API_EXPORTED int libusb_get_device_descriptor(libusb_device *dev,
+ struct libusb_device_descriptor *desc)
{
- return &dev->desc;
+ unsigned char raw_desc[DEVICE_DESC_LENGTH];
+ int r;
+
+ r = usbi_backend->get_device_descriptor(dev, raw_desc);
+ if (r < 0)
+ return r;
+
+ usbi_parse_descriptor(raw_desc, "bbWbbbbWWWbbbb", desc);
+ return 0;
}
/** \ingroup desc
- * Get the USB configuration descriptor for a given device.
- * \param dev the device
- * \returns the USB configuration descriptor
+ * Get the USB configuration descriptor for the currently active configuration.
+ * This is a non-blocking function which does not involve any requests being
+ * 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
+ * \see libusb_get_config_descriptor
*/
-API_EXPORTED const struct libusb_config_descriptor *libusb_get_config_descriptor(
+API_EXPORTED
+struct libusb_config_descriptor *libusb_get_active_config_descriptor(
libusb_device *dev)
{
- return dev->config;
+ struct libusb_config_descriptor *config = malloc(sizeof(*config));
+ unsigned char tmp[8];
+ unsigned char *buf = NULL;
+ int r;
+
+ if (!config)
+ return NULL;
+
+ 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)
+ goto err;
+
+ r = usbi_backend->get_active_config_descriptor(dev, buf,
+ config->wTotalLength);
+ if (r < 0)
+ goto err;
+
+ r = parse_configuration(config, buf);
+ if (r < 0) {
+ usbi_err("parse_configuration failed with error %d", r);
+ goto err;
+ } else if (r > 0) {
+ usbi_warn("descriptor data still left");
+ }
+
+ return config;
+
+err:
+ free(config);
+ if (buf)
+ free(buf);
+ return NULL;
+}
+
+/** \ingroup desc
+ * Get the USB configuration descriptor for the currently active configuration.
+ * This is a non-blocking function which does not involve any requests being
+ * sent to the device.
+ *
+ * \param dev a device
+ * \param bConfigurationValue the bConfigurationValue of the configuration
+ * you wish to retreive
+ * \returns the USB configuration descriptor which must be freed with
+ * libusb_free_config_descriptor() when done
+ * \returns NULL on error
+ * \see libusb_get_active_config_descriptor()
+ */
+API_EXPORTED struct libusb_config_descriptor *libusb_get_config_descriptor(
+ libusb_device *dev, uint8_t bConfigurationValue)
+{
+ struct libusb_config_descriptor *config = malloc(sizeof(*config));
+ unsigned char tmp[8];
+ unsigned char *buf = NULL;
+ int r;
+
+ if (!config)
+ return NULL;
+
+ r = usbi_backend->get_config_descriptor(dev, bConfigurationValue, tmp,
+ sizeof(tmp));
+ if (r < 0)
+ goto err;
+
+ usbi_parse_descriptor(tmp, "bbw", &config);
+ buf = malloc(config->wTotalLength);
+ if (!buf)
+ goto err;
+
+ r = usbi_backend->get_config_descriptor(dev, bConfigurationValue, buf,
+ config->wTotalLength);
+ if (r < 0)
+ goto err;
+
+ r = parse_configuration(config, buf);
+ if (r < 0) {
+ usbi_err("parse_configuration failed with error %d", r);
+ goto err;
+ } else if (r > 0) {
+ usbi_warn("descriptor data still left");
+ }
+
+ return config;
+
+err:
+ free(config);
+ if (buf)
+ free(buf);
+ return NULL;
+}
+
+/** \ingroup desc
+ * Free a configuration descriptor obtained from
+ * libusb_get_active_config_descriptor() or libusb_get_config_descriptor().
+ * It is safe to call this function with a NULL config parameter, in which
+ * case the function simply returns.
+ *
+ * \param config the configuration descriptor to free
+ */
+API_EXPORTED void libusb_free_config_descriptor(
+ struct libusb_config_descriptor *config)
+{
+ if (!config)
+ return;
+
+ clear_configuration(config);
+ free(config);
}
/** \ingroup desc
diff --git a/libusb/libusb.h b/libusb/libusb.h
index 07847bc..62ed0ea 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -662,12 +662,16 @@ void libusb_exit(void);
ssize_t libusb_get_device_list(libusb_device ***list);
void libusb_free_device_list(libusb_device **list, int unref_devices);
-const struct libusb_device_descriptor *libusb_get_device_descriptor(
- libusb_device *dev);
-const struct libusb_config_descriptor *libusb_get_config_descriptor(
- libusb_device *dev);
libusb_device *libusb_ref_device(libusb_device *dev);
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);
+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);
int libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint);
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index ca44512..e596b86 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -153,8 +153,6 @@ struct libusb_device {
struct list_head list;
unsigned long session_data;
- struct libusb_device_descriptor desc;
- struct libusb_config_descriptor *config;
unsigned char os_priv[0];
};
@@ -223,16 +221,13 @@ void usbi_io_init(void);
struct libusb_device *usbi_alloc_device(unsigned long session_id);
struct libusb_device *usbi_get_device_by_session_id(unsigned long session_id);
-int usbi_discover_device(struct libusb_device *dev);
+int usbi_sanitize_device(struct libusb_device *dev);
void usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
enum libusb_transfer_status status);
void usbi_handle_transfer_cancellation(struct usbi_transfer *transfer);
int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest);
-int usbi_parse_configuration(struct libusb_config_descriptor *config,
- unsigned char *buffer);
-void usbi_clear_configurations(struct libusb_device *dev);
/* polling */
@@ -274,12 +269,12 @@ struct usbi_os_backend {
int (*open)(struct libusb_device_handle *handle);
void (*close)(struct libusb_device_handle *handle);
- int (*begin_discovery)(struct libusb_device *device, void **user_data);
int (*get_device_descriptor)(struct libusb_device *device,
- unsigned char *buffer, void *user_data);
- int (*get_config_descriptor)(struct libusb_device *device, int index,
- unsigned char *buffer, size_t len, void *user_data);
- void (*end_discovery)(struct libusb_device *device, void *user_data);
+ unsigned char *buffer);
+ int (*get_active_config_descriptor)(struct libusb_device *device,
+ unsigned char *buffer, size_t len);
+ int (*get_config_descriptor)(struct libusb_device *device, uint8_t config,
+ unsigned char *buffer, size_t len);
int (*set_configuration)(struct libusb_device_handle *handle, int config);
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 820100f..6efaaa1 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -41,9 +41,31 @@
static const char *usbfs_path = NULL;
static int have_sysfs;
+/* sysfs vs usbfs:
+ * opening a usbfs node causes the device to be resumed, so we attempt to
+ * avoid this during enumeration.
+ *
+ * sysfs allows us to read the kernel's in-memory copies of device descriptors
+ * and so forth, avoiding the need to open the device:
+ * - The binary "descriptors" file was added in 2.6.23.
+ * - The "busnum" file was added in 2.6.22
+ * - The "devnum" file has been present since pre-2.6.18
+ * Hence we check for the existance of a descriptors file to determine whether
+ * sysfs provides all the information we need. We effectively require 2.6.23
+ * in order to avoid waking suspended devices during enumeration.
+ */
+
struct linux_device_priv {
+ /* FIXME remove this, infer from dev->busnum etc */
char *nodepath;
- char sysfs_dir[SYSFS_DIR_LENGTH];
+
+ union {
+ char sysfs_dir[SYSFS_DIR_LENGTH];
+ struct {
+ unsigned char *dev_descriptor;
+ unsigned char *config_descriptor;
+ };
+ };
};
struct linux_device_handle_priv {
@@ -149,71 +171,167 @@ static int op_init(void)
return 0;
}
-struct discovery_data {
- int fd;
-};
+static int usbfs_get_device_descriptor(struct libusb_device *dev,
+ unsigned char *buffer)
+{
+ struct linux_device_priv *priv = __device_priv(dev);
-static int op_begin_discovery(struct libusb_device *dev, void **user_data)
+ /* return cached copy */
+ memcpy(buffer, priv->dev_descriptor, DEVICE_DESC_LENGTH);
+ return 0;
+}
+
+static int open_sysfs_descriptors(struct libusb_device *dev)
{
struct linux_device_priv *priv = __device_priv(dev);
- struct discovery_data *ddata = malloc(sizeof(*ddata));
- if (!ddata)
- return LIBUSB_ERROR_NO_MEM;
+ char filename[PATH_MAX + 1];
+ int fd;
- if (have_sysfs) {
- char filename[PATH_MAX + 1];
- snprintf(filename, PATH_MAX, "%s/%s/descriptors",
- SYSFS_DEVICE_PATH, priv->sysfs_dir);
- ddata->fd = open(filename, O_RDONLY);
- } else {
- ddata->fd = open(priv->nodepath, O_RDONLY);
+ snprintf(filename, PATH_MAX, "%s/%s/descriptors", SYSFS_DEVICE_PATH,
+ priv->sysfs_dir);
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ usbi_err("open '%s' failed, ret=%d errno=%d", filename, fd, errno);
+ return LIBUSB_ERROR_IO;
}
- if (ddata->fd < 0) {
- usbi_dbg("open '%s' failed, ret=%d errno=%d", priv->nodepath,
- ddata->fd, errno);
- free(ddata);
+ return fd;
+}
+
+static int sysfs_get_device_descriptor(struct libusb_device *dev,
+ unsigned char *buffer)
+{
+ int fd;
+ ssize_t r;
+
+ /* sysfs provides access to an in-memory copy of the device descriptor,
+ * so we use that rather than keeping our own copy */
+
+ fd = open_sysfs_descriptors(dev);
+ if (fd < 0)
+ return fd;
+
+ r = read(fd, buffer, DEVICE_DESC_LENGTH);;
+ close(fd);
+ if (r < 0) {
+ usbi_err("read failed, ret=%d errno=%d", fd, errno);
+ return LIBUSB_ERROR_IO;
+ } else if (r < DEVICE_DESC_LENGTH) {
+ usbi_err("short read %d/%d", r, DEVICE_DESC_LENGTH);
return LIBUSB_ERROR_IO;
}
- *user_data = ddata;
return 0;
}
-static int op_get_device_descriptor(struct libusb_device *device,
- unsigned char *buffer, void *user_data)
+static int op_get_device_descriptor(struct libusb_device *dev,
+ unsigned char *buffer)
+{
+ if (have_sysfs)
+ return sysfs_get_device_descriptor(dev, buffer);
+ else
+ return usbfs_get_device_descriptor(dev, buffer);
+}
+
+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);
+ /* retrieve cached copy */
+ memcpy(buffer, priv->config_descriptor, len);
+ return 0;
+}
+
+static int sysfs_get_active_config_descriptor(struct libusb_device *dev,
+ unsigned char *buffer, size_t len)
{
- struct discovery_data *ddata = user_data;
- int r = read(ddata->fd, buffer, DEVICE_DESC_LENGTH);
+ int fd;
+ ssize_t r;
+ off_t off;
+
+ /* sysfs provides access to an in-memory copy of the device descriptor,
+ * so we use that rather than keeping our own copy */
+
+ fd = open_sysfs_descriptors(dev);
+ if (fd < 0)
+ return fd;
+
+ off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET);
+ if (off < 0) {
+ usbi_err("seek failed, ret=%d errno=%d", off, errno);
+ close(fd);
+ return LIBUSB_ERROR_IO;
+ }
+
+ r = read(fd, buffer, len);
+ close(fd);
if (r < 0) {
- usbi_err("read failed ret=%d errno=%d", r, errno);
+ usbi_err("read failed, ret=%d errno=%d", fd, errno);
return LIBUSB_ERROR_IO;
- } else if (r < DEVICE_DESC_LENGTH) {
- usbi_err("short descriptor read %d/%d", r, DEVICE_DESC_LENGTH);
+ } else if (r < len) {
+ usbi_err("short read %d/%d", r, len);
return LIBUSB_ERROR_IO;
}
return 0;
}
-static int op_get_config_descriptor(struct libusb_device *device,
- int config_index, unsigned char *buffer, size_t len, void *user_data)
+static int op_get_active_config_descriptor(struct libusb_device *dev,
+ unsigned char *buffer, size_t len)
+{
+ if (have_sysfs)
+ return sysfs_get_active_config_descriptor(dev, buffer, len);
+ else
+ return usbfs_get_active_config_descriptor(dev, buffer, len);
+}
+
+
+/* takes a usbfs fd, attempts to find the requested config and copy a certain
+ * amount of it into an output buffer. a bConfigurationValue of -1 indicates
+ * that the first config should be retreived. */
+static int get_config_descriptor(int fd, int bConfigurationValue,
+ unsigned char *buffer, size_t len)
{
- struct discovery_data *ddata = user_data;
+ unsigned char tmp[8];
+ uint8_t num_configurations;
off_t off;
ssize_t r;
- int fd = ddata->fd;
- off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET);
+ if (bConfigurationValue == -1) {
+ /* read first configuration */
+ off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET);
+ if (off < 0) {
+ usbi_err("seek failed, ret=%d errno=%d", off, errno);
+ return LIBUSB_ERROR_IO;
+ }
+ r = read(fd, buffer, len);
+ if (r < 0) {
+ usbi_err("read failed ret=%d errno=%d", r, errno);
+ return LIBUSB_ERROR_IO;
+ } else if (r < len) {
+ usbi_err("short output read %d/%d", r, len);
+ return LIBUSB_ERROR_IO;
+ }
+ return 0;
+ }
+
+ /* seek to last byte of device descriptor to determine number of
+ * configurations */
+ off = lseek(fd, DEVICE_DESC_LENGTH - 1, SEEK_SET);
if (off < 0) {
- usbi_err("seek failed ret=%d errno=%d", off, errno);
+ usbi_err("seek failed, ret=%d errno=%d", off, errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ r = read(fd, &num_configurations, 1);
+ if (r < 0) {
+ usbi_err("read num_configurations failed, ret=%d errno=%d", off, errno);
return LIBUSB_ERROR_IO;
}
/* might need to skip some configuration descriptors to reach the
- * requested index */
- while (config_index > 0) {
- unsigned char tmp[8];
+ * requested configuration */
+ while (num_configurations) {
struct libusb_config_descriptor config;
/* read first 8 bytes of descriptor */
@@ -225,8 +343,10 @@ static int op_get_config_descriptor(struct libusb_device *device,
usbi_err("short descriptor read %d/%d", r, sizeof(tmp));
return LIBUSB_ERROR_IO;
}
-
- usbi_parse_descriptor(buffer, "bbw", &config);
+
+ usbi_parse_descriptor(tmp, "bbwbb", &config);
+ if (config.bConfigurationValue == bConfigurationValue)
+ break;
/* seek forward to end of config */
off = lseek(fd, config.wTotalLength - sizeof(tmp), SEEK_CUR);
@@ -235,27 +355,80 @@ static int op_get_config_descriptor(struct libusb_device *device,
return LIBUSB_ERROR_IO;
}
- config_index--;
+ num_configurations--;
}
- /* read the actual config */
- r = read(fd, buffer, len);
+ if (num_configurations == 0)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ /* copy config-so-far */
+ memcpy(buffer, tmp, sizeof(tmp));
+
+ /* read the rest of the descriptor */
+ r = read(fd, buffer + sizeof(tmp), len - sizeof(tmp));
if (r < 0) {
usbi_err("read failed ret=%d errno=%d", r, errno);
return LIBUSB_ERROR_IO;
- } else if (r < len) {
- usbi_err("short descriptor read %d/%d", r, len);
+ } else if (r < (len - sizeof(tmp))) {
+ usbi_err("short output read %d/%d", r, len);
return LIBUSB_ERROR_IO;
}
return 0;
}
-static void op_end_discovery(struct libusb_device *device, void *user_data)
+static int op_get_config_descriptor(struct libusb_device *dev, uint8_t config,
+ unsigned char *buffer, size_t len)
{
- struct discovery_data *ddata = user_data;
- close(ddata->fd);
- free(ddata);
+ struct linux_device_priv *priv = __device_priv(dev);
+ int fd;
+ int r;
+
+ /* always read from usbfs: sysfs only has the active descriptor
+ * this will involve waking the device up, but oh well! */
+
+ fd = open(priv->nodepath, O_RDONLY);
+ if (fd < 0) {
+ usbi_err("open '%s' failed, ret=%d errno=%d",
+ priv->nodepath, fd, errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ r = get_config_descriptor(fd, config, buffer, len);
+ close(fd);
+ return r;
+}
+
+static int cache_active_config(struct libusb_device *dev, int fd,
+ int active_config)
+{
+ struct linux_device_priv *priv = __device_priv(dev);
+ struct libusb_config_descriptor config;
+ unsigned char tmp[8];
+ unsigned char *buf;
+ int r;
+
+ r = get_config_descriptor(fd, active_config, tmp, sizeof(tmp));
+ if (r < 0) {
+ usbi_err("first read error %d", r);
+ return r;
+ }
+
+ usbi_parse_descriptor(tmp, "bbw", &config);
+ buf = malloc(config.wTotalLength);
+ if (!buf)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = get_config_descriptor(fd, active_config, buf, config.wTotalLength);
+ if (r < 0) {
+ free(buf);
+ return r;
+ }
+
+ if (priv->config_descriptor)
+ free(priv->config_descriptor);
+ priv->config_descriptor = buf;
+ return 0;
}
static int initialize_device(struct libusb_device *dev, uint8_t busnum,
@@ -271,6 +444,82 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
snprintf(path, PATH_MAX, "%s/%03d/%03d", usbfs_path, busnum, devaddr);
usbi_dbg("%s", path);
+ if (!have_sysfs) {
+ /* cache device descriptor in memory so that we can retrieve it later
+ * without waking the device up (op_get_device_descriptor) */
+ unsigned char *dev_buf = malloc(DEVICE_DESC_LENGTH);
+ int fd;
+ ssize_t r;
+ int tmp;
+ int active_config = 0;
+
+ struct usbfs_ctrltransfer ctrl = {
+ .bmRequestType = LIBUSB_ENDPOINT_IN,
+ .bRequest = LIBUSB_REQUEST_GET_CONFIGURATION,
+ .wValue = 0,
+ .wIndex = 0,
+ .wLength = 1,
+ .timeout = 1000,
+ .data = &active_config
+ };
+
+ priv->dev_descriptor = NULL;
+ priv->config_descriptor = NULL;
+ if (!dev_buf)
+ return LIBUSB_ERROR_NO_MEM;
+
+ fd = open(path, O_RDWR);
+ if (fd < 0 && errno == EACCES) {
+ usbi_dbg("sysfs unavailable and read-only access to usbfs --> "
+ "cannot determine which configuration is active");
+ fd = open(path, O_RDONLY);
+ /* if we only have read-only access to the device, we cannot
+ * send a control message to determine the active config. just
+ * assume the first one is active. */
+ active_config = -1;
+ }
+
+ if (fd < 0) {
+ usbi_err("open failed, ret=%d errno=%d", fd, errno);
+ free(dev_buf);
+ return LIBUSB_ERROR_IO;
+ }
+
+ r = read(fd, dev_buf, DEVICE_DESC_LENGTH);
+ if (r < 0) {
+ usbi_err("read descriptor failed ret=%d errno=%d", fd, errno);
+ free(dev_buf);
+ close(fd);
+ return LIBUSB_ERROR_IO;
+ } else if (r < DEVICE_DESC_LENGTH) {
+ usbi_err("short descriptor read (%d)", r);
+ free(dev_buf);
+ close(fd);
+ return LIBUSB_ERROR_IO;
+ }
+
+ if (active_config == 0) {
+ /* determine active configuration and cache the descriptor */
+ tmp = ioctl(fd, IOCTL_USBFS_CONTROL, &ctrl);
+ if (tmp < 0) {
+ usbi_err("get_configuration failed ret=%d errno=%d", tmp, errno);
+ free(dev_buf);
+ close(fd);
+ return LIBUSB_ERROR_IO;
+ }
+ }
+
+ r = cache_active_config(dev, fd, active_config);
+ if (r < 0) {
+ free(dev_buf);
+ close(fd);
+ return r;
+ }
+
+ priv->dev_descriptor = dev_buf;
+ close(fd);
+ }
+
if (sysfs_dir)
strncpy(priv->sysfs_dir, sysfs_dir, SYSFS_DIR_LENGTH);
@@ -311,7 +560,7 @@ static int enumerate_device(struct discovered_devs **_discdevs,
r = initialize_device(dev, busnum, devaddr, sysfs_dir);
if (r < 0)
goto out;
- r = usbi_discover_device(dev);
+ r = usbi_sanitize_device(dev);
if (r < 0)
goto out;
}
@@ -542,6 +791,14 @@ static int op_set_configuration(struct libusb_device_handle *handle, int config)
usbi_err("failed, error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
}
+
+ if (!have_sysfs) {
+ /* 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);
+ }
+
return 0;
}
@@ -672,9 +929,16 @@ static int op_detach_kernel_driver(struct libusb_device_handle *handle,
static void op_destroy_device(struct libusb_device *dev)
{
- unsigned char *nodepath = __device_priv(dev)->nodepath;
- if (nodepath)
- free(nodepath);
+ struct linux_device_priv *priv = __device_priv(dev);
+ if (priv->nodepath)
+ free(priv->nodepath);
+
+ if (!have_sysfs) {
+ if (priv->dev_descriptor)
+ free(priv->dev_descriptor);
+ if (priv->config_descriptor)
+ free(priv->config_descriptor);
+ }
}
static void free_iso_urbs(struct linux_transfer_priv *tpriv)
@@ -1321,10 +1585,9 @@ const struct usbi_os_backend linux_usbfs_backend = {
.init = op_init,
.exit = NULL,
.get_device_list = op_get_device_list,
- .begin_discovery = op_begin_discovery,
.get_device_descriptor = op_get_device_descriptor,
+ .get_active_config_descriptor = op_get_active_config_descriptor,
.get_config_descriptor = op_get_config_descriptor,
- .end_discovery = op_end_discovery,
.open = op_open,
.close = op_close,