From: Maximilian Martin Date: Mon, 18 Aug 2025 14:34:13 +0000 (+0200) Subject: util: implement support for finding host USB devices by port X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=73677f062fa4b678b4bd078bc592035ae99da935;p=thirdparty%2Flibvirt.git util: implement support for finding host USB devices by port Extend the API for finding host USB devices, to allow requesting a search based on the port. 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 3717fbd47d..7f1fb012be 100644 --- a/src/hypervisor/virhostdev.c +++ b/src/hypervisor/virhostdev.c @@ -1373,7 +1373,7 @@ virHostdevFindUSBDeviceWithFlags(virDomainHostdevDef *hostdev, g_autoptr(virUSBDeviceList) devs = NULL; int rc; - rc = virUSBDeviceFind(vendor, product, bus, device, NULL, + rc = virUSBDeviceFind(vendor, product, bus, device, NULL, NULL, mandatory, flags, &devs); if (rc < 0) return -1; diff --git a/src/util/virusb.c b/src/util/virusb.c index 8d4ce08c09..85ad3d58ce 100644 --- a/src/util/virusb.c +++ b/src/util/virusb.c @@ -96,11 +96,29 @@ static int virUSBSysReadFile(const char *f_name, const char *d_name, return 0; } +static int +virUSBSysReadFileStr(const char *f_name, + const char *d_name, + char **value) +{ + char *buf = NULL; + g_autofree char *filename = NULL; + + filename = g_strdup_printf(USB_SYSFS "/devices/%s/%s", d_name, f_name); + + if (virFileReadAll(filename, 1024, &buf) < 0) + return -1; + + *value = buf; + return 0; +} + static virUSBDeviceList * virUSBDeviceSearch(unsigned int vendor, unsigned int product, unsigned int bus, unsigned int devno, + const char *port, const char *vroot, unsigned int flags) { @@ -121,6 +139,8 @@ virUSBDeviceSearch(unsigned int vendor, while ((direrr = virDirRead(dir, &de, USB_SYSFS "/devices")) > 0) { unsigned int found_prod, found_vend, found_bus, found_devno; + g_autofree char *found_port = NULL; + bool port_matches; char *tmpstr = de->d_name; if (strchr(de->d_name, ':')) @@ -148,6 +168,14 @@ virUSBDeviceSearch(unsigned int vendor, 10, &found_devno) < 0) goto cleanup; + if (virUSBSysReadFileStr("devpath", de->d_name, + &found_port) < 0) { + goto cleanup; + } else { + virStringTrimOptionalNewline(found_port); + port_matches = STREQ_NULLABLE(found_port, port); + } + if (flags & USB_DEVICE_FIND_BY_VENDOR) { if (found_prod != product || found_vend != vendor) continue; @@ -159,6 +187,12 @@ virUSBDeviceSearch(unsigned int vendor, 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) @@ -185,6 +219,7 @@ virUSBDeviceFind(unsigned int vendor, unsigned int product, unsigned int bus, unsigned int devno, + const char *port, const char *vroot, bool mandatory, unsigned int flags, @@ -193,7 +228,7 @@ virUSBDeviceFind(unsigned int vendor, g_autoptr(virUSBDeviceList) list = NULL; int count; - if (!(list = virUSBDeviceSearch(vendor, product, bus, devno, + if (!(list = virUSBDeviceSearch(vendor, product, bus, devno, port, vroot, flags))) return -1; @@ -206,8 +241,8 @@ virUSBDeviceFind(unsigned int vendor, } virReportError(VIR_ERR_INTERNAL_ERROR, - _("Did not find matching USB device: vid:%1$04x, pid:%2$04x, bus:%3$u, device:%4$u"), - vendor, product, bus, devno); + _("Did not find matching USB device: vid:%1$04x, pid:%2$04x, bus:%3$u, device:%4$u, port:%5$s"), + vendor, product, bus, devno, port ? port : ""); return -1; } diff --git a/src/util/virusb.h b/src/util/virusb.h index 73bb9c1d77..86cc0a9d3d 100644 --- a/src/util/virusb.h +++ b/src/util/virusb.h @@ -34,6 +34,7 @@ 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; virUSBDevice *virUSBDeviceNew(unsigned int bus, @@ -44,6 +45,7 @@ int virUSBDeviceFind(unsigned int vendor, unsigned int product, unsigned int bus, unsigned int devno, + const char *port, const char *vroot, bool mandatory, unsigned int flags, diff --git a/tests/virusbtest.c b/tests/virusbtest.c index 94e432beb8..12ac338df9 100644 --- a/tests/virusbtest.c +++ b/tests/virusbtest.c @@ -28,7 +28,9 @@ typedef enum { FIND_BY_VENDOR, FIND_BY_DEVICE, + FIND_BY_PORT, FIND_BY_VENDOR_AND_DEVICE, + FIND_BY_VENDOR_AND_PORT } testUSBFindFlags; struct findTestInfo { @@ -37,6 +39,7 @@ struct findTestInfo { unsigned int product; unsigned int bus; unsigned int devno; + const char *port; const char *vroot; bool mandatory; int how; @@ -79,14 +82,21 @@ static int testDeviceFind(const void *opaque) case FIND_BY_DEVICE: flags = USB_DEVICE_FIND_BY_DEVICE; break; + case FIND_BY_PORT: + flags = USB_DEVICE_FIND_BY_PORT; + break; case FIND_BY_VENDOR_AND_DEVICE: flags = USB_DEVICE_FIND_BY_VENDOR | USB_DEVICE_FIND_BY_DEVICE; break; + case FIND_BY_VENDOR_AND_PORT: + flags = USB_DEVICE_FIND_BY_VENDOR | + USB_DEVICE_FIND_BY_PORT; + break; } rv = virUSBDeviceFind(info->vendor, info->product, - info->bus, info->devno, + info->bus, info->devno, info->port, info->vroot, info->mandatory, flags, &devs); if (info->expectFailure) { @@ -112,7 +122,9 @@ static int testDeviceFind(const void *opaque) switch (info->how) { case FIND_BY_DEVICE: + case FIND_BY_PORT: case FIND_BY_VENDOR_AND_DEVICE: + case FIND_BY_VENDOR_AND_PORT: if (virUSBDeviceFileIterate(dev, testDeviceFileActor, NULL) < 0) goto cleanup; break; @@ -166,7 +178,7 @@ testUSBList(const void *opaque G_GNUC_UNUSED) goto cleanup; #define EXPECTED_NDEVS_ONE 3 - if (virUSBDeviceFind(0x1d6b, 0x0002, 0, 0, NULL, true, + if (virUSBDeviceFind(0x1d6b, 0x0002, 0, 0, NULL, NULL, true, USB_DEVICE_FIND_BY_VENDOR, &devlist) < 0) goto cleanup; @@ -190,7 +202,7 @@ testUSBList(const void *opaque G_GNUC_UNUSED) goto cleanup; #define EXPECTED_NDEVS_TWO 3 - if (virUSBDeviceFind(0x18d1, 0x4e22, 0, 0, NULL, true, + if (virUSBDeviceFind(0x18d1, 0x4e22, 0, 0, NULL, NULL, true, USB_DEVICE_FIND_BY_VENDOR, &devlist) < 0) goto cleanup; @@ -211,7 +223,7 @@ testUSBList(const void *opaque G_GNUC_UNUSED) EXPECTED_NDEVS_ONE + EXPECTED_NDEVS_TWO) < 0) goto cleanup; - rv = virUSBDeviceFind(0x18d1, 0x4e22, 1, 20, NULL, true, + rv = virUSBDeviceFind(0x18d1, 0x4e22, 1, 20, NULL, NULL, true, USB_DEVICE_FIND_BY_VENDOR | USB_DEVICE_FIND_BY_DEVICE, &devs); if (rv != 1) { @@ -253,36 +265,50 @@ mymain(void) int rv = 0; #define DO_TEST_FIND_FULL(name, vend, prod, bus, devno, \ - vroot, mand, how, fail) \ + port, vroot, mand, how, fail) \ do { \ struct findTestInfo data = { name, vend, prod, bus, \ - devno, vroot, mand, how, fail \ + devno, port, vroot, mand, how, fail \ }; \ if (virTestRun("USBDeviceFind " name, testDeviceFind, &data) < 0) \ rv = -1; \ } while (0) #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) #define DO_TEST_FIND_BY_DEVICE(name, bus, devno) \ - DO_TEST_FIND_FULL(name, 0x1010, 0x2020, bus, devno, NULL, true, \ + DO_TEST_FIND_FULL(name, 0x1010, 0x2020, bus, devno, NULL, 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, \ + DO_TEST_FIND_FULL(name, 0x1010, 0x2020, bus, devno, NULL, NULL, true, \ FIND_BY_DEVICE, true) +#define DO_TEST_FIND_BY_PORT(name, bus, port) \ + DO_TEST_FIND_FULL(name, 0x1010, 0x2020, bus, 456, port, NULL, true, \ + FIND_BY_PORT, false) +#define DO_TEST_FIND_BY_PORT_FAIL(name, bus, port) \ + DO_TEST_FIND_FULL(name, 0x1010, 0x2020, bus, 456, port, NULL, true, \ + FIND_BY_PORT, true) + #define DO_TEST_FIND_BY_VENDOR_AND_DEVICE(name, vend, prod, bus, devno) \ - DO_TEST_FIND_FULL(name, vend, prod, bus, devno, NULL, true, \ + DO_TEST_FIND_FULL(name, vend, prod, bus, devno, NULL, 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, \ + DO_TEST_FIND_FULL(name, vend, prod, bus, devno, NULL, NULL, true, \ FIND_BY_VENDOR_AND_DEVICE, true) +#define DO_TEST_FIND_BY_VENDOR_AND_PORT(name, vend, prod, bus, port) \ + DO_TEST_FIND_FULL(name, vend, prod, bus, 456, port, NULL, true, \ + FIND_BY_VENDOR_AND_PORT, false) +#define DO_TEST_FIND_BY_VENDOR_AND_PORT_FAIL(name, vend, prod, bus, port) \ + DO_TEST_FIND_FULL(name, vend, prod, bus, 456, port, NULL, true, \ + FIND_BY_VENDOR_AND_PORT, 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); @@ -292,11 +318,21 @@ mymain(void) 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"); + 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); + DO_TEST_FIND_BY_VENDOR_AND_PORT("Nexus", 0x046d, 0xc069, 1, "1.5.3.3"); + DO_TEST_FIND_BY_VENDOR_AND_PORT_FAIL("Bogus vendor and product", 0xf00d, 0xbeef, 1, "1.5.3.3"); + DO_TEST_FIND_BY_VENDOR_AND_PORT_FAIL("Nexus wrong port", 0x18d1, 0x4e22, 1, "8.2.5"); + DO_TEST_FIND_BY_VENDOR_AND_PORT_FAIL("Bogus", 0xf00d, 0xbeef, 1024, "1.1.1.1"); + if (virTestRun("USB List test", testUSBList, NULL) < 0) rv = -1;