diff options
author | Daniel Drake <dsd@gentoo.org> | 2008-06-28 21:18:41 -0500 |
---|---|---|
committer | Daniel Drake <dsd@gentoo.org> | 2008-06-28 21:18:41 -0500 |
commit | fbad9a5426f0369394c88987355a8fb06a741ca1 (patch) | |
tree | bc11af2d84fc149ce6093abab3fd4072e5b11d54 | |
parent | 819e65f880ca43526036e56c65c415042c91f58f (diff) | |
download | libusb-fbad9a5426f0369394c88987355a8fb06a741ca1.tar.gz |
Linux: Compatibility with new sysfs descriptors file
As of 2.6.26, the descriptors file now includes all descriptors, not
just the active one.
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | libusb/os/linux_usbfs.c | 237 |
2 files changed, 156 insertions, 83 deletions
@@ -1,5 +1,3 @@ -new sysfs descriptors format - for 1.1 or future ================== optional timerfd support (runtime detection) diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index 5ca8640..418a017 100644 --- a/libusb/os/linux_usbfs.c +++ b/libusb/os/linux_usbfs.c @@ -56,6 +56,10 @@ * * If we also have descriptors, we can obtain the device descriptor and active * configuration without touching usbfs at all. + * + * The descriptors file originally only contained the active configuration + * descriptor alongside the device descriptor, but all configurations are + * included as of Linux 2.6.26. */ static const char *usbfs_path = NULL; @@ -262,12 +266,96 @@ static int usbfs_get_active_config_descriptor(struct libusb_device *dev, return 0; } +/* read the bConfigurationValue for a device */ +static int sysfs_get_active_config(struct libusb_device *dev, int *config) +{ + char *endptr; + char tmp[4] = {0, 0, 0, 0}; + long num; + int fd; + size_t r; + + fd = __open_sysfs_attr(dev, "bConfigurationValue"); + if (fd < 0) + return fd; + + r = read(fd, tmp, sizeof(tmp)); + close(fd); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), + "read bConfigurationValue failed ret=%d errno=%d", r, errno); + return LIBUSB_ERROR_IO; + } else if (r == 0) { + usbi_err(DEVICE_CTX(dev), "device unconfigured"); + *config = -1; + return 0; + } + + if (tmp[sizeof(tmp) - 1] != 0) { + usbi_err(DEVICE_CTX(dev), "not null-terminated?"); + return LIBUSB_ERROR_IO; + } else if (tmp[0] == 0) { + usbi_err(DEVICE_CTX(dev), "no configuration value?"); + return LIBUSB_ERROR_IO; + } + + num = strtol(tmp, &endptr, 10); + if (endptr == tmp) { + usbi_err(DEVICE_CTX(dev), "error converting '%s' to integer", tmp); + return LIBUSB_ERROR_IO; + } + + *config = (int) num; + 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; + int 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, 1); + 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 */ @@ -276,6 +364,19 @@ static int sysfs_get_active_config_descriptor(struct libusb_device *dev, 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); @@ -283,20 +384,54 @@ static int sysfs_get_active_config_descriptor(struct libusb_device *dev, return LIBUSB_ERROR_IO; } - r = read(fd, buffer, len); - 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 == 0) { - usbi_dbg("device is unconfigured"); - return LIBUSB_ERROR_NOT_FOUND; - } else if (r < len) { - usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, len); - 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; } - return 0; + 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 = LIBUSB_ERROR_IO; + } + } else { + r = 0; + } + + close(fd); + return r; } static int op_get_active_config_descriptor(struct libusb_device *dev, @@ -310,14 +445,11 @@ static int op_get_active_config_descriptor(struct libusb_device *dev, } } - /* 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. */ + * 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) { - unsigned char tmp[8]; off_t off; ssize_t r; @@ -329,28 +461,10 @@ static int get_config_descriptor(struct libusb_context *ctx, int fd, /* might need to skip some configuration descriptors to reach the * requested configuration */ - while (config_index) { - struct libusb_config_descriptor config; - - /* read first 8 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; - } - - usbi_parse_descriptor(tmp, "bbwbb", &config, 1); - - /* seek forward to end of config */ - 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; - } - + while (config_index > 0) { + r = seek_to_next_config(ctx, fd); + if (r < 0) + return r; config_index--; } @@ -377,6 +491,10 @@ static int op_get_config_descriptor(struct libusb_device *dev, /* always read from usbfs: sysfs only has the active descriptor * this will involve waking the device up, but oh well! */ + /* 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. */ + __get_usbfs_path(dev, filename); fd = open(filename, O_RDONLY); if (fd < 0) { @@ -438,49 +556,6 @@ static int cache_active_config(struct libusb_device *dev, int fd, return 0; } -/* read the bConfigurationValue for a device */ -static int sysfs_get_active_config(struct libusb_device *dev, int *config) -{ - char *endptr; - char tmp[4] = {0, 0, 0, 0}; - long num; - int fd; - size_t r; - - fd = __open_sysfs_attr(dev, "bConfigurationValue"); - if (fd < 0) - return fd; - - r = read(fd, tmp, sizeof(tmp)); - close(fd); - if (r < 0) { - usbi_err(DEVICE_CTX(dev), - "read bConfigurationValue failed ret=%d errno=%d", r, errno); - return LIBUSB_ERROR_IO; - } else if (r == 0) { - usbi_err(DEVICE_CTX(dev), "device unconfigured"); - *config = -1; - return 0; - } - - if (tmp[sizeof(tmp) - 1] != 0) { - usbi_err(DEVICE_CTX(dev), "not null-terminated?"); - return LIBUSB_ERROR_IO; - } else if (tmp[0] == 0) { - usbi_err(DEVICE_CTX(dev), "no configuration value?"); - return LIBUSB_ERROR_IO; - } - - num = strtol(tmp, &endptr, 10); - if (endptr == tmp) { - usbi_err(DEVICE_CTX(dev), "error converting '%s' to integer", tmp); - return LIBUSB_ERROR_IO; - } - - *config = (int) num; - return 0; -} - /* send a control message to retrieve active configuration */ static int usbfs_get_active_config(struct libusb_device *dev, int fd) { |