---
docs/schemas/domain.rng | 14 +++++
src/conf/domain_conf.c | 62 +++++++++++++++++++-
src/conf/domain_conf.h | 10 +++
src/qemu/qemu_command.c | 40 ++++++++++---
src/qemu/qemu_command.h | 6 +-
src/qemu/qemu_hotplug.c | 2 +-
.../qemuxml2argv-input-usbmouse-addr.args | 1 +
.../qemuxml2argv-input-usbmouse-addr.xml | 27 +++++++++
tests/qemuxml2argvtest.c | 2 +
9 files changed, 149 insertions(+), 15 deletions(-)
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng
index 82a4339..632e029 100644
--- a/docs/schemas/domain.rng
+++ b/docs/schemas/domain.rng
@@ -2029,6 +2029,14 @@
</attribute>
</element>
</define>
+ <define name="usbportaddress">
+ <attribute name="bus">
+ <ref name="usbAddr"/>
+ </attribute>
+ <attribute name="port">
+ <ref name="usbAddr"/>
+ </attribute>
+ </define>
<define name="pciaddress">
<optional>
<attribute name="domain">
@@ -2360,6 +2368,12 @@
</attribute>
<ref name="ccidaddress"/>
</group>
+ <group>
+ <attribute name="type">
+ <value>usb</value>
+ </attribute>
+ <ref name="usbportaddress"/>
+ </group>
</choice>
</element>
</define>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 10bc130..5487e0e 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -133,7 +133,8 @@ VIR_ENUM_IMPL(virDomainDeviceAddress,
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST,
"pci",
"drive",
"virtio-serial",
- "ccid")
+ "ccid",
+ "usb")
VIR_ENUM_IMPL(virDomainDisk, VIR_DOMAIN_DISK_TYPE_LAST,
"block",
@@ -1398,6 +1399,9 @@ int virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info,
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE:
return virDomainDeviceDriveAddressIsValid(&info->addr.drive);
+
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
+ return virDomainDeviceUSBAddressIsValid(&info->addr.usb);
}
return 0;
@@ -1419,6 +1423,13 @@ int
virDomainDeviceDriveAddressIsValid(virDomainDeviceDriveAddressPtr addr ATTRI
return 1; /* 0 is valid for all fields, so any successfully parsed addr is valid */
}
+int virDomainDeviceUSBAddressIsValid(virDomainDeviceUSBAddressPtr addr)
+{
+ if (addr->port >= 128) /* FIXME: is this correct */
+ return 0;
+
+ return 1;
+}
int virDomainDeviceVirtioSerialAddressIsValid(
virDomainDeviceVirtioSerialAddressPtr addr ATTRIBUTE_UNUSED)
@@ -1787,6 +1798,40 @@ cleanup:
return ret;
}
+static int
+virDomainDeviceUSBAddressParseXML(xmlNodePtr node,
+ virDomainDeviceUSBAddressPtr addr)
+{
+ char *port, *bus;
+ int ret = -1;
+
+ memset(addr, 0, sizeof(*addr));
+
+ port = virXMLPropString(node, "port");
+ bus = virXMLPropString(node, "bus");
+
+ if (port &&
+ virStrToLong_ui(port, NULL, 10, &addr->port) < 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cannot parse <address> 'port'
attribute"));
+ goto cleanup;
+ }
+
+ if (bus &&
+ virStrToLong_ui(bus, NULL, 10, &addr->bus) < 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cannot parse <address> 'bus'
attribute"));
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(bus);
+ VIR_FREE(port);
+ return ret;
+}
+
/* Parse the XML definition for a device address
* @param node XML nodeset to parse for device address definition
*/
@@ -1860,6 +1905,11 @@ virDomainDeviceInfoParseXML(xmlNodePtr node,
goto cleanup;
break;
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
+ if (virDomainDeviceUSBAddressParseXML(address, &info->addr.usb) < 0)
+ goto cleanup;
+ break;
+
default:
/* Should not happen */
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
@@ -3806,7 +3856,7 @@ error:
goto cleanup;
}
-/* Parse the XML definition for a network interface */
+/* Parse the XML definition for an input device */
static virDomainInputDefPtr
virDomainInputDefParseXML(const char *ostype,
xmlNodePtr node,
@@ -3884,6 +3934,14 @@ virDomainInputDefParseXML(const char *ostype,
if (virDomainDeviceInfoParseXML(node, &def->info, flags) < 0)
goto error;
+ if (def->bus == VIR_DOMAIN_INPUT_BUS_USB &&
+ def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
+ def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) {
+ virDomainReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Invalid address for a USB device"));
+ goto error;
+ }
+
cleanup:
VIR_FREE(type);
VIR_FREE(bus);
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 0f5e974..1ad8071 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -69,6 +69,7 @@ enum virDomainDeviceAddressType {
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE,
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL,
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID,
+ VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB,
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST
};
@@ -105,6 +106,13 @@ struct _virDomainDeviceCcidAddress {
unsigned int slot;
};
+typedef struct _virDomainDeviceUSBAddress virDomainDeviceUSBAddress;
+typedef virDomainDeviceUSBAddress *virDomainDeviceUSBAddressPtr;
+struct _virDomainDeviceUSBAddress {
+ unsigned int bus;
+ unsigned int port;
+};
+
typedef struct _virDomainDeviceInfo virDomainDeviceInfo;
typedef virDomainDeviceInfo *virDomainDeviceInfoPtr;
struct _virDomainDeviceInfo {
@@ -115,6 +123,7 @@ struct _virDomainDeviceInfo {
virDomainDeviceDriveAddress drive;
virDomainDeviceVirtioSerialAddress vioserial;
virDomainDeviceCcidAddress ccid;
+ virDomainDeviceUSBAddress usb;
} addr;
};
@@ -1437,6 +1446,7 @@ int virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info,
int virDomainDevicePCIAddressIsValid(virDomainDevicePCIAddressPtr addr);
int virDomainDeviceDriveAddressIsValid(virDomainDeviceDriveAddressPtr addr);
int virDomainDeviceVirtioSerialAddressIsValid(virDomainDeviceVirtioSerialAddressPtr
addr);
+int virDomainDeviceUSBAddressIsValid(virDomainDeviceUSBAddressPtr addr);
void virDomainDeviceInfoClear(virDomainDeviceInfoPtr info);
void virDomainDefClearPCIAddresses(virDomainDefPtr def);
void virDomainDefClearDeviceAliases(virDomainDefPtr def);
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index c64fe65..716f3fe 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1317,7 +1317,11 @@ qemuBuildDeviceAddressStr(virBufferPtr buf,
info->addr.pci.slot, info->addr.pci.function);
else
virBufferAsprintf(buf, ",addr=0x%x", info->addr.pci.slot);
+ } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) {
+ virBufferAsprintf(buf, ",bus=usb%d.0", info->addr.usb.bus);
+ virBufferAsprintf(buf, ",port=%d", info->addr.usb.port);
}
+
return 0;
}
@@ -2079,7 +2083,8 @@ error:
char *
-qemuBuildUSBInputDevStr(virDomainInputDefPtr dev)
+qemuBuildUSBInputDevStr(virDomainInputDefPtr dev,
+ virBitmapPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
@@ -2087,6 +2092,9 @@ qemuBuildUSBInputDevStr(virDomainInputDefPtr dev)
dev->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ?
"usb-mouse" : "usb-tablet",
dev->info.alias);
+ if (qemuBuildDeviceAddressStr(&buf, &dev->info, qemuCaps) < 0)
+ goto error;
+
if (virBufferError(&buf)) {
virReportOOMError();
goto error;
@@ -2275,9 +2283,10 @@ qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev)
char *
-qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev)
+qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev,
+ virBitmapPtr qemuCaps)
{
- char *ret = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
if (!dev->source.subsys.u.usb.bus &&
!dev->source.subsys.u.usb.device) {
@@ -2286,13 +2295,24 @@ qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev)
return NULL;
}
- if (virAsprintf(&ret, "usb-host,hostbus=%d,hostaddr=%d,id=%s",
- dev->source.subsys.u.usb.bus,
- dev->source.subsys.u.usb.device,
- dev->info.alias) < 0)
+ virBufferAsprintf(&buf, "usb-host,hostbus=%d,hostaddr=%d,id=%s",
+ dev->source.subsys.u.usb.bus,
+ dev->source.subsys.u.usb.device,
+ dev->info.alias);
+
+ if (qemuBuildDeviceAddressStr(&buf, &dev->info, qemuCaps) < 0)
+ goto error;
+
+ if (virBufferError(&buf)) {
virReportOOMError();
+ goto error;
+ }
- return ret;
+ return virBufferContentAndReset(&buf);
+
+error:
+ virBufferFreeAndReset(&buf);
+ return NULL;
}
@@ -4213,7 +4233,7 @@ qemuBuildCommandLine(virConnectPtr conn,
if (qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
char *optstr;
virCommandAddArg(cmd, "-device");
- if (!(optstr = qemuBuildUSBInputDevStr(input)))
+ if (!(optstr = qemuBuildUSBInputDevStr(input, qemuCaps)))
goto error;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
@@ -4731,7 +4751,7 @@ qemuBuildCommandLine(virConnectPtr conn,
if (qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
virCommandAddArg(cmd, "-device");
- if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev)))
+ if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev, qemuCaps)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index 099d683..de09577 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -98,7 +98,8 @@ char * qemuBuildWatchdogDevStr(virDomainWatchdogDefPtr dev,
char * qemuBuildMemballoonDevStr(virDomainMemballoonDefPtr dev,
virBitmapPtr qemuCaps);
-char * qemuBuildUSBInputDevStr(virDomainInputDefPtr dev);
+char * qemuBuildUSBInputDevStr(virDomainInputDefPtr dev,
+ virBitmapPtr qemuCaps);
char * qemuBuildSoundDevStr(virDomainSoundDefPtr sound,
virBitmapPtr qemuCaps);
@@ -115,7 +116,8 @@ int qemuOpenPCIConfig(virDomainHostdevDefPtr dev);
/* Legacy, pre device support */
char * qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev);
/* Current, best practice */
-char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev);
+char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev,
+ virBitmapPtr qemuCaps);
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index b2da6d0..60cd241 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -922,7 +922,7 @@ int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver,
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0)
goto error;
- if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev)))
+ if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev, priv->qemuCaps)))
goto error;
}
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args
b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args
new file mode 100644
index 0000000..b6dc0d3
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args
@@ -0,0 +1 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214
-smp 1 -nographic -nodefconfig -nodefaults -monitor unix:/tmp/test-monitor,server,nowait
-no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -device
usb-mouse,id=input0,bus=usb.0,port=4 -device
virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml
b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml
new file mode 100644
index 0000000..a2fa8e3
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml
@@ -0,0 +1,27 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory>219136</memory>
+ <currentMemory>219136</currentMemory>
+ <vcpu>1</vcpu>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu</emulator>
+ <disk type='block' device='disk'>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0'
unit='0'/>
+ </disk>
+ <controller type='ide' index='0'/>
+ <input type='mouse' bus='usb'>
+ <address type='usb' bus='0' port='4'/>
+ </input>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index f3abc24..b573380 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -493,6 +493,8 @@ mymain(void)
DO_TEST("usb-ich9-ehci-addr", false,
QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG,
QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_ICH9_USB_EHCI1);
+ DO_TEST("input-usbmouse-addr", false,
+ QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG);
DO_TEST("smbios", false, QEMU_CAPS_SMBIOS_TYPE);
--
1.7.6