]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
conf: add support for host USB port matching
authorMaximilian Martin <maximilian_martin@gmx.de>
Wed, 11 Feb 2026 18:20:45 +0000 (18:20 +0000)
committerDaniel P. Berrangé <berrange@redhat.com>
Wed, 11 Feb 2026 18:25:33 +0000 (18:25 +0000)
This patch implements USB bus/port matching in the XML schema.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Maximilian Martin <maximilian_martin@gmx.de>
[DB: split host USB search parts out into previous patches]
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
src/conf/domain_conf.c
src/conf/domain_conf.h
src/hypervisor/virhostdev.c

index e88dc62520e56535a636d0e0dca2eb064ca6e898..00b6e1970c077ba2e74ff44e2c1c0b2c5d7f3fd5 100644 (file)
@@ -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, "<product id='0x%.4x'/>\n", usbsrc->product);
     }
 
-    if (usbsrc->bus || usbsrc->device)
+    if (usbsrc->bus && usbsrc->port) {
+        virBufferAsprintf(&sourceChildBuf, "<address %sbus='%d' port='%s'/>\n",
+                          includeTypeInAddr ? "type='usb' " : "",
+                          usbsrc->bus, usbsrc->port);
+    } else if (usbsrc->bus || usbsrc->device) {
         virBufferAsprintf(&sourceChildBuf, "<address %sbus='%d' device='%d'/>\n",
                           includeTypeInAddr ? "type='usb' " : "",
                           usbsrc->bus, usbsrc->device);
+    }
 
     virXMLFormatElement(buf, "source", &sourceAttrBuf, &sourceChildBuf);
 }
index 1e16310ef9ed3b8c768e709bdb7f264dc2cec534..a146f075e4363f49ed7c1b85eaad7ef59c6d5886 100644 (file)
@@ -229,6 +229,7 @@ struct _virDomainHostdevSubsysUSB {
                          on vendor/product */
     unsigned bus;
     unsigned device;
+    char *port;
 
     unsigned vendor;
     unsigned product;
index 7f1fb012be7b9d1a77571e0078feaaf1eec0cfba..19907c76ba8daba24139b6e00bda540944f7401b 100644 (file)
@@ -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;
     }