diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2011-07-06 22:34:48 +0100 |
---|---|---|
committer | Alan Stern <stern@rowland.harvard.edu> | 2011-07-06 22:34:48 +0100 |
commit | cf0c8422147e6fb3031a112e19b58a9f6d3af451 (patch) | |
tree | 8212f6f287becca83521af85837a764c726f7186 | |
parent | b5c9800bbd4e3a5f31f6f314bcee2c178f8f0b47 (diff) | |
download | libusb-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.c | 69 |
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; } |