All: Add parent and port topology calls
authorPete Batard <pete@akeo.ie>
Thu, 10 May 2012 19:01:10 +0000 (20:01 +0100)
committerPete Batard <pete@akeo.ie>
Mon, 28 May 2012 09:26:44 +0000 (10:26 +0100)
* 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
libusb/core.c
libusb/libusb.h
libusb/libusbi.h
libusb/os/darwin_usb.c
libusb/os/linux_usbfs.c
libusb/os/windows_usb.c
libusb/version_nano.h

index 65dda7b..d775781 100644 (file)
@@ -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]);
index 2c3de76..f70ee3c 100644 (file)
@@ -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
index e9690fc..4887b80 100644 (file)
@@ -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,
index 68ccfe1..8623862 100644 (file)
@@ -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;
index 0f6babe..f64a1ec 100644 (file)
@@ -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);
   }
index a843289..a01fff8 100644 (file)
@@ -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;
 }
 
index 62e41e8..c4212c0 100644 (file)
@@ -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) {
index f1695b2..77e865c 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 10507
+#define LIBUSB_NANO 10508