From: Maximilian Martin Date: Wed, 11 Feb 2026 18:20:45 +0000 (+0000) Subject: conf: add support for host USB port matching X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7a888d722cc9dca1342cefbe6c34326626f68a71;p=thirdparty%2Flibvirt.git conf: add support for host USB port matching This patch implements USB bus/port matching in the XML schema. Reviewed-by: Daniel P. Berrangé Signed-off-by: Maximilian Martin [DB: split host USB search parts out into previous patches] Signed-off-by: Daniel P. Berrangé --- diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e88dc62520..00b6e1970c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2698,6 +2698,15 @@ virDomainHostdevSubsysSCSIClear(virDomainHostdevSubsysSCSI *scsisrc) } } +static void +virDomainHostdevSubsysUSBClear(virDomainHostdevSubsysUSB *usbsrc) +{ + if (!usbsrc) + return; + + VIR_FREE(usbsrc->port); +} + static void virDomainHostdevDefClear(virDomainHostdevDef *def) @@ -2741,6 +2750,8 @@ virDomainHostdevDefClear(virDomainHostdevDef *def) g_clear_pointer(&def->source.subsys.u.pci.origstates, virBitmapFree); break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + virDomainHostdevSubsysUSBClear(&def->source.subsys.u.usb); + break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST: break; @@ -6028,13 +6039,38 @@ virDomainHostdevSubsysUSBDefParseXML(xmlNodePtr node, } if ((addressNode = virXPathNode("./address", ctxt))) { + bool foundDevice = false; + bool foundPort = false; + g_autofree char *port = NULL; + int rc = -1; + if (virXMLPropUInt(addressNode, "bus", 0, - VIR_XML_PROP_REQUIRED, &usbsrc->bus) < 0) + VIR_XML_PROP_REQUIRED, &usbsrc->bus) < 0) { return -1; + } - if (virXMLPropUInt(addressNode, "device", 0, - VIR_XML_PROP_REQUIRED, &usbsrc->device) < 0) + rc = virXMLPropUInt(addressNode, "device", 0, + VIR_XML_PROP_NONE, &usbsrc->device); + if (rc < 0) return -1; + else if (rc > 0) + foundDevice = true; + + port = virXMLPropString(addressNode, "port"); + if (port && *port) { + usbsrc->port = g_steal_pointer(&port); + foundPort = true; + } + + if (!foundDevice && !foundPort) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("usb address needs either device id or port")); + return -1; + } else if (foundDevice && foundPort) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("found both device id and port in usb address (ambiguous setting)")); + return -1; + } } return 0; @@ -15024,8 +15060,13 @@ virDomainHostdevMatchSubsysUSB(virDomainHostdevDef *first, virDomainHostdevSubsysUSB *first_usbsrc = &first->source.subsys.u.usb; virDomainHostdevSubsysUSB *second_usbsrc = &second->source.subsys.u.usb; - if (first_usbsrc->bus && first_usbsrc->device) { - /* specified by bus location on host */ + if (first_usbsrc->bus && first_usbsrc->port) { + /* specified by bus and port on host */ + if (first_usbsrc->bus == second_usbsrc->bus && + STREQ_NULLABLE(first_usbsrc->port, second_usbsrc->port)) + return 1; + } else if (first_usbsrc->bus && first_usbsrc->device) { + /* specified by bus and device id on host */ if (first_usbsrc->bus == second_usbsrc->bus && first_usbsrc->device == second_usbsrc->device) return 1; @@ -24864,10 +24905,15 @@ virDomainHostdevDefFormatSubsysUSB(virBuffer *buf, virBufferAsprintf(&sourceChildBuf, "\n", usbsrc->product); } - if (usbsrc->bus || usbsrc->device) + if (usbsrc->bus && usbsrc->port) { + virBufferAsprintf(&sourceChildBuf, "
\n", + includeTypeInAddr ? "type='usb' " : "", + usbsrc->bus, usbsrc->port); + } else if (usbsrc->bus || usbsrc->device) { virBufferAsprintf(&sourceChildBuf, "
\n", includeTypeInAddr ? "type='usb' " : "", usbsrc->bus, usbsrc->device); + } virXMLFormatElement(buf, "source", &sourceAttrBuf, &sourceChildBuf); } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 1e16310ef9..a146f075e4 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -229,6 +229,7 @@ struct _virDomainHostdevSubsysUSB { on vendor/product */ unsigned bus; unsigned device; + char *port; unsigned vendor; unsigned product; diff --git a/src/hypervisor/virhostdev.c b/src/hypervisor/virhostdev.c index 7f1fb012be..19907c76ba 100644 --- a/src/hypervisor/virhostdev.c +++ b/src/hypervisor/virhostdev.c @@ -1369,11 +1369,12 @@ virHostdevFindUSBDeviceWithFlags(virDomainHostdevDef *hostdev, unsigned vendor = usbsrc->vendor; unsigned product = usbsrc->product; unsigned bus = usbsrc->bus; + const char *port = usbsrc->port; unsigned device = usbsrc->device; g_autoptr(virUSBDeviceList) devs = NULL; int rc; - rc = virUSBDeviceFind(vendor, product, bus, device, NULL, NULL, + rc = virUSBDeviceFind(vendor, product, bus, device, port, NULL, mandatory, flags, &devs); if (rc < 0) return -1; @@ -1403,6 +1404,7 @@ virHostdevFindUSBDevice(virDomainHostdevDef *hostdev, unsigned vendor = usbsrc->vendor; unsigned bus = usbsrc->bus; unsigned device = usbsrc->device; + const char *port = usbsrc->port; bool autoAddress = usbsrc->autoAddress; unsigned int flags = 0; int rc; @@ -1413,14 +1415,19 @@ virHostdevFindUSBDevice(virDomainHostdevDef *hostdev, flags |= USB_DEVICE_FIND_BY_VENDOR; if (device) flags |= USB_DEVICE_FIND_BY_DEVICE; + if (port) + flags |= USB_DEVICE_FIND_BY_PORT; /* Rule out invalid cases. */ - if (vendor && device) { + if (vendor && device && port) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("Cannot match USB device on vendor/product and bus/device at once")); - } else if (!vendor && !device) { + _("Cannot match USB device on vendor/product, bus/device, and bus/port at once.")); + } else if (device && port) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("No matching fields for USB device found, vendor/product or bus/device required")); + _("Cannot match USB device on bus/device and bus/port at once.")); + } else if (!vendor && !device && !port) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("No matching fields for USB device found. Vendor/product, bus/device, or bus/port required.")); return -1; }