summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPete Batard <pete@akeo.ie>2012-05-10 20:01:10 +0100
committerPete Batard <pete@akeo.ie>2012-05-28 10:26:44 +0100
commitcfb8610242394d532778a483570089c2bed52c84 (patch)
tree729c43ea4aecb8d4a4b39872820603df72026041
parenta544e5972bf2b0ac9e006439576f681a8439d311 (diff)
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
-rw-r--r--examples/xusb.c4
-rw-r--r--libusb/core.c64
-rw-r--r--libusb/libusb.h3
-rw-r--r--libusb/libusbi.h2
-rw-r--r--libusb/os/darwin_usb.c70
-rw-r--r--libusb/os/linux_usbfs.c69
-rw-r--r--libusb/os/windows_usb.c2
-rw-r--r--libusb/version_nano.h2
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