[libvirt] [PATCH] usb: allow host devices to be specified by port

Previously, only VID/PID and bus/device matching were supported. Neither of these provide a stable and persistent way of assigning a guest a specific host device out of several with the same VID and PID, as device numbers change on every enumeration. Add a third method of matching, bus/port, which allows a specific port on the host to be specified using the dotted notation found in Linux's "devpath" sysfs attribute. --- src/conf/domain_conf.c | 36 ++++- src/conf/domain_conf.h | 2 + src/libvirt_private.syms | 2 - src/util/virhostdev.c | 126 ++++++++-------- src/util/virusb.c | 160 ++++++++------------- src/util/virusb.h | 23 ++- tests/virusbtest.c | 124 ++++++++++------ .../sys_bus_usb/devices/1-1.5.3.1/devpath | 1 + .../sys_bus_usb/devices/1-1.5.3.3/devpath | 1 + .../sys_bus_usb/devices/1-1.5.3/devpath | 1 + .../sys_bus_usb/devices/1-1.5.4/devpath | 1 + .../sys_bus_usb/devices/1-1.5.5/devpath | 1 + .../sys_bus_usb/devices/1-1.5.6/devpath | 1 + .../sys_bus_usb/devices/1-1.5/devpath | 1 + .../sys_bus_usb/devices/1-1.6/devpath | 1 + .../virusbtestdata/sys_bus_usb/devices/1-1/devpath | 1 + .../sys_bus_usb/devices/2-1.2/devpath | 1 + .../virusbtestdata/sys_bus_usb/devices/2-1/devpath | 1 + .../sys_bus_usb/devices/usb1/devpath | 1 + .../sys_bus_usb/devices/usb2/devpath | 1 + .../sys_bus_usb/devices/usb3/devpath | 1 + .../sys_bus_usb/devices/usb4/devpath | 1 + 22 files changed, 266 insertions(+), 222 deletions(-) create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.1/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.3/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.4/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.5/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5.6/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.5/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1.6/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/1-1/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/2-1.2/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/2-1/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/usb1/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/usb2/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/usb3/devpath create mode 100644 tests/virusbtestdata/sys_bus_usb/devices/usb4/devpath diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 9f7b906..a587cc2 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5531,7 +5531,8 @@ virDomainHostdevSubsysUSBDefParseXML(xmlNodePtr node, goto out; } } else if (xmlStrEqual(cur->name, BAD_CAST "address")) { - char *bus, *device; + char *bus, *device, *port; + bool dev_or_port = false; bus = virXMLPropString(cur, "bus"); if (bus) { @@ -5557,10 +5558,24 @@ virDomainHostdevSubsysUSBDefParseXML(xmlNodePtr node, VIR_FREE(device); goto out; } + dev_or_port = true; VIR_FREE(device); - } else { + } + + port = virXMLPropString(cur, "port"); + if (port) { + if (*port) { + usbsrc->port = port; + dev_or_port = true; + } else { + VIR_FREE(port); + } + } + + if (!dev_or_port) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("usb address needs device id")); + _("usb address needs either device id " + "or port")); goto out; } } else { @@ -20326,10 +20341,17 @@ virDomainHostdevDefFormatSubsys(virBufferPtr buf, virBufferAsprintf(buf, "<vendor id='0x%.4x'/>\n", usbsrc->vendor); virBufferAsprintf(buf, "<product id='0x%.4x'/>\n", usbsrc->product); } - if (usbsrc->bus || usbsrc->device) { - virBufferAsprintf(buf, "<address %sbus='%d' device='%d'/>\n", - includeTypeInAddr ? "type='usb' " : "", - usbsrc->bus, usbsrc->device); + if (usbsrc->bus || usbsrc->device || usbsrc->port) { + virBufferAddLit(buf, "<address"); + if (includeTypeInAddr) + virBufferAddLit(buf, " type='usb'"); + if (usbsrc->bus) + virBufferAsprintf(buf, " bus='%u'", usbsrc->bus); + if (usbsrc->device) + virBufferAsprintf(buf, " device='%u'", usbsrc->device); + if (usbsrc->port) + virBufferAsprintf(buf, " port='%s'", usbsrc->port); + virBufferAddLit(buf, "/>\n"); } break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ba0ad5f..91bd621 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -317,6 +317,8 @@ VIR_ENUM_DECL(virDomainHostdevSubsysSCSIProtocol) typedef struct _virDomainHostdevSubsysUSB virDomainHostdevSubsysUSB; typedef virDomainHostdevSubsysUSB *virDomainHostdevSubsysUSBPtr; struct _virDomainHostdevSubsysUSB { + char *port; + bool autoAddress; /* bus/device were filled automatically based on vendor/product */ unsigned bus; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ccb4c5e..d412eac 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2511,8 +2511,6 @@ virURIResolveAlias; # util/virusb.h virUSBDeviceFileIterate; virUSBDeviceFind; -virUSBDeviceFindByBus; -virUSBDeviceFindByVendor; virUSBDeviceFree; virUSBDeviceGetBus; virUSBDeviceGetDevno; diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c index 9b5ca6f..e809c97 100644 --- a/src/util/virhostdev.c +++ b/src/util/virhostdev.c @@ -1179,89 +1179,95 @@ virHostdevMarkUSBDevices(virHostdevManagerPtr mgr, static int +virHostdevFindUSBDeviceWithFlags(virDomainHostdevDefPtr hostdev, + bool mandatory, + unsigned int flags, + virUSBDevicePtr *usb) +{ + virDomainHostdevSubsysUSBPtr usbsrc = &hostdev->source.subsys.u.usb; + unsigned vendor = usbsrc->vendor; + unsigned product = usbsrc->product; + unsigned bus = usbsrc->bus; + char *port = usbsrc->port; + unsigned device = usbsrc->device; + virUSBDeviceListPtr devs; + int rc; + + rc = virUSBDeviceFind(vendor, product, bus, device, port, NULL, + mandatory, flags, &devs); + if (rc < 0) + return -1; + + if (rc == 1) { + *usb = virUSBDeviceListGet(devs, 0); + virUSBDeviceListSteal(devs, *usb); + } + virObjectUnref(devs); + + if (rc > 1) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Multiple USB devices for %x:%x, " + "use <address> to specify one"), + vendor, product); + return -1; + } + + return rc; +} + +static int virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev, bool mandatory, virUSBDevicePtr *usb) { virDomainHostdevSubsysUSBPtr usbsrc = &hostdev->source.subsys.u.usb; unsigned vendor = usbsrc->vendor; - unsigned product = usbsrc->product; unsigned bus = usbsrc->bus; unsigned device = usbsrc->device; + char *port = usbsrc->port; 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); - } - } + /* First attempt, matching on all known fields. */ + if (vendor) + flags |= USB_DEVICE_FIND_BY_VENDOR; + if (device) + flags |= USB_DEVICE_FIND_BY_DEVICE; + if (port) + flags |= USB_DEVICE_FIND_BY_PORT; + + rc = virHostdevFindUSBDeviceWithFlags(hostdev, + autoAddress ? false : mandatory, + flags, usb); + if (rc < 0) + 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) { - virUSBDeviceListPtr devs; + if (rc != 1 && autoAddress) { + VIR_INFO("USB device could not be found at previous address " + "(bus:%u device:%u)", bus, device); - rc = virUSBDeviceFindByVendor(vendor, product, NULL, mandatory, &devs); - if (rc < 0) - return -1; + /* Second attempt, for when the device number has changed. */ + flags &= ~((unsigned int) USB_DEVICE_FIND_BY_DEVICE); + usbsrc->device = 0; - if (rc == 1) { - *usb = virUSBDeviceListGet(devs, 0); - virUSBDeviceListSteal(devs, *usb); - } - virObjectUnref(devs); - - if (rc == 0) { - goto out; - } else if (rc > 1) { - if (autoAddress) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("Multiple USB devices for %x:%x were found," - " but none of them is at bus:%u device:%u"), - vendor, product, bus, device); - } else { - virReportError(VIR_ERR_OPERATION_FAILED, - _("Multiple USB devices for %x:%x, " - "use <address> to specify one"), - vendor, product); - } + rc = virHostdevFindUSBDeviceWithFlags(hostdev, mandatory, + flags, usb); + + if (rc < 0) return -1; - } + } + 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 (!vendor && bus) { - if (virUSBDeviceFindByBus(bus, device, NULL, mandatory, usb) < 0) - return -1; } - out: - if (!*usb) - hostdev->missing = true; return 0; } diff --git a/src/util/virusb.c b/src/util/virusb.c index 6a001a7..2e15849 100644 --- a/src/util/virusb.c +++ b/src/util/virusb.c @@ -68,12 +68,6 @@ struct _virUSBDeviceList { virUSBDevicePtr *devs; }; -typedef enum { - USB_DEVICE_ALL = 0, - USB_DEVICE_FIND_BY_VENDOR = 1 << 0, - USB_DEVICE_FIND_BY_BUS = 1 << 1, -} virUSBDeviceFindFlags; - static virClassPtr virUSBDeviceListClass; static void virUSBDeviceListDispose(void *obj); @@ -119,11 +113,33 @@ static int virUSBSysReadFile(const char *f_name, const char *d_name, return ret; } +static int virUSBSysReadFileStr(const char *f_name, const char *d_name, + char **value) +{ + int ret = -1, tmp; + char *buf = NULL; + char *filename = NULL; + + tmp = virAsprintf(&filename, USB_SYSFS "/devices/%s/%s", d_name, f_name); + if (tmp < 0) + goto cleanup; + + if (virFileReadAll(filename, 1024, &buf) < 0) + goto cleanup; + + *value = buf; + ret = 0; + cleanup: + VIR_FREE(filename); + return ret; +} + static virUSBDeviceListPtr virUSBDeviceSearch(unsigned int vendor, unsigned int product, unsigned int bus, unsigned int devno, + const char *port, const char *vroot, unsigned int flags) { @@ -143,6 +159,8 @@ virUSBDeviceSearch(unsigned int vendor, while ((direrr = virDirRead(dir, &de, USB_SYSFS "/devices")) > 0) { unsigned int found_prod, found_vend, found_bus, found_devno; + char *found_port; + bool port_matches; char *tmpstr = de->d_name; if (strchr(de->d_name, ':')) @@ -170,16 +188,31 @@ virUSBDeviceSearch(unsigned int vendor, 10, &found_devno) < 0) goto cleanup; + if (virUSBSysReadFileStr("devpath", de->d_name, + &found_port) < 0) { + goto cleanup; + } else { + found_port[strlen(found_port) - 1] = '\0'; /* remove newline */ + port_matches = STREQ_NULLABLE(found_port, port); + VIR_FREE(found_port); + } + if ((flags & USB_DEVICE_FIND_BY_VENDOR) && (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; } + 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) goto cleanup; @@ -204,36 +237,43 @@ virUSBDeviceSearch(unsigned int vendor, } int -virUSBDeviceFindByVendor(unsigned int vendor, - unsigned int product, - const char *vroot, - bool mandatory, - virUSBDeviceListPtr *devices) +virUSBDeviceFind(unsigned int vendor, + unsigned int product, + unsigned int bus, + unsigned int devno, + const char *port, + const char *vroot, + bool mandatory, + unsigned int flags, + virUSBDeviceListPtr *devices) { virUSBDeviceListPtr list; int count; - if (!(list = virUSBDeviceSearch(vendor, product, 0, 0, - vroot, - USB_DEVICE_FIND_BY_VENDOR))) + if (!(list = virUSBDeviceSearch(vendor, product, bus, devno, port, + vroot, flags))) return -1; - if (list->count == 0) { + count = list->count; + if (count == 0) { virObjectUnref(list); if (!mandatory) { - VIR_DEBUG("Did not find USB device %x:%x", - vendor, product); if (devices) *devices = NULL; return 0; } virReportError(VIR_ERR_INTERNAL_ERROR, - _("Did not find USB device %x:%x"), vendor, product); + _("Did not find a matching USB device (matching " + "on%s%s%s all others ignored): vid:%04x, pid:%04x, " + "bus:%u, device:%u, port:%s"), + flags & USB_DEVICE_FIND_BY_VENDOR ? " vid/pid," : "", + flags & USB_DEVICE_FIND_BY_DEVICE ? " bus/device," : "", + flags & USB_DEVICE_FIND_BY_PORT ? " bus/port," : "", + vendor, product, bus, devno, port ? port : "(null)"); return -1; } - count = list->count; if (devices) *devices = list; else @@ -242,86 +282,6 @@ virUSBDeviceFindByVendor(unsigned int vendor, return count; } -int -virUSBDeviceFindByBus(unsigned int bus, - unsigned int devno, - const char *vroot, - bool mandatory, - virUSBDevicePtr *usb) -{ - virUSBDeviceListPtr 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:%u device:%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, - unsigned int bus, - unsigned int devno, - const char *vroot, - bool mandatory, - virUSBDevicePtr *usb) -{ - virUSBDeviceListPtr list; - - 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); - if (!mandatory) { - VIR_DEBUG("Did not find USB device %x:%x bus:%u device:%u", - vendor, product, bus, devno); - if (usb) - *usb = NULL; - return 0; - } - - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Did not find USB device %x:%x bus:%u device:%u"), - vendor, product, bus, devno); - return -1; - } - - if (usb) { - *usb = virUSBDeviceListGet(list, 0); - virUSBDeviceListSteal(list, *usb); - } - virObjectUnref(list); - - return 0; -} - virUSBDevicePtr virUSBDeviceNew(unsigned int bus, unsigned int devno, diff --git a/src/util/virusb.h b/src/util/virusb.h index f98ea21..0834cd4 100644 --- a/src/util/virusb.h +++ b/src/util/virusb.h @@ -35,29 +35,26 @@ typedef virUSBDevice *virUSBDevicePtr; typedef struct _virUSBDeviceList virUSBDeviceList; typedef virUSBDeviceList *virUSBDeviceListPtr; +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; + virUSBDevicePtr virUSBDeviceNew(unsigned int bus, unsigned int devno, const char *vroot); -int virUSBDeviceFindByBus(unsigned int bus, - unsigned int devno, - const char *vroot, - bool mandatory, - virUSBDevicePtr *usb); - -int virUSBDeviceFindByVendor(unsigned int vendor, - unsigned int product, - const char *vroot, - bool mandatory, - virUSBDeviceListPtr *devices); - int virUSBDeviceFind(unsigned int vendor, unsigned int product, unsigned int bus, unsigned int devno, + const char *port, const char *vroot, bool mandatory, - virUSBDevicePtr *usb); + unsigned int flags, + virUSBDeviceListPtr *devices); void virUSBDeviceFree(virUSBDevicePtr dev); int virUSBDeviceSetUsedBy(virUSBDevicePtr dev, diff --git a/tests/virusbtest.c b/tests/virusbtest.c index 4bbfe4a..0782b73 100644 --- a/tests/virusbtest.c +++ b/tests/virusbtest.c @@ -33,7 +33,8 @@ typedef enum { FIND_BY_ALL, FIND_BY_VENDOR, - FIND_BY_BUS + FIND_BY_DEVICE, + FIND_BY_PORT } testUSBFindFlags; struct findTestInfo { @@ -42,6 +43,7 @@ struct findTestInfo { unsigned int product; unsigned int bus; unsigned int devno; + const char *port; const char *vroot; bool mandatory; int how; @@ -78,25 +80,31 @@ static int testDeviceFind(const void *opaque) virUSBDeviceListPtr 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); + flags = USB_DEVICE_FIND_BY_VENDOR | + USB_DEVICE_FIND_BY_DEVICE | + USB_DEVICE_FIND_BY_PORT; 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_BUS: - rv = virUSBDeviceFindByBus(info->bus, info->devno, - info->vroot, info->mandatory, &dev); + case FIND_BY_DEVICE: + flags = USB_DEVICE_FIND_BY_DEVICE; + break; + case FIND_BY_PORT: + flags = USB_DEVICE_FIND_BY_PORT; break; } + rv = virUSBDeviceFind(info->vendor, info->product, + info->bus, info->devno, info->port, + info->vroot, info->mandatory, flags, &devs); + if (info->expectFailure) { - if (rv == 0) { + if (rv >= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", "unexpected success"); } else { @@ -107,9 +115,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: if (virUSBDeviceFileIterate(dev, testDeviceFileActor, NULL) < 0) goto cleanup; break; @@ -155,14 +172,17 @@ testUSBList(const void *opaque ATTRIBUTE_UNUSED) virUSBDeviceListPtr list = NULL; virUSBDeviceListPtr devlist = NULL; virUSBDevicePtr dev = NULL; + virUSBDeviceListPtr 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, NULL, true, + USB_DEVICE_FIND_BY_VENDOR, &devlist) < 0) goto cleanup; ndevs = virUSBDeviceListCount(devlist); @@ -186,7 +206,8 @@ testUSBList(const void *opaque ATTRIBUTE_UNUSED) goto cleanup; #define EXPECTED_NDEVS_TWO 3 - if (virUSBDeviceFindByVendor(0x18d1, 0x4e22, NULL, true, &devlist) < 0) + if (virUSBDeviceFind(0x18d1, 0x4e22, 0, 0, NULL, NULL, true, + USB_DEVICE_FIND_BY_VENDOR, &devlist) < 0) goto cleanup; ndevs = virUSBDeviceListCount(devlist); @@ -206,8 +227,17 @@ 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) + rv = virUSBDeviceFind(0x18d1, 0x4e22, 1, 20, "1.5.6", NULL, true, + USB_DEVICE_FIND_BY_VENDOR | + USB_DEVICE_FIND_BY_DEVICE | + USB_DEVICE_FIND_BY_PORT, &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, @@ -239,49 +269,63 @@ mymain(void) { int rv = 0; -#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 \ - }; \ - if (virTestRun("USBDeviceFind " name, testDeviceFind, &data) < 0) \ - rv = -1; \ +#define DO_TEST_FIND_FULL(name, vend, prod, bus, devno, \ + port, vroot, mand, how, fail) \ + do { \ + struct findTestInfo data = { name, vend, prod, bus, \ + devno, port, vroot, mand, how, fail \ + }; \ + if (virTestRun("USBDeviceFind " name, testDeviceFind, &data) < 0) \ + rv = -1; \ } while (0) -#define DO_TEST_FIND(name, vend, prod, bus, devno) \ - DO_TEST_FIND_FULL(name, vend, prod, bus, devno, NULL, true, \ +#define DO_TEST_FIND(name, vend, prod, bus, devno, port) \ + DO_TEST_FIND_FULL(name, vend, prod, bus, devno, port, 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, \ +#define DO_TEST_FIND_FAIL(name, vend, prod, bus, devno, port) \ + DO_TEST_FIND_FULL(name, vend, prod, bus, devno, port, 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_DEVICE(name, bus, devno) \ + DO_TEST_FIND_FULL(name, 101, 202, bus, devno, NULL, NULL, true, \ + FIND_BY_DEVICE, false) +#define DO_TEST_FIND_BY_DEVICE_FAIL(name, bus, devno) \ + DO_TEST_FIND_FULL(name, 101, 202, bus, devno, NULL, NULL, true, \ + FIND_BY_DEVICE, 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, 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) - 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_PORT(name, bus, port) \ + DO_TEST_FIND_FULL(name, 101, 202, bus, 456, port, NULL, true, \ + FIND_BY_PORT, false) +#define DO_TEST_FIND_BY_PORT_FAIL(name, bus, port) \ + DO_TEST_FIND_FULL(name, 101, 202, bus, 456, port, NULL, true, \ + FIND_BY_PORT, true) + + DO_TEST_FIND("Nexus", 0x18d1, 0x4e22, 1, 20, "1.5.6"); + DO_TEST_FIND_FAIL("Nexus wrong devnum", 0x18d1, 0x4e22, 1, 25, "1.5.6"); + DO_TEST_FIND_FAIL("Nexus wrong port", 0x18d1, 0x4e22, 1, 25, "1.5.4"); + DO_TEST_FIND_FAIL("Bogus", 0xf00d, 0xbeef, 1024, 768, "1.2.3.4"); - 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); + 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_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"); + if (virTestRun("USB List test", testUSBList, NULL) < 0) rv = -1; diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.1/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.1/devpath new file mode 100644 index 0000000..02a7fbe --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.1/devpath @@ -0,0 +1 @@ +1.5.3.1 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.3/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.3/devpath new file mode 100644 index 0000000..23ca863 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3.3/devpath @@ -0,0 +1 @@ +1.5.3.3 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3/devpath new file mode 100644 index 0000000..8af85be --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.3/devpath @@ -0,0 +1 @@ +1.5.3 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.4/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.4/devpath new file mode 100644 index 0000000..94fe62c --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.4/devpath @@ -0,0 +1 @@ +1.5.4 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.5/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.5/devpath new file mode 100644 index 0000000..9075be4 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.5/devpath @@ -0,0 +1 @@ +1.5.5 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.6/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.6/devpath new file mode 100644 index 0000000..eac1e0a --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5.6/devpath @@ -0,0 +1 @@ +1.5.6 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.5/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5/devpath new file mode 100644 index 0000000..c239c60 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.5/devpath @@ -0,0 +1 @@ +1.5 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1.6/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1.6/devpath new file mode 100644 index 0000000..810ee4e --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1.6/devpath @@ -0,0 +1 @@ +1.6 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/1-1/devpath b/tests/virusbtestdata/sys_bus_usb/devices/1-1/devpath new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/1-1/devpath @@ -0,0 +1 @@ +1 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/2-1.2/devpath b/tests/virusbtestdata/sys_bus_usb/devices/2-1.2/devpath new file mode 100644 index 0000000..5625e59 --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/2-1.2/devpath @@ -0,0 +1 @@ +1.2 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/2-1/devpath b/tests/virusbtestdata/sys_bus_usb/devices/2-1/devpath new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/2-1/devpath @@ -0,0 +1 @@ +1 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/usb1/devpath b/tests/virusbtestdata/sys_bus_usb/devices/usb1/devpath new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/usb1/devpath @@ -0,0 +1 @@ +0 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/usb2/devpath b/tests/virusbtestdata/sys_bus_usb/devices/usb2/devpath new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/usb2/devpath @@ -0,0 +1 @@ +0 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/usb3/devpath b/tests/virusbtestdata/sys_bus_usb/devices/usb3/devpath new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/usb3/devpath @@ -0,0 +1 @@ +0 diff --git a/tests/virusbtestdata/sys_bus_usb/devices/usb4/devpath b/tests/virusbtestdata/sys_bus_usb/devices/usb4/devpath new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/virusbtestdata/sys_bus_usb/devices/usb4/devpath @@ -0,0 +1 @@ +0 -- 2.7.4

Thanks for the patch. Sorry this didn't get a timely response. Note there's an upstream bug for this as well but it doesn't add much: https://bugzilla.redhat.com/show_bug.cgi?id=914883 The patch is a bit difficult to review because it's mixing up some refactorings in virhostdev.c with the functional additions. Please separate the refactoring and submit it separately, before the functional changes. Also missing is docs/formatdomain.html.in extension, which can go in the patch with the functional changes. We can help with that though if you don't want to wade into editing XML. On 07/05/2016 05:21 PM, Thomas Hebb wrote:
Previously, only VID/PID and bus/device matching were supported. Neither of these provide a stable and persistent way of assigning a guest a specific host device out of several with the same VID and PID, as device numbers change on every enumeration.
Add a third method of matching, bus/port, which allows a specific port on the host to be specified using the dotted notation found in Linux's "devpath" sysfs attribute.
Is there any way to determine the host port outside of checking sysfs? Some 'lsusb' switch that makes this easy to lookup? Thanks, Cole
participants (2)
-
Cole Robinson
-
Thomas Hebb