]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
util: generalize the host USB device search APIs
authorMaximilian Martin <maximilian_martin@gmx.de>
Mon, 18 Aug 2025 14:34:13 +0000 (16:34 +0200)
committerDaniel P. Berrangé <berrange@redhat.com>
Wed, 11 Feb 2026 18:24:20 +0000 (18:24 +0000)
Prepare for adding the ability to find host USB devices based
on their port, by generalizing the APIs for device searching
into one all-purpose API

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Maximilian Martin <maximilian_martin@gmx.de>
[DB: split out of bigger patch]
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
src/hypervisor/virhostdev.c
src/libvirt_private.syms
src/util/virusb.c
src/util/virusb.h
tests/virusbtest.c

index 7d7df4418d1159ea23632b48a57f8422788310ce..3717fbd47d7107101cd6c698962273fb6f2e8a43 100644 (file)
@@ -1359,6 +1359,40 @@ virHostdevMarkUSBDevices(virHostdevManager *mgr,
     return -1;
 }
 
+static int
+virHostdevFindUSBDeviceWithFlags(virDomainHostdevDef *hostdev,
+                                 bool mandatory,
+                                 unsigned int flags,
+                                 virUSBDevice **usb)
+{
+    virDomainHostdevSubsysUSB *usbsrc = &hostdev->source.subsys.u.usb;
+    unsigned vendor = usbsrc->vendor;
+    unsigned product = usbsrc->product;
+    unsigned bus = usbsrc->bus;
+    unsigned device = usbsrc->device;
+    g_autoptr(virUSBDeviceList) devs = NULL;
+    int rc;
+
+    rc = virUSBDeviceFind(vendor, product, bus, device, NULL,
+                          mandatory, flags, &devs);
+    if (rc < 0)
+        return -1;
+
+    if (rc == 1) {
+        *usb = virUSBDeviceListGet(devs, 0);
+        virUSBDeviceListSteal(devs, *usb);
+    }
+
+    if (rc > 1) {
+        virReportError(VIR_ERR_OPERATION_FAILED,
+                       _("Multiple USB devices for %1$x:%2$x, use <address> to specify one"),
+                       vendor, product);
+        return -1;
+    }
+
+    return rc;
+}
+
 
 int
 virHostdevFindUSBDevice(virDomainHostdevDef *hostdev,
@@ -1367,77 +1401,59 @@ virHostdevFindUSBDevice(virDomainHostdevDef *hostdev,
 {
     virDomainHostdevSubsysUSB *usbsrc = &hostdev->source.subsys.u.usb;
     unsigned vendor = usbsrc->vendor;
-    unsigned product = usbsrc->product;
     unsigned bus = usbsrc->bus;
     unsigned device = usbsrc->device;
     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);
-        }
+    if (vendor)
+        flags |= USB_DEVICE_FIND_BY_VENDOR;
+    if (device)
+        flags |= USB_DEVICE_FIND_BY_DEVICE;
+
+    /* Rule out invalid cases. */
+    if (vendor && device) {
+        virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                       _("Cannot match USB device on vendor/product and bus/device at once"));
+    } else if (!vendor && !device) {
+        virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                       _("No matching fields for USB device found, vendor/product or bus/device required"));
+        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) {
-        g_autoptr(virUSBDeviceList) devs = NULL;
+    /* First attempt, matching on all valid fields. */
+    rc = virHostdevFindUSBDeviceWithFlags(hostdev,
+                                          autoAddress ? false : mandatory,
+                                          flags, usb);
+    if (rc < 0)
+        return -1;
 
-        rc = virUSBDeviceFindByVendor(vendor, product, NULL, mandatory, &devs);
-        if (rc < 0) {
-            return -1;
-        } else if (rc == 0) {
-            goto out;
-        } else if (rc > 1) {
-            if (autoAddress) {
-                virReportError(VIR_ERR_OPERATION_FAILED,
-                               _("Multiple USB devices for %1$x:%2$x were found, but none of them is at bus:%3$u device:%4$u"),
-                               vendor, product, bus, device);
-            } else {
-                virReportError(VIR_ERR_OPERATION_FAILED,
-                               _("Multiple USB devices for %1$x:%2$x, use <address> to specify one"),
-                               vendor, product);
-            }
+    if (rc != 1 && autoAddress && device) {
+        VIR_INFO("USB device could not be found at previous address "
+                 "(bus:%u device:%u)", bus, device);
+
+        /* Second attempt, for when the device number has changed. */
+        flags &= ~USB_DEVICE_FIND_BY_DEVICE;
+        usbsrc->device = 0;
+
+        rc = virHostdevFindUSBDeviceWithFlags(hostdev, mandatory,
+                                              flags, usb);
+        if (rc < 0)
             return -1;
-        }
 
-        *usb = virUSBDeviceListGet(devs, 0);
-        virUSBDeviceListSteal(devs, *usb);
+        usbsrc->autoAddress = true;
+    }
 
+    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 (bus) {
-        if (virUSBDeviceFindByBus(bus, device, NULL, mandatory, usb) < 0)
-            return -1;
     }
 
- out:
-    if (!*usb)
-        hostdev->missing = true;
     return 0;
 }
 
index d81b30f0b69c4eef6a3e429dcd607492bbc3288d..e84b2d0e595001a53e43a9ce66ee580616220cf0 100644 (file)
@@ -3681,8 +3681,6 @@ virURIResolveAlias;
 # util/virusb.h
 virUSBDeviceFileIterate;
 virUSBDeviceFind;
-virUSBDeviceFindByBus;
-virUSBDeviceFindByVendor;
 virUSBDeviceFree;
 virUSBDeviceGetBus;
 virUSBDeviceGetDevno;
index cf3461f80a557857665a9535d70c2448ea0b8806..8d4ce08c0970eb30f3ba4e538e5abc8a73ab7dfe 100644 (file)
@@ -61,12 +61,6 @@ struct _virUSBDeviceList {
     virUSBDevice **devs;
 };
 
-typedef enum {
-    USB_DEVICE_ALL = 0,
-    USB_DEVICE_FIND_BY_VENDOR = 1 << 0,
-    USB_DEVICE_FIND_BY_BUS = 1 << 1,
-} virUSBDeviceFindFlags;
-
 static virClass *virUSBDeviceListClass;
 
 static void virUSBDeviceListDispose(void *obj);
@@ -154,11 +148,12 @@ virUSBDeviceSearch(unsigned int vendor,
                               10, &found_devno) < 0)
             goto cleanup;
 
-        if ((flags & USB_DEVICE_FIND_BY_VENDOR) &&
-            (found_prod != product || found_vend != vendor))
-            continue;
+        if (flags & USB_DEVICE_FIND_BY_VENDOR) {
+            if (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;
@@ -185,84 +180,6 @@ virUSBDeviceSearch(unsigned int vendor,
     return ret;
 }
 
-int
-virUSBDeviceFindByVendor(unsigned int vendor,
-                         unsigned int product,
-                         const char *vroot,
-                         bool mandatory,
-                         virUSBDeviceList **devices)
-{
-    virUSBDeviceList *list;
-    int count;
-
-    if (!(list = virUSBDeviceSearch(vendor, product, 0, 0,
-                                    vroot,
-                                    USB_DEVICE_FIND_BY_VENDOR)))
-        return -1;
-
-    if (list->count == 0) {
-        virObjectUnref(list);
-        if (!mandatory) {
-            VIR_DEBUG("Did not find USB device %04x:%04x",
-                      vendor, product);
-            if (devices)
-                *devices = NULL;
-            return 0;
-        }
-
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Did not find USB device %1$04x:%2$04x"), vendor, product);
-        return -1;
-    }
-
-    count = list->count;
-    if (devices)
-        *devices = list;
-    else
-        virObjectUnref(list);
-
-    return count;
-}
-
-int
-virUSBDeviceFindByBus(unsigned int bus,
-                      unsigned int devno,
-                      const char *vroot,
-                      bool mandatory,
-                      virUSBDevice **usb)
-{
-    virUSBDeviceList *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:%1$u device:%2$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,
@@ -270,38 +187,34 @@ virUSBDeviceFind(unsigned int vendor,
                  unsigned int devno,
                  const char *vroot,
                  bool mandatory,
-                 virUSBDevice **usb)
+                 unsigned int flags,
+                 virUSBDeviceList **devices)
 {
-    virUSBDeviceList *list;
+    g_autoptr(virUSBDeviceList) list = NULL;
+    int count;
 
-    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);
+    count = list->count;
+    if (count == 0) {
         if (!mandatory) {
-            VIR_DEBUG("Did not find USB device %04x:%04x bus:%u device:%u",
-                      vendor, product, bus, devno);
-            if (usb)
-                *usb = NULL;
+            if (devices)
+                *devices = NULL;
             return 0;
         }
 
         virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Did not find USB device %1$04x:%2$04x bus:%3$u device:%4$u"),
+                       _("Did not find matching USB device: vid:%1$04x, pid:%2$04x, bus:%3$u, device:%4$u"),
                        vendor, product, bus, devno);
         return -1;
     }
 
-    if (usb) {
-        *usb = virUSBDeviceListGet(list, 0);
-        virUSBDeviceListSteal(list, *usb);
-    }
-    virObjectUnref(list);
+    if (devices)
+        *devices = g_steal_pointer(&list);
 
-    return 0;
+    return count;
 }
 
 virUSBDevice *
index d2b3f699428b27a345a4c64decdf64dc05ed53f0..73bb9c1d773eccb46f6742c7fe4742a656794749 100644 (file)
@@ -30,30 +30,24 @@ typedef struct _virUSBDeviceList virUSBDeviceList;
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virUSBDeviceList, virObjectUnref);
 
+typedef enum {
+    USB_DEVICE_ALL = 0,
+    USB_DEVICE_FIND_BY_VENDOR = 1 << 0,
+    USB_DEVICE_FIND_BY_DEVICE = 1 << 1,
+} virUSBDeviceFindFlags;
 
 virUSBDevice *virUSBDeviceNew(unsigned int bus,
                                 unsigned int devno,
                                 const char *vroot);
 
-int virUSBDeviceFindByBus(unsigned int bus,
-                          unsigned int devno,
-                          const char *vroot,
-                          bool mandatory,
-                          virUSBDevice **usb);
-
-int virUSBDeviceFindByVendor(unsigned int vendor,
-                             unsigned int product,
-                             const char *vroot,
-                             bool mandatory,
-                             virUSBDeviceList **devices);
-
 int virUSBDeviceFind(unsigned int vendor,
                      unsigned int product,
                      unsigned int bus,
                      unsigned int devno,
                      const char *vroot,
                      bool mandatory,
-                     virUSBDevice **usb);
+                     unsigned int flags,
+                     virUSBDeviceList **devices);
 
 void virUSBDeviceFree(virUSBDevice *dev);
 int virUSBDeviceSetUsedBy(virUSBDevice *dev,
index 870e1363218b3c813d62fe3dfbeba2b1df2f9c42..94e432beb887603888b25993d13c215bc51a181c 100644 (file)
@@ -26,9 +26,9 @@
 #define VIR_FROM_THIS VIR_FROM_NONE
 
 typedef enum {
-    FIND_BY_ALL,
     FIND_BY_VENDOR,
-    FIND_BY_BUS
+    FIND_BY_DEVICE,
+    FIND_BY_VENDOR_AND_DEVICE,
 } testUSBFindFlags;
 
 struct findTestInfo {
@@ -70,25 +70,27 @@ static int testDeviceFind(const void *opaque)
     g_autoptr(virUSBDeviceList) 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);
-        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_DEVICE:
+        flags = USB_DEVICE_FIND_BY_DEVICE;
         break;
-    case FIND_BY_BUS:
-        rv = virUSBDeviceFindByBus(info->bus, info->devno,
-                                   info->vroot, info->mandatory, &dev);
+    case FIND_BY_VENDOR_AND_DEVICE:
+        flags = USB_DEVICE_FIND_BY_VENDOR |
+                USB_DEVICE_FIND_BY_DEVICE;
         break;
     }
 
+    rv = virUSBDeviceFind(info->vendor, info->product,
+                          info->bus, info->devno,
+                          info->vroot, info->mandatory, flags, &devs);
+
     if (info->expectFailure) {
-        if (rv == 0) {
+        if (rv >= 0) {
             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                            "unexpected success");
         } else {
@@ -99,9 +101,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:
+    case FIND_BY_VENDOR_AND_DEVICE:
         if (virUSBDeviceFileIterate(dev, testDeviceFileActor, NULL) < 0)
             goto cleanup;
         break;
@@ -146,14 +157,17 @@ testUSBList(const void *opaque G_GNUC_UNUSED)
     virUSBDeviceList *list = NULL;
     virUSBDeviceList *devlist = NULL;
     virUSBDevice *dev = NULL;
+    virUSBDeviceList *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, true,
+                         USB_DEVICE_FIND_BY_VENDOR, &devlist) < 0)
         goto cleanup;
 
     ndevs = virUSBDeviceListCount(devlist);
@@ -176,7 +190,8 @@ testUSBList(const void *opaque G_GNUC_UNUSED)
         goto cleanup;
 
 #define EXPECTED_NDEVS_TWO 3
-    if (virUSBDeviceFindByVendor(0x18d1, 0x4e22, NULL, true, &devlist) < 0)
+    if (virUSBDeviceFind(0x18d1, 0x4e22, 0, 0, NULL, true,
+                         USB_DEVICE_FIND_BY_VENDOR, &devlist) < 0)
         goto cleanup;
 
     ndevs = virUSBDeviceListCount(devlist);
@@ -196,8 +211,16 @@ testUSBList(const void *opaque G_GNUC_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, NULL, true,
+                            USB_DEVICE_FIND_BY_VENDOR |
+                            USB_DEVICE_FIND_BY_DEVICE, &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,
@@ -229,7 +252,8 @@ mymain(void)
 {
     int rv = 0;
 
-#define DO_TEST_FIND_FULL(name, vend, prod, bus, devno, vroot, mand, how, fail) \
+#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 \
@@ -238,20 +262,6 @@ mymain(void)
             rv = -1; \
     } while (0)
 
-#define DO_TEST_FIND(name, vend, prod, bus, devno) \
-    DO_TEST_FIND_FULL(name, vend, prod, bus, devno, 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, \
-                      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_VENDOR(name, vend, prod) \
     DO_TEST_FIND_FULL(name, vend, prod, 123, 456, NULL, true, \
                       FIND_BY_VENDOR, false)
@@ -259,19 +269,34 @@ mymain(void)
     DO_TEST_FIND_FULL(name, vend, prod, 123, 456, 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_DEVICE(name, bus, devno) \
+    DO_TEST_FIND_FULL(name, 0x1010, 0x2020, bus, devno, NULL, true, \
+                      FIND_BY_DEVICE, false)
+#define DO_TEST_FIND_BY_DEVICE_FAIL(name, bus, devno) \
+    DO_TEST_FIND_FULL(name, 0x1010, 0x2020, bus, devno, NULL, true, \
+                      FIND_BY_DEVICE, true)
 
-    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);
+#define DO_TEST_FIND_BY_VENDOR_AND_DEVICE(name, vend, prod, bus, devno) \
+    DO_TEST_FIND_FULL(name, vend, prod, bus, devno, NULL, true, \
+                      FIND_BY_VENDOR_AND_DEVICE, false)
+#define DO_TEST_FIND_BY_VENDOR_AND_DEVICE_FAIL(name, vend, prod, bus, devno) \
+    DO_TEST_FIND_FULL(name, vend, prod, bus, devno, NULL, true, \
+                      FIND_BY_VENDOR_AND_DEVICE, true)
+
+    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_VENDOR_AND_DEVICE("Nexus", 0x18d1, 0x4e22, 1, 20);
+    DO_TEST_FIND_BY_VENDOR_AND_DEVICE_FAIL("Bogus vendor and product", 0xf00d, 0xbeef, 1, 25);
+    DO_TEST_FIND_BY_VENDOR_AND_DEVICE_FAIL("Nexus wrong devnum", 0x18d1, 0x4e22, 1, 25);
+    DO_TEST_FIND_BY_VENDOR_AND_DEVICE_FAIL("Bogus", 0xf00d, 0xbeef, 1024, 768);
+
     if (virTestRun("USB List test", testUSBList, NULL) < 0)
         rv = -1;