[libvirt] [PATCH] usb: allow host devices to be specified by port

Thomas Hebb thomas.hebb at editshare.com
Tue Jul 5 21:21:24 UTC 2016


Previously, only VID/PID and bus/device matching were supported. Neither
of these provide a stable and persistent way of assigning a guest a
specific host device out of several with the same VID and PID, as device
numbers change on every enumeration.

Add a third method of matching, bus/port, which allows a specific port on
the host to be specified using the dotted notation found in Linux's
"devpath" sysfs attribute.
---
 src/conf/domain_conf.c                             |  36 ++++-
 src/conf/domain_conf.h                             |   2 +
 src/libvirt_private.syms                           |   2 -
 src/util/virhostdev.c                              | 126 ++++++++--------
 src/util/virusb.c                                  | 160 ++++++++-------------
 src/util/virusb.h                                  |  23 ++-
 tests/virusbtest.c                                 | 124 ++++++++++------
 .../sys_bus_usb/devices/1-1.5.3.1/devpath          |   1 +
 .../sys_bus_usb/devices/1-1.5.3.3/devpath          |   1 +
 .../sys_bus_usb/devices/1-1.5.3/devpath            |   1 +
 .../sys_bus_usb/devices/1-1.5.4/devpath            |   1 +
 .../sys_bus_usb/devices/1-1.5.5/devpath            |   1 +
 .../sys_bus_usb/devices/1-1.5.6/devpath            |   1 +
 .../sys_bus_usb/devices/1-1.5/devpath              |   1 +
 .../sys_bus_usb/devices/1-1.6/devpath              |   1 +
 .../virusbtestdata/sys_bus_usb/devices/1-1/devpath |   1 +
 .../sys_bus_usb/devices/2-1.2/devpath              |   1 +
 .../virusbtestdata/sys_bus_usb/devices/2-1/devpath |   1 +
 .../sys_bus_usb/devices/usb1/devpath               |   1 +
 .../sys_bus_usb/devices/usb2/devpath               |   1 +
 .../sys_bus_usb/devices/usb3/devpath               |   1 +
 .../sys_bus_usb/devices/usb4/devpath               |   1 +
 22 files changed, 266 insertions(+), 222 deletions(-)
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.1/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.3/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.4/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.5/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.6/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.6/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/2-1.2/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/2-1/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/usb1/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/usb2/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/usb3/devpath
 create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/usb4/devpath

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 9f7b906..a587cc2 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -5531,7 +5531,8 @@ virDomainHostdevSubsysUSBDefParseXML(xmlNodePtr node,
                     goto out;
                 }
             } else if (xmlStrEqual(cur->name, BAD_CAST "address")) {
-                char *bus, *device;
+                char *bus, *device, *port;
+                bool dev_or_port = false;
 
                 bus = virXMLPropString(cur, "bus");
                 if (bus) {
@@ -5557,10 +5558,24 @@ virDomainHostdevSubsysUSBDefParseXML(xmlNodePtr node,
                         VIR_FREE(device);
                         goto out;
                     }
+                    dev_or_port = true;
                     VIR_FREE(device);
-                } else {
+                }
+
+                port = virXMLPropString(cur, "port");
+                if (port) {
+                    if (*port) {
+                        usbsrc->port = port;
+                        dev_or_port = true;
+                    } else {
+                        VIR_FREE(port);
+                    }
+                }
+
+                if (!dev_or_port) {
                     virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                                   _("usb address needs device id"));
+                                   _("usb address needs either device id "
+                                     "or port"));
                     goto out;
                 }
             } else {
@@ -20326,10 +20341,17 @@ virDomainHostdevDefFormatSubsys(virBufferPtr buf,
             virBufferAsprintf(buf, "<vendor id='0x%.4x'/>\n", usbsrc->vendor);
             virBufferAsprintf(buf, "<product id='0x%.4x'/>\n", usbsrc->product);
         }
-        if (usbsrc->bus || usbsrc->device) {
-            virBufferAsprintf(buf, "<address %sbus='%d' device='%d'/>\n",
-                              includeTypeInAddr ? "type='usb' " : "",
-                              usbsrc->bus, usbsrc->device);
+        if (usbsrc->bus || usbsrc->device || usbsrc->port) {
+            virBufferAddLit(buf, "<address");
+            if (includeTypeInAddr)
+                virBufferAddLit(buf, " type='usb'");
+            if (usbsrc->bus)
+                virBufferAsprintf(buf, " bus='%u'", usbsrc->bus);
+            if (usbsrc->device)
+                virBufferAsprintf(buf, " device='%u'", usbsrc->device);
+            if (usbsrc->port)
+                virBufferAsprintf(buf, " port='%s'", usbsrc->port);
+            virBufferAddLit(buf, "/>\n");
         }
         break;
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index ba0ad5f..91bd621 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -317,6 +317,8 @@ VIR_ENUM_DECL(virDomainHostdevSubsysSCSIProtocol)
 typedef struct _virDomainHostdevSubsysUSB virDomainHostdevSubsysUSB;
 typedef virDomainHostdevSubsysUSB *virDomainHostdevSubsysUSBPtr;
 struct _virDomainHostdevSubsysUSB {
+    char *port;
+
     bool autoAddress; /* bus/device were filled automatically based
                          on vendor/product */
     unsigned bus;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index ccb4c5e..d412eac 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2511,8 +2511,6 @@ virURIResolveAlias;
 # util/virusb.h
 virUSBDeviceFileIterate;
 virUSBDeviceFind;
-virUSBDeviceFindByBus;
-virUSBDeviceFindByVendor;
 virUSBDeviceFree;
 virUSBDeviceGetBus;
 virUSBDeviceGetDevno;
diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c
index 9b5ca6f..e809c97 100644
--- a/src/util/virhostdev.c
+++ b/src/util/virhostdev.c
@@ -1179,89 +1179,95 @@ virHostdevMarkUSBDevices(virHostdevManagerPtr mgr,
 
 
 static int
+virHostdevFindUSBDeviceWithFlags(virDomainHostdevDefPtr hostdev,
+                                 bool mandatory,
+                                 unsigned int flags,
+                                 virUSBDevicePtr *usb)
+{
+    virDomainHostdevSubsysUSBPtr usbsrc = &hostdev->source.subsys.u.usb;
+    unsigned vendor = usbsrc->vendor;
+    unsigned product = usbsrc->product;
+    unsigned bus = usbsrc->bus;
+    char *port = usbsrc->port;
+    unsigned device = usbsrc->device;
+    virUSBDeviceListPtr devs;
+    int rc;
+
+    rc = virUSBDeviceFind(vendor, product, bus, device, port, NULL,
+                          mandatory, flags, &devs);
+    if (rc < 0)
+        return -1;
+
+    if (rc == 1) {
+        *usb = virUSBDeviceListGet(devs, 0);
+        virUSBDeviceListSteal(devs, *usb);
+    }
+    virObjectUnref(devs);
+
+    if (rc > 1) {
+        virReportError(VIR_ERR_OPERATION_FAILED,
+                       _("Multiple USB devices for %x:%x, "
+                         "use <address> to specify one"),
+                       vendor, product);
+        return -1;
+    }
+
+    return rc;
+}
+
+static int
 virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev,
                         bool mandatory,
                         virUSBDevicePtr *usb)
 {
     virDomainHostdevSubsysUSBPtr usbsrc = &hostdev->source.subsys.u.usb;
     unsigned vendor = usbsrc->vendor;
-    unsigned product = usbsrc->product;
     unsigned bus = usbsrc->bus;
     unsigned device = usbsrc->device;
+    char *port = usbsrc->port;
     bool autoAddress = usbsrc->autoAddress;
+    unsigned int flags = 0;
     int rc;
 
     *usb = NULL;
 
-    if (vendor && bus) {
-        rc = virUSBDeviceFind(vendor, product, bus, device,
-                              NULL,
-                              autoAddress ? false : mandatory,
-                              usb);
-        if (rc < 0) {
-            return -1;
-        } else if (!autoAddress) {
-            goto out;
-        } else {
-            VIR_INFO("USB device %x:%x could not be found at previous"
-                     " address (bus:%u device:%u)",
-                     vendor, product, bus, device);
-        }
-    }
+    /* First attempt, matching on all known fields. */
+    if (vendor)
+        flags |= USB_DEVICE_FIND_BY_VENDOR;
+    if (device)
+        flags |= USB_DEVICE_FIND_BY_DEVICE;
+    if (port)
+        flags |= USB_DEVICE_FIND_BY_PORT;
+
+    rc = virHostdevFindUSBDeviceWithFlags(hostdev,
+                                          autoAddress ? false : mandatory,
+                                          flags, usb);
+    if (rc < 0)
+        return -1;
 
-    /* When vendor is specified, its USB address is either unspecified or the
-     * device could not be found at the USB device where it had been
-     * automatically found before.
-     */
-    if (vendor) {
-        virUSBDeviceListPtr devs;
+    if (rc != 1 && autoAddress) {
+        VIR_INFO("USB device could not be found at previous address "
+                 "(bus:%u device:%u)", bus, device);
 
-        rc = virUSBDeviceFindByVendor(vendor, product, NULL, mandatory, &devs);
-        if (rc < 0)
-            return -1;
+        /* Second attempt, for when the device number has changed. */
+        flags &= ~((unsigned int) USB_DEVICE_FIND_BY_DEVICE);
+        usbsrc->device = 0;
 
-        if (rc == 1) {
-            *usb = virUSBDeviceListGet(devs, 0);
-            virUSBDeviceListSteal(devs, *usb);
-        }
-        virObjectUnref(devs);
-
-        if (rc == 0) {
-            goto out;
-        } else if (rc > 1) {
-            if (autoAddress) {
-                virReportError(VIR_ERR_OPERATION_FAILED,
-                               _("Multiple USB devices for %x:%x were found,"
-                                 " but none of them is at bus:%u device:%u"),
-                               vendor, product, bus, device);
-            } else {
-                virReportError(VIR_ERR_OPERATION_FAILED,
-                               _("Multiple USB devices for %x:%x, "
-                                 "use <address> to specify one"),
-                               vendor, product);
-            }
+        rc = virHostdevFindUSBDeviceWithFlags(hostdev, mandatory,
+                                              flags, usb);
+
+        if (rc < 0)
             return -1;
-        }
+    }
 
+    if (!*usb) {
+        hostdev->missing = true;
+    } else if (!usbsrc->bus || !usbsrc->device) {
         usbsrc->bus = virUSBDeviceGetBus(*usb);
         usbsrc->device = virUSBDeviceGetDevno(*usb);
         usbsrc->autoAddress = true;
-
-        if (autoAddress) {
-            VIR_INFO("USB device %x:%x found at bus:%u device:%u (moved"
-                     " from bus:%u device:%u)",
-                     vendor, product,
-                     usbsrc->bus, usbsrc->device,
-                     bus, device);
-        }
-    } else if (!vendor && bus) {
-        if (virUSBDeviceFindByBus(bus, device, NULL, mandatory, usb) < 0)
-            return -1;
     }
 
- out:
-    if (!*usb)
-        hostdev->missing = true;
     return 0;
 }
 
diff --git a/src/util/virusb.c b/src/util/virusb.c
index 6a001a7..2e15849 100644
--- a/src/util/virusb.c
+++ b/src/util/virusb.c
@@ -68,12 +68,6 @@ struct _virUSBDeviceList {
     virUSBDevicePtr *devs;
 };
 
-typedef enum {
-    USB_DEVICE_ALL = 0,
-    USB_DEVICE_FIND_BY_VENDOR = 1 << 0,
-    USB_DEVICE_FIND_BY_BUS = 1 << 1,
-} virUSBDeviceFindFlags;
-
 static virClassPtr virUSBDeviceListClass;
 
 static void virUSBDeviceListDispose(void *obj);
@@ -119,11 +113,33 @@ static int virUSBSysReadFile(const char *f_name, const char *d_name,
     return ret;
 }
 
+static int virUSBSysReadFileStr(const char *f_name, const char *d_name,
+                                char **value)
+{
+    int ret = -1, tmp;
+    char *buf = NULL;
+    char *filename = NULL;
+
+    tmp = virAsprintf(&filename, USB_SYSFS "/devices/%s/%s", d_name, f_name);
+    if (tmp < 0)
+        goto cleanup;
+
+    if (virFileReadAll(filename, 1024, &buf) < 0)
+        goto cleanup;
+
+    *value = buf;
+    ret = 0;
+ cleanup:
+    VIR_FREE(filename);
+    return ret;
+}
+
 static virUSBDeviceListPtr
 virUSBDeviceSearch(unsigned int vendor,
                    unsigned int product,
                    unsigned int bus,
                    unsigned int devno,
+                   const char *port,
                    const char *vroot,
                    unsigned int flags)
 {
@@ -143,6 +159,8 @@ virUSBDeviceSearch(unsigned int vendor,
 
     while ((direrr = virDirRead(dir, &de, USB_SYSFS "/devices")) > 0) {
         unsigned int found_prod, found_vend, found_bus, found_devno;
+        char *found_port;
+        bool port_matches;
         char *tmpstr = de->d_name;
 
         if (strchr(de->d_name, ':'))
@@ -170,16 +188,31 @@ virUSBDeviceSearch(unsigned int vendor,
                               10, &found_devno) < 0)
             goto cleanup;
 
+        if (virUSBSysReadFileStr("devpath", de->d_name,
+                                 &found_port) < 0) {
+            goto cleanup;
+        } else {
+            found_port[strlen(found_port) - 1] = '\0'; /* remove newline */
+            port_matches = STREQ_NULLABLE(found_port, port);
+            VIR_FREE(found_port);
+        }
+
         if ((flags & USB_DEVICE_FIND_BY_VENDOR) &&
             (found_prod != product || found_vend != vendor))
             continue;
 
-        if (flags & USB_DEVICE_FIND_BY_BUS) {
+        if (flags & USB_DEVICE_FIND_BY_DEVICE) {
             if (found_bus != bus || found_devno != devno)
                 continue;
             found = true;
         }
 
+        if (flags & USB_DEVICE_FIND_BY_PORT) {
+            if (found_bus != bus || !port_matches)
+                continue;
+            found = true;
+        }
+
         usb = virUSBDeviceNew(found_bus, found_devno, vroot);
         if (!usb)
             goto cleanup;
@@ -204,36 +237,43 @@ virUSBDeviceSearch(unsigned int vendor,
 }
 
 int
-virUSBDeviceFindByVendor(unsigned int vendor,
-                         unsigned int product,
-                         const char *vroot,
-                         bool mandatory,
-                         virUSBDeviceListPtr *devices)
+virUSBDeviceFind(unsigned int vendor,
+                 unsigned int product,
+                 unsigned int bus,
+                 unsigned int devno,
+                 const char *port,
+                 const char *vroot,
+                 bool mandatory,
+                 unsigned int flags,
+                 virUSBDeviceListPtr *devices)
 {
     virUSBDeviceListPtr list;
     int count;
 
-    if (!(list = virUSBDeviceSearch(vendor, product, 0, 0,
-                                    vroot,
-                                    USB_DEVICE_FIND_BY_VENDOR)))
+    if (!(list = virUSBDeviceSearch(vendor, product, bus, devno, port,
+                                    vroot, flags)))
         return -1;
 
-    if (list->count == 0) {
+    count = list->count;
+    if (count == 0) {
         virObjectUnref(list);
         if (!mandatory) {
-            VIR_DEBUG("Did not find USB device %x:%x",
-                      vendor, product);
             if (devices)
                 *devices = NULL;
             return 0;
         }
 
         virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Did not find USB device %x:%x"), vendor, product);
+                       _("Did not find a matching USB device (matching "
+                         "on%s%s%s all others ignored): vid:%04x, pid:%04x, "
+                         "bus:%u, device:%u, port:%s"),
+                       flags & USB_DEVICE_FIND_BY_VENDOR ? " vid/pid," : "",
+                       flags & USB_DEVICE_FIND_BY_DEVICE ? " bus/device," : "",
+                       flags & USB_DEVICE_FIND_BY_PORT ? " bus/port," : "",
+                       vendor, product, bus, devno, port ? port : "(null)");
         return -1;
     }
 
-    count = list->count;
     if (devices)
         *devices = list;
     else
@@ -242,86 +282,6 @@ virUSBDeviceFindByVendor(unsigned int vendor,
     return count;
 }
 
-int
-virUSBDeviceFindByBus(unsigned int bus,
-                      unsigned int devno,
-                      const char *vroot,
-                      bool mandatory,
-                      virUSBDevicePtr *usb)
-{
-    virUSBDeviceListPtr list;
-
-    if (!(list = virUSBDeviceSearch(0, 0, bus, devno,
-                                    vroot,
-                                    USB_DEVICE_FIND_BY_BUS)))
-        return -1;
-
-    if (list->count == 0) {
-        virObjectUnref(list);
-        if (!mandatory) {
-            VIR_DEBUG("Did not find USB device bus:%u device:%u",
-                      bus, devno);
-            if (usb)
-                *usb = NULL;
-            return 0;
-        }
-
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Did not find USB device bus:%u device:%u"),
-                       bus, devno);
-        return -1;
-    }
-
-    if (usb) {
-        *usb = virUSBDeviceListGet(list, 0);
-        virUSBDeviceListSteal(list, *usb);
-    }
-    virObjectUnref(list);
-
-    return 0;
-}
-
-int
-virUSBDeviceFind(unsigned int vendor,
-                 unsigned int product,
-                 unsigned int bus,
-                 unsigned int devno,
-                 const char *vroot,
-                 bool mandatory,
-                 virUSBDevicePtr *usb)
-{
-    virUSBDeviceListPtr list;
-
-    unsigned int flags = USB_DEVICE_FIND_BY_VENDOR|USB_DEVICE_FIND_BY_BUS;
-    if (!(list = virUSBDeviceSearch(vendor, product, bus, devno,
-                                    vroot, flags)))
-        return -1;
-
-    if (list->count == 0) {
-        virObjectUnref(list);
-        if (!mandatory) {
-            VIR_DEBUG("Did not find USB device %x:%x bus:%u device:%u",
-                      vendor, product, bus, devno);
-            if (usb)
-                *usb = NULL;
-            return 0;
-        }
-
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Did not find USB device %x:%x bus:%u device:%u"),
-                       vendor, product, bus, devno);
-        return -1;
-    }
-
-    if (usb) {
-        *usb = virUSBDeviceListGet(list, 0);
-        virUSBDeviceListSteal(list, *usb);
-    }
-    virObjectUnref(list);
-
-    return 0;
-}
-
 virUSBDevicePtr
 virUSBDeviceNew(unsigned int bus,
                 unsigned int devno,
diff --git a/src/util/virusb.h b/src/util/virusb.h
index f98ea21..0834cd4 100644
--- a/src/util/virusb.h
+++ b/src/util/virusb.h
@@ -35,29 +35,26 @@ typedef virUSBDevice *virUSBDevicePtr;
 typedef struct _virUSBDeviceList virUSBDeviceList;
 typedef virUSBDeviceList *virUSBDeviceListPtr;
 
+typedef enum {
+    USB_DEVICE_ALL = 0,
+    USB_DEVICE_FIND_BY_VENDOR = 1 << 0,
+    USB_DEVICE_FIND_BY_DEVICE = 1 << 1,
+    USB_DEVICE_FIND_BY_PORT = 1 << 2,
+} virUSBDeviceFindFlags;
+
 virUSBDevicePtr virUSBDeviceNew(unsigned int bus,
                                 unsigned int devno,
                                 const char *vroot);
 
-int virUSBDeviceFindByBus(unsigned int bus,
-                          unsigned int devno,
-                          const char *vroot,
-                          bool mandatory,
-                          virUSBDevicePtr *usb);
-
-int virUSBDeviceFindByVendor(unsigned int vendor,
-                             unsigned int product,
-                             const char *vroot,
-                             bool mandatory,
-                             virUSBDeviceListPtr *devices);
-
 int virUSBDeviceFind(unsigned int vendor,
                      unsigned int product,
                      unsigned int bus,
                      unsigned int devno,
+                     const char *port,
                      const char *vroot,
                      bool mandatory,
-                     virUSBDevicePtr *usb);
+                     unsigned int flags,
+                     virUSBDeviceListPtr *devices);
 
 void virUSBDeviceFree(virUSBDevicePtr dev);
 int virUSBDeviceSetUsedBy(virUSBDevicePtr dev,
diff --git a/tests/virusbtest.c b/tests/virusbtest.c
index 4bbfe4a..0782b73 100644
--- a/tests/virusbtest.c
+++ b/tests/virusbtest.c
@@ -33,7 +33,8 @@
 typedef enum {
     FIND_BY_ALL,
     FIND_BY_VENDOR,
-    FIND_BY_BUS
+    FIND_BY_DEVICE,
+    FIND_BY_PORT
 } testUSBFindFlags;
 
 struct findTestInfo {
@@ -42,6 +43,7 @@ struct findTestInfo {
     unsigned int product;
     unsigned int bus;
     unsigned int devno;
+    const char *port;
     const char *vroot;
     bool mandatory;
     int how;
@@ -78,25 +80,31 @@ static int testDeviceFind(const void *opaque)
     virUSBDeviceListPtr devs = NULL;
     int rv = 0;
     size_t i, ndevs = 0;
+    unsigned int flags = 0;
 
     switch (info->how) {
     case FIND_BY_ALL:
-        rv = virUSBDeviceFind(info->vendor, info->product,
-                              info->bus, info->devno,
-                              info->vroot, info->mandatory, &dev);
+        flags = USB_DEVICE_FIND_BY_VENDOR |
+                USB_DEVICE_FIND_BY_DEVICE |
+                USB_DEVICE_FIND_BY_PORT;
         break;
     case FIND_BY_VENDOR:
-        rv = virUSBDeviceFindByVendor(info->vendor, info->product,
-                                      info->vroot, info->mandatory, &devs);
+        flags = USB_DEVICE_FIND_BY_VENDOR;
         break;
-    case FIND_BY_BUS:
-        rv = virUSBDeviceFindByBus(info->bus, info->devno,
-                                   info->vroot, info->mandatory, &dev);
+    case FIND_BY_DEVICE:
+        flags = USB_DEVICE_FIND_BY_DEVICE;
+        break;
+    case FIND_BY_PORT:
+        flags = USB_DEVICE_FIND_BY_PORT;
         break;
     }
 
+    rv = virUSBDeviceFind(info->vendor, info->product,
+                          info->bus, info->devno, info->port,
+                          info->vroot, info->mandatory, flags, &devs);
+
     if (info->expectFailure) {
-        if (rv == 0) {
+        if (rv >= 0) {
             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                            "unexpected success");
         } else {
@@ -107,9 +115,18 @@ static int testDeviceFind(const void *opaque)
         goto cleanup;
     }
 
+    if (info->how != FIND_BY_VENDOR) {
+        if (rv == 1) {
+            dev = virUSBDeviceListGet(devs, 0);
+            virUSBDeviceListSteal(devs, dev);
+        } else {
+            goto cleanup;
+        }
+    }
+
     switch (info->how) {
     case FIND_BY_ALL:
-    case FIND_BY_BUS:
+    case FIND_BY_DEVICE:
         if (virUSBDeviceFileIterate(dev, testDeviceFileActor, NULL) < 0)
             goto cleanup;
         break;
@@ -155,14 +172,17 @@ testUSBList(const void *opaque ATTRIBUTE_UNUSED)
     virUSBDeviceListPtr list = NULL;
     virUSBDeviceListPtr devlist = NULL;
     virUSBDevicePtr dev = NULL;
+    virUSBDeviceListPtr devs = NULL;
     int ret = -1;
+    int rv;
     size_t i, ndevs;
 
     if (!(list = virUSBDeviceListNew()))
         goto cleanup;
 
 #define EXPECTED_NDEVS_ONE 3
-    if (virUSBDeviceFindByVendor(0x1d6b, 0x0002, NULL, true, &devlist) < 0)
+    if (virUSBDeviceFind(0x1d6b, 0x0002, 0, 0, NULL, NULL, true,
+                         USB_DEVICE_FIND_BY_VENDOR, &devlist) < 0)
         goto cleanup;
 
     ndevs = virUSBDeviceListCount(devlist);
@@ -186,7 +206,8 @@ testUSBList(const void *opaque ATTRIBUTE_UNUSED)
         goto cleanup;
 
 #define EXPECTED_NDEVS_TWO 3
-    if (virUSBDeviceFindByVendor(0x18d1, 0x4e22, NULL, true, &devlist) < 0)
+    if (virUSBDeviceFind(0x18d1, 0x4e22, 0, 0, NULL, NULL, true,
+                         USB_DEVICE_FIND_BY_VENDOR, &devlist) < 0)
         goto cleanup;
 
     ndevs = virUSBDeviceListCount(devlist);
@@ -206,8 +227,17 @@ testUSBList(const void *opaque ATTRIBUTE_UNUSED)
                        EXPECTED_NDEVS_ONE + EXPECTED_NDEVS_TWO) < 0)
         goto cleanup;
 
-    if (virUSBDeviceFind(0x18d1, 0x4e22, 1, 20, NULL, true, &dev) < 0)
+    rv = virUSBDeviceFind(0x18d1, 0x4e22, 1, 20, "1.5.6", NULL, true,
+                          USB_DEVICE_FIND_BY_VENDOR |
+                          USB_DEVICE_FIND_BY_DEVICE |
+                          USB_DEVICE_FIND_BY_PORT, &devs);
+    if (rv != 1) {
         goto cleanup;
+    } else {
+        dev = virUSBDeviceListGet(devs, 0);
+        virUSBDeviceListSteal(devs, dev);
+    }
+    virObjectUnref(devs);
 
     if (!virUSBDeviceListFind(list, dev)) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -239,49 +269,63 @@ mymain(void)
 {
     int rv = 0;
 
-#define DO_TEST_FIND_FULL(name, vend, prod, bus, devno, vroot, mand, how, fail) \
-    do {                                                                        \
-        struct findTestInfo data = { name, vend, prod, bus,                     \
-            devno, vroot, mand, how, fail                                       \
-        };                                                                      \
-        if (virTestRun("USBDeviceFind " name, testDeviceFind, &data) < 0)       \
-            rv = -1;                                                            \
+#define DO_TEST_FIND_FULL(name, vend, prod, bus, devno,                     \
+                          port, vroot, mand, how, fail)                     \
+    do {                                                                    \
+        struct findTestInfo data = { name, vend, prod, bus,                 \
+            devno, port, vroot, mand, how, fail                             \
+        };                                                                  \
+        if (virTestRun("USBDeviceFind " name, testDeviceFind, &data) < 0)   \
+            rv = -1;                                                        \
     } while (0)
 
-#define DO_TEST_FIND(name, vend, prod, bus, devno)                          \
-    DO_TEST_FIND_FULL(name, vend, prod, bus, devno, NULL, true,             \
+#define DO_TEST_FIND(name, vend, prod, bus, devno, port)                    \
+    DO_TEST_FIND_FULL(name, vend, prod, bus, devno, port, NULL, true,       \
                       FIND_BY_ALL, false)
-#define DO_TEST_FIND_FAIL(name, vend, prod, bus, devno)                     \
-    DO_TEST_FIND_FULL(name, vend, prod, bus, devno, NULL, true,             \
+#define DO_TEST_FIND_FAIL(name, vend, prod, bus, devno, port)               \
+    DO_TEST_FIND_FULL(name, vend, prod, bus, devno, port, NULL, true,       \
                       FIND_BY_ALL, true)
 
-#define DO_TEST_FIND_BY_BUS(name, bus, devno)                               \
-    DO_TEST_FIND_FULL(name, 101, 202, bus, devno, NULL, true,               \
-                      FIND_BY_BUS, false)
-#define DO_TEST_FIND_BY_BUS_FAIL(name, bus, devno)                          \
-    DO_TEST_FIND_FULL(name, 101, 202, bus, devno, NULL, true,               \
-                      FIND_BY_BUS, true)
+#define DO_TEST_FIND_BY_DEVICE(name, bus, devno)                               \
+    DO_TEST_FIND_FULL(name, 101, 202, bus, devno, NULL, NULL, true,         \
+                      FIND_BY_DEVICE, false)
+#define DO_TEST_FIND_BY_DEVICE_FAIL(name, bus, devno)                          \
+    DO_TEST_FIND_FULL(name, 101, 202, bus, devno, NULL, NULL, true,         \
+                      FIND_BY_DEVICE, true)
 
 #define DO_TEST_FIND_BY_VENDOR(name, vend, prod)                            \
-    DO_TEST_FIND_FULL(name, vend, prod, 123, 456, NULL, true,               \
+    DO_TEST_FIND_FULL(name, vend, prod, 123, 456, NULL, NULL, true,         \
                       FIND_BY_VENDOR, false)
 #define DO_TEST_FIND_BY_VENDOR_FAIL(name, vend, prod)                       \
-    DO_TEST_FIND_FULL(name, vend, prod, 123, 456, NULL, true,               \
+    DO_TEST_FIND_FULL(name, vend, prod, 123, 456, NULL, NULL, true,         \
                       FIND_BY_VENDOR, true)
 
-    DO_TEST_FIND("Nexus", 0x18d1, 0x4e22, 1, 20);
-    DO_TEST_FIND_FAIL("Nexus wrong devnum", 0x18d1, 0x4e22, 1, 25);
-    DO_TEST_FIND_FAIL("Bogus", 0xf00d, 0xbeef, 1024, 768);
+#define DO_TEST_FIND_BY_PORT(name, bus, port)                               \
+    DO_TEST_FIND_FULL(name, 101, 202, bus, 456, port, NULL, true,           \
+                      FIND_BY_PORT, false)
+#define DO_TEST_FIND_BY_PORT_FAIL(name, bus, port)                          \
+    DO_TEST_FIND_FULL(name, 101, 202, bus, 456, port, NULL, true,           \
+                      FIND_BY_PORT, true)
+
+    DO_TEST_FIND("Nexus", 0x18d1, 0x4e22, 1, 20, "1.5.6");
+    DO_TEST_FIND_FAIL("Nexus wrong devnum", 0x18d1, 0x4e22, 1, 25, "1.5.6");
+    DO_TEST_FIND_FAIL("Nexus wrong port", 0x18d1, 0x4e22, 1, 25, "1.5.4");
+    DO_TEST_FIND_FAIL("Bogus", 0xf00d, 0xbeef, 1024, 768, "1.2.3.4");
 
-    DO_TEST_FIND_BY_BUS("integrated camera", 1, 5);
-    DO_TEST_FIND_BY_BUS_FAIL("wrong bus/devno combination", 2, 20);
-    DO_TEST_FIND_BY_BUS_FAIL("missing bus", 5, 20);
-    DO_TEST_FIND_BY_BUS_FAIL("missing devnum", 1, 158);
+    DO_TEST_FIND_BY_DEVICE("integrated camera", 1, 5);
+    DO_TEST_FIND_BY_DEVICE_FAIL("wrong bus/devno combination", 2, 20);
+    DO_TEST_FIND_BY_DEVICE_FAIL("missing bus", 5, 20);
+    DO_TEST_FIND_BY_DEVICE_FAIL("missing devnum", 1, 158);
 
     DO_TEST_FIND_BY_VENDOR("Nexus (multiple results)", 0x18d1, 0x4e22);
     DO_TEST_FIND_BY_VENDOR_FAIL("Bogus vendor and product", 0xf00d, 0xbeef);
     DO_TEST_FIND_BY_VENDOR_FAIL("Valid vendor", 0x1d6b, 0xbeef);
 
+    DO_TEST_FIND_BY_PORT("Logitech mouse", 1, "1.5.3.3");
+    DO_TEST_FIND_BY_PORT_FAIL("wrong bus/port combination", 2, "1.5.3.3");
+    DO_TEST_FIND_BY_PORT_FAIL("missing bus", 5, "1.5.3.3");
+    DO_TEST_FIND_BY_PORT_FAIL("missing port", 1, "8.2.5");
+
     if (virTestRun("USB List test", testUSBList, NULL) < 0)
         rv = -1;
 
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.1/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.1/devpath
new file mode 100644
index 0000000..02a7fbe
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.1/devpath
@@ -0,0 +1 @@
+1.5.3.1
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.3/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.3/devpath
new file mode 100644
index 0000000..23ca863
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.3/devpath
@@ -0,0 +1 @@
+1.5.3.3
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3/devpath
new file mode 100644
index 0000000..8af85be
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3/devpath
@@ -0,0 +1 @@
+1.5.3
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.4/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.4/devpath
new file mode 100644
index 0000000..94fe62c
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.4/devpath
@@ -0,0 +1 @@
+1.5.4
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.5/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.5/devpath
new file mode 100644
index 0000000..9075be4
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.5/devpath
@@ -0,0 +1 @@
+1.5.5
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.6/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.6/devpath
new file mode 100644
index 0000000..eac1e0a
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.6/devpath
@@ -0,0 +1 @@
+1.5.6
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5/devpath
new file mode 100644
index 0000000..c239c60
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5/devpath
@@ -0,0 +1 @@
+1.5
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.6/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.6/devpath
new file mode 100644
index 0000000..810ee4e
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.6/devpath
@@ -0,0 +1 @@
+1.6
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1/devpath
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1/devpath
@@ -0,0 +1 @@
+1
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/2-1.2/devpath b/tests/virusbtestdata/sys_bus_usb/devices/2-1.2/devpath
new file mode 100644
index 0000000..5625e59
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/2-1.2/devpath
@@ -0,0 +1 @@
+1.2
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/2-1/devpath b/tests/virusbtestdata/sys_bus_usb/devices/2-1/devpath
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/2-1/devpath
@@ -0,0 +1 @@
+1
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/usb1/devpath b/tests/virusbtestdata/sys_bus_usb/devices/usb1/devpath
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/usb1/devpath
@@ -0,0 +1 @@
+0
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/usb2/devpath b/tests/virusbtestdata/sys_bus_usb/devices/usb2/devpath
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/usb2/devpath
@@ -0,0 +1 @@
+0
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/usb3/devpath b/tests/virusbtestdata/sys_bus_usb/devices/usb3/devpath
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/usb3/devpath
@@ -0,0 +1 @@
+0
diff --git a/tests/virusbtestdata/sys_bus_usb/devices/usb4/devpath b/tests/virusbtestdata/sys_bus_usb/devices/usb4/devpath
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/virusbtestdata/sys_bus_usb/devices/usb4/devpath
@@ -0,0 +1 @@
+0
-- 
2.7.4




More information about the libvir-list mailing list