[libvirt] [v2 13/13] Add usb-redir device

Marc-André Lureau marcandre.lureau at gmail.com
Thu Aug 25 22:44:29 UTC 2011


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




More information about the libvir-list mailing list