diff options
author | Hans de Goede <hdegoede@redhat.com> | 2013-05-22 12:57:32 +0200 |
---|---|---|
committer | Hans de Goede <hdegoede@redhat.com> | 2013-05-23 12:57:26 +0200 |
commit | af9997c4085ed0c26b022070ce9f5e19cd8cfec4 (patch) | |
tree | 2830f9c6b84e60cc25341fbece33ca095362f47b | |
parent | 7201f72f2b21ce90436cae6cf021dc65123a1de6 (diff) | |
download | libusb-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.c | 126 | ||||
-rw-r--r-- | libusb/version_nano.h | 2 |
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 |