summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2013-05-22 20:38:51 +0200
committerHans de Goede <hdegoede@redhat.com>2013-05-24 13:58:36 +0200
commit200931805bc352219a9f991c46dcb2d6f204c426 (patch)
treecd9b0759c04dca6322ac47f244d2c4adab8e94cf
parent4a34eda8bfe56ea6637a8d549bf4458f93059003 (diff)
downloadlibusb-200931805bc352219a9f991c46dcb2d6f204c426.tar.gz
linux: Use cached config descriptors
Use cached config descriptors instead of doing tons file io, because: - Less fileio is more - Less code is more, diffstat for this patch: 1 file changed, 128 insertions(+), 307 deletions(-) Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-rw-r--r--libusb/os/linux_usbfs.c441
-rw-r--r--libusb/version_nano.h2
2 files changed, 133 insertions, 310 deletions
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 578ae5d..c0cdaaf 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -3,6 +3,7 @@
* Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org>
* Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
* Copyright © 2013 Nathan Hjelm <hjelmn@mac.com>
+ * Copyright © 2012-2013 Hans de Goede <hdegoede@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -135,7 +136,7 @@ struct linux_device_priv {
char *sysfs_dir;
unsigned char *descriptors;
int descriptors_len;
- unsigned char *config_descriptor;
+ int active_config; /* cache val for !sysfs_can_relate_devices */
};
struct linux_device_handle_priv {
@@ -538,18 +539,6 @@ static int op_get_device_descriptor(struct libusb_device *dev,
return 0;
}
-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;
-}
-
/* read the bConfigurationValue for a device */
static int sysfs_get_active_config(struct libusb_device *dev, int *config)
{
@@ -593,131 +582,6 @@ static int sysfs_get_active_config(struct libusb_device *dev, int *config)
return 0;
}
-/* takes a usbfs/descriptors fd seeked to the start of a configuration, and
- * seeks to the next one. */
-static int seek_to_next_config(struct libusb_context *ctx, int fd)
-{
- struct libusb_config_descriptor config;
- unsigned char tmp[6];
- off_t off;
- ssize_t r;
-
- /* read first 6 bytes of descriptor */
- r = read(fd, tmp, sizeof(tmp));
- if (r < 0) {
- usbi_err(ctx, "read failed ret=%d errno=%d", r, errno);
- return LIBUSB_ERROR_IO;
- } else if (r < sizeof(tmp)) {
- usbi_err(ctx, "short descriptor read %d/%d", r, sizeof(tmp));
- return LIBUSB_ERROR_IO;
- }
-
- /* seek forward to end of config */
- usbi_parse_descriptor(tmp, "bbwbb", &config, 0);
- off = lseek(fd, config.wTotalLength - sizeof(tmp), SEEK_CUR);
- if (off < 0) {
- usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno);
- return LIBUSB_ERROR_IO;
- }
-
- return 0;
-}
-
-static int sysfs_get_active_config_descriptor(struct libusb_device *dev,
- unsigned char *buffer, size_t len)
-{
- int fd;
- ssize_t r;
- off_t off;
- int to_copy;
- int config;
- unsigned char tmp[6];
-
- r = sysfs_get_active_config(dev, &config);
- if (r < 0)
- return r;
- if (config == -1)
- return LIBUSB_ERROR_NOT_FOUND;
-
- usbi_dbg("active configuration %d", config);
-
- /* 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_attr(dev, "descriptors");
- if (fd < 0)
- return fd;
-
- /* device might have been unconfigured since we read bConfigurationValue,
- * so first check that there is any config descriptor data at all... */
- off = lseek(fd, 0, SEEK_END);
- if (off < 1) {
- usbi_err(DEVICE_CTX(dev), "end seek failed, ret=%d errno=%d",
- off, errno);
- close(fd);
- return LIBUSB_ERROR_IO;
- } else if (off == DEVICE_DESC_LENGTH) {
- close(fd);
- return LIBUSB_ERROR_NOT_FOUND;
- }
-
- off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET);
- if (off < 0) {
- usbi_err(DEVICE_CTX(dev), "seek failed, ret=%d errno=%d", off, errno);
- close(fd);
- return LIBUSB_ERROR_IO;
- }
-
- /* unbounded loop: we expect the descriptor to be present under all
- * circumstances */
- while (1) {
- r = read(fd, tmp, sizeof(tmp));
- if (r < 0) {
- usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d",
- fd, errno);
- return LIBUSB_ERROR_IO;
- } else if (r < sizeof(tmp)) {
- usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, sizeof(tmp));
- return LIBUSB_ERROR_IO;
- }
-
- /* check bConfigurationValue */
- if (tmp[5] == config)
- break;
-
- /* try the next descriptor */
- off = lseek(fd, 0 - sizeof(tmp), SEEK_CUR);
- if (off < 0)
- return LIBUSB_ERROR_IO;
-
- r = seek_to_next_config(DEVICE_CTX(dev), fd);
- if (r < 0)
- return r;
- }
-
- to_copy = (len < sizeof(tmp)) ? len : sizeof(tmp);
- memcpy(buffer, tmp, to_copy);
- if (len > sizeof(tmp)) {
- r = read(fd, buffer + sizeof(tmp), len - sizeof(tmp));
- if (r < 0) {
- usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d",
- fd, errno);
- r = LIBUSB_ERROR_IO;
- } else if (r == 0) {
- usbi_dbg("device is unconfigured");
- r = LIBUSB_ERROR_NOT_FOUND;
- } else if (r < len - sizeof(tmp)) {
- usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, len);
- r = 0;
- }
- } else {
- r = 0;
- }
-
- close(fd);
- return r;
-}
-
int linux_get_device_address (struct libusb_context *ctx, int detached,
uint8_t *busnum, uint8_t *devaddr,const char *dev_node,
const char *sys_name)
@@ -757,123 +621,122 @@ int linux_get_device_address (struct libusb_context *ctx, int detached,
return LIBUSB_SUCCESS;
}
-static int op_get_active_config_descriptor(struct libusb_device *dev,
- unsigned char *buffer, size_t len, int *host_endian)
+/* Return offset to next config */
+static int seek_to_next_config(struct libusb_context *ctx,
+ unsigned char *buffer, int size)
{
- /* Unlike the device desc. config descs. are always in raw format */
- *host_endian = 0;
- if (sysfs_has_descriptors) {
- return sysfs_get_active_config_descriptor(dev, buffer, len);
- } else {
- return usbfs_get_active_config_descriptor(dev, buffer, len);
- }
-}
+ struct libusb_config_descriptor config;
-/* takes a usbfs fd, attempts to find the requested config and copy a certain
- * amount of it into an output buffer. */
-static int get_config_descriptor(struct libusb_context *ctx, int fd,
- uint8_t config_index, unsigned char *buffer, size_t len)
-{
- off_t off;
- ssize_t r;
+ if (size == 0)
+ return LIBUSB_ERROR_NOT_FOUND;
- off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET);
- if (off < 0) {
- usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno);
+ if (size < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(ctx, "short descriptor read %d/%d",
+ size, LIBUSB_DT_CONFIG_SIZE);
return LIBUSB_ERROR_IO;
}
- /* might need to skip some configuration descriptors to reach the
- * requested configuration */
- while (config_index > 0) {
- r = seek_to_next_config(ctx, fd);
- if (r < 0)
- return r;
- config_index--;
- }
+ usbi_parse_descriptor(buffer, "bbwbbbbb", &config, 0);
- /* read the rest of the descriptor */
- r = read(fd, buffer, len);
- if (r < 0) {
- usbi_err(ctx, "read failed ret=%d errno=%d", r, errno);
+ if (config.wTotalLength < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(ctx, "invalid wTotalLength %d", config.wTotalLength);
return LIBUSB_ERROR_IO;
- } else if (r < len) {
- usbi_err(ctx, "short output read %d/%d", r, len);
- }
-
- return 0;
+ } else if (config.wTotalLength > size) {
+ usbi_warn(ctx, "short descriptor read %d/%d",
+ size, config.wTotalLength);
+ return size;
+ } else
+ return config.wTotalLength;
}
-static int op_get_config_descriptor(struct libusb_device *dev,
- uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian)
+static int get_config_descriptor_by_value(struct libusb_device *dev,
+ unsigned char **buffer, uint8_t value)
{
- int fd;
- int r;
-
- /* Unlike the device desc. config descs. are always in raw format */
- *host_endian = 0;
-
- /* always read from usbfs: sysfs only has the active descriptor
- * this will involve waking the device up, but oh well! */
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ struct linux_device_priv *priv = _device_priv(dev);
+ unsigned char *descriptors = priv->descriptors;
+ int size = priv->descriptors_len;
+ struct libusb_config_descriptor *config;
- /* FIXME: the above is no longer true, new kernels have all descriptors
- * in the descriptors file. but its kinda hard to detect if the kernel
- * is sufficiently new. */
+ *buffer = NULL;
- fd = _get_usbfs_fd(dev, O_RDONLY, 0);
- if (fd < 0)
- return fd;
+ /* Skip device header */
+ descriptors += DEVICE_DESC_LENGTH;
+ size -= DEVICE_DESC_LENGTH;
- r = get_config_descriptor(DEVICE_CTX(dev), fd, config_index, buffer, len);
- close(fd);
- return r;
+ /* Seek till the config is found, or till "EOF" */
+ while (1) {
+ int next = seek_to_next_config(ctx, descriptors, size);
+ if (next < 0)
+ return next;
+ config = (struct libusb_config_descriptor *)descriptors;
+ if (config->bConfigurationValue == value) {
+ *buffer = descriptors;
+ return next;
+ }
+ size -= next;
+ descriptors += next;
+ }
}
-/* cache the active config descriptor in memory. a value of -1 means that
- * we aren't sure which one is active, so just assume the first one.
- * only for usbfs. */
-static int cache_active_config(struct libusb_device *dev, int fd,
- int active_config)
+static int op_get_active_config_descriptor(struct libusb_device *dev,
+ unsigned char *buffer, size_t len, int *host_endian)
{
- struct linux_device_priv *priv = _device_priv(dev);
- struct libusb_config_descriptor config;
- unsigned char tmp[8];
- unsigned char *buf;
- int idx;
- int r;
+ int r, config;
+ unsigned char *config_desc;
- if (active_config == -1) {
- idx = 0;
- } else {
- r = usbi_get_config_index_by_value(dev, active_config, &idx);
+ /* Unlike the device desc. config descs. are always in raw format */
+ *host_endian = 0;
+
+ if (sysfs_can_relate_devices) {
+ r = sysfs_get_active_config(dev, &config);
if (r < 0)
return r;
- if (idx == -1)
- return LIBUSB_ERROR_NOT_FOUND;
+ } else {
+ /* Use cached bConfigurationValue */
+ struct linux_device_priv *priv = _device_priv(dev);
+ config = priv->active_config;
}
+ if (config == -1)
+ return LIBUSB_ERROR_NOT_FOUND;
- r = get_config_descriptor(DEVICE_CTX(dev), fd, idx, tmp, sizeof(tmp));
- if (r < 0) {
- usbi_err(DEVICE_CTX(dev), "first read error %d", r);
+ r = get_config_descriptor_by_value(dev, &config_desc, config);
+ if (r < 0)
return r;
- }
- usbi_parse_descriptor(tmp, "bbw", &config, 0);
- buf = malloc(config.wTotalLength);
- if (!buf)
- return LIBUSB_ERROR_NO_MEM;
+ len = MIN(len, r);
+ memcpy(buffer, config_desc, len);
+ return len;
+}
- r = get_config_descriptor(DEVICE_CTX(dev), fd, idx, buf,
- config.wTotalLength);
- if (r < 0) {
- free(buf);
- return r;
+static int op_get_config_descriptor(struct libusb_device *dev,
+ uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+ unsigned char *descriptors = priv->descriptors;
+ int i, r, size = priv->descriptors_len;
+
+ /* Unlike the device desc. config descs. are always in raw format */
+ *host_endian = 0;
+
+ /* Skip device header */
+ descriptors += DEVICE_DESC_LENGTH;
+ size -= DEVICE_DESC_LENGTH;
+
+ /* Seek till the config is found, or till "EOF" */
+ for (i = 0; ; i++) {
+ r = seek_to_next_config(DEVICE_CTX(dev), descriptors, size);
+ if (r < 0)
+ return r;
+ if (i == config_index)
+ break;
+ size -= r;
+ descriptors += r;
}
- if (priv->config_descriptor)
- free(priv->config_descriptor);
- priv->config_descriptor = buf;
- return 0;
+ len = MIN(len, r);
+ memcpy(buffer, descriptors, len);
+ return len;
}
/* send a control message to retrieve active configuration */
@@ -912,10 +775,7 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
struct linux_device_priv *priv = _device_priv(dev);
struct libusb_context *ctx = DEVICE_CTX(dev);
int descriptors_size = 512; /* Begin with a 1024 byte alloc */
- char path[PATH_MAX];
int fd, speed;
- int active_config = 0;
- int device_configured = 1;
ssize_t r;
dev->bus_number = busnum;
@@ -982,71 +842,50 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
return LIBUSB_ERROR_IO;
}
- if (sysfs_has_descriptors)
- return 0;
-
- /* cache device descriptor in memory so that we can retrieve it later
- * without waking the device up (op_get_device_descriptor) */
-
- priv->config_descriptor = NULL;
-
- if (sysfs_can_relate_devices) {
- int tmp = sysfs_get_active_config(dev, &active_config);
- if (tmp < 0)
- return tmp;
- if (active_config == -1)
- device_configured = 0;
- }
+ if (sysfs_can_relate_devices)
+ return LIBUSB_SUCCESS;
+ /* cache active config */
fd = _get_usbfs_fd(dev, O_RDWR, 1);
- if (fd == LIBUSB_ERROR_ACCESS) {
- fd = _get_usbfs_fd(dev, O_RDONLY, 0);
- /* 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) {
- return LIBUSB_ERROR_IO;
- }
+ /* cannot send a control message to determine the active
+ * config. just assume the first one is active. */
+ usbi_warn(ctx, "Missing rw usbfs access; cannot determine "
+ "active configuration descriptor");
+ if (priv->descriptors_len >=
+ (DEVICE_DESC_LENGTH + LIBUSB_DT_CONFIG_SIZE)) {
+ struct libusb_config_descriptor config;
+ usbi_parse_descriptor(
+ priv->descriptors + DEVICE_DESC_LENGTH,
+ "bbwbbbbb", &config, 0);
+ priv->active_config = config.bConfigurationValue;
+ } else
+ priv->active_config = -1; /* No config dt */
- if (!sysfs_can_relate_devices) {
- if (active_config == -1) {
- /* 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. */
- usbi_warn(DEVICE_CTX(dev), "access to %s is read-only; cannot "
- "determine active configuration descriptor", path);
- } else {
- active_config = usbfs_get_active_config(dev, fd);
- if (active_config == LIBUSB_ERROR_IO) {
- /* buggy devices sometimes fail to report their active config.
- * assume unconfigured and continue the probing */
- usbi_warn(DEVICE_CTX(dev), "couldn't query active "
- "configuration, assumung unconfigured");
- device_configured = 0;
- } else 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("active cfg 0? assuming unconfigured device");
- device_configured = 0;
- }
- }
+ return LIBUSB_SUCCESS;
}
- /* 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 = priv->descriptors[DEVICE_DESC_LENGTH - 1];
-
- if (device_configured)
- r = cache_active_config(dev, fd, active_config);
+ r = usbfs_get_active_config(dev, fd);
+ if (r > 0) {
+ priv->active_config = r;
+ r = LIBUSB_SUCCESS;
+ } else if (r == 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("active cfg 0? assuming unconfigured device");
+ priv->active_config = -1;
+ r = LIBUSB_SUCCESS;
+ } else if (r == LIBUSB_ERROR_IO) {
+ /* buggy devices sometimes fail to report their active config.
+ * assume unconfigured and continue the probing */
+ usbi_warn(ctx, "couldn't query active configuration, assuming"
+ " unconfigured");
+ priv->active_config = -1;
+ r = LIBUSB_SUCCESS;
+ } /* else r < 0, just return the error code */
close(fd);
return r;
@@ -1412,22 +1251,10 @@ static int op_set_configuration(struct libusb_device_handle *handle, int config)
return LIBUSB_ERROR_OTHER;
}
- if (!sysfs_has_descriptors) {
- /* update our cached active config descriptor */
- 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(HANDLE_CTX(handle),
- "failed to update cached config descriptor, error %d", r);
- }
- }
+ /* update our cached active config descriptor */
+ priv->active_config = config;
- return 0;
+ return LIBUSB_SUCCESS;
}
static int op_claim_interface(struct libusb_device_handle *handle, int iface)
@@ -1646,10 +1473,6 @@ static int op_attach_kernel_driver(struct libusb_device_handle *handle,
static void op_destroy_device(struct libusb_device *dev)
{
struct linux_device_priv *priv = _device_priv(dev);
- if (!sysfs_has_descriptors) {
- if (priv->config_descriptor)
- free(priv->config_descriptor);
- }
if (priv->descriptors)
free(priv->descriptors);
if (priv->sysfs_dir)
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 5138420..92e63f1 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 10706
+#define LIBUSB_NANO 10707