Index: third_party/libusb/src/libusb/os/linux_usbfs.c |
diff --git a/third_party/libusb/src/libusb/os/linux_usbfs.c b/third_party/libusb/src/libusb/os/linux_usbfs.c |
index 02d182d65aa751ee55d9886a77c0f177a2e94709..09288af719cf694fadcf263f28760bcb1f032ac9 100644 |
--- a/third_party/libusb/src/libusb/os/linux_usbfs.c |
+++ b/third_party/libusb/src/libusb/os/linux_usbfs.c |
@@ -1,7 +1,9 @@ |
/* |
- * Linux usbfs backend for libusb |
- * Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org> |
- * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> |
+ * Linux usbfs backend for libusbx |
+ * 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 |
@@ -18,7 +20,9 @@ |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
*/ |
-#include <config.h> |
+#include "config.h" |
+ |
+#include <assert.h> |
#include <ctype.h> |
#include <dirent.h> |
#include <errno.h> |
@@ -43,23 +47,23 @@ |
* |
* sysfs allows us to read the kernel's in-memory copies of device descriptors |
* and so forth, avoiding the need to open the device: |
- * - The binary "descriptors" file was added in 2.6.23. |
- * - The "busnum" file was added in 2.6.22 |
+ * - The binary "descriptors" file contains all config descriptors since |
+ * 2.6.26, commit 217a9081d8e69026186067711131b77f0ce219ed |
+ * - The binary "descriptors" file was added in 2.6.23, commit |
+ * 69d42a78f935d19384d1f6e4f94b65bb162b36df, but it only contains the |
+ * active config descriptors |
+ * - The "busnum" file was added in 2.6.22, commit |
+ * 83f7d958eab2fbc6b159ee92bf1493924e1d0f72 |
* - The "devnum" file has been present since pre-2.6.18 |
* - the "bConfigurationValue" file has been present since pre-2.6.18 |
* |
* If we have bConfigurationValue, busnum, and devnum, then we can determine |
* the active configuration without having to open the usbfs node in RDWR mode. |
- * We assume this is the case if we see the busnum file (indicates 2.6.22+). |
* The busnum file is important as that is the only way we can relate sysfs |
* devices to usbfs nodes. |
* |
- * If we also have descriptors, we can obtain the device descriptor and active |
+ * If we also have all descriptors, we can obtain the device descriptor and |
* 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. |
*/ |
/* endianness for multi-byte fields: |
@@ -67,6 +71,8 @@ |
* Descriptors exposed by usbfs have the multi-byte fields in the device |
* descriptor as host endian. Multi-byte fields in the other descriptors are |
* bus-endian. The kernel documentation says otherwise, but it is wrong. |
+ * |
+ * In sysfs all descriptors are bus-endian. |
*/ |
static const char *usbfs_path = NULL; |
@@ -101,21 +107,42 @@ static int supports_flag_zero_packet = -1; |
* systems. appropriate choice made at initialization time. */ |
static clockid_t monotonic_clkid = -1; |
-/* do we have a busnum to relate devices? this also implies that we can read |
+/* Linux 2.6.22 (commit 83f7d958eab2fbc6b159ee92bf1493924e1d0f72) adds a busnum |
+ * to sysfs, so we can relate devices. This also implies that we can read |
* the active configuration through bConfigurationValue */ |
-static int sysfs_can_relate_devices = 0; |
+static int sysfs_can_relate_devices = -1; |
+ |
+/* Linux 2.6.26 (commit 217a9081d8e69026186067711131b77f0ce219ed) adds all |
+ * config descriptors (rather then just the active config) to the sysfs |
+ * descriptors file, so from then on we can use them. */ |
+static int sysfs_has_descriptors = -1; |
+ |
+/* how many times have we initted (and not exited) ? */ |
+static volatile int init_count = 0; |
+ |
+/* Serialize hotplug start/stop, scan-devices, event-thread, and poll */ |
+usbi_mutex_static_t linux_hotplug_lock = USBI_MUTEX_INITIALIZER; |
-/* do we have a descriptors file? */ |
-static int sysfs_has_descriptors = 0; |
+static int linux_start_event_monitor(void); |
+static int linux_stop_event_monitor(void); |
+static int linux_scan_devices(struct libusb_context *ctx); |
+static int sysfs_scan_device(struct libusb_context *ctx, const char *devname); |
+static int detach_kernel_driver_and_claim(struct libusb_device_handle *, int); |
+ |
+#if !defined(USE_UDEV) |
+static int linux_default_scan_devices (struct libusb_context *ctx); |
+#endif |
struct linux_device_priv { |
char *sysfs_dir; |
- unsigned char *dev_descriptor; |
- unsigned char *config_descriptor; |
+ unsigned char *descriptors; |
+ int descriptors_len; |
+ int active_config; /* cache val for !sysfs_can_relate_devices */ |
}; |
struct linux_device_handle_priv { |
int fd; |
+ uint32_t caps; |
}; |
enum reap_action { |
@@ -142,21 +169,43 @@ struct linux_transfer_priv { |
enum reap_action reap_action; |
int num_urbs; |
- unsigned int num_retired; |
+ int num_retired; |
enum libusb_transfer_status reap_status; |
/* next iso packet in user-supplied transfer to be populated */ |
int iso_packet_offset; |
}; |
-static void _get_usbfs_path(struct libusb_device *dev, char *path) |
+static int _get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent) |
{ |
+ struct libusb_context *ctx = DEVICE_CTX(dev); |
+ char path[PATH_MAX]; |
+ int fd; |
+ |
if (usbdev_names) |
snprintf(path, PATH_MAX, "%s/usbdev%d.%d", |
usbfs_path, dev->bus_number, dev->device_address); |
else |
snprintf(path, PATH_MAX, "%s/%03d/%03d", |
usbfs_path, dev->bus_number, dev->device_address); |
+ |
+ fd = open(path, mode); |
+ if (fd != -1) |
+ return fd; /* Success */ |
+ |
+ if (!silent) { |
+ usbi_err(ctx, "libusbx couldn't open USB device %s: %s", |
+ path, strerror(errno)); |
+ if (errno == EACCES && mode == O_RDWR) |
+ usbi_err(ctx, "libusbx requires write access to USB " |
+ "device nodes."); |
+ } |
+ |
+ if (errno == EACCES) |
+ return LIBUSB_ERROR_ACCESS; |
+ if (errno == ENOENT) |
+ return LIBUSB_ERROR_NO_DEVICE; |
+ return LIBUSB_ERROR_IO; |
} |
static struct linux_device_priv *_device_priv(struct libusb_device *dev) |
@@ -299,22 +348,6 @@ static int kernel_version_ge(int major, int minor, int sublevel) |
return ksublevel >= sublevel; |
} |
-/* Return 1 if filename exists inside dirname in sysfs. |
- SYSFS_DEVICE_PATH is assumed to be the beginning of the path. */ |
-static int sysfs_has_file(const char *dirname, const char *filename) |
-{ |
- struct stat statbuf; |
- char path[PATH_MAX]; |
- int r; |
- |
- snprintf(path, PATH_MAX, "%s/%s/%s", SYSFS_DEVICE_PATH, dirname, filename); |
- r = stat(path, &statbuf); |
- if (r == 0 && S_ISREG(statbuf.st_mode)) |
- return 1; |
- |
- return 0; |
-} |
- |
static int op_init(struct libusb_context *ctx) |
{ |
struct stat statbuf; |
@@ -353,79 +386,103 @@ static int op_init(struct libusb_context *ctx) |
if (supports_flag_zero_packet) |
usbi_dbg("zero length packet flag supported"); |
- r = stat(SYSFS_DEVICE_PATH, &statbuf); |
- if (r == 0 && S_ISDIR(statbuf.st_mode)) { |
- DIR *devices = opendir(SYSFS_DEVICE_PATH); |
- struct dirent *entry; |
- |
- usbi_dbg("found usb devices in sysfs"); |
+ if (-1 == sysfs_has_descriptors) { |
+ /* sysfs descriptors has all descriptors since Linux 2.6.26 */ |
+ sysfs_has_descriptors = kernel_version_ge(2,6,26); |
+ if (-1 == sysfs_has_descriptors) { |
+ usbi_err(ctx, "error checking for sysfs descriptors"); |
+ return LIBUSB_ERROR_OTHER; |
+ } |
+ } |
- if (!devices) { |
- usbi_err(ctx, "opendir devices failed errno=%d", errno); |
- return LIBUSB_ERROR_IO; |
+ if (-1 == sysfs_can_relate_devices) { |
+ /* sysfs has busnum since Linux 2.6.22 */ |
+ sysfs_can_relate_devices = kernel_version_ge(2,6,22); |
+ if (-1 == sysfs_can_relate_devices) { |
+ usbi_err(ctx, "error checking for sysfs busnum"); |
+ return LIBUSB_ERROR_OTHER; |
} |
+ } |
- /* Make sure sysfs supports all the required files. If it |
- * does not, then usbfs will be used instead. Determine |
- * this by looping through the directories in |
- * SYSFS_DEVICE_PATH. With the assumption that there will |
- * always be subdirectories of the name usbN (usb1, usb2, |
- * etc) representing the root hubs, check the usbN |
- * subdirectories to see if they have all the needed files. |
- * This algorithm uses the usbN subdirectories (root hubs) |
- * because a device disconnection will cause a race |
- * condition regarding which files are available, sometimes |
- * causing an incorrect result. The root hubs are used |
- * because it is assumed that they will always be present. |
- * See the "sysfs vs usbfs" comment at the top of this file |
- * for more details. */ |
- while ((entry = readdir(devices))) { |
- int has_busnum=0, has_devnum=0, has_descriptors=0; |
- int has_configuration_value=0; |
- |
- /* Only check the usbN directories. */ |
- if (strncmp(entry->d_name, "usb", 3) != 0) |
- continue; |
+ if (sysfs_can_relate_devices || sysfs_has_descriptors) { |
+ r = stat(SYSFS_DEVICE_PATH, &statbuf); |
+ if (r != 0 || !S_ISDIR(statbuf.st_mode)) { |
+ usbi_warn(ctx, "sysfs not mounted"); |
+ sysfs_can_relate_devices = 0; |
+ sysfs_has_descriptors = 0; |
+ } |
+ } |
- /* Check for the files libusb needs from sysfs. */ |
- has_busnum = sysfs_has_file(entry->d_name, "busnum"); |
- has_devnum = sysfs_has_file(entry->d_name, "devnum"); |
- has_descriptors = sysfs_has_file(entry->d_name, "descriptors"); |
- has_configuration_value = sysfs_has_file(entry->d_name, "bConfigurationValue"); |
+ if (sysfs_can_relate_devices) |
+ usbi_dbg("sysfs can relate devices"); |
- if (has_busnum && has_devnum && has_configuration_value) |
- sysfs_can_relate_devices = 1; |
- if (has_descriptors) |
- sysfs_has_descriptors = 1; |
+ if (sysfs_has_descriptors) |
+ usbi_dbg("sysfs has complete descriptors"); |
+ |
+ usbi_mutex_static_lock(&linux_hotplug_lock); |
+ r = LIBUSB_SUCCESS; |
+ if (init_count == 0) { |
+ /* start up hotplug event handler */ |
+ r = linux_start_event_monitor(); |
+ } |
+ if (r == LIBUSB_SUCCESS) { |
+ r = linux_scan_devices(ctx); |
+ if (r == LIBUSB_SUCCESS) |
+ init_count++; |
+ else if (init_count == 0) |
+ linux_stop_event_monitor(); |
+ } else |
+ usbi_err(ctx, "error starting hotplug event monitor"); |
+ usbi_mutex_static_unlock(&linux_hotplug_lock); |
- /* Only need to check until we've found ONE device which |
- has all the attributes. */ |
- if (sysfs_has_descriptors && sysfs_can_relate_devices) |
- break; |
- } |
- closedir(devices); |
+ return r; |
+} |
- /* Only use sysfs descriptors if the rest of |
- sysfs will work for libusb. */ |
- if (!sysfs_can_relate_devices) |
- sysfs_has_descriptors = 0; |
- } else { |
- usbi_dbg("sysfs usb info not available"); |
- sysfs_has_descriptors = 0; |
- sysfs_can_relate_devices = 0; |
+static void op_exit(void) |
+{ |
+ usbi_mutex_static_lock(&linux_hotplug_lock); |
+ assert(init_count != 0); |
+ if (!--init_count) { |
+ /* tear down event handler */ |
+ (void)linux_stop_event_monitor(); |
} |
+ usbi_mutex_static_unlock(&linux_hotplug_lock); |
+} |
- return 0; |
+static int linux_start_event_monitor(void) |
+{ |
+#if defined(USE_UDEV) |
+ return linux_udev_start_event_monitor(); |
+#else |
+ return linux_netlink_start_event_monitor(); |
+#endif |
} |
-static int usbfs_get_device_descriptor(struct libusb_device *dev, |
- unsigned char *buffer) |
+static int linux_stop_event_monitor(void) |
{ |
- struct linux_device_priv *priv = _device_priv(dev); |
+#if defined(USE_UDEV) |
+ return linux_udev_stop_event_monitor(); |
+#else |
+ return linux_netlink_stop_event_monitor(); |
+#endif |
+} |
- /* return cached copy */ |
- memcpy(buffer, priv->dev_descriptor, DEVICE_DESC_LENGTH); |
- return 0; |
+static int linux_scan_devices(struct libusb_context *ctx) |
+{ |
+#if defined(USE_UDEV) |
+ return linux_udev_scan_devices(ctx); |
+#else |
+ return linux_default_scan_devices(ctx); |
+#endif |
+} |
+ |
+static void op_hotplug_poll(void) |
+{ |
+#if defined(USE_UDEV) |
+ linux_udev_hotplug_poll(); |
+#else |
+ linux_netlink_hotplug_poll(); |
+#endif |
} |
static int _open_sysfs_attr(struct libusb_device *dev, const char *attr) |
@@ -481,52 +538,14 @@ static int __read_sysfs_attr(struct libusb_context *ctx, |
return value; |
} |
-static int sysfs_get_device_descriptor(struct libusb_device *dev, |
- unsigned char *buffer) |
-{ |
- int fd; |
- ssize_t r; |
- |
- /* 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; |
- } |
- |
- return 0; |
-} |
- |
static int op_get_device_descriptor(struct libusb_device *dev, |
unsigned char *buffer, int *host_endian) |
{ |
- if (sysfs_has_descriptors) { |
- return sysfs_get_device_descriptor(dev, buffer); |
- } else { |
- *host_endian = 1; |
- return usbfs_get_device_descriptor(dev, buffer); |
- } |
-} |
- |
-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); |
+ *host_endian = sysfs_has_descriptors ? 0 : 1; |
+ memcpy(buffer, priv->descriptors, DEVICE_DESC_LENGTH); |
+ |
return 0; |
} |
@@ -573,250 +592,213 @@ 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, |
- int host_endian) |
+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) |
{ |
- struct libusb_config_descriptor config; |
- unsigned char tmp[6]; |
- off_t off; |
- ssize_t r; |
+ usbi_dbg("getting address for device: %s detached: %d", sys_name, detached); |
+ /* can't use sysfs to read the bus and device number if the |
+ * device has been detached */ |
+ if (!sysfs_can_relate_devices || detached || NULL == sys_name) { |
+ if (NULL == dev_node) { |
+ return LIBUSB_ERROR_OTHER; |
+ } |
- /* 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; |
- } |
+ /* will this work with all supported kernel versions? */ |
+ if (!strncmp(dev_node, "/dev/bus/usb", 12)) { |
+ sscanf (dev_node, "/dev/bus/usb/%hhd/%hhd", busnum, devaddr); |
+ } else if (!strncmp(dev_node, "/proc/bus/usb", 13)) { |
+ sscanf (dev_node, "/proc/bus/usb/%hhd/%hhd", busnum, devaddr); |
+ } |
- /* seek forward to end of config */ |
- usbi_parse_descriptor(tmp, "bbwbb", &config, host_endian); |
- 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 LIBUSB_SUCCESS; |
} |
- return 0; |
+ usbi_dbg("scan %s", sys_name); |
+ |
+ *busnum = __read_sysfs_attr(ctx, sys_name, "busnum"); |
+ if (0 > *busnum) |
+ return *busnum; |
+ |
+ *devaddr = __read_sysfs_attr(ctx, sys_name, "devnum"); |
+ if (0 > *devaddr) |
+ return *devaddr; |
+ |
+ usbi_dbg("bus=%d dev=%d", *busnum, *devaddr); |
+ if (*busnum > 255 || *devaddr > 255) |
+ return LIBUSB_ERROR_INVALID_PARAM; |
+ |
+ return LIBUSB_SUCCESS; |
} |
-static int sysfs_get_active_config_descriptor(struct libusb_device *dev, |
- unsigned char *buffer, size_t len) |
+/* Return offset of the next descriptor with the given type */ |
+static int seek_to_next_descriptor(struct libusb_context *ctx, |
+ uint8_t descriptor_type, unsigned char *buffer, int size) |
{ |
- int fd; |
- ssize_t r; |
- off_t off; |
- int to_copy; |
- int config; |
- unsigned char tmp[6]; |
+ struct usb_descriptor_header header; |
+ int i; |
- r = sysfs_get_active_config(dev, &config); |
- if (r < 0) |
- return r; |
- if (config == -1) |
- return LIBUSB_ERROR_NOT_FOUND; |
+ for (i = 0; size >= 0; i += header.bLength, size -= header.bLength) { |
+ if (size == 0) |
+ return LIBUSB_ERROR_NOT_FOUND; |
- usbi_dbg("active configuration %d", config); |
+ if (size < 2) { |
+ usbi_err(ctx, "short descriptor read %d/2", size); |
+ return LIBUSB_ERROR_IO; |
+ } |
+ usbi_parse_descriptor(buffer + i, "bb", &header, 0); |
- /* sysfs provides access to an in-memory copy of the device descriptor, |
- * so we use that rather than keeping our own copy */ |
+ if (i && header.bDescriptorType == descriptor_type) |
+ return i; |
+ } |
+ usbi_err(ctx, "bLength overflow by %d bytes", -size); |
+ return LIBUSB_ERROR_IO; |
+} |
- fd = _open_sysfs_attr(dev, "descriptors"); |
- if (fd < 0) |
- return fd; |
+/* Return offset to next config */ |
+static int seek_to_next_config(struct libusb_context *ctx, |
+ unsigned char *buffer, int size) |
+{ |
+ struct libusb_config_descriptor config; |
- /* 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); |
+ if (size == 0) |
return LIBUSB_ERROR_NOT_FOUND; |
+ |
+ if (size < LIBUSB_DT_CONFIG_SIZE) { |
+ usbi_err(ctx, "short descriptor read %d/%d", |
+ size, LIBUSB_DT_CONFIG_SIZE); |
+ return LIBUSB_ERROR_IO; |
} |
- 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); |
+ usbi_parse_descriptor(buffer, "bbwbbbbb", &config, 0); |
+ if (config.bDescriptorType != LIBUSB_DT_CONFIG) { |
+ usbi_err(ctx, "descriptor is not a config desc (type 0x%02x)", |
+ config.bDescriptorType); |
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)); |
+ /* |
+ * In usbfs the config descriptors are config.wTotalLength bytes apart, |
+ * with any short reads from the device appearing as holes in the file. |
+ * |
+ * In sysfs wTotalLength is ignored, instead the kernel returns a |
+ * config descriptor with verified bLength fields, with descriptors |
+ * with an invalid bLength removed. |
+ */ |
+ if (sysfs_has_descriptors) { |
+ int next = seek_to_next_descriptor(ctx, LIBUSB_DT_CONFIG, |
+ buffer, size); |
+ if (next == LIBUSB_ERROR_NOT_FOUND) |
+ next = size; |
+ if (next < 0) |
+ return next; |
+ |
+ if (next != config.wTotalLength) |
+ usbi_warn(ctx, "config length mismatch wTotalLength " |
+ "%d real %d", config.wTotalLength, next); |
+ return next; |
+ } else { |
+ if (config.wTotalLength < LIBUSB_DT_CONFIG_SIZE) { |
+ usbi_err(ctx, "invalid wTotalLength %d", |
+ config.wTotalLength); |
return LIBUSB_ERROR_IO; |
- } |
+ } else if (config.wTotalLength > size) { |
+ usbi_warn(ctx, "short descriptor read %d/%d", |
+ size, config.wTotalLength); |
+ return size; |
+ } else |
+ return config.wTotalLength; |
+ } |
+} |
- /* check bConfigurationValue */ |
- if (tmp[5] == config) |
- break; |
+static int op_get_config_descriptor_by_value(struct libusb_device *dev, |
+ uint8_t value, unsigned char **buffer, int *host_endian) |
+{ |
+ 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; |
- /* try the next descriptor */ |
- off = lseek(fd, 0 - sizeof(tmp), SEEK_CUR); |
- if (off < 0) |
- return LIBUSB_ERROR_IO; |
+ *buffer = NULL; |
+ /* Unlike the device desc. config descs. are always in raw format */ |
+ *host_endian = 0; |
- r = seek_to_next_config(DEVICE_CTX(dev), fd, 0); |
- if (r < 0) |
- return r; |
- } |
+ /* Skip device header */ |
+ descriptors += DEVICE_DESC_LENGTH; |
+ size -= DEVICE_DESC_LENGTH; |
- 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; |
+ /* 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; |
} |
- } else { |
- r = 0; |
+ size -= next; |
+ descriptors += next; |
} |
- |
- close(fd); |
- return r; |
} |
static int op_get_active_config_descriptor(struct libusb_device *dev, |
unsigned char *buffer, size_t len, int *host_endian) |
{ |
- if (sysfs_has_descriptors) { |
- return sysfs_get_active_config_descriptor(dev, buffer, len); |
- } else { |
- return usbfs_get_active_config_descriptor(dev, buffer, len); |
- } |
-} |
- |
-/* 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; |
+ int r, config; |
+ unsigned char *config_desc; |
- off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET); |
- if (off < 0) { |
- usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno); |
- 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, 1); |
+ if (sysfs_can_relate_devices) { |
+ r = sysfs_get_active_config(dev, &config); |
if (r < 0) |
return r; |
- config_index--; |
+ } else { |
+ /* Use cached bConfigurationValue */ |
+ struct linux_device_priv *priv = _device_priv(dev); |
+ config = priv->active_config; |
} |
+ if (config == -1) |
+ return LIBUSB_ERROR_NOT_FOUND; |
- /* 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); |
- return LIBUSB_ERROR_IO; |
- } else if (r < len) { |
- usbi_err(ctx, "short output read %d/%d", r, len); |
- return LIBUSB_ERROR_IO; |
- } |
+ r = op_get_config_descriptor_by_value(dev, config, &config_desc, |
+ host_endian); |
+ if (r < 0) |
+ return r; |
- return 0; |
+ len = MIN(len, r); |
+ memcpy(buffer, config_desc, len); |
+ return len; |
} |
static int op_get_config_descriptor(struct libusb_device *dev, |
uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) |
{ |
- char filename[PATH_MAX]; |
- int fd; |
- int r; |
- |
- /* 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) { |
- usbi_err(DEVICE_CTX(dev), |
- "open '%s' failed, ret=%d errno=%d", filename, fd, errno); |
- return LIBUSB_ERROR_IO; |
- } |
+ struct linux_device_priv *priv = _device_priv(dev); |
+ unsigned char *descriptors = priv->descriptors; |
+ int i, r, size = priv->descriptors_len; |
- r = get_config_descriptor(DEVICE_CTX(dev), fd, config_index, buffer, len); |
- close(fd); |
- return r; |
-} |
+ /* Unlike the device desc. config descs. are always in raw format */ |
+ *host_endian = 0; |
-/* 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) |
-{ |
- struct linux_device_priv *priv = _device_priv(dev); |
- struct libusb_config_descriptor config; |
- unsigned char tmp[8]; |
- unsigned char *buf; |
- int idx; |
- int r; |
+ /* Skip device header */ |
+ descriptors += DEVICE_DESC_LENGTH; |
+ size -= DEVICE_DESC_LENGTH; |
- if (active_config == -1) { |
- idx = 0; |
- } else { |
- r = usbi_get_config_index_by_value(dev, active_config, &idx); |
+ /* 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 (idx == -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); |
- return r; |
- } |
- |
- usbi_parse_descriptor(tmp, "bbw", &config, 0); |
- buf = malloc(config.wTotalLength); |
- if (!buf) |
- return LIBUSB_ERROR_NO_MEM; |
- |
- r = get_config_descriptor(DEVICE_CTX(dev), fd, idx, buf, |
- config.wTotalLength); |
- if (r < 0) { |
- free(buf); |
- 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 */ |
@@ -853,11 +835,9 @@ 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; |
- char path[PATH_MAX]; |
+ struct libusb_context *ctx = DEVICE_CTX(dev); |
+ int descriptors_size = 512; /* Begin with a 1024 byte alloc */ |
int fd, speed; |
- int active_config = 0; |
- int device_configured = 1; |
ssize_t r; |
dev->bus_number = busnum; |
@@ -884,113 +864,162 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum, |
} |
} |
+ /* cache descriptors in memory */ |
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) */ |
+ fd = _open_sysfs_attr(dev, "descriptors"); |
+ else |
+ fd = _get_usbfs_fd(dev, O_RDONLY, 0); |
+ if (fd < 0) |
+ return fd; |
- priv->dev_descriptor = NULL; |
- priv->config_descriptor = NULL; |
+ do { |
+ descriptors_size *= 2; |
+ priv->descriptors = usbi_reallocf(priv->descriptors, |
+ descriptors_size); |
+ if (!priv->descriptors) { |
+ close(fd); |
+ return LIBUSB_ERROR_NO_MEM; |
+ } |
+ /* usbfs has holes in the file */ |
+ if (!sysfs_has_descriptors) { |
+ memset(priv->descriptors + priv->descriptors_len, |
+ 0, descriptors_size - priv->descriptors_len); |
+ } |
+ 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 (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 (priv->descriptors_len < DEVICE_DESC_LENGTH) { |
+ usbi_err(ctx, "short descriptor read (%d)", |
+ priv->descriptors_len); |
+ return LIBUSB_ERROR_IO; |
} |
- _get_usbfs_path(dev, path); |
- fd = open(path, O_RDWR); |
- if (fd < 0 && errno == EACCES) { |
- fd = open(path, O_RDONLY); |
- /* 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 (sysfs_can_relate_devices) |
+ return LIBUSB_SUCCESS; |
+ /* cache active config */ |
+ fd = _get_usbfs_fd(dev, O_RDWR, 1); |
if (fd < 0) { |
- usbi_err(DEVICE_CTX(dev), "open failed, ret=%d errno=%d", fd, errno); |
- 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 */ |
+ |
+ return LIBUSB_SUCCESS; |
+ } |
+ |
+ 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 */ |
- 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; |
- } |
+ close(fd); |
+ return r; |
+} |
+ |
+static int linux_get_parent_info(struct libusb_device *dev, const char *sysfs_dir) |
+{ |
+ struct libusb_context *ctx = DEVICE_CTX(dev); |
+ struct libusb_device *it; |
+ char *parent_sysfs_dir, *tmp; |
+ int ret, add_parent = 1; |
+ |
+ /* XXX -- can we figure out the topology when using usbfs? */ |
+ if (NULL == sysfs_dir || 0 == strncmp(sysfs_dir, "usb", 3)) { |
+ /* either using usbfs or finding the parent of a root hub */ |
+ return LIBUSB_SUCCESS; |
+ } |
+ |
+ parent_sysfs_dir = strdup(sysfs_dir); |
+ if (NULL != (tmp = strrchr(parent_sysfs_dir, '.')) || |
+ NULL != (tmp = strrchr(parent_sysfs_dir, '-'))) { |
+ dev->port_number = atoi(tmp + 1); |
+ *tmp = '\0'; |
+ } else { |
+ usbi_warn(ctx, "Can not parse sysfs_dir: %s, no parent info", |
+ parent_sysfs_dir); |
+ free (parent_sysfs_dir); |
+ return LIBUSB_SUCCESS; |
+ } |
+ |
+ /* is the parent a root hub? */ |
+ if (NULL == strchr(parent_sysfs_dir, '-')) { |
+ tmp = parent_sysfs_dir; |
+ ret = asprintf (&parent_sysfs_dir, "usb%s", tmp); |
+ free (tmp); |
+ if (0 > ret) { |
+ return LIBUSB_ERROR_NO_MEM; |
} |
} |
- dev_buf = malloc(DEVICE_DESC_LENGTH); |
- if (!dev_buf) { |
- close(fd); |
- return LIBUSB_ERROR_NO_MEM; |
+retry: |
+ /* find the parent in the context */ |
+ usbi_mutex_lock(&ctx->usb_devs_lock); |
+ list_for_each_entry(it, &ctx->usb_devs, list, struct libusb_device) { |
+ struct linux_device_priv *priv = _device_priv(it); |
+ if (0 == strcmp (priv->sysfs_dir, parent_sysfs_dir)) { |
+ dev->parent_dev = libusb_ref_device(it); |
+ break; |
+ } |
} |
+ usbi_mutex_unlock(&ctx->usb_devs_lock); |
- 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; |
+ if (!dev->parent_dev && add_parent) { |
+ usbi_dbg("parent_dev %s not enumerated yet, enumerating now", |
+ parent_sysfs_dir); |
+ sysfs_scan_device(ctx, parent_sysfs_dir); |
+ add_parent = 0; |
+ goto retry; |
} |
- /* 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]; |
+ usbi_dbg("Dev %p (%s) has parent %p (%s) port %d", dev, sysfs_dir, |
+ dev->parent_dev, parent_sysfs_dir, dev->port_number); |
- if (device_configured) { |
- r = cache_active_config(dev, fd, active_config); |
- if (r < 0) { |
- close(fd); |
- free(dev_buf); |
- return r; |
- } |
- } |
+ free (parent_sysfs_dir); |
- close(fd); |
- priv->dev_descriptor = dev_buf; |
- return 0; |
+ return LIBUSB_SUCCESS; |
} |
-static int enumerate_device(struct libusb_context *ctx, |
- struct discovered_devs **_discdevs, uint8_t busnum, uint8_t devaddr, |
- const char *sysfs_dir) |
+int linux_enumerate_device(struct libusb_context *ctx, |
+ uint8_t busnum, uint8_t devaddr, const char *sysfs_dir) |
{ |
- struct discovered_devs *discdevs; |
unsigned long session_id; |
- int need_unref = 0; |
struct libusb_device *dev; |
int r = 0; |
@@ -1001,48 +1030,73 @@ static int enumerate_device(struct libusb_context *ctx, |
usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr, |
session_id); |
- dev = usbi_get_device_by_session_id(ctx, session_id); |
- if (dev) { |
- usbi_dbg("using existing device for %d/%d (session %ld)", |
- busnum, devaddr, session_id); |
- } else { |
- usbi_dbg("allocating new device for %d/%d (session %ld)", |
- busnum, devaddr, session_id); |
- dev = usbi_alloc_device(ctx, session_id); |
- if (!dev) |
- return LIBUSB_ERROR_NO_MEM; |
- need_unref = 1; |
- r = initialize_device(dev, busnum, devaddr, sysfs_dir); |
- if (r < 0) |
- goto out; |
- r = usbi_sanitize_device(dev); |
- if (r < 0) |
- goto out; |
+ if (usbi_get_device_by_session_id(ctx, session_id)) { |
+ /* device already exists in the context */ |
+ usbi_dbg("session_id %ld already exists", session_id); |
+ return LIBUSB_SUCCESS; |
} |
- discdevs = discovered_devs_append(*_discdevs, dev); |
- if (!discdevs) |
- r = LIBUSB_ERROR_NO_MEM; |
- else |
- *_discdevs = discdevs; |
+ usbi_dbg("allocating new device for %d/%d (session %ld)", |
+ busnum, devaddr, session_id); |
+ dev = usbi_alloc_device(ctx, session_id); |
+ if (!dev) |
+ return LIBUSB_ERROR_NO_MEM; |
+ r = initialize_device(dev, busnum, devaddr, sysfs_dir); |
+ if (r < 0) |
+ goto out; |
+ r = usbi_sanitize_device(dev); |
+ if (r < 0) |
+ goto out; |
+ |
+ r = linux_get_parent_info(dev, sysfs_dir); |
+ if (r < 0) |
+ goto out; |
out: |
- if (need_unref) |
+ if (r < 0) |
libusb_unref_device(dev); |
+ else |
+ usbi_connect_device(dev); |
+ |
return r; |
} |
-/* open a bus directory and adds all discovered devices to discdevs. on |
- * failure (non-zero return) the pre-existing discdevs should be destroyed |
- * (and devices freed). on success, the new discdevs pointer should be used |
- * as it may have been moved. */ |
-static int usbfs_scan_busdir(struct libusb_context *ctx, |
- struct discovered_devs **_discdevs, uint8_t busnum) |
+void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_name) |
+{ |
+ struct libusb_context *ctx; |
+ |
+ usbi_mutex_static_lock(&active_contexts_lock); |
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { |
+ linux_enumerate_device(ctx, busnum, devaddr, sys_name); |
+ } |
+ usbi_mutex_static_unlock(&active_contexts_lock); |
+} |
+ |
+void linux_hotplug_disconnected(uint8_t busnum, uint8_t devaddr, const char *sys_name) |
+{ |
+ struct libusb_context *ctx; |
+ struct libusb_device *dev; |
+ unsigned long session_id = busnum << 8 | devaddr; |
+ |
+ usbi_mutex_static_lock(&active_contexts_lock); |
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { |
+ dev = usbi_get_device_by_session_id (ctx, session_id); |
+ if (NULL != dev) { |
+ usbi_disconnect_device (dev); |
+ } else { |
+ usbi_dbg("device not found for session %x", session_id); |
+ } |
+ } |
+ usbi_mutex_static_unlock(&active_contexts_lock); |
+} |
+ |
+#if !defined(USE_UDEV) |
+/* open a bus directory and adds all discovered devices to the context */ |
+static int usbfs_scan_busdir(struct libusb_context *ctx, uint8_t busnum) |
{ |
DIR *dir; |
char dirpath[PATH_MAX]; |
struct dirent *entry; |
- struct discovered_devs *discdevs = *_discdevs; |
int r = LIBUSB_ERROR_IO; |
snprintf(dirpath, PATH_MAX, "%s/%03d", usbfs_path, busnum); |
@@ -1067,7 +1121,7 @@ static int usbfs_scan_busdir(struct libusb_context *ctx, |
continue; |
} |
- if (enumerate_device(ctx, &discdevs, busnum, (uint8_t) devaddr, NULL)) { |
+ if (linux_enumerate_device(ctx, busnum, (uint8_t) devaddr, NULL)) { |
usbi_dbg("failed to enumerate dir entry %s", entry->d_name); |
continue; |
} |
@@ -1075,18 +1129,14 @@ static int usbfs_scan_busdir(struct libusb_context *ctx, |
r = 0; |
} |
- if (!r) |
- *_discdevs = discdevs; |
closedir(dir); |
return r; |
} |
-static int usbfs_get_device_list(struct libusb_context *ctx, |
- struct discovered_devs **_discdevs) |
+static int usbfs_get_device_list(struct libusb_context *ctx) |
{ |
struct dirent *entry; |
DIR *buses = opendir(usbfs_path); |
- struct discovered_devs *discdevs = *_discdevs; |
int r = 0; |
if (!buses) { |
@@ -1095,7 +1145,6 @@ static int usbfs_get_device_list(struct libusb_context *ctx, |
} |
while ((entry = readdir(buses))) { |
- struct discovered_devs *discdevs_new = discdevs; |
int busnum; |
if (entry->d_name[0] == '.') |
@@ -1106,8 +1155,7 @@ static int usbfs_get_device_list(struct libusb_context *ctx, |
if (!_is_usbdev_entry(entry, &busnum, &devaddr)) |
continue; |
- r = enumerate_device(ctx, &discdevs_new, busnum, |
- (uint8_t) devaddr, NULL); |
+ r = linux_enumerate_device(ctx, busnum, (uint8_t) devaddr, NULL); |
if (r < 0) { |
usbi_dbg("failed to enumerate dir entry %s", entry->d_name); |
continue; |
@@ -1119,48 +1167,35 @@ static int usbfs_get_device_list(struct libusb_context *ctx, |
continue; |
} |
- r = usbfs_scan_busdir(ctx, &discdevs_new, busnum); |
+ r = usbfs_scan_busdir(ctx, busnum); |
if (r < 0) |
- goto out; |
+ break; |
} |
- discdevs = discdevs_new; |
} |
-out: |
closedir(buses); |
- *_discdevs = discdevs; |
return r; |
} |
+#endif |
-static int sysfs_scan_device(struct libusb_context *ctx, |
- struct discovered_devs **_discdevs, const char *devname) |
+static int sysfs_scan_device(struct libusb_context *ctx, const char *devname) |
{ |
- int busnum; |
- int devaddr; |
- |
- usbi_dbg("scan %s", devname); |
+ uint8_t busnum, devaddr; |
+ int ret; |
- busnum = __read_sysfs_attr(ctx, devname, "busnum"); |
- if (busnum < 0) |
- return busnum; |
- |
- devaddr = __read_sysfs_attr(ctx, devname, "devnum"); |
- if (devaddr < 0) |
- return devaddr; |
- |
- usbi_dbg("bus=%d dev=%d", busnum, devaddr); |
- if (busnum > 255 || devaddr > 255) |
- return LIBUSB_ERROR_INVALID_PARAM; |
+ ret = linux_get_device_address (ctx, 0, &busnum, &devaddr, NULL, devname); |
+ if (LIBUSB_SUCCESS != ret) { |
+ return ret; |
+ } |
- return enumerate_device(ctx, _discdevs, busnum & 0xff, devaddr & 0xff, |
+ return linux_enumerate_device(ctx, busnum & 0xff, devaddr & 0xff, |
devname); |
} |
-static int sysfs_get_device_list(struct libusb_context *ctx, |
- struct discovered_devs **_discdevs) |
+#if !defined(USE_UDEV) |
+static int sysfs_get_device_list(struct libusb_context *ctx) |
{ |
- struct discovered_devs *discdevs = *_discdevs; |
DIR *devices = opendir(SYSFS_DEVICE_PATH); |
struct dirent *entry; |
int r = LIBUSB_ERROR_IO; |
@@ -1171,29 +1206,23 @@ static int sysfs_get_device_list(struct libusb_context *ctx, |
} |
while ((entry = readdir(devices))) { |
- struct discovered_devs *discdevs_new = discdevs; |
- |
if ((!isdigit(entry->d_name[0]) && strncmp(entry->d_name, "usb", 3)) |
|| strchr(entry->d_name, ':')) |
continue; |
- if (sysfs_scan_device(ctx, &discdevs_new, entry->d_name)) { |
+ if (sysfs_scan_device(ctx, entry->d_name)) { |
usbi_dbg("failed to enumerate dir entry %s", entry->d_name); |
continue; |
} |
r = 0; |
- discdevs = discdevs_new; |
} |
- if (!r) |
- *_discdevs = discdevs; |
closedir(devices); |
return r; |
} |
-static int op_get_device_list(struct libusb_context *ctx, |
- struct discovered_devs **_discdevs) |
+static int linux_default_scan_devices (struct libusb_context *ctx) |
{ |
/* we can retrieve device list and descriptors from sysfs or usbfs. |
* sysfs is preferable, because if we use usbfs we end up resuming |
@@ -1206,35 +1235,32 @@ static int op_get_device_list(struct libusb_context *ctx, |
* adequacy of sysfs and sets sysfs_can_relate_devices. |
*/ |
if (sysfs_can_relate_devices != 0) |
- return sysfs_get_device_list(ctx, _discdevs); |
+ return sysfs_get_device_list(ctx); |
else |
- return usbfs_get_device_list(ctx, _discdevs); |
+ return usbfs_get_device_list(ctx); |
} |
+#endif |
static int op_open(struct libusb_device_handle *handle) |
{ |
struct linux_device_handle_priv *hpriv = _device_handle_priv(handle); |
- char filename[PATH_MAX]; |
+ int r; |
- _get_usbfs_path(handle->dev, filename); |
- usbi_dbg("opening %s", filename); |
- hpriv->fd = open(filename, O_RDWR); |
- if (hpriv->fd < 0) { |
- if (errno == EACCES) { |
- usbi_err(HANDLE_CTX(handle), "libusb couldn't open USB device %s: " |
- "Permission denied.", filename); |
- usbi_err(HANDLE_CTX(handle), |
- "libusb requires write access to USB device nodes."); |
- return LIBUSB_ERROR_ACCESS; |
- } else if (errno == ENOENT) { |
- usbi_err(HANDLE_CTX(handle), "libusb couldn't open USB device %s: " |
- "No such file or directory.", filename); |
- return LIBUSB_ERROR_NO_DEVICE; |
- } else { |
- usbi_err(HANDLE_CTX(handle), |
- "open failed, code %d errno %d", hpriv->fd, errno); |
- return LIBUSB_ERROR_IO; |
- } |
+ hpriv->fd = _get_usbfs_fd(handle->dev, O_RDWR, 0); |
+ if (hpriv->fd < 0) |
+ return hpriv->fd; |
+ |
+ r = ioctl(hpriv->fd, IOCTL_USBFS_GET_CAPABILITIES, &hpriv->caps); |
+ if (r < 0) { |
+ if (errno == ENOTTY) |
+ usbi_dbg("getcap not available"); |
+ else |
+ usbi_err(HANDLE_CTX(handle), "getcap failed (%d)", errno); |
+ hpriv->caps = 0; |
+ if (supports_flag_zero_packet) |
+ hpriv->caps |= USBFS_CAP_ZERO_PACKET; |
+ if (supports_flag_bulk_continuation) |
+ hpriv->caps |= USBFS_CAP_BULK_CONTINUATION; |
} |
return usbi_add_pollfd(HANDLE_CTX(handle), hpriv->fd, POLLOUT); |
@@ -1251,10 +1277,13 @@ static int op_get_configuration(struct libusb_device_handle *handle, |
int *config) |
{ |
int r; |
- if (sysfs_can_relate_devices != 1) |
- return LIBUSB_ERROR_NOT_SUPPORTED; |
- r = sysfs_get_active_config(handle->dev, config); |
+ if (sysfs_can_relate_devices) { |
+ r = sysfs_get_active_config(handle->dev, config); |
+ } else { |
+ r = usbfs_get_active_config(handle->dev, |
+ _device_handle_priv(handle)->fd); |
+ } |
if (r < 0) |
return r; |
@@ -1283,25 +1312,13 @@ 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) |
+static int claim_interface(struct libusb_device_handle *handle, int iface) |
{ |
int fd = _device_handle_priv(handle)->fd; |
int r = ioctl(fd, IOCTL_USBFS_CLAIMINTF, &iface); |
@@ -1320,7 +1337,7 @@ static int op_claim_interface(struct libusb_device_handle *handle, int iface) |
return 0; |
} |
-static int op_release_interface(struct libusb_device_handle *handle, int iface) |
+static int release_interface(struct libusb_device_handle *handle, int iface) |
{ |
int fd = _device_handle_priv(handle)->fd; |
int r = ioctl(fd, IOCTL_USBFS_RELEASEINTF, &iface); |
@@ -1391,7 +1408,7 @@ static int op_reset_device(struct libusb_device_handle *handle) |
getting bound to the in kernel driver if any). */ |
for (i = 0; i < USB_MAXINTERFACES; i++) { |
if (handle->claimed_interfaces & (1L << i)) { |
- op_release_interface(handle, i); |
+ release_interface(handle, i); |
} |
} |
@@ -1412,11 +1429,18 @@ static int op_reset_device(struct libusb_device_handle *handle) |
/* And re-claim any interfaces which were claimed before the reset */ |
for (i = 0; i < USB_MAXINTERFACES; i++) { |
if (handle->claimed_interfaces & (1L << i)) { |
- r = op_claim_interface(handle, i); |
+ /* |
+ * A driver may have completed modprobing during |
+ * IOCTL_USBFS_RESET, and bound itself as soon as |
+ * IOCTL_USBFS_RESET released the device lock |
+ */ |
+ r = detach_kernel_driver_and_claim(handle, i); |
if (r) { |
usbi_warn(HANDLE_CTX(handle), |
- "failed to re-claim interface %d after reset", i); |
+ "failed to re-claim interface %d after reset: %s", |
+ i, libusb_error_name(r)); |
handle->claimed_interfaces &= ~(1L << i); |
+ ret = LIBUSB_ERROR_NOT_FOUND; |
} |
} |
} |
@@ -1445,7 +1469,7 @@ static int op_kernel_driver_active(struct libusb_device_handle *handle, |
return LIBUSB_ERROR_OTHER; |
} |
- return 1; |
+ return (strcmp(getdrv.driver, "usbfs") == 0) ? 0 : 1; |
} |
static int op_detach_kernel_driver(struct libusb_device_handle *handle, |
@@ -1453,12 +1477,18 @@ static int op_detach_kernel_driver(struct libusb_device_handle *handle, |
{ |
int fd = _device_handle_priv(handle)->fd; |
struct usbfs_ioctl command; |
+ struct usbfs_getdriver getdrv; |
int r; |
command.ifno = interface; |
command.ioctl_code = IOCTL_USBFS_DISCONNECT; |
command.data = NULL; |
+ getdrv.interface = interface; |
+ r = ioctl(fd, IOCTL_USBFS_GETDRIVER, &getdrv); |
+ if (r == 0 && strcmp(getdrv.driver, "usbfs") == 0) |
+ return LIBUSB_ERROR_NOT_FOUND; |
+ |
r = ioctl(fd, IOCTL_USBFS_IOCTL, &command); |
if (r) { |
if (errno == ENODATA) |
@@ -1508,15 +1538,69 @@ static int op_attach_kernel_driver(struct libusb_device_handle *handle, |
return 0; |
} |
+static int detach_kernel_driver_and_claim(struct libusb_device_handle *handle, |
+ int interface) |
+{ |
+ struct usbfs_disconnect_claim dc; |
+ int r, fd = _device_handle_priv(handle)->fd; |
+ |
+ dc.interface = interface; |
+ strcpy(dc.driver, "usbfs"); |
+ dc.flags = USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER; |
+ r = ioctl(fd, IOCTL_USBFS_DISCONNECT_CLAIM, &dc); |
+ if (r == 0 || (r != 0 && errno != ENOTTY)) { |
+ if (r == 0) |
+ return 0; |
+ |
+ switch (errno) { |
+ case EBUSY: |
+ return LIBUSB_ERROR_BUSY; |
+ case EINVAL: |
+ return LIBUSB_ERROR_INVALID_PARAM; |
+ case ENODEV: |
+ return LIBUSB_ERROR_NO_DEVICE; |
+ } |
+ usbi_err(HANDLE_CTX(handle), |
+ "disconnect-and-claim failed errno %d", errno); |
+ return LIBUSB_ERROR_OTHER; |
+ } |
+ |
+ /* Fallback code for kernels which don't support the |
+ disconnect-and-claim ioctl */ |
+ r = op_detach_kernel_driver(handle, interface); |
+ if (r != 0 && r != LIBUSB_ERROR_NOT_FOUND) |
+ return r; |
+ |
+ return claim_interface(handle, interface); |
+} |
+ |
+static int op_claim_interface(struct libusb_device_handle *handle, int iface) |
+{ |
+ if (handle->auto_detach_kernel_driver) |
+ return detach_kernel_driver_and_claim(handle, iface); |
+ else |
+ return claim_interface(handle, iface); |
+} |
+ |
+static int op_release_interface(struct libusb_device_handle *handle, int iface) |
+{ |
+ int r; |
+ |
+ r = release_interface(handle, iface); |
+ if (r) |
+ return r; |
+ |
+ if (handle->auto_detach_kernel_driver) |
+ op_attach_kernel_driver(handle, iface); |
+ |
+ return 0; |
+} |
+ |
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); |
} |
@@ -1583,6 +1667,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer, |
struct usbfs_urb *urbs; |
int is_out = (transfer->endpoint & LIBUSB_ENDPOINT_DIR_MASK) |
== LIBUSB_ENDPOINT_OUT; |
+ int bulk_buffer_len, use_bulk_continuation; |
int r; |
int i; |
size_t alloc_size; |
@@ -1590,30 +1675,67 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer, |
if (tpriv->urbs) |
return LIBUSB_ERROR_BUSY; |
- if (is_out && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET && |
- !supports_flag_zero_packet) |
+ if (is_out && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) && |
+ !(dpriv->caps & USBFS_CAP_ZERO_PACKET)) |
return LIBUSB_ERROR_NOT_SUPPORTED; |
- /* usbfs places a 16kb limit on bulk URBs. we divide up larger requests |
- * into smaller units to meet such restriction, then fire off all the |
- * units at once. it would be simpler if we just fired one unit at a time, |
- * but there is a big performance gain through doing it this way. */ |
- int num_urbs = transfer->length / MAX_BULK_BUFFER_LENGTH; |
+ /* |
+ * Older versions of usbfs place a 16kb limit on bulk URBs. We work |
+ * around this by splitting large transfers into 16k blocks, and then |
+ * submit all urbs at once. it would be simpler to submit one urb at |
+ * a time, but there is a big performance gain doing it this way. |
+ * |
+ * Newer versions lift the 16k limit (USBFS_CAP_NO_PACKET_SIZE_LIM), |
+ * using arbritary large transfers can still be a bad idea though, as |
+ * the kernel needs to allocate physical contiguous memory for this, |
+ * which may fail for large buffers. |
+ * |
+ * The kernel solves this problem by splitting the transfer into |
+ * blocks itself when the host-controller is scatter-gather capable |
+ * (USBFS_CAP_BULK_SCATTER_GATHER), which most controllers are. |
+ * |
+ * Last, there is the issue of short-transfers when splitting, for |
+ * short split-transfers to work reliable USBFS_CAP_BULK_CONTINUATION |
+ * is needed, but this is not always available. |
+ */ |
+ if (dpriv->caps & USBFS_CAP_BULK_SCATTER_GATHER) { |
+ /* Good! Just submit everything in one go */ |
+ bulk_buffer_len = transfer->length ? transfer->length : 1; |
+ use_bulk_continuation = 0; |
+ } else if (dpriv->caps & USBFS_CAP_BULK_CONTINUATION) { |
+ /* Split the transfers and use bulk-continuation to |
+ avoid issues with short-transfers */ |
+ bulk_buffer_len = MAX_BULK_BUFFER_LENGTH; |
+ use_bulk_continuation = 1; |
+ } else if (dpriv->caps & USBFS_CAP_NO_PACKET_SIZE_LIM) { |
+ /* Don't split, assume the kernel can alloc the buffer |
+ (otherwise the submit will fail with -ENOMEM) */ |
+ bulk_buffer_len = transfer->length ? transfer->length : 1; |
+ use_bulk_continuation = 0; |
+ } else { |
+ /* Bad, splitting without bulk-continuation, short transfers |
+ which end before the last urb will not work reliable! */ |
+ /* Note we don't warn here as this is "normal" on kernels < |
+ 2.6.32 and not a problem for most applications */ |
+ bulk_buffer_len = MAX_BULK_BUFFER_LENGTH; |
+ use_bulk_continuation = 0; |
+ } |
+ |
+ int num_urbs = transfer->length / bulk_buffer_len; |
int last_urb_partial = 0; |
if (transfer->length == 0) { |
num_urbs = 1; |
- } else if ((transfer->length % MAX_BULK_BUFFER_LENGTH) > 0) { |
+ } else if ((transfer->length % bulk_buffer_len) > 0) { |
last_urb_partial = 1; |
num_urbs++; |
} |
usbi_dbg("need %d urbs for new transfer with length %d", num_urbs, |
transfer->length); |
alloc_size = num_urbs * sizeof(struct usbfs_urb); |
- urbs = malloc(alloc_size); |
+ urbs = calloc(1, alloc_size); |
if (!urbs) |
return LIBUSB_ERROR_NO_MEM; |
- memset(urbs, 0, alloc_size); |
tpriv->urbs = urbs; |
tpriv->num_urbs = num_urbs; |
tpriv->num_retired = 0; |
@@ -1625,17 +1747,18 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer, |
urb->usercontext = itransfer; |
urb->type = urb_type; |
urb->endpoint = transfer->endpoint; |
- urb->buffer = transfer->buffer + (i * MAX_BULK_BUFFER_LENGTH); |
- if (supports_flag_bulk_continuation && !is_out) |
+ urb->buffer = transfer->buffer + (i * bulk_buffer_len); |
+ /* don't set the short not ok flag for the last URB */ |
+ if (use_bulk_continuation && !is_out && (i < num_urbs - 1)) |
urb->flags = USBFS_URB_SHORT_NOT_OK; |
if (i == num_urbs - 1 && last_urb_partial) |
- urb->buffer_length = transfer->length % MAX_BULK_BUFFER_LENGTH; |
+ urb->buffer_length = transfer->length % bulk_buffer_len; |
else if (transfer->length == 0) |
urb->buffer_length = 0; |
else |
- urb->buffer_length = MAX_BULK_BUFFER_LENGTH; |
+ urb->buffer_length = bulk_buffer_len; |
- if (i > 0 && supports_flag_bulk_continuation) |
+ if (i > 0 && use_bulk_continuation) |
urb->flags |= USBFS_URB_BULK_CONTINUATION; |
/* we have already checked that the flag is supported */ |
@@ -1667,7 +1790,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer, |
* complications: |
* - discarding is asynchronous - discarded urbs will be reaped |
* later. the user must not have freed the transfer when the |
- * discarded URBs are reaped, otherwise libusb will be using |
+ * discarded URBs are reaped, otherwise libusbx will be using |
* freed memory. |
* - the earlier URBs may have completed successfully and we do |
* not want to throw away any data. |
@@ -1722,11 +1845,17 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) |
/* usbfs places a 32kb limit on iso URBs. we divide up larger requests |
* into smaller units to meet such restriction, then fire off all the |
* units at once. it would be simpler if we just fired one unit at a time, |
- * but there is a big performance gain through doing it this way. */ |
+ * but there is a big performance gain through doing it this way. |
+ * |
+ * Newer kernels lift the 32k limit (USBFS_CAP_NO_PACKET_SIZE_LIM), |
+ * using arbritary large transfers is still be a bad idea though, as |
+ * the kernel needs to allocate physical contiguous memory for this, |
+ * which may fail for large buffers. |
+ */ |
/* calculate how many URBs we need */ |
for (i = 0; i < num_packets; i++) { |
- int space_remaining = MAX_ISO_BUFFER_LENGTH - this_urb_len; |
+ unsigned int space_remaining = MAX_ISO_BUFFER_LENGTH - this_urb_len; |
packet_len = transfer->iso_packet_desc[i].length; |
if (packet_len > space_remaining) { |
@@ -1739,10 +1868,9 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) |
usbi_dbg("need %d 32k URBs for transfer", num_urbs); |
alloc_size = num_urbs * sizeof(*urbs); |
- urbs = malloc(alloc_size); |
+ urbs = calloc(1, alloc_size); |
if (!urbs) |
return LIBUSB_ERROR_NO_MEM; |
- memset(urbs, 0, alloc_size); |
tpriv->iso_urbs = urbs; |
tpriv->num_urbs = num_urbs; |
@@ -1753,7 +1881,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) |
/* allocate + initialize each URB with the correct number of packets */ |
for (i = 0; i < num_urbs; i++) { |
struct usbfs_urb *urb; |
- int space_remaining_in_urb = MAX_ISO_BUFFER_LENGTH; |
+ unsigned int space_remaining_in_urb = MAX_ISO_BUFFER_LENGTH; |
int urb_packet_offset = 0; |
unsigned char *urb_buffer_orig = urb_buffer; |
int j; |
@@ -1776,12 +1904,11 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) |
alloc_size = sizeof(*urb) |
+ (urb_packet_offset * sizeof(struct usbfs_iso_packet_desc)); |
- urb = malloc(alloc_size); |
+ urb = calloc(1, alloc_size); |
if (!urb) { |
free_iso_urbs(tpriv); |
return LIBUSB_ERROR_NO_MEM; |
} |
- memset(urb, 0, alloc_size); |
urbs[i] = urb; |
/* populate packet lengths */ |
@@ -1825,7 +1952,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) |
* complications: |
* - discarding is asynchronous - discarded urbs will be reaped |
* later. the user must not have freed the transfer when the |
- * discarded URBs are reaped, otherwise libusb will be using |
+ * discarded URBs are reaped, otherwise libusbx will be using |
* freed memory. |
* - the earlier URBs may have completed successfully and we do |
* not want to throw away any data. |
@@ -1865,10 +1992,9 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) |
if (transfer->length - LIBUSB_CONTROL_SETUP_SIZE > MAX_CTRL_BUFFER_LENGTH) |
return LIBUSB_ERROR_INVALID_PARAM; |
- urb = malloc(sizeof(struct usbfs_urb)); |
+ urb = calloc(1, sizeof(struct usbfs_urb)); |
if (!urb) |
return LIBUSB_ERROR_NO_MEM; |
- memset(urb, 0, sizeof(struct usbfs_urb)); |
tpriv->urbs = urb; |
tpriv->num_urbs = 1; |
tpriv->reap_action = NORMAL; |
@@ -2001,7 +2127,7 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer, |
* |
* When this happens, our objectives are not to lose any "surplus" data, |
* and also to stick it at the end of the previously-received data |
- * (closing any holes), so that libusb reports the total amount of |
+ * (closing any holes), so that libusbx reports the total amount of |
* transferred data and presents it in a contiguous chunk. |
*/ |
if (urb->actual_length > 0) { |
@@ -2335,7 +2461,7 @@ static int op_handle_events(struct libusb_context *ctx, |
struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) |
{ |
int r; |
- int i = 0; |
+ unsigned int i = 0; |
usbi_mutex_lock(&ctx->open_devs_lock); |
for (i = 0; i < nfds && num_ready > 0; i++) { |
@@ -2359,7 +2485,9 @@ static int op_handle_events(struct libusb_context *ctx, |
continue; |
} |
- r = reap_for_handle(handle); |
+ do { |
+ r = reap_for_handle(handle); |
+ } while (r == 0); |
if (r == 1 || r == LIBUSB_ERROR_NO_DEVICE) |
continue; |
else if (r < 0) |
@@ -2394,12 +2522,15 @@ static clockid_t op_get_timerfd_clockid(void) |
const struct usbi_os_backend linux_usbfs_backend = { |
.name = "Linux usbfs", |
+ .caps = USBI_CAP_HAS_HID_ACCESS|USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER, |
.init = op_init, |
- .exit = NULL, |
- .get_device_list = op_get_device_list, |
+ .exit = op_exit, |
+ .get_device_list = NULL, |
+ .hotplug_poll = op_hotplug_poll, |
.get_device_descriptor = op_get_device_descriptor, |
.get_active_config_descriptor = op_get_active_config_descriptor, |
.get_config_descriptor = op_get_config_descriptor, |
+ .get_config_descriptor_by_value = op_get_config_descriptor_by_value, |
.open = op_open, |
.close = op_close, |
@@ -2435,4 +2566,3 @@ const struct usbi_os_backend linux_usbfs_backend = { |
.transfer_priv_size = sizeof(struct linux_transfer_priv), |
.add_iso_packet_size = 0, |
}; |
- |