blob: eae63bda89ac1a5c9b9a35b913acbc7fa5bb369c [file] [log] [blame]
From 1988e0599135ffe641056085b3b20cc89d543dd6 Mon Sep 17 00:00:00 2001
From: Dmitry Torokhov <dtor@chromium.org>
Date: Fri, 7 Jun 2019 14:11:31 -0700
Subject: [PATCH 10/11] CHROMIUM: linux_usbfs: parse devpath in sysfs to get
port numbers
On linux, when sysfs is available, we can parse "devpath" attribute to
get port data. This is helpful when we do not have full USB device tree
present in libsysfs.
Signed-off-by: Dmitry Torokhov <dtor@chromium.org>
---
libusb/os/linux_usbfs.c | 72 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 71 insertions(+), 1 deletion(-)
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 3695023..72fa2ae 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -131,6 +131,11 @@ static int sysfs_can_relate_devices = -1;
* descriptors file, so from then on we can use them. */
static int sysfs_has_descriptors = -1;
+/* Linux v2.6.33 (commit 9af23624ae2c7978313b46e58fdc4ca5d8b799f5) adds
+ * devpath sysfs attribute containing list of ports on the way to the
+ * root port the device is connected to, so from then on we can use it. */
+static int sysfs_has_devpath = -1;
+
/* how many times have we initted (and not exited) ? */
static int init_count = 0;
@@ -484,12 +489,19 @@ static int op_init(struct libusb_context *ctx)
sysfs_can_relate_devices = kernel_version_ge(&kversion,2,6,22);
}
- if (sysfs_can_relate_devices || sysfs_has_descriptors) {
+ if (-1 == sysfs_has_devpath) {
+ /* sysfs has devpath since Linux 2.6.33 */
+ sysfs_has_devpath = kernel_version_ge(&kversion,2,6,33);
+ }
+
+ if (sysfs_can_relate_devices || sysfs_has_descriptors ||
+ sysfs_has_devpath) {
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;
+ sysfs_has_devpath = 0;
}
}
@@ -499,6 +511,9 @@ static int op_init(struct libusb_context *ctx)
if (sysfs_has_descriptors)
usbi_dbg("sysfs has complete descriptors");
+ if (sysfs_has_devpath)
+ usbi_dbg("sysfs has device path");
+
usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
r = LIBUSB_SUCCESS;
if (init_count == 0) {
@@ -1091,6 +1106,58 @@ static int device_speed_from_kernel(struct libusb_context *ctx, int speed)
}
}
+static int device_ports_from_sysfs(struct libusb_device *dev)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ char buf[LINE_MAX];
+ char *str, *rest, *port;
+ int fd;
+ FILE *f;
+ int count;
+
+ fd = _open_sysfs_attr(dev, "devpath");
+ if (fd < 0)
+ return fd;
+
+ f = fdopen(fd, "r");
+ if (f == NULL) {
+ usbi_err(ctx, "fdopen failed errno=%d", errno);
+ close(fd);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ str = fgets(buf, sizeof(buf), f);
+ fclose(f);
+
+ if (!str)
+ return LIBUSB_ERROR_IO;
+
+ /* Count number of "dots" delimiting port numbers. We do not care if
+ * we get malformed path with multiple consecutive dots, as it will
+ * simply result in allocating a few extra bytes for the ports
+ * array. */
+ str = buf;
+ count = 1; /* We should have at least one port */
+ while (*str != 0) {
+ if (*str == '.')
+ count++;
+ str++;
+ }
+
+ priv->ports = malloc(count);
+ if (!priv->ports)
+ return LIBUSB_ERROR_NO_MEM;
+
+ for (port = strtok_r(buf, ".\n", &rest);
+ port != NULL;
+ port = strtok_r(NULL, ".\n", &rest)) {
+ priv->ports[priv->num_ports++] = atoi(port);
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
static int device_cache_active_config(struct libusb_device *dev, int wrapped_fd)
{
struct linux_device_priv *priv = _device_priv(dev);
@@ -1198,6 +1265,9 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
if (ret != LIBUSB_SUCCESS)
return ret;
}
+
+ if (sysfs_has_devpath)
+ device_ports_from_sysfs(dev);
}
if (!priv->descriptors) {
--
2.22.0.rc2.383.gf4fbbf30c2-goog