Fixed since v1:
- avoid setup/restore security contexts
- some hotplug code improvements, although the lack of dynamic chardev
limits the utility of this code for now
---
docs/formatdomain.html.in | 15 ++++-
docs/schemas/domain.rng | 66 ++++++++++++--------
src/conf/domain_conf.c | 63 ++++++++++++++++---
src/conf/domain_conf.h | 2 +
src/qemu/qemu_cgroup.c | 3 +-
src/qemu/qemu_command.c | 36 +++++++++--
src/qemu/qemu_hostdev.c | 2 +
src/qemu/qemu_hotplug.c | 54 +++++++++++++++-
src/qemu/qemu_hotplug.h | 3 +
src/security/security_dac.c | 6 ++
src/security/security_selinux.c | 6 ++
tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args | 8 +++
tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml | 33 ++++++++++
tests/qemuxml2argvtest.c | 4 +
14 files changed, 255 insertions(+), 46 deletions(-)
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index e35b76b..0ab08de 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1314,6 +1314,12 @@
0.4.4 for USB and 0.6.0 for PCI (KVM only)</span>:
</p>
+ <p>
+ Device redirection through a character device is
+ supported <span class="since">since after 0.9.5 for USB (KVM
+ only)</span>:
+ </p>
+
<pre>
...
<devices>
@@ -1348,14 +1354,19 @@
"subsystem" and <code>type</code> is "usb" for a
USB device and "pci"
for a PCI device. When <code>managed</code> is "yes" for a
PCI
device, it is detached from the host before being passed on to
- the guest.</dd>
+ the guest. Redirection through a character device is enable by
+ specifying the <code>redirection</code> character device
+ type.</dd>
<dt><code>source</code></dt>
<dd>The source element describes the device as seen from the host.
The USB device can either be addressed by vendor / product id using the
<code>vendor</code> and <code>product</code> elements or by
the device's
address on the hosts using the <code>address</code> element.
PCI devices on the other hand can only be described by their
- <code>address</code></dd>
+ <code>address</code>
+ In case of device redirection, the source element describes the
+ character device to redirect from.
+ </dd>
<dt><code>vendor</code>,
<code>product</code></dt>
<dd>The <code>vendor</code> and <code>product</code>
elements each have an
<code>id</code> attribute that specifies the USB vendor and product
id.
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng
index b496a32..9b790f6 100644
--- a/docs/schemas/domain.rng
+++ b/docs/schemas/domain.rng
@@ -1723,21 +1723,25 @@
</element>
</define>
+ <define name="qemucdevSrcTypeChoice">
+ <choice>
+ <value>dev</value>
+ <value>file</value>
+ <value>pipe</value>
+ <value>unix</value>
+ <value>tcp</value>
+ <value>udp</value>
+ <value>null</value>
+ <value>stdio</value>
+ <value>vc</value>
+ <value>pty</value>
+ <value>spicevmc</value>
+ </choice>
+ </define>
+
<define name="qemucdevSrcType">
<attribute name="type">
- <choice>
- <value>dev</value>
- <value>file</value>
- <value>pipe</value>
- <value>unix</value>
- <value>tcp</value>
- <value>udp</value>
- <value>null</value>
- <value>stdio</value>
- <value>vc</value>
- <value>pty</value>
- <value>spicevmc</value>
- </choice>
+ <ref name="qemucdevSrcTypeChoice"/>
</attribute>
</define>
<define name="qemucdevSrcDef">
@@ -1998,21 +2002,29 @@
</choice>
</attribute>
</optional>
+ <optional>
+ <attribute name="redirection">
+ <ref name="qemucdevSrcTypeChoice"/>
+ </attribute>
+ </optional>
<group>
- <element name="source">
- <choice>
- <group>
- <ref name="usbproduct"/>
- <optional>
- <ref name="usbaddress"/>
- </optional>
- </group>
- <ref name="usbaddress"/>
- <element name="address">
- <ref name="pciaddress"/>
- </element>
- </choice>
- </element>
+ <choice>
+ <ref name="qemucdevSrcDef"/>
+ <element name="source">
+ <choice>
+ <group>
+ <ref name="usbproduct"/>
+ <optional>
+ <ref name="usbaddress"/>
+ </optional>
+ </group>
+ <ref name="usbaddress"/>
+ <element name="address">
+ <ref name="pciaddress"/>
+ </element>
+ </choice>
+ </element>
+ </choice>
</group>
<optional>
<ref name="deviceBoot"/>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 80fe2a0..31330a5 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -5165,18 +5165,26 @@ error:
static int
virDomainHostdevSubsysUsbDefParseXML(const xmlNodePtr node,
- virDomainHostdevDefPtr def)
+ virDomainHostdevDefPtr def,
+ unsigned int flags)
{
int ret = -1;
int got_product, got_vendor;
xmlNodePtr cur;
+ int remaining;
/* Product can validly be 0, so we need some extra help to determine
* if it is uninitialized*/
got_product = 0;
got_vendor = 0;
+ if (def->redirection) {
+ remaining = virDomainChrSourceDefParseXML(&def->source.subsys.u.chr, node,
flags);
+ if (remaining < 0)
+ goto out;
+ }
+
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
@@ -5341,7 +5349,7 @@ virDomainHostdevDefParseXML(const xmlNodePtr node,
xmlNodePtr cur;
virDomainHostdevDefPtr def;
- char *mode, *type = NULL, *managed = NULL;
+ char *mode, *type = NULL, *managed = NULL, *redirection = NULL;
if (VIR_ALLOC(def) < 0) {
virReportOOMError();
@@ -5379,14 +5387,30 @@ virDomainHostdevDefParseXML(const xmlNodePtr node,
VIR_FREE(managed);
}
+ redirection = virXMLPropString(node, "redirection");
+ if (redirection != NULL) {
+ def->redirection = 1;
+ if ((def->source.subsys.u.chr.type = virDomainChrTypeFromString(redirection))
< 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown redirection character device type
'%s'"),
+ redirection);
+ goto error;
+ }
+ if (def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("only usb redirection is supported"));
+ goto error;
+ }
+ }
+
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(cur->name, BAD_CAST "source")) {
if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
- if (virDomainHostdevSubsysUsbDefParseXML(cur, def) < 0)
- goto error;
+ if (virDomainHostdevSubsysUsbDefParseXML(cur, def, flags) < 0)
+ goto error;
}
if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
@@ -10105,6 +10129,7 @@ virDomainHostdevDefFormat(virBufferPtr buf,
{
const char *mode = virDomainHostdevModeTypeToString(def->mode);
const char *type;
+ const char *redirection = NULL;
if (!mode || def->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
@@ -10120,11 +10145,32 @@ virDomainHostdevDefFormat(virBufferPtr buf,
return -1;
}
- virBufferAsprintf(buf, " <hostdev mode='%s' type='%s'
managed='%s'>\n",
+ if (def->redirection) {
+ redirection = virDomainChrTypeToString(def->source.subsys.u.chr.type);
+ if (!redirection) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unexpected redirection type %d"),
+ def->source.subsys.u.chr.type);
+ return -1;
+ }
+ }
+
+ virBufferAsprintf(buf, " <hostdev mode='%s' type='%s'
managed='%s'",
mode, type, def->managed ? "yes" : "no");
- virBufferAddLit(buf, " <source>\n");
+ if (redirection != NULL) {
+ virBufferAsprintf(buf, " redirection='%s'", redirection);
+ }
+ virBufferAddLit(buf, ">\n");
+ virBufferAddLit(buf, " <source");
+ if (def->redirection) {
+ virBufferAsprintf(buf, " mode='connect' host='%s'
service='%s'/",
+ def->source.subsys.u.chr.data.tcp.host,
+ def->source.subsys.u.chr.data.tcp.service);
+ }
+ virBufferAddLit(buf, ">\n");
- if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
+ if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
+ redirection == NULL) {
if (def->source.subsys.u.usb.vendor) {
virBufferAsprintf(buf, " <vendor
id='0x%.4x'/>\n",
def->source.subsys.u.usb.vendor);
@@ -10144,7 +10190,8 @@ virDomainHostdevDefFormat(virBufferPtr buf,
def->source.subsys.u.pci.function);
}
- virBufferAddLit(buf, " </source>\n");
+ if (!def->redirection)
+ virBufferAddLit(buf, " </source>\n");
if (def->bootIndex)
virBufferAsprintf(buf, " <boot order='%d'/>\n",
def->bootIndex);
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 4b48efc..ff25743 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -914,6 +914,7 @@ typedef virDomainHostdevDef *virDomainHostdevDefPtr;
struct _virDomainHostdevDef {
int mode; /* enum virDomainHostdevMode */
unsigned int managed : 1;
+ unsigned int redirection : 1;
union {
struct {
int type; /* enum virDomainHostdevBusType */
@@ -926,6 +927,7 @@ struct _virDomainHostdevDef {
unsigned product;
} usb;
virDomainDevicePCIAddress pci; /* host address */
+ virDomainChrSourceDef chr;
} u;
} subsys;
struct {
diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c
index 2a10bd2..ca9c86f 100644
--- a/src/qemu/qemu_cgroup.c
+++ b/src/qemu/qemu_cgroup.c
@@ -284,9 +284,10 @@ int qemuSetupCgroup(struct qemud_driver *driver,
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
continue;
+ if (hostdev->redirection)
+ continue;
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
continue;
-
if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus,
hostdev->source.subsys.u.usb.device)) == NULL)
goto cleanup;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index f50d927..aaaad87 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -590,12 +590,14 @@ qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr
net, int idx)
int
qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev, int
idx)
{
+ const char *prefix = hostdev->redirection ? "usbredir" :
"hostdev";
+
if (idx == -1) {
int i;
idx = 0;
for (i = 0 ; i < def->nhostdevs ; i++) {
int thisidx;
- if ((thisidx = qemuDomainDeviceAliasIndex(&def->hostdevs[i]->info,
"hostdev")) < 0) {
+ if ((thisidx = qemuDomainDeviceAliasIndex(&def->hostdevs[i]->info,
prefix)) < 0) {
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to determine device index for hostdev
device"));
return -1;
@@ -605,7 +607,7 @@ qemuAssignDeviceHostdevAlias(virDomainDefPtr def,
virDomainHostdevDefPtr hostdev
}
}
- if (virAsprintf(&hostdev->info.alias, "hostdev%d", idx) < 0) {
+ if (virAsprintf(&hostdev->info.alias, "%s%d", prefix, idx) < 0)
{
virReportOOMError();
return -1;
}
@@ -2352,10 +2354,16 @@ qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev,
return NULL;
}
- 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 (dev->redirection) {
+ virBufferAsprintf(&buf, "usb-redir,chardev=char%s,id=%s",
+ dev->info.alias,
+ dev->info.alias);
+ } else {
+ 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;
@@ -4854,6 +4862,22 @@ qemuBuildCommandLine(virConnectPtr conn,
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
+ if (hostdev->redirection) {
+ if (!qemuCapsGet(qemuCaps, QEMU_CAPS_USB_REDIR)) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("USB redirection is not supported "
+ "by this version of QEMU"));
+ goto error;
+ }
+ virCommandAddArg(cmd, "-chardev");
+ if (!(devstr =
qemuBuildChrChardevStr(&hostdev->source.subsys.u.chr,
+ hostdev->info.alias,
+ qemuCaps))) {
+ goto error;
+ }
+ virCommandAddArg(cmd, devstr);
+ }
+
if (qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
virCommandAddArg(cmd, "-device");
if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev, qemuCaps)))
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
index 7f5ad51..357fc94 100644
--- a/src/qemu/qemu_hostdev.c
+++ b/src/qemu/qemu_hostdev.c
@@ -200,6 +200,8 @@ qemuPrepareHostUSBDevices(struct qemud_driver *driver
ATTRIBUTE_UNUSED,
continue;
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
continue;
+ if (hostdev->redirection)
+ continue;
/* Resolve a vendor/product to bus/device */
if (hostdev->source.subsys.u.usb.vendor) {
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 60cd241..42bffa8 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -911,6 +911,49 @@ error:
}
+int qemuDomainAttachUsbRedirDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainHostdevDefPtr hostdev)
+{
+ int ret;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ char *devstr = NULL;
+
+ if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
+ if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0)
+ goto error;
+ if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev, priv->qemuCaps)))
+ goto error;
+ }
+
+ if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE))
+ ret = qemuMonitorAddDevice(priv->mon, devstr);
+ else
+ goto error;
+
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ virDomainAuditHostdev(vm, hostdev, "attach", ret == 0);
+ if (ret < 0)
+ goto error;
+
+ vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
+
+ VIR_FREE(devstr);
+
+ return 0;
+
+error:
+ VIR_FREE(devstr);
+ return -1;
+
+}
+
int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev)
@@ -960,6 +1003,7 @@ int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver,
ret = qemuMonitorAddUSBDeviceExact(priv->mon,
hostdev->source.subsys.u.usb.bus,
hostdev->source.subsys.u.usb.device);
+
qemuDomainObjExitMonitorWithDriver(driver, vm);
virDomainAuditHostdev(vm, hostdev, "attach", ret == 0);
if (ret < 0)
@@ -990,6 +1034,7 @@ int qemuDomainAttachHostDevice(struct qemud_driver *driver,
/* Resolve USB product/vendor to bus/device */
if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
+ !hostdev->redirection &&
hostdev->source.subsys.u.usb.vendor) {
usbDevice *usb
= usbFindDevice(hostdev->source.subsys.u.usb.vendor,
@@ -1017,9 +1062,14 @@ int qemuDomainAttachHostDevice(struct qemud_driver *driver,
break;
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
- if (qemuDomainAttachHostUsbDevice(driver, vm,
- hostdev) < 0)
+ if (hostdev->redirection) {
+ if (qemuDomainAttachUsbRedirDevice(driver, vm,
+ hostdev) < 0)
+ goto error;
+ } else if (qemuDomainAttachHostUsbDevice(driver, vm,
+ hostdev) < 0)
goto error;
+
break;
default:
diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h
index 009f1f6..85c5d31 100644
--- a/src/qemu/qemu_hotplug.h
+++ b/src/qemu/qemu_hotplug.h
@@ -53,6 +53,9 @@ int qemuDomainAttachHostPciDevice(struct qemud_driver *driver,
int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev);
+int qemuDomainAttachUsbRedirDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainHostdevDefPtr hostdev);
int qemuDomainAttachHostDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev);
diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index 58d57ec..3f50052 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -280,6 +280,9 @@ virSecurityDACSetSecurityHostdevLabel(virSecurityManagerPtr mgr,
if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
return 0;
+ if (dev->redirection)
+ return 0;
+
switch (dev->source.subsys.type) {
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
@@ -351,6 +354,9 @@ virSecurityDACRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr,
if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
return 0;
+ if (dev->redirection)
+ return 0;
+
switch (dev->source.subsys.type) {
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 5e6145f..fd39e33 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -667,6 +667,9 @@ SELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr
ATTRIBUTE_UNUSED,
if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
return 0;
+ if (dev->redirection)
+ return 0;
+
switch (dev->source.subsys.type) {
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
@@ -736,6 +739,9 @@ SELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr
ATTRIBUTE_UNUSED,
if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
return 0;
+ if (dev->redirection)
+ return 0;
+
switch (dev->source.subsys.type) {
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args
b/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args
new file mode 100644
index 0000000..0949585
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args
@@ -0,0 +1,8 @@
+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 -chardev
socket,id=charmonitor,path=/tmp/test-monitor,server,nowait -mon
chardev=charmonitor,id=monitor,mode=readline -no-acpi -boot c \
+-device ich9-usb-ehci1,id=usb,bus=pci.0,multifunction=on,addr=0x4.0x7 \
+-device
ich9-usb-uhci1,masterbus=usb.0,firstport=0,bus=pci.0,multifunction=on,addr=0x4.0x0 \
+-device
ich9-usb-uhci2,masterbus=usb.0,firstport=2,bus=pci.0,multifunction=on,addr=0x4.0x1 \
+-device
ich9-usb-uhci3,masterbus=usb.0,firstport=4,bus=pci.0,multifunction=on,addr=0x4.0x2 \
+-chardev socket,id=charusbredir0,host=localhost,port=4000 \
+-device usb-redir,chardev=charusbredir0,id=usbredir0 \
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,multifunction=on,addr=0x3.0x0
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml
b/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml
new file mode 100644
index 0000000..bb50b81
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml
@@ -0,0 +1,33 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory>219136</memory>
+ <currentMemory>219200</currentMemory>
+ <vcpu>1</vcpu>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <devices>
+ <emulator>/usr/bin/qemu</emulator>
+ <controller type='usb' index='0' model='ich9-ehci1'>
+ <address type='pci' domain='0' bus='0' slot='4'
function='7'/>
+ </controller>
+ <controller type='usb' index='0' model='ich9-uhci1'>
+ <master startport='0'/>
+ <address type='pci' domain='0' bus='0' slot='4'
function='0'/>
+ </controller>
+ <controller type='usb' index='0' model='ich9-uhci2'>
+ <master startport='2'/>
+ <address type='pci' domain='0' bus='0' slot='4'
function='1'/>
+ </controller>
+ <controller type='usb' index='0' model='ich9-uhci3'>
+ <master startport='4'/>
+ <address type='pci' domain='0' bus='0' slot='4'
function='2'/>
+ </controller>
+ <hostdev mode='subsystem' type='usb'
redirection='tcp'>
+ <source mode='connect' host='localhost'
service='4000'/>
+ </hostdev>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 9a8ecca..35e6d27 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -508,6 +508,10 @@ mymain(void)
QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG,
QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_PIIX3_USB_UHCI,
QEMU_CAPS_USB_HUB, QEMU_CAPS_ICH9_USB_EHCI1);
+ DO_TEST("usb-redir", false,
+ QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG,
+ QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_USB_HUB,
+ QEMU_CAPS_ICH9_USB_EHCI1, QEMU_CAPS_USB_REDIR);
DO_TEST("smbios", false, QEMU_CAPS_SMBIOS_TYPE);
--
1.7.6