Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Unified Diff: third_party/libusb/src/libusb/os/linux_usbfs.c

Issue 19490008: Recommit: Update libusb 1.0.9 to libusbx 1.0.16 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/libusb/src/libusb/os/linux_usbfs.h ('k') | third_party/libusb/src/libusb/os/openbsd_usb.c » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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,
};
-
« no previous file with comments | « third_party/libusb/src/libusb/os/linux_usbfs.h ('k') | third_party/libusb/src/libusb/os/openbsd_usb.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698