From: Maximilian Martin Date: Mon, 18 Aug 2025 14:34:13 +0000 (+0200) Subject: util: generalize the host USB device search APIs X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4b97cdd1a58079daba6d2b605e943c1538b5356f;p=thirdparty%2Flibvirt.git util: generalize the host USB device search APIs 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é Signed-off-by: Maximilian Martin [DB: split out of bigger patch] Signed-off-by: Daniel P. Berrangé --- diff --git a/src/hypervisor/virhostdev.c b/src/hypervisor/virhostdev.c index 7d7df4418d..3717fbd47d 100644 --- a/src/hypervisor/virhostdev.c +++ b/src/hypervisor/virhostdev.c @@ -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
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
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; } diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index d81b30f0b6..e84b2d0e59 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3681,8 +3681,6 @@ virURIResolveAlias; # util/virusb.h virUSBDeviceFileIterate; virUSBDeviceFind; -virUSBDeviceFindByBus; -virUSBDeviceFindByVendor; virUSBDeviceFree; virUSBDeviceGetBus; virUSBDeviceGetDevno; diff --git a/src/util/virusb.c b/src/util/virusb.c index cf3461f80a..8d4ce08c09 100644 --- a/src/util/virusb.c +++ b/src/util/virusb.c @@ -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 * diff --git a/src/util/virusb.h b/src/util/virusb.h index d2b3f69942..73bb9c1d77 100644 --- a/src/util/virusb.h +++ b/src/util/virusb.h @@ -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, diff --git a/tests/virusbtest.c b/tests/virusbtest.c index 870e136321..94e432beb8 100644 --- a/tests/virusbtest.c +++ b/tests/virusbtest.c @@ -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;