summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2013-05-22 12:57:32 +0200
committerHans de Goede <hdegoede@redhat.com>2013-05-23 12:57:26 +0200
commitaf9997c4085ed0c26b022070ce9f5e19cd8cfec4 (patch)
tree2830f9c6b84e60cc25341fbece33ca095362f47b
parent7201f72f2b21ce90436cae6cf021dc65123a1de6 (diff)
downloadlibusb-af9997c4085ed0c26b022070ce9f5e19cd8cfec4.tar.gz
linux: Cache the entire descriptors at device-init
Now that the core caches device-descriptors, we end up opening each usbfs-node or sysfs-descriptors file once on libusb_init anyways. So we might as well do this on device-init, rather then waiting for the core to call op_get_device_descriptor. This allows us to simplify the code in various places. While we've it open, read the entire file rather then only reading the device-descriptor. This is practically free, since most of the cost is in the opening of the file, not in reading it. Running the stress test, which does 10000 libusb_init calls, takes 21.8 seconds on avarage on my idle system with 17 usb devices both before and after this patch, showing that the cost of also reading the config descriptors while we've the file open is truely neglible. Note that this patch does not yet use the cached config descriptors, this is done by a later patch in this series. Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-rw-r--r--libusb/os/linux_usbfs.c126
-rw-r--r--libusb/version_nano.h2
2 files changed, 50 insertions, 78 deletions
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 49fa42d..dd11b18 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -128,7 +128,8 @@ static int linux_default_scan_devices (struct libusb_context *ctx);
struct linux_device_priv {
char *sysfs_dir;
- unsigned char *dev_descriptor;
+ unsigned char *descriptors;
+ int descriptors_len;
unsigned char *config_descriptor;
};
@@ -513,16 +514,6 @@ static int linux_scan_devices(struct libusb_context *ctx)
#endif
}
-static int usbfs_get_device_descriptor(struct libusb_device *dev,
- unsigned char *buffer)
-{
- struct linux_device_priv *priv = _device_priv(dev);
-
- /* return cached copy */
- memcpy(buffer, priv->dev_descriptor, DEVICE_DESC_LENGTH);
- return 0;
-}
-
static int _open_sysfs_attr(struct libusb_device *dev, const char *attr)
{
struct linux_device_priv *priv = _device_priv(dev);
@@ -576,44 +567,17 @@ static int __read_sysfs_attr(struct libusb_context *ctx,
return value;
}
-static int sysfs_get_device_descriptor(struct libusb_device *dev,
- unsigned char *buffer)
+static int op_get_device_descriptor(struct libusb_device *dev,
+ unsigned char *buffer, int *host_endian)
{
- int fd;
- ssize_t r;
+ struct linux_device_priv *priv = _device_priv(dev);
- /* 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;
-
- r = read(fd, buffer, DEVICE_DESC_LENGTH);;
- close(fd);
- if (r < 0) {
- usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", fd, errno);
- return LIBUSB_ERROR_IO;
- } else if (r < DEVICE_DESC_LENGTH) {
- usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, DEVICE_DESC_LENGTH);
- return LIBUSB_ERROR_IO;
- }
+ *host_endian = sysfs_has_descriptors ? 0 : 1;
+ memcpy(buffer, priv->descriptors, DEVICE_DESC_LENGTH);
return 0;
}
-static int op_get_device_descriptor(struct libusb_device *dev,
- unsigned char *buffer, int *host_endian)
-{
- if (sysfs_has_descriptors) {
- *host_endian = 0;
- return sysfs_get_device_descriptor(dev, buffer);
- } else {
- *host_endian = 1; /* usbfs converts the device desc to host */
- return usbfs_get_device_descriptor(dev, buffer);
- }
-}
-
static int usbfs_get_active_config_descriptor(struct libusb_device *dev,
unsigned char *buffer, size_t len)
{
@@ -986,7 +950,8 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
uint8_t devaddr, const char *sysfs_dir)
{
struct linux_device_priv *priv = _device_priv(dev);
- unsigned char *dev_buf;
+ 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;
@@ -1017,13 +982,47 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
}
}
+ /* cache descriptors in memory */
+ if (sysfs_has_descriptors)
+ fd = _open_sysfs_attr(dev, "descriptors");
+ else
+ fd = _get_usbfs_fd(dev, O_RDONLY, 0);
+ if (fd < 0)
+ return fd;
+
+ do {
+ descriptors_size *= 2;
+ priv->descriptors = usbi_reallocf(priv->descriptors,
+ descriptors_size);
+ if (!priv->descriptors) {
+ close(fd);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+ r = read(fd, priv->descriptors + priv->descriptors_len,
+ descriptors_size - priv->descriptors_len);
+ if (r < 0) {
+ usbi_err(ctx, "read descriptor failed ret=%d errno=%d",
+ fd, errno);
+ close(fd);
+ return LIBUSB_ERROR_IO;
+ }
+ priv->descriptors_len += r;
+ } while (priv->descriptors_len == descriptors_size);
+
+ close(fd);
+
+ if (priv->descriptors_len < DEVICE_DESC_LENGTH) {
+ usbi_err(ctx, "short descriptor read (%d)",
+ priv->descriptors_len);
+ 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->dev_descriptor = NULL;
priv->config_descriptor = NULL;
if (sysfs_can_relate_devices) {
@@ -1077,42 +1076,15 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
}
}
- dev_buf = malloc(DEVICE_DESC_LENGTH);
- if (!dev_buf) {
- close(fd);
- return LIBUSB_ERROR_NO_MEM;
- }
-
- r = read(fd, dev_buf, DEVICE_DESC_LENGTH);
- if (r < 0) {
- usbi_err(DEVICE_CTX(dev),
- "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(DEVICE_CTX(dev), "short descriptor read (%d)", r);
- free(dev_buf);
- close(fd);
- return LIBUSB_ERROR_IO;
- }
-
/* 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];
+ dev->num_configurations = priv->descriptors[DEVICE_DESC_LENGTH - 1];
- if (device_configured) {
+ 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;
+ return r;
}
static int linux_get_parent_info(struct libusb_device *dev, const char *sysfs_dir)
@@ -1710,11 +1682,11 @@ static void op_destroy_device(struct libusb_device *dev)
{
struct linux_device_priv *priv = _device_priv(dev);
if (!sysfs_has_descriptors) {
- if (priv->dev_descriptor)
- free(priv->dev_descriptor);
if (priv->config_descriptor)
free(priv->config_descriptor);
}
+ if (priv->descriptors)
+ free(priv->descriptors);
if (priv->sysfs_dir)
free(priv->sysfs_dir);
}
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index cdb9883..079ba39 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 10701
+#define LIBUSB_NANO 10702