This patch enables the attachment of USB devices (emulated or passthrough) on the ARM virt model, without the need of a PCI USB controller, by adding a generic EHCI USB controller to the system bus.
Signed-off-by: Nikolay Nikolaev <n.nikol...@virtualopensystems.com> Signed-off-by: Jérémy Fanguède <j.fangu...@virtualopensystems.com> --- A new EHCI USB controller named "generic_ehci" is introduced (hw/usb/hcd-ehci-sysbus.c). Then, in the virt model, this controller is added to the system bus. For this purpose a new function, create_usb, modeled on other create_* functions, is in charge to create this device and dynamically add the node needed to the device tree. The Linux guest OS should include the EHCI platform driver (CONFIG_USB_EHCI_HCD_PLATFORM). This patch works fine with TCG on x86 and ARM machines, for both emulated and host devices, with this kind of cmdline: -usb \ -device usb-tablet \ -device usb-host,hostbus=1,hostaddr=5 Unfortunately, with KVM, only emulated devices works, for host devices (passthrough), a random kernel panic occurred. Tests for KVM are performed on a Samsung Chromebook, with upstream kernel and a USB stick. --- hw/arm/virt.c | 29 +++++++++++++++++++++++++++++ hw/usb/hcd-ehci-sysbus.c | 16 ++++++++++++++++ hw/usb/hcd-ehci.h | 1 + 3 files changed, 46 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 7d082e2..c362685 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -39,6 +39,7 @@ #include "sysemu/kvm.h" #include "hw/boards.h" #include "hw/loader.h" +#include "hw/usb/hcd-ehci.h" #include "exec/address-spaces.h" #include "qemu/bitops.h" #include "qemu/error-report.h" @@ -71,6 +72,7 @@ enum { VIRT_RTC, VIRT_FW_CFG, VIRT_PCIE, + VIRT_USB, }; typedef struct MemMapEntry { @@ -129,6 +131,7 @@ static const MemMapEntry a15memmap[] = { [VIRT_UART] = { 0x09000000, 0x00001000 }, [VIRT_RTC] = { 0x09010000, 0x00001000 }, [VIRT_FW_CFG] = { 0x09020000, 0x0000000a }, + [VIRT_USB] = { 0x09030000, 0x00001000 }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ /* @@ -146,6 +149,7 @@ static const int a15irqmap[] = { [VIRT_UART] = 1, [VIRT_RTC] = 2, [VIRT_PCIE] = 3, /* ... to 6 */ + [VIRT_USB] = 7, [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ }; @@ -448,6 +452,27 @@ static void create_rtc(const VirtBoardInfo *vbi, qemu_irq *pic) g_free(nodename); } +static void create_usb(const VirtBoardInfo *vbi, qemu_irq *pic) +{ + char *nodename; + hwaddr base = vbi->memmap[VIRT_USB].base; + hwaddr size = vbi->memmap[VIRT_USB].size; + int irq = vbi->irqmap[VIRT_USB]; + const char compat[] = "generic-ehci"; + + sysbus_create_simple(TYPE_GENERIC_EHCI, base, pic[irq]); + + nodename = g_strdup_printf("/ehci@%" PRIx64, base); + qemu_fdt_add_subnode(vbi->fdt, nodename); + qemu_fdt_setprop(vbi->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + 2, base, 2, size); + qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + g_free(nodename); +} + static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) { int i; @@ -821,6 +846,10 @@ static void machvirt_init(MachineState *machine) create_pcie(vbi, pic, gic_phandle); + if (usb_enabled()) { + create_usb(vbi, pic); + } + /* Create mmio transports, so the user can create virtio backends * (which will be automatically plugged in to the transports). If * no backend is created the transport will just sit harmlessly idle. diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index cd1cc14..4cf8cc1 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -217,6 +217,21 @@ static const TypeInfo ehci_fusbh200_type_info = { .class_init = fusbh200_ehci_class_init, }; +static void generic_ehci_class_init(ObjectClass *oc, void *data) +{ + SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + sec->capsbase = 0x0; + sec->opregbase = 0x10; + set_bit(DEVICE_CATEGORY_USB, dc->categories); +} + +static const TypeInfo ehci_generic_type_info = { + .name = TYPE_GENERIC_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = generic_ehci_class_init, +}; static void ehci_sysbus_register_types(void) { type_register_static(&ehci_type_info); @@ -224,6 +239,7 @@ static void ehci_sysbus_register_types(void) type_register_static(&ehci_exynos4210_type_info); type_register_static(&ehci_tegra2_type_info); type_register_static(&ehci_fusbh200_type_info); + type_register_static(&ehci_generic_type_info); } type_init(ehci_sysbus_register_types) diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 87b240f..9f1d759 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -343,6 +343,7 @@ typedef struct EHCIPCIState { #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb" #define TYPE_TEGRA2_EHCI "tegra2-ehci-usb" #define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb" +#define TYPE_GENERIC_EHCI "generic-ehci-usb" #define SYS_BUS_EHCI(obj) \ OBJECT_CHECK(EHCISysBusState, (obj), TYPE_SYS_BUS_EHCI) -- 1.9.1