summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2011-07-06 22:34:48 +0100
committerAlan Stern <stern@rowland.harvard.edu>2011-07-06 22:34:48 +0100
commitcf0c8422147e6fb3031a112e19b58a9f6d3af451 (patch)
tree8212f6f287becca83521af85837a764c726f7186
parentb5c9800bbd4e3a5f31f6f314bcee2c178f8f0b47 (diff)
downloadlibusb-cf0c8422147e6fb3031a112e19b58a9f6d3af451.tar.gz
[linux] add topology-analysis code to Linux backend
* Determines the port_number and parent_dev fields for each device found by sysfs_get_device_list().
-rw-r--r--libusb/os/linux_usbfs.c69
1 files changed, 69 insertions, 0 deletions
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 72db57a..78d33ad 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -1003,6 +1003,74 @@ static int sysfs_scan_device(struct libusb_context *ctx,
devname);
}
+static void sysfs_analyze_topology(struct discovered_devs *discdevs)
+{
+ struct linux_device_priv *priv;
+ int i, j;
+ struct libusb_device *dev1, *dev2;
+ const char *sysfs_dir1, *sysfs_dir2;
+ const char *p;
+ int n, boundary_char;
+
+ /* Fill in the port_number and parent_dev fields for each device */
+
+ for (i = 0; i < discdevs->len; ++i) {
+ dev1 = discdevs->devices[i];
+ priv = __device_priv(dev1);
+ if (!priv)
+ continue;
+ sysfs_dir1 = priv->sysfs_dir;
+
+ /* Root hubs have sysfs_dir names of the form "usbB",
+ * where B is the bus number. All other devices have
+ * sysfs_dir names of the form "B-P[.P ...]", where the
+ * P values are port numbers leading from the root hub
+ * to the device.
+ */
+
+ /* Root hubs don't have parents or port numbers */
+ if (sysfs_dir1[0] == 'u')
+ continue;
+
+ /* The rightmost component is the device's port number */
+ p = strrchr(sysfs_dir1, '.');
+ if (!p) {
+ p = strchr(sysfs_dir1, '-');
+ if (!p)
+ continue; /* Should never happen */
+ }
+ dev1->port_number = atoi(p + 1);
+
+ /* Search for the parent device */
+ boundary_char = *p;
+ n = p - sysfs_dir1;
+ for (j = 0; j < discdevs->len; ++j) {
+ dev2 = discdevs->devices[j];
+ priv = __device_priv(dev2);
+ if (!priv)
+ continue;
+ sysfs_dir2 = priv->sysfs_dir;
+
+ if (boundary_char == '-') {
+ /* The parent's name must begin with 'usb';
+ * skip past that part of sysfs_dir2.
+ */
+ if (sysfs_dir2[0] != 'u')
+ continue;
+ sysfs_dir2 += 3;
+ }
+
+ /* The remainder of the parent's name must be equal to
+ * the first n bytes of sysfs_dir1.
+ */
+ if (memcmp(sysfs_dir1, sysfs_dir2, n) == 0 && !sysfs_dir2[n]) {
+ dev1->parent_dev = dev2;
+ break;
+ }
+ }
+ }
+}
+
static int sysfs_get_device_list(struct libusb_context *ctx,
struct discovered_devs **_discdevs, int *usbfs_fallback)
{
@@ -1033,6 +1101,7 @@ static int sysfs_get_device_list(struct libusb_context *ctx,
out:
closedir(devices);
*_discdevs = discdevs;
+ sysfs_analyze_topology(discdevs);
return r;
}