[libvirt] [RFC 00/12] add support for new USB2 emulation / EHCI device

Hi, The following patch intents to implement support for new USB2 and USB redirection added in QEMU 0.15. It follows somewhat Daniel's plan send earlier on this ML: https://www.redhat.com/archives/libvir-list/2011-August/msg00816.html There are some parts that I don't really know what they should be like: the "autoassign" or the default values at the domain XML level. Related RHBZ https://bugzilla.redhat.com/show_bug.cgi?id=725670 cheers Marc-André Lureau (12): Add various USB devices QEMU_CAPS Add USB controller models Add a new controller type 'usb' with optionnal 'model' USB controller can have a PCI address child element USB devices gain a new USB address child element Add USB companion controllers support Add USB hub device Modify USB port to be defined as a port path RFC: Don't reserve slot 1 if a USB controller is defined there RFC: Don't append 0 at usb id, so that it is compatible with legacy -usb Add a usb1 & usb2 qemuxml2argv test Add usb-redir device docs/formatdomain.html.in | 79 +++++- docs/schemas/domain.rng | 124 ++++++-- src/conf/domain_conf.c | 333 +++++++++++++++++++- src/conf/domain_conf.h | 59 ++++ src/libvirt_private.syms | 2 + src/qemu/qemu_capabilities.c | 34 ++ src/qemu/qemu_capabilities.h | 11 + src/qemu/qemu_command.c | 262 ++++++++++++++-- src/qemu/qemu_command.h | 10 +- src/qemu/qemu_hotplug.c | 12 +- src/vmx/vmx.c | 12 +- tests/qemuhelptest.c | 16 +- .../qemuxml2argv-input-usbmouse-addr.args | 1 + .../qemuxml2argv-input-usbmouse-addr.xml | 27 ++ .../qemuxml2argv-usb-controller.args | 1 + .../qemuxml2argv-usb-controller.xml | 16 + tests/qemuxml2argvdata/qemuxml2argv-usb-hub.args | 1 + tests/qemuxml2argvdata/qemuxml2argv-usb-hub.xml | 19 ++ .../qemuxml2argv-usb-ich9-companion.args | 6 + .../qemuxml2argv-usb-ich9-companion.xml | 30 ++ .../qemuxml2argv-usb-ich9-ehci-addr.args | 1 + .../qemuxml2argv-usb-ich9-ehci-addr.xml | 18 + .../qemuxml2argv-usb-piix3-controller.args | 1 + .../qemuxml2argv-usb-piix3-controller.xml | 16 + tests/qemuxml2argvdata/qemuxml2argv-usb-ports.args | 1 + tests/qemuxml2argvdata/qemuxml2argv-usb-ports.xml | 31 ++ tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args | 8 + tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml | 33 ++ tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.args | 1 + tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.xml | 74 +++++ tests/qemuxml2argvtest.c | 34 ++ 31 files changed, 1196 insertions(+), 77 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-controller.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-controller.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-hub.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-hub.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ports.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ports.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.xml -- 1.7.6

--- src/qemu/qemu_capabilities.c | 31 +++++++++++++++++++++++++++++++ src/qemu/qemu_capabilities.h | 10 ++++++++++ tests/qemuhelptest.c | 13 ++++++++++--- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index f665de4..a33ad4e 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -125,6 +125,17 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST, "sga", "virtio-blk-pci.event_idx", "virtio-net-pci.event_idx", + + "piix3-usb-uhci", /* 65 */ + "piix4-usb-uhci", + "usb-ehci", + "ich9-usb-ehci1", + "ich9-usb-uhci1", + + "ich9-usb-uhci2", /* 70 */ + "ich9-usb-uhci3", + "vt82c686b-usb-uhci", + "usb-redir", ); struct qemu_feature_flags { @@ -1196,6 +1207,26 @@ qemuCapsParseDeviceStr(const char *str, virBitmapPtr flags) qemuCapsSet(flags, QEMU_CAPS_CCID_EMULATED); if (strstr(str, "name \"ccid-card-passthru\"")) qemuCapsSet(flags, QEMU_CAPS_CCID_PASSTHRU); + + if (strstr(str, "name \"piix3-usb-uhci\"")) + qemuCapsSet(flags, QEMU_CAPS_PIIX3_USB_UHCI); + if (strstr(str, "name \"piix4-usb-uhci\"")) + qemuCapsSet(flags, QEMU_CAPS_PIIX4_USB_UHCI); + if (strstr(str, "name \"usb-ehci\"")) + qemuCapsSet(flags, QEMU_CAPS_USB_EHCI); + if (strstr(str, "name \"ich9-usb-ehci1\"")) + qemuCapsSet(flags, QEMU_CAPS_ICH9_USB_EHCI1); + if (strstr(str, "name \"ich9-usb-uhci1\"")) + qemuCapsSet(flags, QEMU_CAPS_ICH9_USB_UHCI1); + if (strstr(str, "name \"ich9-usb-uhci2\"")) + qemuCapsSet(flags, QEMU_CAPS_ICH9_USB_UHCI2); + if (strstr(str, "name \"ich9-usb-uhci3\"")) + qemuCapsSet(flags, QEMU_CAPS_ICH9_USB_UHCI3); + if (strstr(str, "name \"vt82c686b-usb-uhci\"")) + qemuCapsSet(flags, QEMU_CAPS_VT82C686B_USB_UHCI); + if (strstr(str, "name \"usb-redir\"")) + qemuCapsSet(flags, QEMU_CAPS_USB_REDIR); + /* Prefer -chardev spicevmc (detected earlier) over -device spicevmc */ if (!qemuCapsGet(flags, QEMU_CAPS_CHARDEV_SPICEVMC) && strstr(str, "name \"spicevmc\"")) diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 13af0b9..7f94392 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -101,6 +101,16 @@ enum qemuCapsFlags { QEMU_CAPS_VIRTIO_BLK_EVENT_IDX = 63, /* virtio-blk-pci.event_idx */ QEMU_CAPS_VIRTIO_NET_EVENT_IDX = 64, /* virtio-net-pci.event_idx */ + QEMU_CAPS_PIIX3_USB_UHCI = 65, /* -device piix3-usb-uhci */ + QEMU_CAPS_PIIX4_USB_UHCI = 66, /* -device piix4-usb-uhci */ + QEMU_CAPS_USB_EHCI = 67, /* -device usb-ehci */ + QEMU_CAPS_ICH9_USB_EHCI1 = 68, /* -device ich9-usb-ehci1 */ + QEMU_CAPS_ICH9_USB_UHCI1 = 69, /* -device ich9-usb-uhci1 */ + QEMU_CAPS_ICH9_USB_UHCI2 = 70, /* -device ich9-usb-uhci2 */ + QEMU_CAPS_ICH9_USB_UHCI3 = 71, /* -device ich9-usb-uhci3 */ + QEMU_CAPS_VT82C686B_USB_UHCI = 72, /* -device vt82c686b-usb-uhci */ + QEMU_CAPS_USB_REDIR = 73, /* -device usb-redir */ + QEMU_CAPS_LAST, /* this must always be the last item */ }; diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c index 102dd22..7002cdb 100644 --- a/tests/qemuhelptest.c +++ b/tests/qemuhelptest.c @@ -347,7 +347,9 @@ mymain(void) QEMU_CAPS_VGA_NONE, QEMU_CAPS_MIGRATE_QEMU_FD, QEMU_CAPS_DRIVE_AIO, - QEMU_CAPS_DEVICE_SPICEVMC); + QEMU_CAPS_DEVICE_SPICEVMC, + QEMU_CAPS_PIIX3_USB_UHCI, + QEMU_CAPS_PIIX4_USB_UHCI); DO_TEST("qemu-kvm-0.12.3", 12003, 1, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -431,7 +433,10 @@ mymain(void) QEMU_CAPS_MIGRATE_QEMU_FD, QEMU_CAPS_DRIVE_AIO, QEMU_CAPS_DEVICE_SPICEVMC, - QEMU_CAPS_PCI_MULTIFUNCTION); + QEMU_CAPS_PCI_MULTIFUNCTION, + QEMU_CAPS_PIIX3_USB_UHCI, + QEMU_CAPS_PIIX4_USB_UHCI, + QEMU_CAPS_VT82C686B_USB_UHCI); DO_TEST("qemu-kvm-0.12.1.2-rhel61", 12001, 1, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -476,7 +481,9 @@ mymain(void) QEMU_CAPS_CHARDEV_SPICEVMC, QEMU_CAPS_DEVICE_QXL_VGA, QEMU_CAPS_VIRTIO_TX_ALG, - QEMU_CAPS_VIRTIO_IOEVENTFD); + QEMU_CAPS_VIRTIO_IOEVENTFD, + QEMU_CAPS_PIIX3_USB_UHCI, + QEMU_CAPS_PIIX4_USB_UHCI); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 1.7.6

+ "piix3-usb-uhci", /* 65 */ + "piix4-usb-uhci", + "usb-ehci", + "ich9-usb-ehci1", + "ich9-usb-uhci1", + + "ich9-usb-uhci2", /* 70 */ + "ich9-usb-uhci3", + "vt82c686b-usb-uhci", + "usb-redir",
There is also "pci-ohci" cheers, Gerd

On Sun, Aug 21, 2011 at 10:01:12PM +0300, Marc-André Lureau wrote:
--- src/qemu/qemu_capabilities.c | 31 +++++++++++++++++++++++++++++++ src/qemu/qemu_capabilities.h | 10 ++++++++++ tests/qemuhelptest.c | 13 ++++++++++--- 3 files changed, 51 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index f665de4..a33ad4e 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -125,6 +125,17 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST, "sga", "virtio-blk-pci.event_idx", "virtio-net-pci.event_idx", + + "piix3-usb-uhci", /* 65 */ + "piix4-usb-uhci", + "usb-ehci", + "ich9-usb-ehci1", + "ich9-usb-uhci1", + + "ich9-usb-uhci2", /* 70 */ + "ich9-usb-uhci3", + "vt82c686b-usb-uhci", + "usb-redir", );
IMHO this is a little bit overkill. If bunch of things were introduced at the same time there is no need to probe for each one of them. ie you'll never find that you have 'ich9-usb-echi', but not also have 'ich9-usb-uhci1,2,3', so probing for all 4 individually just bloats our capabilities. Likewise we don't generally bother probing for things which have been around since 0.9.0 (our min version) ie the piix USB controllers. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

--- docs/schemas/domain.rng | 8 ++++++++ src/conf/domain_conf.c | 10 +++++++++- src/conf/domain_conf.h | 8 ++++++++ src/vmx/vmx.c | 12 +++++++++++- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index dd8c41a..34208c5 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -909,6 +909,14 @@ <value>lsilogic</value> <value>lsisas1068</value> <value>vmpvscsi</value> + <value>piix3-uhci</value> + <value>piix4-uhci</value> + <value>ehci</value> + <value>ich9-ehci1</value> + <value>ich9-uhci1</value> + <value>ich9-uhci2</value> + <value>ich9-uhci3</value> + <value>vt82c686b-uhci</value> </choice> </attribute> </optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index ce1f3c5..6e83003 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -200,7 +200,15 @@ VIR_ENUM_IMPL(virDomainControllerModel, VIR_DOMAIN_CONTROLLER_MODEL_LAST, "buslogic", "lsilogic", "lsisas1068", - "vmpvscsi") + "vmpvscsi", + "piix3-uhci", + "piix4-uhci", + "ehci", + "ich9-ehci1", + "ich9-uhci1", + "ich9-uhci2", + "ich9-uhci3", + "vt82c686b-uhci") VIR_ENUM_IMPL(virDomainFS, VIR_DOMAIN_FS_TYPE_LAST, "mount", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 2cc9b06..ca88254 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -263,6 +263,14 @@ enum virDomainControllerModel { VIR_DOMAIN_CONTROLLER_MODEL_LSILOGIC, VIR_DOMAIN_CONTROLLER_MODEL_LSISAS1068, VIR_DOMAIN_CONTROLLER_MODEL_VMPVSCSI, + VIR_DOMAIN_CONTROLLER_MODEL_PIIX3_UHCI, + VIR_DOMAIN_CONTROLLER_MODEL_PIIX4_UHCI, + VIR_DOMAIN_CONTROLLER_MODEL_EHCI, + VIR_DOMAIN_CONTROLLER_MODEL_ICH9_EHCI1, + VIR_DOMAIN_CONTROLLER_MODEL_ICH9_UHCI1, + VIR_DOMAIN_CONTROLLER_MODEL_ICH9_UHCI2, + VIR_DOMAIN_CONTROLLER_MODEL_ICH9_UHCI3, + VIR_DOMAIN_CONTROLLER_MODEL_VT82C686B_UHCI, VIR_DOMAIN_CONTROLLER_MODEL_LAST }; diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index 08c2716..298a366 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -480,11 +480,21 @@ def->parallels[0]... #define VMX_BUILD_NAME(_suffix) \ VMX_BUILD_NAME_EXTRA(_suffix, #_suffix) +enum virVMXSCSIControllerModel { + VIR_VMX_SCSI_CONTROLLER_MODEL_AUTO, + VIR_VMX_SCSI_CONTROLLER_MODEL_BUSLOGIC, + VIR_VMX_SCSI_CONTROLLER_MODEL_LSILOGIC, + VIR_VMX_SCSI_CONTROLLER_MODEL_LSISAS1068, + VIR_VMX_SCSI_CONTROLLER_MODEL_PVSCSI, + + VIR_VMX_SCSI_CONTROLLER_MODEL_LAST +}; + /* directly map the virDomainControllerModel to virVMXSCSIControllerModel, * this is good enough for now because all virDomainControllerModel values * are actually SCSI controller models in the ESX case */ VIR_ENUM_DECL(virVMXSCSIControllerModel) -VIR_ENUM_IMPL(virVMXSCSIControllerModel, VIR_DOMAIN_CONTROLLER_MODEL_LAST, +VIR_ENUM_IMPL(virVMXSCSIControllerModel, VIR_VMX_SCSI_CONTROLLER_MODEL_LAST, "auto", /* just to match virDomainControllerModel, will never be used */ "buslogic", "lsilogic", -- 1.7.6

On Sun, Aug 21, 2011 at 10:01:13PM +0300, Marc-André Lureau wrote:
--- docs/schemas/domain.rng | 8 ++++++++ src/conf/domain_conf.c | 10 +++++++++- src/conf/domain_conf.h | 8 ++++++++ src/vmx/vmx.c | 12 +++++++++++- 4 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index dd8c41a..34208c5 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -909,6 +909,14 @@ <value>lsilogic</value> <value>lsisas1068</value> <value>vmpvscsi</value> + <value>piix3-uhci</value> + <value>piix4-uhci</value> + <value>ehci</value> + <value>ich9-ehci1</value> + <value>ich9-uhci1</value> + <value>ich9-uhci2</value> + <value>ich9-uhci3</value> + <value>vt82c686b-uhci</value> </choice> </attribute> </optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index ce1f3c5..6e83003 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -200,7 +200,15 @@ VIR_ENUM_IMPL(virDomainControllerModel, VIR_DOMAIN_CONTROLLER_MODEL_LAST, "buslogic", "lsilogic", "lsisas1068", - "vmpvscsi") + "vmpvscsi", + "piix3-uhci", + "piix4-uhci", + "ehci", + "ich9-ehci1", + "ich9-uhci1", + "ich9-uhci2", + "ich9-uhci3", + "vt82c686b-uhci")
VIR_ENUM_IMPL(virDomainFS, VIR_DOMAIN_FS_TYPE_LAST, "mount", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 2cc9b06..ca88254 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -263,6 +263,14 @@ enum virDomainControllerModel { VIR_DOMAIN_CONTROLLER_MODEL_LSILOGIC, VIR_DOMAIN_CONTROLLER_MODEL_LSISAS1068, VIR_DOMAIN_CONTROLLER_MODEL_VMPVSCSI, + VIR_DOMAIN_CONTROLLER_MODEL_PIIX3_UHCI, + VIR_DOMAIN_CONTROLLER_MODEL_PIIX4_UHCI, + VIR_DOMAIN_CONTROLLER_MODEL_EHCI, + VIR_DOMAIN_CONTROLLER_MODEL_ICH9_EHCI1, + VIR_DOMAIN_CONTROLLER_MODEL_ICH9_UHCI1, + VIR_DOMAIN_CONTROLLER_MODEL_ICH9_UHCI2, + VIR_DOMAIN_CONTROLLER_MODEL_ICH9_UHCI3, + VIR_DOMAIN_CONTROLLER_MODEL_VT82C686B_UHCI,
VIR_DOMAIN_CONTROLLER_MODEL_LAST };
The current model handling code here is a little dodgy. Those existing models are only valid for type=scsi, so we should only be parsing the 'model' attribute for that type. We should rename that existing enum to virDomainControllerSCSIModel and create a new enum virDomainControllerUSBModel and make sure to call the right one at the right place. -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

The model by default is piix3-uchi. Example: <controller type='usb' index='0' model='ich9-ehci'/> --- docs/formatdomain.html.in | 17 ++- docs/schemas/domain.rng | 1 + src/conf/domain_conf.c | 3 +- src/conf/domain_conf.h | 1 + src/qemu/qemu_command.c | 112 ++++++++++++++++++-- src/qemu/qemu_command.h | 3 +- src/qemu/qemu_hotplug.c | 10 ++- .../qemuxml2argv-usb-controller.args | 1 + .../qemuxml2argv-usb-controller.xml | 16 +++ .../qemuxml2argv-usb-piix3-controller.args | 1 + .../qemuxml2argv-usb-piix3-controller.xml | 16 +++ tests/qemuxml2argvtest.c | 7 ++ 12 files changed, 170 insertions(+), 18 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-controller.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-controller.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index f46771d..0a383f6 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1217,17 +1217,22 @@ <p> Each controller has a mandatory attribute <code>type</code>, - which must be one of "ide", "fdc", "scsi", "sata", "ccid", or - "virtio-serial", and a mandatory attribute <code>index</code> - which is the decimal integer describing in which order the bus - controller is encountered (for use in <code>controller</code> - attributes of <code><address></code> 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 <code>index</code> which is the decimal integer + describing in which order the bus controller is encountered (for + use in <code>controller</code> attributes + of <code><address></code> elements). The "virtio-serial" + controller has two additional optional attributes <code>ports</code> and <code>vectors</code>, which control how many devices can be connected through the controller. A "scsi" controller has an optional attribute <code>model</code>, which is one of "auto", "buslogic", "lsilogic", "lsias1068", or "vmpvscsi". + A "usb" controller has an optional attribute <code>model</code>, + which is one of "piix3-uhci", "piix4-uhci", "ehci", + "ich9-ehci1", "ich9-uhci1", "ich9-uhci2", "ich9-uhci3", + or "vt82c686b-uhci". </p> <p> diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 34208c5..a059ce0 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -877,6 +877,7 @@ <value>scsi</value> <value>sata</value> <value>ccid</value> + <value>usb</value> </choice> </attribute> </optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6e83003..7fe7f4c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -193,7 +193,8 @@ VIR_ENUM_IMPL(virDomainController, VIR_DOMAIN_CONTROLLER_TYPE_LAST, "scsi", "sata", "virtio-serial", - "ccid") + "ccid", + "usb") VIR_ENUM_IMPL(virDomainControllerModel, VIR_DOMAIN_CONTROLLER_MODEL_LAST, "auto", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ca88254..c859e51 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -252,6 +252,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 dbfc7d9..4168504 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -83,6 +83,24 @@ VIR_ENUM_IMPL(qemuVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, "", /* don't support vbox */ "qxl"); +VIR_ENUM_DECL(qemuControllerModel) + +VIR_ENUM_IMPL(qemuControllerModel, VIR_DOMAIN_CONTROLLER_MODEL_LAST, + "", /* auto */ + "", /* buslogic don't support */ + "", /* lsilogic don't support */ + "", /* lsisas don't support */ + "", /* vmpvscsi don't support */ + "piix3-usb-uhci", + "piix4-usb-uhci", + "usb-ehci", + "ich9-usb-ehci1", + "ich9-usb-uhci1", + "ich9-usb-uhci2", + "ich9-usb-uhci3", + "vt82c686b-usb-uhci"); + + static void uname_normalize (struct utsname *ut) { @@ -1684,9 +1702,61 @@ error: } +static int +qemuControllerModelToCaps(int model) +{ + switch (model) { + case VIR_DOMAIN_CONTROLLER_MODEL_PIIX3_UHCI: + return QEMU_CAPS_PIIX3_USB_UHCI; + case VIR_DOMAIN_CONTROLLER_MODEL_PIIX4_UHCI: + return QEMU_CAPS_PIIX4_USB_UHCI; + case VIR_DOMAIN_CONTROLLER_MODEL_EHCI: + return QEMU_CAPS_USB_EHCI; + case VIR_DOMAIN_CONTROLLER_MODEL_ICH9_EHCI1: + return QEMU_CAPS_ICH9_USB_EHCI1; + case VIR_DOMAIN_CONTROLLER_MODEL_ICH9_UHCI1: + return QEMU_CAPS_ICH9_USB_UHCI1; + case VIR_DOMAIN_CONTROLLER_MODEL_ICH9_UHCI2: + return QEMU_CAPS_ICH9_USB_UHCI2; + case VIR_DOMAIN_CONTROLLER_MODEL_ICH9_UHCI3: + return QEMU_CAPS_ICH9_USB_UHCI3; + case VIR_DOMAIN_CONTROLLER_MODEL_VT82C686B_UHCI: + return QEMU_CAPS_VT82C686B_USB_UHCI; + 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_AUTO) + model = VIR_DOMAIN_CONTROLLER_MODEL_PIIX3_UHCI; + + smodel = qemuControllerModelTypeToString(model); + caps = qemuControllerModelToCaps(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; @@ -1718,6 +1788,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: @@ -2885,7 +2964,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) @@ -3404,14 +3484,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); + } } } @@ -4116,7 +4208,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 87660f2..099d683 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 b7fdfa0..b2da6d0 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/qemuxml2argvdata/qemuxml2argv-usb-controller.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-controller.args new file mode 100644 index 0000000..f21efc7 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-controller.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 virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-controller.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-controller.xml new file mode 100644 index 0000000..82b503e --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-controller.xml @@ -0,0 +1,16 @@ +<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'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.args new file mode 100644 index 0000000..799b75f --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.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 -device piix3-usb-uhci,id=usb0,bus=pci.0,addr=0x3 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.xml new file mode 100644 index 0000000..1996d20 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.xml @@ -0,0 +1,16 @@ +<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='piix3-uhci'/> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 6e8da5e..692a02b 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -484,6 +484,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); -- 1.7.6

On Sun, Aug 21, 2011 at 10:01:14PM +0300, Marc-André Lureau wrote:
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index dbfc7d9..4168504 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -83,6 +83,24 @@ VIR_ENUM_IMPL(qemuVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, "", /* don't support vbox */ "qxl");
+VIR_ENUM_DECL(qemuControllerModel) + +VIR_ENUM_IMPL(qemuControllerModel, VIR_DOMAIN_CONTROLLER_MODEL_LAST, + "", /* auto */ + "", /* buslogic don't support */ + "", /* lsilogic don't support */ + "", /* lsisas don't support */ + "", /* vmpvscsi don't support */ + "piix3-usb-uhci", + "piix4-usb-uhci", + "usb-ehci", + "ich9-usb-ehci1", + "ich9-usb-uhci1", + "ich9-usb-uhci2", + "ich9-usb-uhci3", + "vt82c686b-usb-uhci");
If we separate out the model enums per controller type, then we can avoid the nasty hacks of "" in this declaration.
+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_AUTO) + model = VIR_DOMAIN_CONTROLLER_MODEL_PIIX3_UHCI; + + smodel = qemuControllerModelTypeToString(model); + caps = qemuControllerModelToCaps(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; +} +
Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

--- .../qemuxml2argv-usb-ich9-ehci-addr.args | 1 + .../qemuxml2argv-usb-ich9-ehci-addr.xml | 18 ++++++++++++++++++ tests/qemuxml2argvtest.c | 3 +++ 3 files changed, 22 insertions(+), 0 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.xml diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.args new file mode 100644 index 0000000..502244d --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.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 -device ich9-usb-ehci1,id=usb0,bus=pci.0,multifunction=on,addr=0x4.0x7 -device virtio-balloon-pci,id=balloon0,bus=pci.0,multifunction=on,addr=0x3.0x0 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.xml new file mode 100644 index 0000000..49a57e2 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.xml @@ -0,0 +1,18 @@ +<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> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 692a02b..f3abc24 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -490,6 +490,9 @@ mymain(void) DO_TEST("usb-piix3-controller", false, QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_PIIX3_USB_UHCI, QEMU_CAPS_NODEFCONFIG); + DO_TEST("usb-ich9-ehci-addr", false, + QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG, + QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_ICH9_USB_EHCI1); DO_TEST("smbios", false, QEMU_CAPS_SMBIOS_TYPE); -- 1.7.6

--- docs/schemas/domain.rng | 14 +++++ src/conf/domain_conf.c | 62 +++++++++++++++++++- src/conf/domain_conf.h | 10 +++ src/qemu/qemu_command.c | 40 ++++++++++--- src/qemu/qemu_command.h | 6 +- src/qemu/qemu_hotplug.c | 2 +- .../qemuxml2argv-input-usbmouse-addr.args | 1 + .../qemuxml2argv-input-usbmouse-addr.xml | 27 +++++++++ tests/qemuxml2argvtest.c | 2 + 9 files changed, 149 insertions(+), 15 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index a059ce0..cc0bbca 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -2028,6 +2028,14 @@ </attribute> </element> </define> + <define name="usbportaddress"> + <attribute name="bus"> + <ref name="usbAddr"/> + </attribute> + <attribute name="port"> + <ref name="usbAddr"/> + </attribute> + </define> <define name="pciaddress"> <optional> <attribute name="domain"> @@ -2359,6 +2367,12 @@ </attribute> <ref name="ccidaddress"/> </group> + <group> + <attribute name="type"> + <value>usb</value> + </attribute> + <ref name="usbportaddress"/> + </group> </choice> </element> </define> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7fe7f4c..00345a3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -133,7 +133,8 @@ VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST, "pci", "drive", "virtio-serial", - "ccid") + "ccid", + "usb") VIR_ENUM_IMPL(virDomainDisk, VIR_DOMAIN_DISK_TYPE_LAST, "block", @@ -1395,6 +1396,9 @@ int virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info, case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE: return virDomainDeviceDriveAddressIsValid(&info->addr.drive); + + case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB: + return virDomainDeviceUSBAddressIsValid(&info->addr.usb); } return 0; @@ -1416,6 +1420,13 @@ int virDomainDeviceDriveAddressIsValid(virDomainDeviceDriveAddressPtr addr ATTRI return 1; /* 0 is valid for all fields, so any successfully parsed addr is valid */ } +int virDomainDeviceUSBAddressIsValid(virDomainDeviceUSBAddressPtr addr) +{ + if (addr->port >= 128) /* FIXME: is this correct */ + return 0; + + return 1; +} int virDomainDeviceVirtioSerialAddressIsValid( virDomainDeviceVirtioSerialAddressPtr addr ATTRIBUTE_UNUSED) @@ -1784,6 +1795,40 @@ cleanup: return ret; } +static int +virDomainDeviceUSBAddressParseXML(xmlNodePtr node, + virDomainDeviceUSBAddressPtr addr) +{ + char *port, *bus; + int ret = -1; + + memset(addr, 0, sizeof(*addr)); + + port = virXMLPropString(node, "port"); + bus = virXMLPropString(node, "bus"); + + if (port && + virStrToLong_ui(port, NULL, 10, &addr->port) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot parse <address> 'port' attribute")); + goto cleanup; + } + + if (bus && + virStrToLong_ui(bus, NULL, 10, &addr->bus) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot parse <address> 'bus' attribute")); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(bus); + VIR_FREE(port); + return ret; +} + /* Parse the XML definition for a device address * @param node XML nodeset to parse for device address definition */ @@ -1857,6 +1902,11 @@ virDomainDeviceInfoParseXML(xmlNodePtr node, goto cleanup; break; + case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB: + if (virDomainDeviceUSBAddressParseXML(address, &info->addr.usb) < 0) + goto cleanup; + break; + default: /* Should not happen */ virDomainReportError(VIR_ERR_INTERNAL_ERROR, @@ -3791,7 +3841,7 @@ error: goto cleanup; } -/* Parse the XML definition for a network interface */ +/* Parse the XML definition for an input device */ static virDomainInputDefPtr virDomainInputDefParseXML(const char *ostype, xmlNodePtr node, @@ -3869,6 +3919,14 @@ virDomainInputDefParseXML(const char *ostype, if (virDomainDeviceInfoParseXML(node, &def->info, flags) < 0) goto error; + if (def->bus == VIR_DOMAIN_INPUT_BUS_USB && + def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { + virDomainReportError(VIR_ERR_XML_ERROR, "%s", + _("Invalid address for a USB device")); + goto error; + } + cleanup: VIR_FREE(type); VIR_FREE(bus); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index c859e51..f231725 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -69,6 +69,7 @@ enum virDomainDeviceAddressType { VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID, + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST }; @@ -105,6 +106,13 @@ struct _virDomainDeviceCcidAddress { unsigned int slot; }; +typedef struct _virDomainDeviceUSBAddress virDomainDeviceUSBAddress; +typedef virDomainDeviceUSBAddress *virDomainDeviceUSBAddressPtr; +struct _virDomainDeviceUSBAddress { + unsigned int bus; + unsigned int port; +}; + typedef struct _virDomainDeviceInfo virDomainDeviceInfo; typedef virDomainDeviceInfo *virDomainDeviceInfoPtr; struct _virDomainDeviceInfo { @@ -115,6 +123,7 @@ struct _virDomainDeviceInfo { virDomainDeviceDriveAddress drive; virDomainDeviceVirtioSerialAddress vioserial; virDomainDeviceCcidAddress ccid; + virDomainDeviceUSBAddress usb; } addr; }; @@ -1431,6 +1440,7 @@ int virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info, int virDomainDevicePCIAddressIsValid(virDomainDevicePCIAddressPtr addr); int virDomainDeviceDriveAddressIsValid(virDomainDeviceDriveAddressPtr addr); int virDomainDeviceVirtioSerialAddressIsValid(virDomainDeviceVirtioSerialAddressPtr addr); +int virDomainDeviceUSBAddressIsValid(virDomainDeviceUSBAddressPtr addr); void virDomainDeviceInfoClear(virDomainDeviceInfoPtr info); void virDomainDefClearPCIAddresses(virDomainDefPtr def); void virDomainDefClearDeviceAliases(virDomainDefPtr def); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 4168504..280b7ae 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1321,7 +1321,11 @@ qemuBuildDeviceAddressStr(virBufferPtr buf, info->addr.pci.slot, info->addr.pci.function); else virBufferAsprintf(buf, ",addr=0x%x", info->addr.pci.slot); + } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { + virBufferAsprintf(buf, ",bus=usb%d.0", info->addr.usb.bus); + virBufferAsprintf(buf, ",port=%d", info->addr.usb.port); } + return 0; } @@ -2084,7 +2088,8 @@ error: char * -qemuBuildUSBInputDevStr(virDomainInputDefPtr dev) +qemuBuildUSBInputDevStr(virDomainInputDefPtr dev, + virBitmapPtr qemuCaps) { virBuffer buf = VIR_BUFFER_INITIALIZER; @@ -2092,6 +2097,9 @@ qemuBuildUSBInputDevStr(virDomainInputDefPtr dev) dev->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ? "usb-mouse" : "usb-tablet", dev->info.alias); + if (qemuBuildDeviceAddressStr(&buf, &dev->info, qemuCaps) < 0) + goto error; + if (virBufferError(&buf)) { virReportOOMError(); goto error; @@ -2280,9 +2288,10 @@ qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev) char * -qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev) +qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev, + virBitmapPtr qemuCaps) { - char *ret = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; if (!dev->source.subsys.u.usb.bus && !dev->source.subsys.u.usb.device) { @@ -2291,13 +2300,24 @@ qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev) return NULL; } - if (virAsprintf(&ret, "usb-host,hostbus=%d,hostaddr=%d,id=%s", - dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device, - dev->info.alias) < 0) + 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; + + if (virBufferError(&buf)) { virReportOOMError(); + goto error; + } - return ret; + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; } @@ -4218,7 +4238,7 @@ qemuBuildCommandLine(virConnectPtr conn, if (qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { char *optstr; virCommandAddArg(cmd, "-device"); - if (!(optstr = qemuBuildUSBInputDevStr(input))) + if (!(optstr = qemuBuildUSBInputDevStr(input, qemuCaps))) goto error; virCommandAddArg(cmd, optstr); VIR_FREE(optstr); @@ -4736,7 +4756,7 @@ qemuBuildCommandLine(virConnectPtr conn, if (qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { virCommandAddArg(cmd, "-device"); - if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev))) + if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev, qemuCaps))) goto error; virCommandAddArg(cmd, devstr); VIR_FREE(devstr); diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 099d683..de09577 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -98,7 +98,8 @@ char * qemuBuildWatchdogDevStr(virDomainWatchdogDefPtr dev, char * qemuBuildMemballoonDevStr(virDomainMemballoonDefPtr dev, virBitmapPtr qemuCaps); -char * qemuBuildUSBInputDevStr(virDomainInputDefPtr dev); +char * qemuBuildUSBInputDevStr(virDomainInputDefPtr dev, + virBitmapPtr qemuCaps); char * qemuBuildSoundDevStr(virDomainSoundDefPtr sound, virBitmapPtr qemuCaps); @@ -115,7 +116,8 @@ int qemuOpenPCIConfig(virDomainHostdevDefPtr dev); /* Legacy, pre device support */ char * qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev); /* Current, best practice */ -char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev); +char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev, + virBitmapPtr qemuCaps); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index b2da6d0..60cd241 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -922,7 +922,7 @@ int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver, if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) { if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0) goto error; - if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev))) + if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev, priv->qemuCaps))) goto error; } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args new file mode 100644 index 0000000..b6dc0d3 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.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 -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -device usb-mouse,id=input0,bus=usb.0,port=4 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml new file mode 100644 index 0000000..a2fa8e3 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml @@ -0,0 +1,27 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219136</memory> + <currentMemory>219136</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> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' unit='0'/> + </disk> + <controller type='ide' index='0'/> + <input type='mouse' bus='usb'> + <address type='usb' bus='0' port='4'/> + </input> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index f3abc24..b573380 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -493,6 +493,8 @@ mymain(void) DO_TEST("usb-ich9-ehci-addr", false, QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_ICH9_USB_EHCI1); + DO_TEST("input-usbmouse-addr", false, + QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG); DO_TEST("smbios", false, QEMU_CAPS_SMBIOS_TYPE); -- 1.7.6

On Sun, Aug 21, 2011 at 10:01:16PM +0300, Marc-André Lureau wrote:
--- docs/schemas/domain.rng | 14 +++++ src/conf/domain_conf.c | 62 +++++++++++++++++++- src/conf/domain_conf.h | 10 +++ src/qemu/qemu_command.c | 40 ++++++++++--- src/qemu/qemu_command.h | 6 +- src/qemu/qemu_hotplug.c | 2 +- .../qemuxml2argv-input-usbmouse-addr.args | 1 + .../qemuxml2argv-input-usbmouse-addr.xml | 27 +++++++++ tests/qemuxml2argvtest.c | 2 + 9 files changed, 149 insertions(+), 15 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml
So this lets users specify an explicit USB address for their devices. If an existing app is using libvirt though, they won't be doing this, and we want to make sure they get stable USB port addresses. With PCI we have a function in the QEMU driver which will assign PCI slots to any PCI device which doesn't already have one. I think we need todo the same for USB ports, so all configs become migration-safe. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Companion controllers take an extra 'master' attribute to associate them. --- docs/formatdomain.html.in | 20 +++++++++ docs/schemas/domain.rng | 15 +++++++ src/conf/domain_conf.c | 44 ++++++++++++++++++++ src/conf/domain_conf.h | 18 ++++++++ src/qemu/qemu_command.c | 7 +++ .../qemuxml2argv-usb-ich9-companion.args | 6 +++ .../qemuxml2argv-usb-ich9-companion.xml | 30 +++++++++++++ tests/qemuxml2argvtest.c | 5 ++ 8 files changed, 145 insertions(+), 0 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 0a383f6..5c232fa 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1243,6 +1243,26 @@ sub-element. </p> + <p> + USB companion controllers have an optional + sub-element <code><master></code> to specify the exact + relationship of the companion to its master controller. + </p> + +<pre> + ... + <devices> + <controller type='usb' index='0' model='ich9-ehci1'> + <address type='pci' domain='0' bus='0' slot='4' function='7'/> + </controller> + <controller type='usb' index='1' model='ich9-uhci1'> + <master bus='0' startport='0'/> + <address type='pci' domain='0' bus='0' slot='4' function='0'/> + </controller> + ... + </devices> + ...</pre> + <h4><a name="elementsLease">Device leases</a></h4> <p> diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index cc0bbca..6a2e71a 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -922,6 +922,9 @@ </attribute> </optional> <optional> + <ref name="usbmaster"/> + </optional> + <optional> <ref name="address"/> </optional> </element> @@ -2377,6 +2380,18 @@ </element> </define> + <define name="usbmaster"> + <element name="master"> + <attribute name="bus"> + <ref name="usbAddr"/> + </attribute> + <attribute name="startport"> + <ref name="usbAddr"/> + </attribute> + <empty/> + </element> + </define> + <define name="filterref-node-attributes"> <attribute name="filter"> <data type="NCName"/> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 00345a3..837e657 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1829,6 +1829,40 @@ cleanup: return ret; } +static int +virDomainDeviceUSBMasterParseXML(xmlNodePtr node, + virDomainDeviceUSBMasterPtr master) +{ + char *bus, *startport; + int ret = -1; + + memset(master, 0, sizeof(*master)); + + bus = virXMLPropString(node, "bus"); + startport = virXMLPropString(node, "startport"); + + if (bus && + virStrToLong_ui(bus, NULL, 10, &master->bus) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot parse <master> 'bus' attribute")); + goto cleanup; + } + + if (startport && + virStrToLong_ui(startport, NULL, 10, &master->startport) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot parse <master> 'startport' attribute")); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(bus); + VIR_FREE(startport); + return ret; +} + /* Parse the XML definition for a device address * @param node XML nodeset to parse for device address definition */ @@ -1839,6 +1873,7 @@ virDomainDeviceInfoParseXML(xmlNodePtr node, { xmlNodePtr cur; xmlNodePtr address = NULL; + xmlNodePtr master = NULL; xmlNodePtr alias = NULL; char *type = NULL; int ret = -1; @@ -1855,6 +1890,9 @@ virDomainDeviceInfoParseXML(xmlNodePtr node, } else if (address == NULL && xmlStrEqual(cur->name, BAD_CAST "address")) { address = cur; + } else if (master == NULL && + xmlStrEqual(cur->name, BAD_CAST "master")) { + master = cur; } } cur = cur->next; @@ -1863,6 +1901,12 @@ virDomainDeviceInfoParseXML(xmlNodePtr node, if (alias) info->alias = virXMLPropString(alias, "name"); + if (master) { + info->mastertype = VIR_DOMAIN_CONTROLLER_MASTER_USB; + if (virDomainDeviceUSBMasterParseXML(master, &info->master.usb) < 0) + goto cleanup; + } + if (!address) return 0; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index f231725..0f51d47 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -113,6 +113,20 @@ struct _virDomainDeviceUSBAddress { unsigned int port; }; +enum virDomainControllerMaster { + VIR_DOMAIN_CONTROLLER_MASTER_NONE, + VIR_DOMAIN_CONTROLLER_MASTER_USB, + + VIR_DOMAIN_CONTROLLER_MASTER_LAST +}; + +typedef struct _virDomainDeviceUSBMaster virDomainDeviceUSBMaster; +typedef virDomainDeviceUSBMaster *virDomainDeviceUSBMasterPtr; +struct _virDomainDeviceUSBMaster { + unsigned int bus; + unsigned int startport; +}; + typedef struct _virDomainDeviceInfo virDomainDeviceInfo; typedef virDomainDeviceInfo *virDomainDeviceInfoPtr; struct _virDomainDeviceInfo { @@ -125,6 +139,10 @@ struct _virDomainDeviceInfo { virDomainDeviceCcidAddress ccid; virDomainDeviceUSBAddress usb; } addr; + int mastertype; + union { + virDomainDeviceUSBMaster usb; + } master; }; typedef struct _virDomainLeaseDef virDomainLeaseDef; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 280b7ae..8deae23 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1754,6 +1754,13 @@ qemuBuildUSBControllerDevStr(virDomainControllerDefPtr def, } virBufferAsprintf(buf, "%s,id=usb%d", smodel, def->idx); + + if (def->info.mastertype == VIR_DOMAIN_CONTROLLER_MASTER_USB) { + virBufferAsprintf(buf, ",masterbus=usb%d.0,firstport=%d", + def->info.master.usb.bus, def->info.master.usb.startport); + } + + return 0; } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.args new file mode 100644 index 0000000..d5a2e08 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.args @@ -0,0 +1,6 @@ +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=usb0,bus=pci.0,multifunction=on,addr=0x4.0x7 \ +-device ich9-usb-uhci1,id=usb1,masterbus=usb0.0,firstport=0,bus=pci.0,multifunction=on,addr=0x4.0x0 \ +-device ich9-usb-uhci2,id=usb2,masterbus=usb0.0,firstport=2,bus=pci.0,multifunction=on,addr=0x4.0x1 \ +-device ich9-usb-uhci3,id=usb3,masterbus=usb0.0,firstport=4,bus=pci.0,multifunction=on,addr=0x4.0x2 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,multifunction=on,addr=0x3.0x0 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.xml new file mode 100644 index 0000000..fe02514 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.xml @@ -0,0 +1,30 @@ +<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='1' model='ich9-uhci1'> + <master bus='0' startport='0'/> + <address type='pci' domain='0' bus='0' slot='4' function='0'/> + </controller> + <controller type='usb' index='2' model='ich9-uhci2'> + <master bus='0' startport='2'/> + <address type='pci' domain='0' bus='0' slot='4' function='1'/> + </controller> + <controller type='usb' index='3' model='ich9-uhci3'> + <master bus='0' startport='4'/> + <address type='pci' domain='0' bus='0' slot='4' function='2'/> + </controller> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index b573380..c938034 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -495,6 +495,11 @@ mymain(void) QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_ICH9_USB_EHCI1); DO_TEST("input-usbmouse-addr", false, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG); + DO_TEST("usb-ich9-companion", false, + QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG, + QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_ICH9_USB_EHCI1, + QEMU_CAPS_ICH9_USB_UHCI1, QEMU_CAPS_ICH9_USB_UHCI2, + QEMU_CAPS_ICH9_USB_UHCI3); DO_TEST("smbios", false, QEMU_CAPS_SMBIOS_TYPE); -- 1.7.6

On Sun, Aug 21, 2011 at 10:01:17PM +0300, Marc-André Lureau wrote:
Companion controllers take an extra 'master' attribute to associate them. --- docs/formatdomain.html.in | 20 +++++++++ docs/schemas/domain.rng | 15 +++++++ src/conf/domain_conf.c | 44 ++++++++++++++++++++ src/conf/domain_conf.h | 18 ++++++++ src/qemu/qemu_command.c | 7 +++ .../qemuxml2argv-usb-ich9-companion.args | 6 +++ .../qemuxml2argv-usb-ich9-companion.xml | 30 +++++++++++++ tests/qemuxml2argvtest.c | 5 ++ 8 files changed, 145 insertions(+), 0 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 0a383f6..5c232fa 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1243,6 +1243,26 @@ sub-element. </p>
+ <p> + USB companion controllers have an optional + sub-element <code><master></code> to specify the exact + relationship of the companion to its master controller. + </p> + +<pre> + ... + <devices> + <controller type='usb' index='0' model='ich9-ehci1'> + <address type='pci' domain='0' bus='0' slot='4' function='7'/> + </controller> + <controller type='usb' index='1' model='ich9-uhci1'> + <master bus='0' startport='0'/> + <address type='pci' domain='0' bus='0' slot='4' function='0'/> + </controller>
The 'index' attribute on controllers is used to link up to the PCI/USB/Drive address on individual devices. This example suggests that each companion controller is a new bus for devices to link upto, but they don't actually work this way. All devices will always link directly to the echi controller, regardless of any companions. So we should not be incrementing the 'index' for the companions. Also, although I suggested it IIRC, we should not in fact have a 'bus' attribute on the <master> element, since it is already implied by the 'index' attribute on the <controller> All we need todo is have <master startport='0'/> to show that its a slave companion, not a new bus. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

--- 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 5c232fa..7b04972 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2082,6 +2082,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 6a2e71a..0aa47db 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1961,6 +1961,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> @@ -2124,6 +2136,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 837e657..13a8d5a 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", @@ -433,6 +434,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") @@ -996,6 +1000,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) @@ -1032,6 +1045,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); @@ -1141,6 +1157,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); @@ -1524,6 +1544,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; } @@ -1588,6 +1611,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); @@ -3984,6 +4013,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, @@ -5561,6 +5631,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")); @@ -6965,6 +7039,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; @@ -7875,6 +7964,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 @@ -8108,6 +8220,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; @@ -10009,6 +10132,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 | \ @@ -10414,6 +10565,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 0f51d47..8f640b0 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -610,6 +610,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, @@ -819,6 +826,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 { @@ -959,6 +972,7 @@ enum virDomainDeviceType { VIR_DOMAIN_DEVICE_WATCHDOG, VIR_DOMAIN_DEVICE_CONTROLLER, VIR_DOMAIN_DEVICE_GRAPHICS, + VIR_DOMAIN_DEVICE_HUB, VIR_DOMAIN_DEVICE_LAST, }; @@ -979,6 +993,7 @@ struct _virDomainDeviceDef { virDomainHostdevDefPtr hostdev; virDomainWatchdogDefPtr watchdog; virDomainGraphicsDefPtr graphics; + virDomainHubDefPtr hub; } data; }; @@ -1306,6 +1321,9 @@ struct _virDomainDef { size_t nleases; virDomainLeaseDefPtr *leases; + int nhubs; + virDomainHubDefPtr *hubs; + /* Only 1 */ virDomainChrDefPtr console; virSecurityLabelDef seclabel; @@ -1452,6 +1470,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); @@ -1731,6 +1750,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 acae122..4af12d6 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -325,6 +325,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 a33ad4e..d1368ab 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -136,6 +136,7 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST, "ich9-usb-uhci3", "vt82c686b-usb-uhci", "usb-redir", + "usb-hub", ); struct qemu_feature_flags { @@ -1226,6 +1227,8 @@ qemuCapsParseDeviceStr(const char *str, virBitmapPtr flags) qemuCapsSet(flags, QEMU_CAPS_VT82C686B_USB_UHCI); 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 7f94392..592e5aa 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -110,6 +110,7 @@ enum qemuCapsFlags { QEMU_CAPS_ICH9_USB_UHCI3 = 71, /* -device ich9-usb-uhci3 */ QEMU_CAPS_VT82C686B_USB_UHCI = 72, /* -device vt82c686b-usb-uhci */ QEMU_CAPS_USB_REDIR = 73, /* -device usb-redir */ + QEMU_CAPS_USB_HUB = 74, /* -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 8deae23..ddab27b 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -683,6 +683,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; @@ -1266,6 +1270,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; @@ -1760,7 +1767,6 @@ qemuBuildUSBControllerDevStr(virDomainControllerDefPtr def, def->info.master.usb.bus, def->info.master.usb.startport); } - return 0; } @@ -2329,6 +2335,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; @@ -4238,6 +4281,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 7002cdb..5be09c3 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, @@ -436,7 +437,8 @@ mymain(void) QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_PIIX3_USB_UHCI, QEMU_CAPS_PIIX4_USB_UHCI, - QEMU_CAPS_VT82C686B_USB_UHCI); + QEMU_CAPS_VT82C686B_USB_UHCI, + QEMU_CAPS_USB_HUB); DO_TEST("qemu-kvm-0.12.1.2-rhel61", 12001, 1, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -483,7 +485,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 c938034..b0c50ee 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -500,6 +500,9 @@ mymain(void) QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_ICH9_USB_EHCI1, QEMU_CAPS_ICH9_USB_UHCI1, QEMU_CAPS_ICH9_USB_UHCI2, QEMU_CAPS_ICH9_USB_UHCI3); + 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

So that devices can be attached to hubs. Example, to attach to first port of a usb-hub on port 1. <hub type='usb'> <address type='usb' bus='0' port='1'/> </hub> <input type='mouse' type='usb'> <address type='usb' bus='0' port='1.1'/> </hub> --- docs/schemas/domain.rng | 11 +++++-- src/conf/domain_conf.c | 24 ++++++++++----- src/conf/domain_conf.h | 2 +- src/qemu/qemu_command.c | 2 +- tests/qemuxml2argvdata/qemuxml2argv-usb-ports.args | 1 + tests/qemuxml2argvdata/qemuxml2argv-usb-ports.xml | 31 ++++++++++++++++++++ tests/qemuxml2argvtest.c | 3 ++ 7 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ports.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-ports.xml diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 0aa47db..e17cc7a 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -2039,7 +2039,7 @@ <ref name="usbAddr"/> </attribute> <attribute name="device"> - <ref name="usbAddr"/> + <ref name="usbPort"/> </attribute> </element> </define> @@ -2048,7 +2048,7 @@ <ref name="usbAddr"/> </attribute> <attribute name="port"> - <ref name="usbAddr"/> + <ref name="usbPort"/> </attribute> </define> <define name="pciaddress"> @@ -2399,7 +2399,7 @@ <ref name="usbAddr"/> </attribute> <attribute name="startport"> - <ref name="usbAddr"/> + <ref name="usbPort"/> </attribute> <empty/> </element> @@ -2550,6 +2550,11 @@ <param name="pattern">(0x)?[0-9a-fA-F]{1,3}</param> </data> </define> + <define name="usbPort"> + <data type="string"> + <param name="pattern">((0x)?[0-9a-fA-F]{1,3}\.){0,3}(0x)?[0-9a-fA-F]{1,3}</param> + </data> + </define> <define name="pciDomain"> <data type="string"> <param name="pattern">(0x)?[0-9a-fA-F]{1,4}</param> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 13a8d5a..6362ef3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1440,12 +1440,9 @@ int virDomainDeviceDriveAddressIsValid(virDomainDeviceDriveAddressPtr addr ATTRI return 1; /* 0 is valid for all fields, so any successfully parsed addr is valid */ } -int virDomainDeviceUSBAddressIsValid(virDomainDeviceUSBAddressPtr addr) +int virDomainDeviceUSBAddressIsValid(virDomainDeviceUSBAddressPtr addr ATTRIBUTE_UNUSED) { - if (addr->port >= 128) /* FIXME: is this correct */ - return 0; - - return 1; + return 1; /* FIXME.. any successfully parsed addr is valid */ } int virDomainDeviceVirtioSerialAddressIsValid( @@ -1469,6 +1466,10 @@ virDomainDeviceInfoIsSet(virDomainDeviceInfoPtr info, unsigned int flags) void virDomainDeviceInfoClear(virDomainDeviceInfoPtr info) { VIR_FREE(info->alias); + if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { + VIR_FREE(info->addr.usb.port); + } + VIR_FREE(info->alias); memset(&info->addr, 0, sizeof(info->addr)); info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE; } @@ -1612,7 +1613,7 @@ virDomainDeviceInfoFormat(virBufferPtr buf, break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB: - virBufferAsprintf(buf, " bus='%d' port='%d'", + virBufferAsprintf(buf, " bus='%d' port='%s'", info->addr.usb.bus, info->addr.usb.port); break; @@ -1828,7 +1829,8 @@ static int virDomainDeviceUSBAddressParseXML(xmlNodePtr node, virDomainDeviceUSBAddressPtr addr) { - char *port, *bus; + char *port, *bus, *tmp; + unsigned int p; int ret = -1; memset(addr, 0, sizeof(*addr)); @@ -1837,12 +1839,18 @@ virDomainDeviceUSBAddressParseXML(xmlNodePtr node, bus = virXMLPropString(node, "bus"); if (port && - virStrToLong_ui(port, NULL, 10, &addr->port) < 0) { + ((virStrToLong_ui(port, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.')) || + (*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.'))) || + (*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.'))) || + (*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0'))))) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Cannot parse <address> 'port' attribute")); goto cleanup; } + addr->port = port; + port = NULL; + if (bus && virStrToLong_ui(bus, NULL, 10, &addr->bus) < 0) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8f640b0..fa883f4 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -110,7 +110,7 @@ typedef struct _virDomainDeviceUSBAddress virDomainDeviceUSBAddress; typedef virDomainDeviceUSBAddress *virDomainDeviceUSBAddressPtr; struct _virDomainDeviceUSBAddress { unsigned int bus; - unsigned int port; + char *port; }; enum virDomainControllerMaster { diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index ddab27b..9ea4b7c 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1330,7 +1330,7 @@ qemuBuildDeviceAddressStr(virBufferPtr buf, virBufferAsprintf(buf, ",addr=0x%x", info->addr.pci.slot); } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { virBufferAsprintf(buf, ",bus=usb%d.0", info->addr.usb.bus); - virBufferAsprintf(buf, ",port=%d", info->addr.usb.port); + virBufferAsprintf(buf, ",port=%s", info->addr.usb.port); } return 0; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-ports.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-ports.args new file mode 100644 index 0000000..556eb4c --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-ports.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 usb-hub,id=hub1,bus=usb.0,port=1.2 -device usb-mouse,id=input0,bus=usb.0,port=1.1 -device usb-mouse,id=input1,bus=usb.0,port=1.2.1 -device usb-mouse,id=input2,bus=usb.0,port=1.2.2 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-ports.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-ports.xml new file mode 100644 index 0000000..e31e5bc --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-ports.xml @@ -0,0 +1,31 @@ +<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> + <input type='mouse' bus='usb'> + <address type='usb' bus='0' port='1.1'/> + </input> + <hub type='usb'> + <address type='usb' bus='0' port='1.2'/> + </hub> + <input type='mouse' bus='usb'> + <address type='usb' bus='0' port='1.2.1'/> + </input> + <input type='mouse' bus='usb'> + <address type='usb' bus='0' port='1.2.2'/> + </input> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index b0c50ee..1d08da3 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -503,6 +503,9 @@ mymain(void) DO_TEST("usb-hub", false, QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_USB_HUB, QEMU_CAPS_NODEFCONFIG); + DO_TEST("usb-ports", 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

--- src/qemu/qemu_command.c | 10 +++++++++- 1 files changed, 9 insertions(+), 1 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 9ea4b7c..d25f34f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1093,6 +1093,7 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs) { int i; bool reservedIDE = false; + bool reservedUSB = false; bool reservedVGA = false; /* Host bridge */ @@ -1123,13 +1124,20 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs) def->controllers[i]->info.addr.pci.slot = 1; def->controllers[i]->info.addr.pci.function = 1; } + } else if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && + def->controllers[i]->idx == 0 && + def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && + def->controllers[i]->info.addr.pci.domain == 0 && + def->controllers[i]->info.addr.pci.bus == 0 && + def->controllers[i]->info.addr.pci.slot == 1) { + reservedUSB = true; } } /* PIIX3 (ISA bridge, IDE controller, something else unknown, USB controller) * hardcoded slot=1, multifunction device */ - if (!reservedIDE && + if (!reservedIDE && !reservedUSB && qemuDomainPCIAddressReserveSlot(addrs, 1) < 0) goto error; -- 1.7.6

At 08/22/2011 03:01 AM, Marc-André Lureau Write:
--- src/qemu/qemu_command.c | 10 +++++++++- 1 files changed, 9 insertions(+), 1 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 9ea4b7c..d25f34f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1093,6 +1093,7 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs) { int i; bool reservedIDE = false; + bool reservedUSB = false; bool reservedVGA = false;
/* Host bridge */ @@ -1123,13 +1124,20 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs) def->controllers[i]->info.addr.pci.slot = 1; def->controllers[i]->info.addr.pci.function = 1; } + } else if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && + def->controllers[i]->idx == 0 && + def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && + def->controllers[i]->info.addr.pci.domain == 0 && + def->controllers[i]->info.addr.pci.bus == 0 && + def->controllers[i]->info.addr.pci.slot == 1) { + reservedUSB = true;
The first use controller's address is 0:0:1:2, you don't check the function value here. I think the code should be like IDE's code: if the type is not VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI: init def->controllers[i]->info else check the pci address, if the address is wrong, report a error Thanks Wen Congyang
} }
/* PIIX3 (ISA bridge, IDE controller, something else unknown, USB controller) * hardcoded slot=1, multifunction device */ - if (!reservedIDE && + if (!reservedIDE && !reservedUSB && qemuDomainPCIAddressReserveSlot(addrs, 1) < 0) goto error;

At 08/24/2011 09:42 AM, Wen Congyang Write:
At 08/22/2011 03:01 AM, Marc-André Lureau Write:
--- src/qemu/qemu_command.c | 10 +++++++++- 1 files changed, 9 insertions(+), 1 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 9ea4b7c..d25f34f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1093,6 +1093,7 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs) { int i; bool reservedIDE = false; + bool reservedUSB = false; bool reservedVGA = false;
/* Host bridge */ @@ -1123,13 +1124,20 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs) def->controllers[i]->info.addr.pci.slot = 1; def->controllers[i]->info.addr.pci.function = 1; } + } else if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && + def->controllers[i]->idx == 0 && + def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && + def->controllers[i]->info.addr.pci.domain == 0 && + def->controllers[i]->info.addr.pci.bus == 0 && + def->controllers[i]->info.addr.pci.slot == 1) { + reservedUSB = true;
The first use controller's address is 0:0:1:2, you don't check the function value here. I think the code should be like IDE's code: if the type is not VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI: init def->controllers[i]->info else check the pci address, if the address is wrong, report a error
I read the patch 3, and find that you do not append -usb in the command line while using usb controller. So all usb controllers should not use slot 1(because we may append -usbdevice in the command line)
Thanks Wen Congyang
} }
/* PIIX3 (ISA bridge, IDE controller, something else unknown, USB controller) * hardcoded slot=1, multifunction device */ - if (!reservedIDE && + if (!reservedIDE && !reservedUSB && qemuDomainPCIAddressReserveSlot(addrs, 1) < 0) goto error;
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

QEMU uses USB bus name "usb.0" when using the legacy -usb argument. If we want to allow USB devices to specify their addresses with legacy -usb, we should either in case of legacy bus name drop the 0 from the address bus, or just drop the 0 from device id. This patch does the later. Another solution would be to permit addressing on non-legacy USB controllers only. --- src/qemu/qemu_command.c | 21 +++++++++++++++---- .../qemuxml2argv-usb-ich9-companion.args | 8 +++--- .../qemuxml2argv-usb-ich9-ehci-addr.args | 2 +- .../qemuxml2argv-usb-piix3-controller.args | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d25f34f..c424ec1 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1288,6 +1288,14 @@ error: return -1; } +static void +qemuUsbId(virBufferPtr buf, int idx) +{ + if (idx == 0) + virBufferAsprintf(buf, "usb"); + else + virBufferAsprintf(buf, "usb%d", idx); +} static int qemuBuildDeviceAddressStr(virBufferPtr buf, @@ -1337,8 +1345,9 @@ qemuBuildDeviceAddressStr(virBufferPtr buf, else virBufferAsprintf(buf, ",addr=0x%x", info->addr.pci.slot); } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { - virBufferAsprintf(buf, ",bus=usb%d.0", info->addr.usb.bus); - virBufferAsprintf(buf, ",port=%s", info->addr.usb.port); + virBufferAsprintf(buf, ",bus="); + qemuUsbId(buf, info->addr.usb.bus); + virBufferAsprintf(buf, ".0,port=%s", info->addr.usb.port); } return 0; @@ -1768,11 +1777,13 @@ qemuBuildUSBControllerDevStr(virDomainControllerDefPtr def, return -1; } - virBufferAsprintf(buf, "%s,id=usb%d", smodel, def->idx); + virBufferAsprintf(buf, "%s,id=", smodel); + qemuUsbId(buf, def->idx); if (def->info.mastertype == VIR_DOMAIN_CONTROLLER_MASTER_USB) { - virBufferAsprintf(buf, ",masterbus=usb%d.0,firstport=%d", - def->info.master.usb.bus, def->info.master.usb.startport); + virBufferAsprintf(buf, ",masterbus="); + qemuUsbId(buf, def->info.master.usb.bus); + virBufferAsprintf(buf, ".0,firstport=%d", def->info.master.usb.startport); } return 0; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.args index d5a2e08..80ca5ce 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-companion.args @@ -1,6 +1,6 @@ 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=usb0,bus=pci.0,multifunction=on,addr=0x4.0x7 \ --device ich9-usb-uhci1,id=usb1,masterbus=usb0.0,firstport=0,bus=pci.0,multifunction=on,addr=0x4.0x0 \ --device ich9-usb-uhci2,id=usb2,masterbus=usb0.0,firstport=2,bus=pci.0,multifunction=on,addr=0x4.0x1 \ --device ich9-usb-uhci3,id=usb3,masterbus=usb0.0,firstport=4,bus=pci.0,multifunction=on,addr=0x4.0x2 \ +-device ich9-usb-ehci1,id=usb,bus=pci.0,multifunction=on,addr=0x4.0x7 \ +-device ich9-usb-uhci1,id=usb1,masterbus=usb.0,firstport=0,bus=pci.0,multifunction=on,addr=0x4.0x0 \ +-device ich9-usb-uhci2,id=usb2,masterbus=usb.0,firstport=2,bus=pci.0,multifunction=on,addr=0x4.0x1 \ +-device ich9-usb-uhci3,id=usb3,masterbus=usb.0,firstport=4,bus=pci.0,multifunction=on,addr=0x4.0x2 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,multifunction=on,addr=0x3.0x0 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.args index 502244d..0059ab5 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-ich9-ehci-addr.args @@ -1 +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 -device ich9-usb-ehci1,id=usb0,bus=pci.0,multifunction=on,addr=0x4.0x7 -device virtio-balloon-pci,id=balloon0,bus=pci.0,multifunction=on,addr=0x3.0x0 +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 virtio-balloon-pci,id=balloon0,bus=pci.0,multifunction=on,addr=0x3.0x0 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.args index 799b75f..c4573ea 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-piix3-controller.args @@ -1 +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 -device piix3-usb-uhci,id=usb0,bus=pci.0,addr=0x3 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 +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 piix3-usb-uhci,id=usb,bus=pci.0,addr=0x3 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 -- 1.7.6

--- tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.args | 14 ++++ tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.xml | 74 ++++++++++++++++++++ tests/qemuxml2argvtest.c | 6 ++ 3 files changed, 94 insertions(+), 0 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.xml diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.args b/tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.args new file mode 100644 index 0000000..8e48783 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.args @@ -0,0 +1,14 @@ +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 piix3-usb-uhci,id=usb,bus=pci.0,multifunction=on,addr=0x1.0x3 \ +-device ich9-usb-ehci1,id=usb1,bus=pci.0,multifunction=on,addr=0x4.0x7 \ +-device ich9-usb-uhci1,id=usb2,masterbus=usb1.0,firstport=0,bus=pci.0,multifunction=on,addr=0x4.0x0 \ +-device ich9-usb-uhci2,id=usb3,masterbus=usb1.0,firstport=2,bus=pci.0,multifunction=on,addr=0x4.0x1 \ +-device ich9-usb-uhci3,id=usb4,masterbus=usb1.0,firstport=4,bus=pci.0,multifunction=on,addr=0x4.0x2 \ +-device ich9-usb-ehci1,id=usb5,bus=pci.0,multifunction=on,addr=0x5.0x7 \ +-device ich9-usb-uhci1,id=usb6,masterbus=usb5.0,firstport=0,bus=pci.0,multifunction=on,addr=0x5.0x0 \ +-device ich9-usb-uhci2,id=usb7,masterbus=usb5.0,firstport=2,bus=pci.0,multifunction=on,addr=0x5.0x1 \ +-device ich9-usb-uhci3,id=usb8,masterbus=usb5.0,firstport=4,bus=pci.0,multifunction=on,addr=0x5.0x2 \ +-device usb-hub,id=hub0,bus=usb1.0,port=1 -device usb-tablet,id=input0,bus=usb.0,port=2 \ +-device usb-host,hostbus=14,hostaddr=6,id=hostdev0,bus=usb5.0,port=1 \ +-device usb-host,hostbus=14,hostaddr=7,id=hostdev1,bus=usb5.0,port=2 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,multifunction=on,addr=0x3.0x0 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.xml new file mode 100644 index 0000000..e78260d --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb1-usb2.xml @@ -0,0 +1,74 @@ +<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> + <memballoon model='virtio'/> + + <controller type='usb' index='0' model='piix3-uhci'> + <address type='pci' domain='0' bus='0' slot='1' function='3'/> + </controller> + + <controller type='usb' index='1' model='ich9-ehci1'> + <address type='pci' domain='0' bus='0' slot='4' function='7'/> + </controller> + <controller type='usb' index='2' model='ich9-uhci1'> + <master bus='1' startport='0'/> + <address type='pci' domain='0' bus='0' slot='4' function='0'/> + </controller> + <controller type='usb' index='3' model='ich9-uhci2'> + <master bus='1' startport='2'/> + <address type='pci' domain='0' bus='0' slot='4' function='1'/> + </controller> + <controller type='usb' index='4' model='ich9-uhci3'> + <master bus='1' startport='4'/> + <address type='pci' domain='0' bus='0' slot='4' function='2'/> + </controller> + + <controller type='usb' index='5' model='ich9-ehci1'> + <address type='pci' domain='0' bus='0' slot='5' function='7'/> + </controller> + <controller type='usb' index='6' model='ich9-uhci1'> + <master bus='5' startport='0'/> + <address type='pci' domain='0' bus='0' slot='5' function='0'/> + </controller> + <controller type='usb' index='7' model='ich9-uhci2'> + <master bus='5' startport='2'/> + <address type='pci' domain='0' bus='0' slot='5' function='1'/> + </controller> + <controller type='usb' index='8' model='ich9-uhci3'> + <master bus='5' startport='4'/> + <address type='pci' domain='0' bus='0' slot='5' function='2'/> + </controller> + + <input type='tablet' bus='usb'> + <address type='usb' bus='0' port='2'/> + </input> + + <hub type='usb'> + <address type='usb' bus='1' port='1'/> + </hub> + + <hostdev mode='subsystem' type='usb'> + <source> + <address bus='14' device='6'/> + </source> + <address type='usb' bus='5' port='1'/> + </hostdev> + + <hostdev mode='subsystem' type='usb'> + <source> + <address bus='14' device='7'/> + </source> + <address type='usb' bus='5' port='2'/> + </hostdev> + + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 1d08da3..923937b 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -506,6 +506,12 @@ mymain(void) DO_TEST("usb-ports", false, QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_USB_HUB, QEMU_CAPS_NODEFCONFIG); + DO_TEST("usb1-usb2", false, + 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, + QEMU_CAPS_ICH9_USB_UHCI1, QEMU_CAPS_ICH9_USB_UHCI2, + QEMU_CAPS_ICH9_USB_UHCI3); DO_TEST("smbios", false, QEMU_CAPS_SMBIOS_TYPE); -- 1.7.6

--- docs/formatdomain.html.in | 15 ++++- docs/schemas/domain.rng | 66 ++++++++++++-------- src/conf/domain_conf.c | 49 +++++++++++++-- src/conf/domain_conf.h | 2 + src/qemu/qemu_command.c | 36 +++++++++-- tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args | 8 +++ tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml | 33 ++++++++++ tests/qemuxml2argvtest.c | 5 ++ 8 files changed, 174 insertions(+), 40 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 7b04972..c702075 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1312,6 +1312,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> @@ -1346,14 +1352,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 e17cc7a..ce2ae52 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1722,21 +1722,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"> @@ -1997,21 +2001,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 6362ef3..046a2b9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5159,18 +5159,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) { @@ -5335,7 +5343,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(); @@ -5373,14 +5381,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) { @@ -10087,6 +10111,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, @@ -10102,8 +10127,22 @@ 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"); + if (redirection != NULL) { + virBufferAsprintf(buf, " redirection='%s'", redirection); + } + virBufferAddLit(buf, ">\n"); virBufferAddLit(buf, " <source>\n"); if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index fa883f4..a7fcf2d 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -908,6 +908,7 @@ typedef virDomainHostdevDef *virDomainHostdevDefPtr; struct _virDomainHostdevDef { int mode; /* enum virDomainHostdevMode */ unsigned int managed : 1; + unsigned int redirection : 1; union { struct { int type; /* enum virDomainHostdevBusType */ @@ -920,6 +921,7 @@ struct _virDomainHostdevDef { unsigned product; } usb; virDomainDevicePCIAddress pci; /* host address */ + virDomainChrSourceDef chr; } u; } subsys; struct { diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index c424ec1..586453c 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -584,12 +584,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; @@ -599,7 +601,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; } @@ -2332,10 +2334,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; @@ -4834,6 +4842,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/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args new file mode 100644 index 0000000..661edce --- /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,id=usb1,masterbus=usb.0,firstport=0,bus=pci.0,multifunction=on,addr=0x4.0x0 \ +-device ich9-usb-uhci2,id=usb2,masterbus=usb.0,firstport=2,bus=pci.0,multifunction=on,addr=0x4.0x1 \ +-device ich9-usb-uhci3,id=usb3,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..6393ac4 --- /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='1' model='ich9-uhci1'> + <master bus='0' startport='0'/> + <address type='pci' domain='0' bus='0' slot='4' function='0'/> + </controller> + <controller type='usb' index='2' model='ich9-uhci2'> + <master bus='0' startport='2'/> + <address type='pci' domain='0' bus='0' slot='4' function='1'/> + </controller> + <controller type='usb' index='3' model='ich9-uhci3'> + <master bus='0' 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 923937b..f848d0e 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -512,6 +512,11 @@ mymain(void) QEMU_CAPS_USB_HUB, QEMU_CAPS_ICH9_USB_EHCI1, QEMU_CAPS_ICH9_USB_UHCI1, QEMU_CAPS_ICH9_USB_UHCI2, QEMU_CAPS_ICH9_USB_UHCI3); + 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_ICH9_USB_UHCI1, QEMU_CAPS_ICH9_USB_UHCI2, + QEMU_CAPS_ICH9_USB_UHCI3, QEMU_CAPS_USB_REDIR); DO_TEST("smbios", false, QEMU_CAPS_SMBIOS_TYPE); -- 1.7.6

Hi On Sun, Aug 21, 2011 at 10:01 PM, Marc-André Lureau <marcandre.lureau@gmail.com> wrote:
There are some parts that I don't really know what they should be like: the "autoassign" or the default values at the domain XML level.
I just figured it could be simply modifying virDomainDefAddImplicitControllers. What else? regards -- Marc-André Lureau
participants (4)
-
Daniel P. Berrange
-
Gerd Hoffmann
-
Marc-André Lureau
-
Wen Congyang