diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c index 365ee40ab6..c3469eec9f 100644 --- a/src/conf/domain_addr.c +++ b/src/conf/domain_addr.c @@ -1604,6 +1604,26 @@ virDomainUSBAddressFindFreePort(virDomainUSBAddressHubPtr hub, } +size_t +virDomainUSBAddressCountAllPorts(virDomainDefPtr def) +{ + size_t i, ret = 0; + + for (i = 0; i < def->ncontrollers; i++) { + virDomainControllerDefPtr cont = def->controllers[i]; + if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) + ret += virDomainUSBAddressControllerModelToPorts(cont); + } + + for (i = 0; i < def->nhubs; i++) { + virDomainHubDefPtr hub = def->hubs[i]; + if (hub->type == VIR_DOMAIN_HUB_TYPE_USB) + ret += VIR_DOMAIN_USB_HUB_PORTS; + } + return ret; +} + + /* Try to find a free port on bus @bus. * * Returns 0 on success diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h index cc36aedd52..ce949812dd 100644 --- a/src/conf/domain_addr.h +++ b/src/conf/domain_addr.h @@ -277,6 +277,8 @@ int virDomainUSBAddressSetAddHub(virDomainUSBAddressSetPtr addrs, virDomainHubDefPtr hub) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +size_t +virDomainUSBAddressCountAllPorts(virDomainDefPtr def); void virDomainUSBAddressSetFree(virDomainUSBAddressSetPtr addrs); int diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index be753333ac..9396c4eb1b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -108,6 +108,7 @@ virDomainPCIAddressSlotInUse; virDomainPCIAddressValidate; virDomainPCIControllerModelToConnectType; virDomainUSBAddressAssign; +virDomainUSBAddressCountAllPorts; virDomainUSBAddressEnsure; virDomainUSBAddressPortFormat; virDomainUSBAddressPortFormatBuf; diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index 7499026f16..787b357d82 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -1679,6 +1679,51 @@ qemuDomainAssignUSBPorts(virDomainUSBAddressSetPtr addrs, } +static int +qemuDomainAssignUSBPortsCounter(virDomainDeviceInfoPtr info ATTRIBUTE_UNUSED, + void *opaque) +{ + struct qemuAssignUSBIteratorInfo *data = opaque; + + data->count++; + return 0; +} + + +static int +qemuDomainUSBAddressAddHubs(virDomainDefPtr def) +{ + struct qemuAssignUSBIteratorInfo data = { .count = 0 }; + virDomainHubDefPtr hub = NULL; + size_t available_ports; + int ret = -1; + + available_ports = virDomainUSBAddressCountAllPorts(def); + ignore_value(virDomainUSBDeviceDefForeach(def, + qemuDomainAssignUSBPortsCounter, + &data, + false)); + VIR_DEBUG("Found %zu USB devices and %zu provided USB ports", + data.count, available_ports); + + /* Add one hub if there are more devices than ports + * otherwise it's up to the user to specify more hubs/controllers */ + if (data.count > available_ports) { + if (VIR_ALLOC(hub) < 0) + return -1; + hub->type = VIR_DOMAIN_HUB_TYPE_USB; + + if (VIR_APPEND_ELEMENT(def->hubs, def->nhubs, hub) < 0) + goto cleanup; + } + + ret = 0; + cleanup: + VIR_FREE(hub); + return ret; +} + + static int qemuDomainAssignUSBAddresses(virDomainDefPtr def, virDomainObjPtr obj) @@ -1690,6 +1735,9 @@ qemuDomainAssignUSBAddresses(virDomainDefPtr def, if (!(addrs = virDomainUSBAddressSetCreate())) goto cleanup; + if (qemuDomainUSBAddressAddHubs(def) < 0) + goto cleanup; + if (virDomainUSBAddressSetAddControllers(addrs, def) < 0) goto cleanup; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.args new file mode 100644 index 0000000000..7467893ecb --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.args @@ -0,0 +1,28 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu \ +-name QEMUGuest1 \ +-S \ +-M pc \ +-m 214 \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefconfig \ +-nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,\ +server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=readline \ +-no-acpi \ +-boot c \ +-usb \ +-device usb-hub,id=hub0,bus=usb.0,port=1 \ +-device usb-mouse,id=input0,bus=usb.0,port=2 \ +-device usb-mouse,id=input1,bus=usb.0,port=1.1 \ +-device usb-mouse,id=input2,bus=usb.0,port=1.2 \ +-device usb-tablet,id=input3,bus=usb.0,port=1.3 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.xml new file mode 100644 index 0000000000..43e0f1f35b --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.xml @@ -0,0 +1,23 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219136 + 219136 + 1 + + hvm + + + + /usr/bin/qemu + + + + + + + + + + + diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index c99568b8f6..356f8435f5 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1184,6 +1184,9 @@ mymain(void) DO_TEST("usb-hub", QEMU_CAPS_CHARDEV, QEMU_CAPS_USB_HUB, QEMU_CAPS_NODEFCONFIG); + DO_TEST("usb-hub-autoadd", + QEMU_CAPS_CHARDEV, QEMU_CAPS_USB_HUB, + QEMU_CAPS_NODEFCONFIG); DO_TEST_PARSE_ERROR("usb-hub-conflict", QEMU_CAPS_CHARDEV, QEMU_CAPS_USB_HUB, QEMU_CAPS_NODEFCONFIG);