From: David Waring <davidjw(a)rd.bbc.co.uk>
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(a)redhat.com>
---
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