diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 8fd6ca1f8d..ee9709436a 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1219,17 +1219,22 @@
Each controller has a mandatory attribute type
,
- which must be one of "ide", "fdc", "scsi", "sata", "ccid", or
- "virtio-serial", and a mandatory attribute index
- which is the decimal integer describing in which order the bus
- controller is encountered (for use in controller
- attributes of <address>
elements). The
- "virtio-serial" controller has two additional optional
+ which must be one of "ide", "fdc", "scsi", "sata", "usb",
+ "ccid", or "virtio-serial", and a mandatory
+ attribute index
which is the decimal integer
+ describing in which order the bus controller is encountered (for
+ use in controller
attributes
+ of <address>
elements). The "virtio-serial"
+ controller has two additional optional
attributes ports
and vectors
, which
control how many devices can be connected through the
controller. A "scsi" controller has an optional
attribute model
, which is one of "auto",
"buslogic", "lsilogic", "lsias1068", or "vmpvscsi".
+ A "usb" controller has an optional attribute model
,
+ which is one of "piix3-uhci", "piix4-uhci", "ehci",
+ "ich9-ehci1", "ich9-uhci1", "ich9-uhci2", "ich9-uhci3",
+ "vt82c686b-uhci" or "pci-ohci".
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng
index 5830421f7d..086d2718dc 100644
--- a/docs/schemas/domain.rng
+++ b/docs/schemas/domain.rng
@@ -878,6 +878,7 @@
scsi
sata
ccid
+ usb
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 8c04a32fd8..82338d64c2 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -194,7 +194,8 @@ VIR_ENUM_IMPL(virDomainController, VIR_DOMAIN_CONTROLLER_TYPE_LAST,
"scsi",
"sata",
"virtio-serial",
- "ccid")
+ "ccid",
+ "usb")
VIR_ENUM_IMPL(virDomainControllerModelSCSI, VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LAST,
"auto",
@@ -2473,6 +2474,8 @@ virDomainControllerModelTypeFromString(const virDomainControllerDefPtr def,
{
if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI)
return virDomainControllerModelSCSITypeFromString(model);
+ else if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_USB)
+ return virDomainControllerModelUSBTypeFromString(model);
return -1;
}
@@ -8756,6 +8759,8 @@ virDomainControllerModelTypeToString(virDomainControllerDefPtr def,
{
if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI)
return virDomainControllerModelSCSITypeToString(model);
+ else if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_USB)
+ return virDomainControllerModelUSBTypeToString(model);
return NULL;
}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 5396f7ae14..68cfa213a7 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -253,6 +253,7 @@ enum virDomainControllerType {
VIR_DOMAIN_CONTROLLER_TYPE_SATA,
VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL,
VIR_DOMAIN_CONTROLLER_TYPE_CCID,
+ VIR_DOMAIN_CONTROLLER_TYPE_USB,
VIR_DOMAIN_CONTROLLER_TYPE_LAST
};
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index fa52dc0ab9..5e2fad0754 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -85,6 +85,20 @@ VIR_ENUM_IMPL(qemuVideo, VIR_DOMAIN_VIDEO_TYPE_LAST,
"", /* don't support vbox */
"qxl");
+VIR_ENUM_DECL(qemuControllerModelUSB)
+
+VIR_ENUM_IMPL(qemuControllerModelUSB, VIR_DOMAIN_CONTROLLER_MODEL_USB_LAST,
+ "piix3-usb-uhci",
+ "piix4-usb-uhci",
+ "usb-ehci",
+ "ich9-usb-ehci1",
+ "ich9-usb-uhci1",
+ "ich9-usb-uhci2",
+ "ich9-usb-uhci3",
+ "vt82c686b-usb-uhci",
+ "pci-ohci");
+
+
static void
uname_normalize (struct utsname *ut)
{
@@ -1703,9 +1717,60 @@ error:
}
+static int
+qemuControllerModelUSBToCaps(int model)
+{
+ switch (model) {
+ case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI:
+ return QEMU_CAPS_PIIX3_USB_UHCI;
+ case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX4_UHCI:
+ return QEMU_CAPS_PIIX4_USB_UHCI;
+ case VIR_DOMAIN_CONTROLLER_MODEL_USB_EHCI:
+ return QEMU_CAPS_USB_EHCI;
+ case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1:
+ case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1:
+ case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2:
+ case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3:
+ return QEMU_CAPS_ICH9_USB_EHCI1;
+ case VIR_DOMAIN_CONTROLLER_MODEL_USB_VT82C686B_UHCI:
+ return QEMU_CAPS_VT82C686B_USB_UHCI;
+ case VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI:
+ return QEMU_CAPS_PCI_OHCI;
+ default:
+ return -1;
+ }
+}
+
+
+static int
+qemuBuildUSBControllerDevStr(virDomainControllerDefPtr def,
+ virBitmapPtr qemuCaps,
+ virBuffer *buf)
+{
+ const char *smodel;
+ int model, caps;
+
+ model = def->model;
+ if (model == -1)
+ model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI;
+
+ smodel = qemuControllerModelUSBTypeToString(model);
+ caps = qemuControllerModelUSBToCaps(model);
+
+ if (caps == -1 || !qemuCapsGet(qemuCaps, caps)) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("%s not supported in this QEMU binary"), smodel);
+ return -1;
+ }
+
+ virBufferAsprintf(buf, "%s,id=usb%d", smodel, def->idx);
+ return 0;
+}
+
char *
qemuBuildControllerDevStr(virDomainControllerDefPtr def,
- virBitmapPtr qemuCaps)
+ virBitmapPtr qemuCaps,
+ int *nusbcontroller)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
@@ -1737,6 +1802,15 @@ qemuBuildControllerDevStr(virDomainControllerDefPtr def,
virBufferAsprintf(&buf, "usb-ccid,id=ccid%d", def->idx);
break;
+ case VIR_DOMAIN_CONTROLLER_TYPE_USB:
+ if (qemuBuildUSBControllerDevStr(def, qemuCaps, &buf) == -1)
+ goto error;
+
+ if (nusbcontroller)
+ *nusbcontroller += 1;
+
+ break;
+
/* We always get an IDE controller, whether we want it or not. */
case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
default:
@@ -2904,7 +2978,8 @@ qemuBuildCommandLine(virConnectPtr conn,
bool has_rbd_hosts = false;
virBuffer rbd_hosts = VIR_BUFFER_INITIALIZER;
bool emitBootindex = false;
-
+ int usbcontroller = 0;
+ bool usblegacy = false;
uname_normalize(&ut);
if (qemuAssignDeviceAliases(def, qemuCaps) < 0)
@@ -3423,14 +3498,26 @@ qemuBuildCommandLine(virConnectPtr conn,
goto error;
}
- virCommandAddArg(cmd, "-device");
+ if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB &&
+ def->controllers[i]->model == -1 &&
+ !qemuCapsGet(qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI)) {
+ if (usblegacy) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Multiple legacy USB controller not supported"));
+ goto error;
+ }
+ usblegacy = true;
+ } else {
+ virCommandAddArg(cmd, "-device");
- char *devstr;
- if (!(devstr = qemuBuildControllerDevStr(def->controllers[i], qemuCaps)))
- goto error;
+ char *devstr;
+ if (!(devstr = qemuBuildControllerDevStr(def->controllers[i], qemuCaps,
+ &usbcontroller)))
+ goto error;
- virCommandAddArg(cmd, devstr);
- VIR_FREE(devstr);
+ virCommandAddArg(cmd, devstr);
+ VIR_FREE(devstr);
+ }
}
}
@@ -4135,7 +4222,9 @@ qemuBuildCommandLine(virConnectPtr conn,
}
}
- virCommandAddArg(cmd, "-usb");
+ if (usbcontroller == 0)
+ virCommandAddArg(cmd, "-usb");
+
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 87660f2f8f..099d683f35 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -89,7 +89,8 @@ char * qemuBuildFSDevStr(virDomainFSDefPtr fs,
virBitmapPtr qemuCaps);
/* Current, best practice */
char * qemuBuildControllerDevStr(virDomainControllerDefPtr def,
- virBitmapPtr qemuCaps);
+ virBitmapPtr qemuCaps,
+ int *nusbcontroller);
char * qemuBuildWatchdogDevStr(virDomainWatchdogDefPtr dev,
virBitmapPtr qemuCaps);
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index b7fdfa04bb..b2da6d06df 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -286,7 +286,15 @@ int qemuDomainAttachPciControllerDevice(struct qemud_driver *driver,
if (qemuAssignDeviceControllerAlias(controller) < 0)
goto cleanup;
- if (!(devstr = qemuBuildControllerDevStr(controller, priv->qemuCaps))) {
+ if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_USB &&
+ controller->model == -1 &&
+ !qemuCapsGet(priv->qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("USB controller hotplug unsupported in this QEMU binary"));
+ goto cleanup;
+ }
+
+ if (!(devstr = qemuBuildControllerDevStr(controller, priv->qemuCaps, NULL))) {
goto cleanup;
}
}
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index b009bf3cb5..3a317b22c6 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -487,6 +487,13 @@ mymain(void)
QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE,
QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_CCID_EMULATED);
+ DO_TEST("usb-controller", false,
+ QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE,
+ QEMU_CAPS_NODEFCONFIG);
+ DO_TEST("usb-piix3-controller", false,
+ QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_PIIX3_USB_UHCI,
+ QEMU_CAPS_NODEFCONFIG);
+
DO_TEST("smbios", false, QEMU_CAPS_SMBIOS_TYPE);
DO_TEST("watchdog", false, NONE);