not used by qemu.</dd>
</dl>
+ <h4><a name="elementsRedir">Redirected devices</a></h4>
+
+ <p>
+ USB device redirection through a character device is
+ supported <span class="since">since after 0.9.5 (KVM
+ only)</span>:
+ </p>
+
+<pre>
+ ...
+ <devices>
+ <redirdev bus='usb' type='tcp'>
+ <source mode='connect' host='localhost' service='4000'/>
+ </redirdev>
+ </devices>
+ ...</pre>
+
+ <dl>
+ <dt><code>redirdev</code></dt>
+ <dd>The <code>redirdev</code> element is the main container for
+ describing redirected devices. <code>bus</code> must be "usb"
+ for a USB device.
+
+ An additional attribute <code>type</code> is required,
+ matching one of the
+ supported <a href="#elementsConsole">serial device</a> types,
+ to describe the host side of the
+ tunnel; <code>type='tcp'</code>
+ or <code>type='spicevmc'</code> (which uses the usbredir
+ channel of a <a href="#elementsGraphics">SPICE graphics
+ device</a>) are typical. Further sub-elements, such
+ as <code><source></code>, may be required according to
+ the given type, although a <code><target></code>
+ sub-element is not required (since the consumer of the
+ character device is the hypervisor itself, rather than a
+ device visible in the guest).</dd>
+
+ </dl>
+
<h4><a name="elementsSmartcard">Smartcard devices</a></h4>
<p>
</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">
</optional>
</element>
</define>
+ <define name="redirdev">
+ <element name="redirdev">
+ <attribute name="bus">
+ <choice>
+ <value>usb</value>
+ </choice>
+ </attribute>
+ <attribute name="type">
+ <ref name="qemucdevSrcTypeChoice"/>
+ </attribute>
+ <ref name="qemucdevSrcDef"/>
+ </element>
+ </define>
<define name="hostdev">
<element name="hostdev">
<optional>
<ref name="channel"/>
<ref name="smartcard"/>
<ref name="hub"/>
+ <ref name="redirdev"/>
</choice>
</zeroOrMore>
<optional>
}
+/**
+ * virDomainAuditRedirdev:
+ * @vm: domain making a change in pass-through host device
+ * @redirdev: device being attached or removed
+ * @reason: one of "start", "attach", or "detach"
+ * @success: true if the device passthrough operation succeeded
+ *
+ * Log an audit message about an attempted device passthrough change.
+ */
+void
+virDomainAuditRedirdev(virDomainObjPtr vm, virDomainRedirdevDefPtr redirdev,
+ const char *reason, bool success)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ char *vmname;
+ char *address;
+ char *device;
+ const char *virt;
+
+ virUUIDFormat(vm->def->uuid, uuidstr);
+ if (!(vmname = virAuditEncode("vm", vm->def->name))) {
+ VIR_WARN("OOM while encoding audit message");
+ return;
+ }
+
+ if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
+ VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType);
+ virt = "?";
+ }
+
+ switch (redirdev->bus) {
+ case VIR_DOMAIN_REDIRDEV_BUS_USB:
+ if (virAsprintf(&address, "USB redirdev") < 0) {
+ VIR_WARN("OOM while encoding audit message");
+ goto cleanup;
+ }
+ default:
+ VIR_WARN("Unexpected redirdev bus while encoding audit message: %d",
+ redirdev->bus);
+ goto cleanup;
+ }
+
+ if (!(device = virAuditEncode("device", VIR_AUDIT_STR(address)))) {
+ VIR_WARN("OOM while encoding audit message");
+ goto cleanup;
+ }
+
+ VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
+ "virt=%s resrc=dev reason=%s %s uuid=%s bus=%s %s",
+ virt, reason, vmname, uuidstr,
+ virDomainRedirdevBusTypeToString(redirdev->bus),
+ device);
+
+cleanup:
+ VIR_FREE(vmname);
+ VIR_FREE(device);
+ VIR_FREE(address);
+}
+
+
/**
* virDomainAuditCgroup:
* @vm: domain making the cgroups ACL change
virDomainAuditHostdev(vm, hostdev, "start", true);
}
+ for (i = 0 ; i < vm->def->nredirdevs ; i++) {
+ virDomainRedirdevDefPtr redirdev = vm->def->redirdevs[i];
+ virDomainAuditRedirdev(vm, redirdev, "start", true);
+ }
+
virDomainAuditMemory(vm, 0, vm->def->mem.cur_balloon, "start", true);
virDomainAuditVcpu(vm, 0, vm->def->vcpus, "start", true);
void virDomainAuditSecurityLabel(virDomainObjPtr vm,
bool success)
ATTRIBUTE_NONNULL(1);
+void virDomainAuditRedirdev(virDomainObjPtr vm,
+ virDomainRedirdevDefPtr def,
+ const char *reason,
+ bool success)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
#endif /* __VIR_DOMAIN_AUDIT_H__ */
"watchdog",
"controller",
"graphics",
- "hub")
+ "hub",
+ "redirdev")
VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST,
"none",
VIR_ENUM_IMPL(virDomainHub, VIR_DOMAIN_HUB_TYPE_LAST,
"usb")
+VIR_ENUM_IMPL(virDomainRedirdevBus, VIR_DOMAIN_REDIRDEV_BUS_LAST,
+ "usb")
+
#define VIR_DOMAIN_NOSTATE_LAST (VIR_DOMAIN_NOSTATE_UNKNOWN + 1)
VIR_ENUM_IMPL(virDomainNostateReason, VIR_DOMAIN_NOSTATE_LAST,
"unknown")
VIR_FREE(def);
}
+void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def)
+{
+ if (!def)
+ return;
+
+ virDomainChrSourceDefClear(&def->source.chr);
+ virDomainDeviceInfoClear(&def->info);
+
+ VIR_FREE(def);
+}
+
void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
{
if (!def)
case VIR_DOMAIN_DEVICE_HUB:
virDomainHubDefFree(def->data.hub);
break;
+ case VIR_DOMAIN_DEVICE_REDIRDEV:
+ virDomainRedirdevDefFree(def->data.redirdev);
+ break;
}
VIR_FREE(def);
virBitmapPtr bootMap,
unsigned int flags)
{
-
xmlNodePtr cur;
virDomainHostdevDefPtr def;
char *mode, *type = NULL, *managed = NULL;
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) < 0)
+ goto error;
}
if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
}
+static virDomainRedirdevDefPtr
+virDomainRedirdevDefParseXML(const xmlNodePtr node,
+ unsigned int flags)
+{
+ xmlNodePtr cur;
+ virDomainRedirdevDefPtr def;
+ char *bus, *type = NULL;
+
+ if (VIR_ALLOC(def) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ bus = virXMLPropString(node, "bus");
+ if (bus) {
+ if ((def->bus = virDomainRedirdevBusTypeFromString(bus)) < 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown redirdev bus '%s'"), bus);
+ goto error;
+ }
+ } else {
+ def->bus = VIR_DOMAIN_REDIRDEV_BUS_USB;
+ }
+
+ type = virXMLPropString(node, "type");
+ if (type) {
+ if ((def->source.chr.type = virDomainChrTypeFromString(type)) < 0) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown redirdev character device type '%s'"), type);
+ goto error;
+ }
+ } else {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing type in redirdev"));
+ goto error;
+ }
+
+ cur = node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ if (xmlStrEqual(cur->name, BAD_CAST "source")) {
+ int remaining;
+
+ remaining = virDomainChrSourceDefParseXML(&def->source.chr, cur, flags);
+ if (remaining != 0)
+ goto error;
+ }
+ }
+ cur = cur->next;
+ }
+
+cleanup:
+ VIR_FREE(bus);
+ VIR_FREE(type);
+ return def;
+
+error:
+ virDomainRedirdevDefFree(def);
+ def = NULL;
+ goto cleanup;
+}
+
static int virDomainLifecycleParseXML(xmlXPathContextPtr ctxt,
const char *xpath,
int *val,
dev->type = VIR_DOMAIN_DEVICE_HUB;
if (!(dev->data.hub = virDomainHubDefParseXML(node, flags)))
goto error;
+ } else if (xmlStrEqual(node->name, BAD_CAST "redirdev")) {
+ dev->type = VIR_DOMAIN_DEVICE_REDIRDEV;
+ if (!(dev->data.redirdev = virDomainRedirdevDefParseXML(node, flags)))
+ goto error;
} else {
virDomainReportError(VIR_ERR_XML_ERROR,
"%s", _("unknown device type"));
}
VIR_FREE(nodes);
+ /* analysis of the redirected devices */
+ if ((n = virXPathNodeSet("./devices/redirdev", ctxt, &nodes)) < 0) {
+ goto error;
+ }
+ if (n && VIR_ALLOC_N(def->redirdevs, n) < 0)
+ goto no_memory;
+ for (i = 0 ; i < n ; i++) {
+ virDomainRedirdevDefPtr redirdev = virDomainRedirdevDefParseXML(nodes[i],
+ flags);
+ if (!redirdev)
+ goto error;
+
+ def->redirdevs[def->nredirdevs++] = redirdev;
+ }
+ VIR_FREE(nodes);
+
/* analysis of security label */
if (virSecurityLabelDefParseXML(def, ctxt, flags) == -1)
goto error;
return 0;
}
+static int
+virDomainRedirdevDefFormat(virBufferPtr buf,
+ virDomainRedirdevDefPtr def,
+ unsigned int flags)
+{
+ const char *bus;
+
+ bus = virDomainRedirdevBusTypeToString(def->bus);
+
+ virBufferAsprintf(buf, " <redirdev bus='%s'", bus);
+ if (virDomainChrSourceDefFormat(buf, &def->source.chr, false, flags) < 0)
+ return -1;
+ virBufferAddLit(buf, " </redirdev>\n");
+
+ return 0;
+}
static int
virDomainHubDefFormat(virBufferPtr buf,
if (virDomainHostdevDefFormat(&buf, def->hostdevs[n], flags) < 0)
goto cleanup;
+ for (n = 0 ; n < def->nredirdevs ; n++)
+ if (virDomainRedirdevDefFormat(&buf, def->redirdevs[n], flags) < 0)
+ goto cleanup;
+
for (n = 0 ; n < def->nhubs ; n++)
if (virDomainHubDefFormat(&buf, def->hubs[n], flags) < 0)
goto cleanup;
virDomainDeviceInfo info; /* Guest address */
};
+enum virDomainRedirdevBus {
+ VIR_DOMAIN_REDIRDEV_BUS_USB,
+
+ VIR_DOMAIN_REDIRDEV_BUS_LAST
+};
+
+typedef struct _virDomainRedirdevDef virDomainRedirdevDef;
+typedef virDomainRedirdevDef *virDomainRedirdevDefPtr;
+struct _virDomainRedirdevDef {
+ int bus; /* enum virDomainRedirdevBus */
+
+ union {
+ virDomainChrSourceDef chr;
+ } source;
+
+ virDomainDeviceInfo info; /* Guest address */
+};
enum {
VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO,
VIR_DOMAIN_DEVICE_CONTROLLER,
VIR_DOMAIN_DEVICE_GRAPHICS,
VIR_DOMAIN_DEVICE_HUB,
+ VIR_DOMAIN_DEVICE_REDIRDEV,
VIR_DOMAIN_DEVICE_LAST,
};
virDomainWatchdogDefPtr watchdog;
virDomainGraphicsDefPtr graphics;
virDomainHubDefPtr hub;
+ virDomainRedirdevDefPtr redirdev;
} data;
};
int nhostdevs;
virDomainHostdevDefPtr *hostdevs;
+ int nredirdevs;
+ virDomainRedirdevDefPtr *redirdevs;
+
int nsmartcards;
virDomainSmartcardDefPtr *smartcards;
void virDomainVideoDefFree(virDomainVideoDefPtr def);
void virDomainHostdevDefFree(virDomainHostdevDefPtr def);
void virDomainHubDefFree(virDomainHubDefPtr def);
+void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def);
void virDomainDeviceDefFree(virDomainDeviceDefPtr def);
int virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info,
int type);
VIR_ENUM_DECL(virDomainHostdevMode)
VIR_ENUM_DECL(virDomainHostdevSubsys)
VIR_ENUM_DECL(virDomainHub)
+VIR_ENUM_DECL(virDomainRedirdevBus)
VIR_ENUM_DECL(virDomainInput)
VIR_ENUM_DECL(virDomainInputBus)
VIR_ENUM_DECL(virDomainGraphics)
virDomainAuditMemory;
virDomainAuditNet;
virDomainAuditNetDevice;
+virDomainAuditRedirdev;
virDomainAuditSecurityLabel;
virDomainAuditStart;
virDomainAuditStop;
virDomainObjTaint;
virDomainObjUnlock;
virDomainObjUnref;
+virDomainRedirdevBusTypeFromString;
+virDomainRedirdevBusTypeToString;
virDomainRemoveInactive;
virDomainSaveConfig;
virDomainSaveStatus;
}
+int
+qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, virDomainRedirdevDefPtr redirdev, int idx)
+{
+ if (idx == -1) {
+ int i;
+ idx = 0;
+ for (i = 0 ; i < def->nredirdevs ; i++) {
+ int thisidx;
+ if ((thisidx = qemuDomainDeviceAliasIndex(&def->redirdevs[i]->info, "redir")) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to determine device index for redirected device"));
+ return -1;
+ }
+ if (thisidx >= idx)
+ idx = thisidx + 1;
+ }
+ }
+
+ if (virAsprintf(&redirdev->info.alias, "redir%d", idx) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+
+ return 0;
+}
+
+
int
qemuAssignDeviceControllerAlias(virDomainControllerDefPtr controller)
{
if (qemuAssignDeviceHostdevAlias(def, def->hostdevs[i], i) < 0)
return -1;
}
+ for (i = 0; i < def->nredirdevs ; i++) {
+ if (qemuAssignDeviceRedirdevAlias(def, def->redirdevs[i], i) < 0)
+ return -1;
+ }
for (i = 0; i < def->nvideos ; i++) {
if (virAsprintf(&def->videos[i]->info.alias, "video%d", i) < 0)
goto no_memory;
}
+char *
+qemuBuildRedirdevDevStr(virDomainRedirdevDefPtr dev,
+ virBitmapPtr qemuCaps)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (dev->bus != VIR_DOMAIN_REDIRDEV_BUS_USB) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Redirection bus %s is not supported by QEMU"),
+ virDomainRedirdevBusTypeToString(dev->bus));
+ goto error;
+ }
+
+ 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;
+ }
+
+ virBufferAsprintf(&buf, "usb-redir,chardev=char%s,id=%s",
+ dev->info.alias,
+ dev->info.alias);
+
+ if (qemuBuildDeviceAddressStr(&buf, &dev->info, qemuCaps) < 0)
+ goto error;
+
+ if (virBufferError(&buf)) {
+ virReportOOMError();
+ goto error;
+ }
+
+ return virBufferContentAndReset(&buf);
+
+error:
+ virBufferFreeAndReset(&buf);
+ return NULL;
+}
+
char *
qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev,
virBitmapPtr qemuCaps)
virCommandAddArgList(cmd, "-watchdog-action", action, NULL);
}
+ /* Add redirected devices */
+ for (i = 0 ; i < def->nredirdevs ; i++) {
+ virDomainRedirdevDefPtr redirdev = def->redirdevs[i];
+ char *devstr;
+
+ virCommandAddArg(cmd, "-chardev");
+ if (!(devstr = qemuBuildChrChardevStr(&redirdev->source.chr,
+ redirdev->info.alias,
+ qemuCaps))) {
+ goto error;
+ }
+
+ virCommandAddArg(cmd, devstr);
+ VIR_FREE(devstr);
+
+ if (!qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE))
+ goto error;
+
+ virCommandAddArg(cmd, "-device");
+ if (!(devstr = qemuBuildRedirdevDevStr(redirdev, qemuCaps)))
+ goto error;
+ virCommandAddArg(cmd, devstr);
+ VIR_FREE(devstr);
+ }
+
+
/* Add host passthrough hardware */
for (i = 0 ; i < def->nhostdevs ; i++) {
virDomainHostdevDefPtr hostdev = def->hostdevs[i];
virBitmapPtr qemuCaps);
char * qemuBuildHubDevStr(virDomainHubDefPtr dev, virBitmapPtr qemuCaps);
+char * qemuBuildRedirdevDevStr(virDomainRedirdevDefPtr dev, virBitmapPtr qemuCaps);
int qemuNetworkIfaceConnect(virDomainDefPtr def,
int qemuDomainNetVLAN(virDomainNetDefPtr def);
int qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx);
int qemuAssignDeviceDiskAlias(virDomainDiskDefPtr def, virBitmapPtr qemuCaps);
-int qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr net, int idx);
+int qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev, int idx);
int qemuAssignDeviceControllerAlias(virDomainControllerDefPtr controller);
+int qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, virDomainRedirdevDefPtr redirdev, int idx);
int
qemuParseKeywords(const char *str,
dev->data.hostdev = NULL;
break;
+ case VIR_DOMAIN_DEVICE_REDIRDEV:
+ ret = qemuDomainAttachRedirdevDevice(driver, vm,
+ dev->data.redirdev);
+ if (!ret)
+ dev->data.redirdev = NULL;
+ break;
+
default:
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("device type '%s' cannot be attached"),
}
+int qemuDomainAttachRedirdevDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainRedirdevDefPtr redirdev)
+{
+ int ret;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ char *devstr = NULL;
+
+ if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
+ if (qemuAssignDeviceRedirdevAlias(vm->def, redirdev, -1) < 0)
+ goto error;
+ if (!(devstr = qemuBuildRedirdevDevStr(redirdev, priv->qemuCaps)))
+ goto error;
+ }
+
+ if (VIR_REALLOC_N(vm->def->redirdevs, vm->def->nredirdevs+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);
+ virDomainAuditRedirdev(vm, redirdev, "attach", ret == 0);
+ if (ret < 0)
+ goto error;
+
+ vm->def->redirdevs[vm->def->nredirdevs++] = redirdev;
+
+ VIR_FREE(devstr);
+
+ return 0;
+
+error:
+ VIR_FREE(devstr);
+ return -1;
+
+}
+
int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev)
int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev);
+int qemuDomainAttachRedirdevDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainRedirdevDefPtr hostdev);
int qemuDomainAttachHostDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev);
--- /dev/null
+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=charredir0,host=localhost,port=4000 \
+-device usb-redir,chardev=charredir0,id=redir0 \
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,multifunction=on,addr=0x3.0x0
--- /dev/null
+<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>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu</emulator>
+ <controller type='usb' index='0' model='ich9-ehci1'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x7'/>
+ </controller>
+ <controller type='usb' index='0' model='ich9-uhci1'>
+ <master startport='0'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
+ </controller>
+ <controller type='usb' index='0' model='ich9-uhci2'>
+ <master startport='2'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x1'/>
+ </controller>
+ <controller type='usb' index='0' model='ich9-uhci3'>
+ <master startport='4'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x2'/>
+ </controller>
+ <redirdev bus='usb' type='tcp'>
+ <source mode='connect' host='localhost' service='4000'/>
+ <protocol type='raw'/>
+ </redirdev>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
DO_TEST("usb-ports", false,
QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_USB_HUB,
QEMU_CAPS_NODEFCONFIG);
+ 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);
DO_TEST("lease");
DO_TEST("event_idx");
+ DO_TEST("usb-redir");
+
/* These tests generate different XML */
DO_TEST_DIFFERENT("balloon-device-auto");
DO_TEST_DIFFERENT("channel-virtio-auto");