From: David Waring <davidjw@xxxxxxxxxxxx> This allows specific USB devices to attached to guests when there may be more than one of the same USB device attached to a host. The serial number is optional so that without it existing behaviour is maintained. https://bugzilla.redhat.com/show_bug.cgi?id=914883 Signed-off-by: Ján Tomko <jtomko@xxxxxxxxxx> --- src/util/virhostdev.c | 17 +++---- src/util/virusb.c | 56 +++++++++++++++++++--- src/util/virusb.h | 2 + tests/virusbtest.c | 40 +++++++++++----- .../sys_bus_usb/devices/1-1.5.3.1/serial | 1 + .../sys_bus_usb/devices/1-1.5.3.3/serial | 1 + .../sys_bus_usb/devices/1-1.5.5/serial | 1 + .../sys_bus_usb/devices/1-1.5.6/serial | 1 + .../sys_bus_usb/devices/1-1.5/serial | 1 + .../sys_bus_usb/devices/1-1.6/serial | 1 + .../virusbtestdata/sys_bus_usb/devices/1-1/serial | 1 + .../sys_bus_usb/devices/2-1.2/serial | 1 + .../virusbtestdata/sys_bus_usb/devices/2-1/serial | 1 + .../virusbtestdata/sys_bus_usb/devices/usb1/serial | 1 + .../virusbtestdata/sys_bus_usb/devices/usb2/serial | 1 + .../virusbtestdata/sys_bus_usb/devices/usb3/serial | 1 + .../virusbtestdata/sys_bus_usb/devices/usb4/serial | 1 + 17 files changed, 102 insertions(+), 26 deletions(-) create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.1/serial create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.3/serial create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.5/serial create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.6/serial create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5/serial create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.6/serial create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1/serial create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/2-1.2/serial create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/2-1/serial create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/usb1/serial create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/usb2/serial create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/usb3/serial create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/usb4/serial diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c index 9dd1df2..ad652c7 100644 --- a/src/util/virhostdev.c +++ b/src/util/virhostdev.c @@ -1049,6 +1049,7 @@ virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev, { unsigned vendor = hostdev->source.subsys.u.usb.vendor; unsigned product = hostdev->source.subsys.u.usb.product; + const char *serial = hostdev->source.subsys.u.usb.serial; unsigned bus = hostdev->source.subsys.u.usb.bus; unsigned device = hostdev->source.subsys.u.usb.device; bool autoAddress = hostdev->source.subsys.u.usb.autoAddress; @@ -1057,7 +1058,7 @@ virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev, *usb = NULL; if (vendor && bus) { - rc = virUSBDeviceFind(vendor, product, bus, device, + rc = virUSBDeviceFind(vendor, product, serial, bus, device, NULL, autoAddress ? false : mandatory, usb); @@ -1066,9 +1067,9 @@ virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev, } 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); + VIR_INFO("USB device %x:%x (serial: %s) could not be found" + " at previous address (bus:%u device:%u)", + vendor, product, serial ? serial : _("none"), bus, device); } } @@ -1079,7 +1080,7 @@ virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev, if (vendor) { virUSBDeviceListPtr devs; - rc = virUSBDeviceFindByVendor(vendor, product, NULL, mandatory, &devs); + rc = virUSBDeviceFindByVendor(vendor, product, serial, NULL, mandatory, &devs); if (rc < 0) return -1; @@ -1100,7 +1101,7 @@ virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev, } else { virReportError(VIR_ERR_OPERATION_FAILED, _("Multiple USB devices for %x:%x, " - "use <address> to specify one"), + "use <address> or <serial> to specify one"), vendor, product); } return -1; @@ -1111,9 +1112,9 @@ virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev, hostdev->source.subsys.u.usb.autoAddress = true; if (autoAddress) { - VIR_INFO("USB device %x:%x found at bus:%u device:%u (moved" + VIR_INFO("USB device %x:%x (serial: %s) found at bus:%u device:%u (moved" " from bus:%u device:%u)", - vendor, product, + vendor, product, serial ? serial : _("none"), hostdev->source.subsys.u.usb.bus, hostdev->source.subsys.u.usb.device, bus, device); diff --git a/src/util/virusb.c b/src/util/virusb.c index 8244771..5dc24b5 100644 --- a/src/util/virusb.c +++ b/src/util/virusb.c @@ -56,6 +56,7 @@ struct _virUSBDevice { char name[USB_ADDR_LEN]; /* domain:bus:slot.function */ char id[USB_ID_LEN]; /* product vendor */ + char *serial; /* serial number */ char *path; /* driver:domain using this dev */ @@ -92,6 +93,34 @@ static int virUSBOnceInit(void) VIR_ONCE_GLOBAL_INIT(virUSB) +static int virUSBSysReadFileString(const char *f_name, const char *d_name, char **buf) +{ + int ret = -1, len; + char *filename = NULL; + + *buf = NULL; + + if (virAsprintf(&filename, USB_SYSFS "/devices/%s/%s", d_name, f_name) < 0) + return -1; + + if (!virFileExists(filename)) { + ret = 0; + goto cleanup; + } + + if ((len = virFileReadAll(filename, 1024, buf)) < 0) + goto cleanup; + + if (len > 0 && (*buf)[len - 1] == '\n') + (*buf)[len - 1] = '\0'; + + ret = 0; + + cleanup: + VIR_FREE(filename); + return ret; +} + static int virUSBSysReadFile(const char *f_name, const char *d_name, int base, unsigned int *value) { @@ -123,6 +152,7 @@ static int virUSBSysReadFile(const char *f_name, const char *d_name, static virUSBDeviceListPtr virUSBDeviceSearch(unsigned int vendor, unsigned int product, + const char *serial, unsigned int bus, unsigned int devno, const char *vroot, @@ -131,6 +161,7 @@ virUSBDeviceSearch(unsigned int vendor, DIR *dir = NULL; bool found = false; char *ignore = NULL; + char *found_serial = NULL; struct dirent *de; virUSBDeviceListPtr list = NULL, ret = NULL; virUSBDevicePtr usb; @@ -162,6 +193,10 @@ virUSBDeviceSearch(unsigned int vendor, 16, &found_prod) < 0) goto cleanup; + VIR_FREE(found_serial); + if (virUSBSysReadFileString("serial", de->d_name, &found_serial) < 0) + goto cleanup; + if (STRPREFIX(de->d_name, "usb")) tmpstr += 3; @@ -177,7 +212,8 @@ virUSBDeviceSearch(unsigned int vendor, goto cleanup; if ((flags & USB_DEVICE_FIND_BY_VENDOR) && - (found_prod != product || found_vend != vendor)) + (found_prod != product || found_vend != vendor || + (serial != NULL && !STREQ_NULLABLE(found_serial, serial)))) continue; if (flags & USB_DEVICE_FIND_BY_BUS) { @@ -203,6 +239,8 @@ virUSBDeviceSearch(unsigned int vendor, ret = list; cleanup: + VIR_FREE(found_serial); + if (dir) { int saved_errno = errno; closedir(dir); @@ -217,6 +255,7 @@ virUSBDeviceSearch(unsigned int vendor, int virUSBDeviceFindByVendor(unsigned int vendor, unsigned int product, + const char *serial, const char *vroot, bool mandatory, virUSBDeviceListPtr *devices) @@ -224,7 +263,7 @@ virUSBDeviceFindByVendor(unsigned int vendor, virUSBDeviceListPtr list; int count; - if (!(list = virUSBDeviceSearch(vendor, product, 0, 0, + if (!(list = virUSBDeviceSearch(vendor, product, serial, 0, 0, vroot, USB_DEVICE_FIND_BY_VENDOR))) return -1; @@ -232,15 +271,16 @@ virUSBDeviceFindByVendor(unsigned int vendor, if (list->count == 0) { virObjectUnref(list); if (!mandatory) { - VIR_DEBUG("Did not find USB device %x:%x", - vendor, product); + VIR_DEBUG("Did not find USB device %x:%x (serial: %s)", + vendor, product, NULLSTR(serial)); if (devices) *devices = NULL; return 0; } virReportError(VIR_ERR_INTERNAL_ERROR, - _("Did not find USB device %x:%x"), vendor, product); + _("Did not find USB device %x:%x (serial: %s)"), + vendor, product, serial ? serial : _("none")); return -1; } @@ -262,7 +302,7 @@ virUSBDeviceFindByBus(unsigned int bus, { virUSBDeviceListPtr list; - if (!(list = virUSBDeviceSearch(0, 0, bus, devno, + if (!(list = virUSBDeviceSearch(0, 0, NULL, bus, devno, vroot, USB_DEVICE_FIND_BY_BUS))) return -1; @@ -295,6 +335,7 @@ virUSBDeviceFindByBus(unsigned int bus, int virUSBDeviceFind(unsigned int vendor, unsigned int product, + const char *serial, unsigned int bus, unsigned int devno, const char *vroot, @@ -304,7 +345,7 @@ virUSBDeviceFind(unsigned int vendor, virUSBDeviceListPtr list; unsigned int flags = USB_DEVICE_FIND_BY_VENDOR|USB_DEVICE_FIND_BY_BUS; - if (!(list = virUSBDeviceSearch(vendor, product, bus, devno, + if (!(list = virUSBDeviceSearch(vendor, product, serial, bus, devno, vroot, flags))) return -1; @@ -382,6 +423,7 @@ virUSBDeviceFree(virUSBDevicePtr dev) if (!dev) return; VIR_DEBUG("%s %s: freeing", dev->id, dev->name); + VIR_FREE(dev->serial); VIR_FREE(dev->path); VIR_FREE(dev->used_by_drvname); VIR_FREE(dev->used_by_domname); diff --git a/src/util/virusb.h b/src/util/virusb.h index f98ea21..bb7644f 100644 --- a/src/util/virusb.h +++ b/src/util/virusb.h @@ -47,12 +47,14 @@ int virUSBDeviceFindByBus(unsigned int bus, int virUSBDeviceFindByVendor(unsigned int vendor, unsigned int product, + const char *serial, const char *vroot, bool mandatory, virUSBDeviceListPtr *devices); int virUSBDeviceFind(unsigned int vendor, unsigned int product, + const char *serial, unsigned int bus, unsigned int devno, const char *vroot, diff --git a/tests/virusbtest.c b/tests/virusbtest.c index d08e03b..c1a07a1 100644 --- a/tests/virusbtest.c +++ b/tests/virusbtest.c @@ -40,6 +40,7 @@ struct findTestInfo { const char *name; unsigned int vendor; unsigned int product; + char *serial; unsigned int bus; unsigned int devno; const char *vroot; @@ -82,11 +83,13 @@ static int testDeviceFind(const void *opaque) switch (info->how) { case FIND_BY_ALL: rv = virUSBDeviceFind(info->vendor, info->product, + info->serial, info->bus, info->devno, info->vroot, info->mandatory, &dev); break; case FIND_BY_VENDOR: rv = virUSBDeviceFindByVendor(info->vendor, info->product, + info->serial, info->vroot, info->mandatory, &devs); break; case FIND_BY_BUS: @@ -162,7 +165,7 @@ testUSBList(const void *opaque ATTRIBUTE_UNUSED) goto cleanup; #define EXPECTED_NDEVS_ONE 3 - if (virUSBDeviceFindByVendor(0x1d6b, 0x0002, NULL, true, &devlist) < 0) + if (virUSBDeviceFindByVendor(0x1d6b, 0x0002, NULL, NULL, true, &devlist) < 0) goto cleanup; ndevs = virUSBDeviceListCount(devlist); @@ -186,7 +189,7 @@ testUSBList(const void *opaque ATTRIBUTE_UNUSED) goto cleanup; #define EXPECTED_NDEVS_TWO 3 - if (virUSBDeviceFindByVendor(0x18d1, 0x4e22, NULL, true, &devlist) < 0) + if (virUSBDeviceFindByVendor(0x18d1, 0x4e22, NULL, NULL, true, &devlist) < 0) goto cleanup; ndevs = virUSBDeviceListCount(devlist); @@ -206,7 +209,7 @@ 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) + if (virUSBDeviceFind(0x18d1, 0x4e22, NULL, 1, 20, NULL, true, &dev) < 0) goto cleanup; if (!virUSBDeviceListFind(list, dev)) { @@ -239,9 +242,10 @@ 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, serial, bus, devno, vroot, \ + mand, how, fail) \ do { \ - struct findTestInfo data = { name, vend, prod, bus, \ + struct findTestInfo data = { name, vend, prod, serial, bus, \ devno, vroot, mand, how, fail \ }; \ if (virtTestRun("USBDeviceFind " name, testDeviceFind, &data) < 0) \ @@ -249,24 +253,31 @@ mymain(void) } while (0) #define DO_TEST_FIND(name, vend, prod, bus, devno) \ - DO_TEST_FIND_FULL(name, vend, prod, bus, devno, NULL, true, \ + DO_TEST_FIND_FULL(name, vend, prod, NULL, 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, \ + DO_TEST_FIND_FULL(name, vend, prod, NULL, 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, \ + DO_TEST_FIND_FULL(name, 101, 202, NULL, 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, \ + DO_TEST_FIND_FULL(name, 101, 202, NULL, 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, \ + DO_TEST_FIND_FULL(name, vend, prod, NULL, 123, 456, 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, NULL, 123, 456, NULL, true, \ + FIND_BY_VENDOR, true) + +#define DO_TEST_FIND_BY_SERIAL(name, vend, prod, serial) \ + DO_TEST_FIND_FULL(name, vend, prod, NULL, 123, 456, NULL, true, \ + FIND_BY_VENDOR, false) +#define DO_TEST_FIND_BY_SERIAL_FAIL(name, vend, prod, serial) \ + DO_TEST_FIND_FULL(name, vend, prod, NULL, 123, 456, NULL, true, \ FIND_BY_VENDOR, true) DO_TEST_FIND("Nexus", 0x18d1, 0x4e22, 1, 20); @@ -282,6 +293,13 @@ 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_SERIAL("Nexus (serial string)", 0x18d1, 0x4e22, + "something something"); + DO_TEST_FIND_BY_SERIAL("Nexus (serial number)", 0x18d1, 0x4e22, + "0118999881999119725 3"); + DO_TEST_FIND_BY_SERIAL_FAIL("Nexus (wrong serial)", 0x18d1, 0x4e22, + "0118999881999119725 3"); + if (virtTestRun("USB List test", testUSBList, NULL) < 0) rv = -1; diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.1/serial b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.1/serial new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.1/serial @@ -0,0 +1 @@ + diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.3/serial b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.3/serial new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.3/serial @@ -0,0 +1 @@ + diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.5/serial b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.5/serial new file mode 100644 index 0000000..a8aae02 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.5/serial @@ -0,0 +1 @@ +something something diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.6/serial b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.6/serial new file mode 100644 index 0000000..5b4cae7 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.6/serial @@ -0,0 +1 @@ +0118999881999119725 3 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5/serial b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5/serial new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5/serial @@ -0,0 +1 @@ + diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.6/serial b/tests/virusbtestdata/sys_bus_usb/devices/1-1.6/serial new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.6/serial @@ -0,0 +1 @@ + diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1/serial b/tests/virusbtestdata/sys_bus_usb/devices/1-1/serial new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1/serial @@ -0,0 +1 @@ + diff --git a/tests/virusbtestdata/sys_bus_usb/devices/2-1.2/serial b/tests/virusbtestdata/sys_bus_usb/devices/2-1.2/serial new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/2-1.2/serial @@ -0,0 +1 @@ + diff --git a/tests/virusbtestdata/sys_bus_usb/devices/2-1/serial b/tests/virusbtestdata/sys_bus_usb/devices/2-1/serial new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/2-1/serial @@ -0,0 +1 @@ + diff --git a/tests/virusbtestdata/sys_bus_usb/devices/usb1/serial b/tests/virusbtestdata/sys_bus_usb/devices/usb1/serial new file mode 100644 index 0000000..7f434da --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/usb1/serial @@ -0,0 +1 @@ +0000:00:1a.0 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/usb2/serial b/tests/virusbtestdata/sys_bus_usb/devices/usb2/serial new file mode 100644 index 0000000..3fed695 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/usb2/serial @@ -0,0 +1 @@ +0000:00:1d.0 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/usb3/serial b/tests/virusbtestdata/sys_bus_usb/devices/usb3/serial new file mode 100644 index 0000000..0d45813 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/usb3/serial @@ -0,0 +1 @@ +0000:0d:00.0 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/usb4/serial b/tests/virusbtestdata/sys_bus_usb/devices/usb4/serial new file mode 100644 index 0000000..0d45813 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/usb4/serial @@ -0,0 +1 @@ +0000:0d:00.0 -- 1.8.3.2 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list