[libvirt] [v2 08/13] Add USB hub device

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


---
 docs/formatdomain.html.in                        |   27 ++++
 docs/schemas/domain.rng                          |   13 ++
 src/conf/domain_conf.c                           |  157 +++++++++++++++++++++-
 src/conf/domain_conf.h                           |   20 +++
 src/libvirt_private.syms                         |    2 +
 src/qemu/qemu_capabilities.c                     |    3 +
 src/qemu/qemu_capabilities.h                     |    1 +
 src/qemu/qemu_command.c                          |   56 ++++++++-
 src/qemu/qemu_command.h                          |    1 +
 tests/qemuhelptest.c                             |    9 +-
 tests/qemuxml2argvdata/qemuxml2argv-usb-hub.args |    1 +
 tests/qemuxml2argvdata/qemuxml2argv-usb-hub.xml  |   19 +++
 tests/qemuxml2argvtest.c                         |    3 +
 13 files changed, 307 insertions(+), 5 deletions(-)
 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-hub.args
 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-hub.xml

diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 633cea1..e35b76b 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -2084,6 +2084,33 @@ qemu-kvm -net nic,model=? /dev/null
       device to a particular PCI slot.
     </p>
 
+    <h4><a name="elementsHub">Hub devices</a></h4>
+
+    <p>
+      A hub is a device that expands a single port into several so
+      that there are more ports available to connect devices to a host
+      system.
+    </p>
+
+<pre>
+  ...
+  <devices>
+    <hub type='usb'/>
+  </devices>
+  ...</pre>
+
+    <dl>
+      <dt><code>hub</code></dt>
+      <dd>The <code>hub</code> element has one mandatory attribute,
+        the <code>type</code> whose value can only be 'usb'.</dd>
+    </dl>
+
+    <p>
+      The <code>hub</code> element has an optional
+      sub-element <code><address></code> which can tie the
+      device to a particular controller.
+    </p>
+
     <h4><a name="elementsGraphics">Graphical framebuffers</a></h4>
 
     <p>
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng
index 455f57d..16e9687 100644
--- a/docs/schemas/domain.rng
+++ b/docs/schemas/domain.rng
@@ -1962,6 +1962,18 @@
       </optional>
     </element>
   </define>
+  <define name="hub">
+    <element name="hub">
+      <attribute name="type">
+        <choice>
+          <value>usb</value>
+        </choice>
+      </attribute>
+      <optional>
+        <ref name="address"/>
+      </optional>
+    </element>
+  </define>
   <define name="hostdev">
     <element name="hostdev">
       <optional>
@@ -2125,6 +2137,7 @@
             <ref name="serial"/>
             <ref name="channel"/>
             <ref name="smartcard"/>
+            <ref name="hub"/>
           </choice>
         </zeroOrMore>
         <optional>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 5ef062a..bd05c2a 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -126,7 +126,8 @@ VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST,
               "hostdev",
               "watchdog",
               "controller",
-              "graphics")
+              "graphics",
+              "usb")
 
 VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST,
               "none",
@@ -436,6 +437,9 @@ VIR_ENUM_IMPL(virDomainState, VIR_DOMAIN_CRASHED+1,
               "shutoff",
               "crashed")
 
+VIR_ENUM_IMPL(virDomainHub, VIR_DOMAIN_HUB_TYPE_LAST,
+              "usb")
+
 #define VIR_DOMAIN_NOSTATE_LAST (VIR_DOMAIN_NOSTATE_UNKNOWN + 1)
 VIR_ENUM_IMPL(virDomainNostateReason, VIR_DOMAIN_NOSTATE_LAST,
               "unknown")
@@ -999,6 +1003,15 @@ void virDomainHostdevDefFree(virDomainHostdevDefPtr def)
     VIR_FREE(def);
 }
 
+void virDomainHubDefFree(virDomainHubDefPtr def)
+{
+    if (!def)
+        return;
+
+    virDomainDeviceInfoClear(&def->info);
+    VIR_FREE(def);
+}
+
 void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
 {
     if (!def)
@@ -1035,6 +1048,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
     case VIR_DOMAIN_DEVICE_GRAPHICS:
         virDomainGraphicsDefFree(def->data.graphics);
         break;
+    case VIR_DOMAIN_DEVICE_HUB:
+        virDomainHubDefFree(def->data.hub);
+        break;
     }
 
     VIR_FREE(def);
@@ -1144,6 +1160,10 @@ void virDomainDefFree(virDomainDefPtr def)
         virDomainHostdevDefFree(def->hostdevs[i]);
     VIR_FREE(def->hostdevs);
 
+    for (i = 0 ; i < def->nhubs ; i++)
+        virDomainHubDefFree(def->hubs[i]);
+    VIR_FREE(def->hubs);
+
     VIR_FREE(def->os.type);
     VIR_FREE(def->os.arch);
     VIR_FREE(def->os.machine);
@@ -1527,6 +1547,9 @@ int virDomainDeviceInfoIterate(virDomainDefPtr def,
     if (def->console)
         if (cb(def, &def->console->info, opaque) < 0)
             return -1;
+    for (i = 0; i < def->nhubs ; i++)
+        if (cb(def, &def->hubs[i]->info, opaque) < 0)
+            return -1;
     return 0;
 }
 
@@ -1591,6 +1614,12 @@ virDomainDeviceInfoFormat(virBufferPtr buf,
                           info->addr.ccid.slot);
         break;
 
+    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
+        virBufferAsprintf(buf, " bus='%d' port='%d'",
+                          info->addr.usb.bus,
+                          info->addr.usb.port);
+        break;
+
     default:
         virDomainReportError(VIR_ERR_INTERNAL_ERROR,
                              _("unknown address type '%d'"), info->type);
@@ -3990,6 +4019,47 @@ error:
 }
 
 
+/* Parse the XML definition for an hub device */
+static virDomainHubDefPtr
+virDomainHubDefParseXML(xmlNodePtr node, unsigned int flags)
+{
+    virDomainHubDefPtr def;
+    char *type = NULL;
+
+    if (VIR_ALLOC(def) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    type = virXMLPropString(node, "type");
+
+    if (!type) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("missing hub device type"));
+        goto error;
+    }
+
+    if ((def->type = virDomainHubTypeFromString(type)) < 0) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                             _("unknown hub device type '%s'"), type);
+        goto error;
+    }
+
+    if (virDomainDeviceInfoParseXML(node, &def->info, flags) < 0)
+        goto error;
+
+cleanup:
+    VIR_FREE(type);
+
+    return def;
+
+error:
+    virDomainHubDefFree(def);
+    def = NULL;
+    goto cleanup;
+}
+
+
 /* Parse the XML definition for a clock timer */
 static virDomainTimerDefPtr
 virDomainTimerDefParseXML(const xmlNodePtr node,
@@ -5567,6 +5637,10 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps,
         dev->type = VIR_DOMAIN_DEVICE_GRAPHICS;
         if (!(dev->data.graphics = virDomainGraphicsDefParseXML(node, ctxt, flags)))
             goto error;
+    } else if (xmlStrEqual(node->name, BAD_CAST "hub")) {
+        dev->type = VIR_DOMAIN_DEVICE_HUB;
+        if (!(dev->data.hub = virDomainHubDefParseXML(node, flags)))
+            goto error;
     } else {
         virDomainReportError(VIR_ERR_XML_ERROR,
                              "%s", _("unknown device type"));
@@ -6971,6 +7045,21 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
         }
     }
 
+    /* analysis of the hub devices */
+    if ((n = virXPathNodeSet("./devices/hub", ctxt, &nodes)) < 0) {
+        goto error;
+    }
+    if (n && VIR_ALLOC_N(def->hubs, n) < 0)
+        goto no_memory;
+    for (i = 0 ; i < n ; i++) {
+        virDomainHubDefPtr hub = virDomainHubDefParseXML(nodes[i], flags);
+        if (!hub)
+            goto error;
+
+        def->hubs[def->nhubs++] = hub;
+    }
+    VIR_FREE(nodes);
+
     /* analysis of security label */
     if (virSecurityLabelDefParseXML(def, ctxt, flags) == -1)
         goto error;
@@ -7881,6 +7970,29 @@ cleanup:
 }
 
 
+static bool virDomainHubDefCheckABIStability(virDomainHubDefPtr src,
+                                                   virDomainHubDefPtr dst)
+{
+    bool identical = false;
+
+    if (src->type != dst->type) {
+        virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                             _("Target hub device type %s does not match source %s"),
+                             virDomainHubTypeToString(dst->type),
+                             virDomainHubTypeToString(src->type));
+        goto cleanup;
+    }
+
+    if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info))
+        goto cleanup;
+
+    identical = true;
+
+cleanup:
+    return identical;
+}
+
+
 /* This compares two configurations and looks for any differences
  * which will affect the guest ABI. This is primarily to allow
  * validation of custom XML config passed in during migration
@@ -8114,6 +8226,17 @@ bool virDomainDefCheckABIStability(virDomainDefPtr src,
         goto cleanup;
     }
 
+    if (src->nhubs != dst->nhubs) {
+        virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                             _("Target domain hub device count %d does not match source %d"),
+                             dst->nhubs, src->nhubs);
+        goto cleanup;
+    }
+
+    for (i = 0 ; i < src->nhubs ; i++)
+        if (!virDomainHubDefCheckABIStability(src->hubs[i], dst->hubs[i]))
+            goto cleanup;
+
     if (src->console &&
         !virDomainConsoleDefCheckABIStability(src->console, dst->console))
         goto cleanup;
@@ -10027,6 +10150,34 @@ virDomainHostdevDefFormat(virBufferPtr buf,
 }
 
 
+static int
+virDomainHubDefFormat(virBufferPtr buf,
+                        virDomainHubDefPtr def,
+                        unsigned int flags)
+{
+    const char *type = virDomainHubTypeToString(def->type);
+
+    if (!type) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected hub type %d"), def->type);
+        return -1;
+    }
+
+    virBufferAsprintf(buf, "    <hub type='%s'", type);
+
+    if (virDomainDeviceInfoIsSet(&def->info, flags)) {
+        virBufferAddLit(buf, ">\n");
+        if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0)
+            return -1;
+        virBufferAddLit(buf, "    </hub>\n");
+    } else {
+        virBufferAddLit(buf, "/>\n");
+    }
+
+    return 0;
+}
+
+
 #define DUMPXML_FLAGS                           \
     (VIR_DOMAIN_XML_SECURE |                    \
      VIR_DOMAIN_XML_INACTIVE |                  \
@@ -10432,6 +10583,10 @@ virDomainDefFormatInternal(virDomainDefPtr def,
         if (virDomainHostdevDefFormat(&buf, def->hostdevs[n], flags) < 0)
             goto cleanup;
 
+    for (n = 0 ; n < def->nhubs ; n++)
+        if (virDomainHubDefFormat(&buf, def->hubs[n], flags) < 0)
+            goto cleanup;
+
     if (def->watchdog)
         virDomainWatchdogDefFormat (&buf, def->watchdog, flags);
 
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 07d60a4..f880264 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -616,6 +616,13 @@ struct _virDomainSmartcardDef {
     virDomainDeviceInfo info;
 };
 
+typedef struct _virDomainHubDef virDomainHubDef;
+typedef virDomainHubDef *virDomainHubDefPtr;
+struct _virDomainHubDef {
+    int type;
+    virDomainDeviceInfo info;
+};
+
 enum virDomainInputType {
     VIR_DOMAIN_INPUT_TYPE_MOUSE,
     VIR_DOMAIN_INPUT_TYPE_TABLET,
@@ -825,6 +832,12 @@ enum virDomainGraphicsListenType {
     VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST,
 };
 
+enum virDomainHubType {
+    VIR_DOMAIN_HUB_TYPE_USB,
+
+    VIR_DOMAIN_HUB_TYPE_LAST,
+};
+
 typedef struct _virDomainGraphicsListenDef virDomainGraphicsListenDef;
 typedef virDomainGraphicsListenDef *virDomainGraphicsListenDefPtr;
 struct _virDomainGraphicsListenDef {
@@ -965,6 +978,7 @@ enum virDomainDeviceType {
     VIR_DOMAIN_DEVICE_WATCHDOG,
     VIR_DOMAIN_DEVICE_CONTROLLER,
     VIR_DOMAIN_DEVICE_GRAPHICS,
+    VIR_DOMAIN_DEVICE_HUB,
 
     VIR_DOMAIN_DEVICE_LAST,
 };
@@ -985,6 +999,7 @@ struct _virDomainDeviceDef {
         virDomainHostdevDefPtr hostdev;
         virDomainWatchdogDefPtr watchdog;
         virDomainGraphicsDefPtr graphics;
+        virDomainHubDefPtr hub;
     } data;
 };
 
@@ -1312,6 +1327,9 @@ struct _virDomainDef {
     size_t nleases;
     virDomainLeaseDefPtr *leases;
 
+    int nhubs;
+    virDomainHubDefPtr *hubs;
+
     /* Only 1 */
     virDomainChrDefPtr console;
     virSecurityLabelDef seclabel;
@@ -1458,6 +1476,7 @@ void virDomainMemballoonDefFree(virDomainMemballoonDefPtr def);
 void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def);
 void virDomainVideoDefFree(virDomainVideoDefPtr def);
 void virDomainHostdevDefFree(virDomainHostdevDefPtr def);
+void virDomainHubDefFree(virDomainHubDefPtr def);
 void virDomainDeviceDefFree(virDomainDeviceDefPtr def);
 int virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info,
                                   int type);
@@ -1738,6 +1757,7 @@ VIR_ENUM_DECL(virDomainWatchdogAction)
 VIR_ENUM_DECL(virDomainVideo)
 VIR_ENUM_DECL(virDomainHostdevMode)
 VIR_ENUM_DECL(virDomainHostdevSubsys)
+VIR_ENUM_DECL(virDomainHub)
 VIR_ENUM_DECL(virDomainInput)
 VIR_ENUM_DECL(virDomainInputBus)
 VIR_ENUM_DECL(virDomainGraphics)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 74948b8..6642ba9 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -327,6 +327,8 @@ virDomainGraphicsTypeToString;
 virDomainHostdevDefFree;
 virDomainHostdevModeTypeToString;
 virDomainHostdevSubsysTypeToString;
+virDomainHubTypeFromString;
+virDomainHubTypeToString;
 virDomainInputDefFree;
 virDomainIoEventFdTypeFromString;
 virDomainIoEventFdTypeToString;
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index a776a0c..5b72a60 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -134,6 +134,7 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST,
 
               "pci-ohci", /* 70 */
               "usb-redir",
+              "usb-hub",
     );
 
 struct qemu_feature_flags {
@@ -1220,6 +1221,8 @@ qemuCapsParseDeviceStr(const char *str, virBitmapPtr flags)
         qemuCapsSet(flags, QEMU_CAPS_PCI_OHCI);
     if (strstr(str, "name \"usb-redir\""))
         qemuCapsSet(flags, QEMU_CAPS_USB_REDIR);
+    if (strstr(str, "name \"usb-hub\""))
+        qemuCapsSet(flags, QEMU_CAPS_USB_HUB);
 
     /* Prefer -chardev spicevmc (detected earlier) over -device spicevmc */
     if (!qemuCapsGet(flags, QEMU_CAPS_CHARDEV_SPICEVMC) &&
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index f4bb368..c9b9b45 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -108,6 +108,7 @@ enum qemuCapsFlags {
     QEMU_CAPS_VT82C686B_USB_UHCI = 69, /* -device vt82c686b-usb-uhci */
     QEMU_CAPS_PCI_OHCI          = 70, /* -device pci-ohci */
     QEMU_CAPS_USB_REDIR         = 71, /* -device usb-redir */
+    QEMU_CAPS_USB_HUB           = 72, /* -device usb-hub */
 
     QEMU_CAPS_LAST,                   /* this must always be the last item */
 };
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 835d06f..9b09daa 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -689,6 +689,10 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virBitmapPtr qemuCaps)
         if (virAsprintf(&def->smartcards[i]->info.alias, "smartcard%d", i) < 0)
             goto no_memory;
     }
+    for (i = 0; i < def->nhubs ; i++) {
+        if (virAsprintf(&def->hubs[i]->info.alias, "hub%d", i) < 0)
+            goto no_memory;
+    }
     if (def->console) {
         if (virAsprintf(&def->console->info.alias, "console%d", i) < 0)
             goto no_memory;
@@ -1272,6 +1276,9 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs)
     for (i = 0; i < def->nchannels ; i++) {
         /* Nada - none are PCI based (yet) */
     }
+    for (i = 0; i < def->nhubs ; i++) {
+        /* Nada - none are PCI based (yet) */
+    }
 
     return 0;
 
@@ -1767,7 +1774,6 @@ qemuBuildUSBControllerDevStr(virDomainControllerDefPtr def,
         virBufferAsprintf(buf, ",id=usb%d", def->idx);
     }
 
-
     return 0;
 }
 
@@ -2336,6 +2342,43 @@ error:
 
 
 char *
+qemuBuildHubDevStr(virDomainHubDefPtr dev,
+                   virBitmapPtr qemuCaps)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+    if (dev->type != VIR_DOMAIN_HUB_TYPE_USB) {
+        qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                        _("hub type %s not supported"),
+                        virDomainHubTypeToString(dev->type));
+        goto error;
+    }
+
+    if (!qemuCapsGet(qemuCaps, QEMU_CAPS_USB_HUB)) {
+        qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                        _("usb-hub not supported by QEMU binary"));
+        goto error;
+    }
+
+    virBufferAddLit(&buf, "usb-hub");
+    virBufferAsprintf(&buf, ",id=%s", 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 *
 qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev)
 {
     char *ret = NULL;
@@ -4245,6 +4288,17 @@ qemuBuildCommandLine(virConnectPtr conn,
     if (usbcontroller == 0)
         virCommandAddArg(cmd, "-usb");
 
+    for (i = 0 ; i < def->nhubs ; i++) {
+        virDomainHubDefPtr hub = def->hubs[i];
+        char *optstr;
+
+        virCommandAddArg(cmd, "-device");
+        if (!(optstr = qemuBuildHubDevStr(hub, qemuCaps)))
+            goto error;
+        virCommandAddArg(cmd, optstr);
+        VIR_FREE(optstr);
+    }
+
     for (i = 0 ; i < def->ninputs ; i++) {
         virDomainInputDefPtr input = def->inputs[i];
 
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index de09577..22bc15d 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -119,6 +119,7 @@ char * qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev);
 char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev,
                                  virBitmapPtr qemuCaps);
 
+char * qemuBuildHubDevStr(virDomainHubDefPtr dev, virBitmapPtr qemuCaps);
 
 
 int qemuNetworkIfaceConnect(virDomainDefPtr def,
diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c
index d57e499..ffd30e2 100644
--- a/tests/qemuhelptest.c
+++ b/tests/qemuhelptest.c
@@ -349,7 +349,8 @@ mymain(void)
             QEMU_CAPS_DRIVE_AIO,
             QEMU_CAPS_DEVICE_SPICEVMC,
             QEMU_CAPS_PIIX3_USB_UHCI,
-            QEMU_CAPS_PIIX4_USB_UHCI);
+            QEMU_CAPS_PIIX4_USB_UHCI,
+            QEMU_CAPS_USB_HUB);
     DO_TEST("qemu-kvm-0.12.3", 12003, 1, 0,
             QEMU_CAPS_VNC_COLON,
             QEMU_CAPS_NO_REBOOT,
@@ -437,7 +438,8 @@ mymain(void)
             QEMU_CAPS_PIIX3_USB_UHCI,
             QEMU_CAPS_PIIX4_USB_UHCI,
             QEMU_CAPS_VT82C686B_USB_UHCI,
-            QEMU_CAPS_PCI_OHCI);
+            QEMU_CAPS_PCI_OHCI,
+            QEMU_CAPS_USB_HUB);
     DO_TEST("qemu-kvm-0.12.1.2-rhel61", 12001, 1, 0,
             QEMU_CAPS_VNC_COLON,
             QEMU_CAPS_NO_REBOOT,
@@ -484,7 +486,8 @@ mymain(void)
             QEMU_CAPS_VIRTIO_TX_ALG,
             QEMU_CAPS_VIRTIO_IOEVENTFD,
             QEMU_CAPS_PIIX3_USB_UHCI,
-            QEMU_CAPS_PIIX4_USB_UHCI);
+            QEMU_CAPS_PIIX4_USB_UHCI,
+            QEMU_CAPS_USB_HUB);
 
     return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-hub.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub.args
new file mode 100644
index 0000000..4911dd4
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub.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 -chardev socket,id=charmonitor,path=/tmp/test-monitor,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 virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-hub.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub.xml
new file mode 100644
index 0000000..5e0b256
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub.xml
@@ -0,0 +1,19 @@
+<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'/>
+    <memballoon model='virtio'/>
+    <hub type='usb'>
+      <address type='usb' bus='0' port='1'/>
+    </hub>
+  </devices>
+</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 33588d0..a053693 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -498,6 +498,9 @@ mymain(void)
     DO_TEST("usb-ich9-companion", false,
             QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG,
             QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_ICH9_USB_EHCI1);
+    DO_TEST("usb-hub", false,
+            QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_USB_HUB,
+            QEMU_CAPS_NODEFCONFIG);
 
     DO_TEST("smbios", false, QEMU_CAPS_SMBIOS_TYPE);
 
-- 
1.7.6




More information about the libvir-list mailing list