summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Drake <dsd@gentoo.org>2008-06-28 21:18:41 -0500
committerDaniel Drake <dsd@gentoo.org>2008-06-28 21:18:41 -0500
commitfbad9a5426f0369394c88987355a8fb06a741ca1 (patch)
treebc11af2d84fc149ce6093abab3fd4072e5b11d54
parent819e65f880ca43526036e56c65c415042c91f58f (diff)
downloadlibusb-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--TODO2
-rw-r--r--libusb/os/linux_usbfs.c237
2 files changed, 156 insertions, 83 deletions
diff --git a/TODO b/TODO
index 5fb4100..6c162a3 100644
--- a/TODO
+++ b/TODO
@@ -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)
{