From cfb8610242394d532778a483570089c2bed52c84 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Thu, 10 May 2012 20:01:10 +0100 Subject: [PATCH] All: Add parent and port topology calls * Adds libusb_get_port_number, libusb_get_parent and libusb_get_port_path * Linux implementation provided by Alan Stern, OS X by Nathan Hjelm * Unsupported for *BSD platforms --- examples/xusb.c | 4 --- libusb/core.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ libusb/libusb.h | 3 +++ libusb/libusbi.h | 2 ++ libusb/os/darwin_usb.c | 70 ++++++++++++++++++++++++++++++++++++++++++------- libusb/os/linux_usbfs.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++ libusb/os/windows_usb.c | 2 ++ libusb/version_nano.h | 2 +- 8 files changed, 201 insertions(+), 15 deletions(-) diff --git a/examples/xusb.c b/examples/xusb.c index 65dda7b..d775781 100644 --- a/examples/xusb.c +++ b/examples/xusb.c @@ -576,9 +576,7 @@ static int test_device(uint16_t vid, uint16_t pid) { libusb_device_handle *handle; libusb_device *dev; -#ifdef HAS_GETPORTPATH uint8_t bus, port_path[8]; -#endif struct libusb_config_descriptor *conf_desc; const struct libusb_endpoint_descriptor *endpoint; int i, j, k, r; @@ -603,7 +601,6 @@ static int test_device(uint16_t vid, uint16_t pid) } dev = libusb_get_device(handle); -#ifdef HAS_GETPORTPATH bus = libusb_get_bus_number(dev); r = libusb_get_port_path(NULL, dev, port_path, sizeof(port_path)); if (r > 0) { @@ -613,7 +610,6 @@ static int test_device(uint16_t vid, uint16_t pid) } printf("\n"); } -#endif r = libusb_get_device_speed(dev); if ((r<0) || (r>4)) r=0; printf("speed: %s\n", speed_name[r]); diff --git a/libusb/core.c b/libusb/core.c index 2c3de76..f70ee3c 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -660,6 +660,70 @@ uint8_t API_EXPORTED libusb_get_bus_number(libusb_device *dev) } /** \ingroup dev + * Get the number of the port that a device is connected to + * \param dev a device + * \returns the port number (0 if not available) + */ +uint8_t API_EXPORTED libusb_get_port_number(libusb_device *dev) +{ + return dev->port_number; +} + +/** \ingroup dev + * Get the list of all port numbers from root for the specified device + * \param dev a device + * \param path the array that should contain the port numbers + * \param path_len the maximum length of the array. As per the USB 3.0 + * specs, the current maximum limit for the depth is 7. + * \returns the number of elements filled + * \returns LIBUSB_ERROR_OVERFLOW if the array is too small + */ +int API_EXPORTED libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t* path, uint8_t path_len) +{ + int i = path_len; + ssize_t r; + struct libusb_device **devs; + + /* The device needs to be open, else the parents may have been destroyed */ + r = libusb_get_device_list(ctx, &devs); + if (r < 0) + return (int)r; + + while(dev) { + // HCDs can be listed as devices and would have port #0 + // TODO: see how the other backends want to implement HCDs as parents + if (dev->port_number == 0) + break; + i--; + if (i < 0) { + return LIBUSB_ERROR_OVERFLOW; + } + path[i] = dev->port_number; + dev = dev->parent_dev; + } + libusb_free_device_list(devs, 1); + memmove(path, &path[i], path_len-i); + return path_len-i; +} + +/** \ingroup dev + * Get the the parent from the specified device [EXPERIMENTAL] + * \param dev a device + * \returns the device parent or NULL if not available + * You should issue a libusb_get_device_list() before calling this + * function and make sure that you only access the parent before issuing + * libusb_free_device_list(). The reason is that libusbx currently does + * not maintain a permanent list of device instances, and therefore can + * only guarantee that parents are fully instantiated within a + * libusb_get_device_list() - libusb_free_device_list() block. + */ +DEFAULT_VISIBILITY +libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev) +{ + return dev->parent_dev; +} + +/** \ingroup dev * Get the address of the device on the bus it is connected to. * \param dev a device * \returns the device address diff --git a/libusb/libusb.h b/libusb/libusb.h index e9690fc..4887b80 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h @@ -976,6 +976,9 @@ int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, void LIBUSB_CALL libusb_free_config_descriptor( struct libusb_config_descriptor *config); uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev); +libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev); +int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t* path, uint8_t path_length); uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev); int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, diff --git a/libusb/libusbi.h b/libusb/libusbi.h index 68ccfe1..8623862 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -293,6 +293,8 @@ struct libusb_device { struct libusb_context *ctx; uint8_t bus_number; + uint8_t port_number; + struct libusb_device* parent_dev; uint8_t device_address; uint8_t num_configurations; enum libusb_speed speed; diff --git a/libusb/os/darwin_usb.c b/libusb/os/darwin_usb.c index 0f6babe..f64a1ec 100644 --- a/libusb/os/darwin_usb.c +++ b/libusb/os/darwin_usb.c @@ -185,11 +185,26 @@ static int usb_setup_device_iterator (io_iterator_t *deviceIterator, long locati return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator); } -static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 *locationp) { +static int get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) { + CFTypeRef cfNumber = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0); + int ret = 0; + + if (cfNumber) { + if (CFGetTypeID(cfNumber) == CFNumberGetTypeID()) { + ret = CFNumberGetValue(cfNumber, type, p); + } + + CFRelease (cfNumber); + } + + return ret; +} + +static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 *locationp, UInt8 *portp, UInt32 *parent_locationp) { io_cf_plugin_ref_t *plugInInterface = NULL; usb_device_t **device; - io_service_t usbDevice; - long result; + io_service_t usbDevice, parent; + kern_return_t result; SInt32 score; if (!IOIteratorIsValid (deviceIterator)) @@ -203,6 +218,22 @@ static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 /* we are done with the usb_device_t */ (void)IOObjectRelease(usbDevice); + + if (portp) { + *portp = 0; + (void) get_ioregistry_value_number (usbDevice, CFSTR("PortNum"), kCFNumberSInt8Type, portp); + } + + if (parent_locationp) { + *parent_locationp = 0; + + result = IORegistryEntryGetParentEntry (usbDevice, kIOUSBPlane, &parent); + + if (kIOReturnSuccess == result) { + (void) get_ioregistry_value_number (parent, CFSTR("locationID"), kCFNumberLongType, parent_locationp); + } + } + if (kIOReturnSuccess == result && plugInInterface) break; @@ -235,7 +266,7 @@ static kern_return_t darwin_get_device (uint32_t dev_location, usb_device_t ***d return kresult; /* This port of libusb uses locations to keep track of devices. */ - while ((*darwin_device = usb_get_next_device (deviceIterator, &location)) != NULL) { + while ((*darwin_device = usb_get_next_device (deviceIterator, &location, NULL, NULL)) != NULL) { if (location == dev_location) break; @@ -691,9 +722,11 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct li return 0; } -static int process_new_device (struct libusb_context *ctx, usb_device_t **device, UInt32 locationID, struct discovered_devs **_discdevs) { +static int process_new_device (struct libusb_context *ctx, usb_device_t **device, UInt32 locationID, + UInt32 parent_location, UInt8 port, struct discovered_devs **_discdevs, + struct libusb_device **last_dev) { struct darwin_device_priv *priv; - struct libusb_device *dev; + struct libusb_device *dev, *parent = NULL; struct discovered_devs *discdevs; UInt16 address; UInt8 devSpeed; @@ -726,6 +759,19 @@ static int process_new_device (struct libusb_context *ctx, usb_device_t **device if (ret < 0) break; + /* the device iterator provides devices in increasing order of location. given this property + * we can use the last device to find the parent. */ + for (parent = *last_dev ; parent ; parent = parent->parent_dev) { + struct darwin_device_priv *parent_priv = (struct darwin_device_priv *) parent->os_priv; + + if (parent_priv->location == parent_location) { + break; + } + } + + dev->parent_dev = parent; + + dev->port_number = port; dev->bus_number = locationID >> 24; dev->device_address = address; @@ -756,8 +802,10 @@ static int process_new_device (struct libusb_context *ctx, usb_device_t **device } *_discdevs = discdevs; + *last_dev = dev; - usbi_info (ctx, "found device with address %d at %s", dev->device_address, priv->sys_path); + usbi_info (ctx, "found device with address %d port = %d parent = %p at %p", dev->device_address, + dev->port_number, priv->sys_path, (void *) parent); } while (0); if (need_unref) @@ -770,14 +818,16 @@ static int darwin_get_device_list(struct libusb_context *ctx, struct discovered_ io_iterator_t deviceIterator; usb_device_t **device; kern_return_t kresult; - UInt32 location; + UInt32 location, parent_location; + UInt8 port; + struct libusb_device *last_dev = NULL; kresult = usb_setup_device_iterator (&deviceIterator, 0); if (kresult != kIOReturnSuccess) return darwin_to_libusb (kresult); - while ((device = usb_get_next_device (deviceIterator, &location)) != NULL) { - (void) process_new_device (ctx, device, location, _discdevs); + while ((device = usb_get_next_device (deviceIterator, &location, &port, &parent_location)) != NULL) { + (void) process_new_device (ctx, device, location, parent_location, port, _discdevs, &last_dev); (*(device))->Release(device); } diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index a843289..a01fff8 100644 --- a/libusb/os/linux_usbfs.c +++ b/libusb/os/linux_usbfs.c @@ -1157,6 +1157,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) { @@ -1189,6 +1257,7 @@ static int sysfs_get_device_list(struct libusb_context *ctx, if (!r) *_discdevs = discdevs; closedir(devices); + sysfs_analyze_topology(discdevs); return r; } diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c index 62e41e8..c4212c0 100644 --- a/libusb/os/windows_usb.c +++ b/libusb/os/windows_usb.c @@ -1002,8 +1002,10 @@ static int init_device(struct libusb_device* dev, struct libusb_device* parent_d } dev->bus_number = parent_dev->bus_number; priv->port = port_number; + dev->port_number = port_number; priv->depth = parent_priv->depth + 1; priv->parent_dev = parent_dev; + dev->parent_dev = parent_dev; // If the device address is already set, we can stop here if (dev->device_address != 0) { diff --git a/libusb/version_nano.h b/libusb/version_nano.h index f1695b2..77e865c 100644 --- a/libusb/version_nano.h +++ b/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 10507 +#define LIBUSB_NANO 10508 -- 2.11.4.GIT