diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2012-11-01 14:34:13 -0500 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2012-11-01 14:34:13 -0500 |
commit | de0a36cd01dab524ee6507c1bd644f0775d341fe (patch) | |
tree | 63d866091c8a69c8bb12dbb34132cd07f9ade7a3 /hw | |
parent | 4ba79505f43bd0ace35c3fe42197eb02e7e0478e (diff) | |
parent | 95a59dc03988d190bbc72d980a149be3a79592fb (diff) | |
download | qemu-de0a36cd01dab524ee6507c1bd644f0775d341fe.zip |
Merge remote-tracking branch 'kraxel/usb.69' into staging
* kraxel/usb.69: (31 commits)
usb-redir: Allow redirecting super speed devices to high speed controllers
usb-redir: Allow to attach USB 2.0 devices to 1.1 host controller
usb-redir: Use reject rather the disconnect on bad ep info
usb-redir: Add an usbredir_setup_usb_eps() helper function
usb-redir: Add support for input pipelining
usb-redir: Add support for 32 bits bulk packet length
combined-packet: Add a workaround for Linux usbfs + live migration
usb: Add packet combining functions
uhci: Don't crash on device disconnect
uhci: Add a uhci_handle_td_error() helper function
usb/ehci-pci: add helper to create ich9 usb controllers
usb/ehci-pci: add ich9 00:1a.* variant
usb/ehci-pci: dynamic type generation
uhci: add ich9 00:1a.* variants
uhci: stick irq routing info into UHCIInfo too.
uhci: dynamic type generation
xilinx_zynq: add USB controllers
usb/ehci: add sysbus variant
usb/ehci: split into multiple source files
usb/ehci: Guard definition of EHCI_DEBUG
...
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/usb.h | 15 | ||||
-rw-r--r-- | hw/usb/Makefile.objs | 4 | ||||
-rw-r--r-- | hw/usb/combined-packet.c | 182 | ||||
-rw-r--r-- | hw/usb/core.c | 1 | ||||
-rw-r--r-- | hw/usb/hcd-ehci-pci.c | 200 | ||||
-rw-r--r-- | hw/usb/hcd-ehci-sysbus.c | 77 | ||||
-rw-r--r-- | hw/usb/hcd-ehci.c | 465 | ||||
-rw-r--r-- | hw/usb/hcd-ehci.h | 320 | ||||
-rw-r--r-- | hw/usb/hcd-uhci.c | 344 | ||||
-rw-r--r-- | hw/usb/hcd-xhci.c | 134 | ||||
-rw-r--r-- | hw/usb/redirect.c | 167 | ||||
-rw-r--r-- | hw/xilinx_zynq.c | 3 |
12 files changed, 1232 insertions, 680 deletions
@@ -160,6 +160,7 @@ typedef struct USBBusOps USBBusOps; typedef struct USBPort USBPort; typedef struct USBDevice USBDevice; typedef struct USBPacket USBPacket; +typedef struct USBCombinedPacket USBCombinedPacket; typedef struct USBEndpoint USBEndpoint; typedef struct USBDesc USBDesc; @@ -356,7 +357,15 @@ struct USBPacket { int result; /* transfer length or USB_RET_* status code */ /* Internal use by the USB layer. */ USBPacketState state; + USBCombinedPacket *combined; QTAILQ_ENTRY(USBPacket) queue; + QTAILQ_ENTRY(USBPacket) combined_entry; +}; + +struct USBCombinedPacket { + USBPacket *first; + QTAILQ_HEAD(packets_head, USBPacket) packets; + QEMUIOVector iov; }; void usb_packet_init(USBPacket *p); @@ -399,6 +408,10 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled); USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, uint64_t id); +void usb_ep_combine_input_packets(USBEndpoint *ep); +void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p); +void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p); + void usb_attach(USBPort *port); void usb_detach(USBPort *port); void usb_port_reset(USBPort *port); @@ -524,5 +537,7 @@ const char *usb_device_get_product_desc(USBDevice *dev); const USBDesc *usb_device_get_usb_desc(USBDevice *dev); +int ehci_create_ich9_with_companions(PCIBus *bus, int slot); + #endif diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 6425c1ff73..5a4eeb6d13 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -1,13 +1,13 @@ common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o -common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o +common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o hcd-ehci-sysbus.o common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o common-obj-y += libhw.o common-obj-$(CONFIG_SMARTCARD) += dev-smartcard-reader.o common-obj-$(CONFIG_USB_REDIR) += redirect.o -common-obj-y += core.o bus.o desc.o dev-hub.o +common-obj-y += core.o combined-packet.o bus.o desc.o dev-hub.o common-obj-y += host-$(HOST_USB).o dev-bluetooth.o common-obj-y += dev-hid.o dev-storage.o dev-wacom.o common-obj-y += dev-serial.o dev-network.o dev-audio.o diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c new file mode 100644 index 0000000000..3904e71634 --- /dev/null +++ b/hw/usb/combined-packet.c @@ -0,0 +1,182 @@ +/* + * QEMU USB packet combining code (for input pipelining) + * + * Copyright(c) 2012 Red Hat, Inc. + * + * Red Hat Authors: + * Hans de Goede <hdegoede@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or(at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ +#include "qemu-common.h" +#include "hw/usb.h" +#include "iov.h" +#include "trace.h" + +static void usb_combined_packet_add(USBCombinedPacket *combined, USBPacket *p) +{ + qemu_iovec_concat(&combined->iov, &p->iov, 0, p->iov.size); + QTAILQ_INSERT_TAIL(&combined->packets, p, combined_entry); + p->combined = combined; +} + +static void usb_combined_packet_remove(USBCombinedPacket *combined, + USBPacket *p) +{ + assert(p->combined == combined); + p->combined = NULL; + QTAILQ_REMOVE(&combined->packets, p, combined_entry); +} + +/* Also handles completion of non combined packets for pipelined input eps */ +void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p) +{ + USBCombinedPacket *combined = p->combined; + USBEndpoint *ep = p->ep; + USBPacket *next; + enum { completing, complete, leftover }; + int result, state = completing; + bool short_not_ok; + + if (combined == NULL) { + usb_packet_complete_one(dev, p); + goto leave; + } + + assert(combined->first == p && p == QTAILQ_FIRST(&combined->packets)); + + result = combined->first->result; + short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok; + + QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) { + if (state == completing) { + /* Distribute data over uncombined packets */ + if (result >= p->iov.size) { + p->result = p->iov.size; + } else { + /* Send short or error packet to complete the transfer */ + p->result = result; + state = complete; + } + p->short_not_ok = short_not_ok; + usb_combined_packet_remove(combined, p); + usb_packet_complete_one(dev, p); + result -= p->result; + } else { + /* Remove any leftover packets from the queue */ + state = leftover; + p->result = USB_RET_REMOVE_FROM_QUEUE; + dev->port->ops->complete(dev->port, p); + } + } + /* + * If we had leftover packets the hcd driver will have cancelled them + * and usb_combined_packet_cancel has already freed combined! + */ + if (state != leftover) { + g_free(combined); + } +leave: + /* Check if there are packets in the queue waiting for our completion */ + usb_ep_combine_input_packets(ep); +} + +/* May only be called for combined packets! */ +void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p) +{ + USBCombinedPacket *combined = p->combined; + assert(combined != NULL); + + usb_combined_packet_remove(combined, p); + if (p == combined->first) { + usb_device_cancel_packet(dev, p); + } + if (QTAILQ_EMPTY(&combined->packets)) { + g_free(combined); + } +} + +/* + * Large input transfers can get split into multiple input packets, this + * function recombines them, removing the short_not_ok checks which all but + * the last packet of such splits transfers have, thereby allowing input + * transfer pipelining (which we cannot do on short_not_ok transfers) + */ +void usb_ep_combine_input_packets(USBEndpoint *ep) +{ + USBPacket *p, *u, *next, *prev = NULL, *first = NULL; + USBPort *port = ep->dev->port; + int ret, totalsize; + + assert(ep->pipeline); + assert(ep->pid == USB_TOKEN_IN); + + QTAILQ_FOREACH_SAFE(p, &ep->queue, queue, next) { + /* Empty the queue on a halt */ + if (ep->halted) { + p->result = USB_RET_REMOVE_FROM_QUEUE; + port->ops->complete(port, p); + continue; + } + + /* Skip packets already submitted to the device */ + if (p->state == USB_PACKET_ASYNC) { + prev = p; + continue; + } + usb_packet_check_state(p, USB_PACKET_QUEUED); + + /* + * If the previous (combined) packet has the short_not_ok flag set + * stop, as we must not submit packets to the device after a transfer + * ending with short_not_ok packet. + */ + if (prev && prev->short_not_ok) { + break; + } + + if (first) { + if (first->combined == NULL) { + USBCombinedPacket *combined = g_new0(USBCombinedPacket, 1); + + combined->first = first; + QTAILQ_INIT(&combined->packets); + qemu_iovec_init(&combined->iov, 2); + usb_combined_packet_add(combined, first); + } + usb_combined_packet_add(first->combined, p); + } else { + first = p; + } + + /* Is this packet the last one of a (combined) transfer? */ + totalsize = (p->combined) ? p->combined->iov.size : p->iov.size; + if ((p->iov.size % ep->max_packet_size) != 0 || !p->short_not_ok || + next == NULL || + /* Work around for Linux usbfs bulk splitting + migration */ + (totalsize == 16348 && p->int_req)) { + ret = usb_device_handle_data(ep->dev, first); + assert(ret == USB_RET_ASYNC); + if (first->combined) { + QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) { + usb_packet_set_state(u, USB_PACKET_ASYNC); + } + } else { + usb_packet_set_state(first, USB_PACKET_ASYNC); + } + first = NULL; + prev = p; + } + } +} diff --git a/hw/usb/core.c b/hw/usb/core.c index 632a8efe47..ab37f6f7b8 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -545,6 +545,7 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id, p->parameter = 0; p->short_not_ok = short_not_ok; p->int_req = int_req; + p->combined = NULL; qemu_iovec_reset(&p->iov); usb_packet_set_state(p, USB_PACKET_SETUP); } diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c new file mode 100644 index 0000000000..d5c7d462d0 --- /dev/null +++ b/hw/usb/hcd-ehci-pci.c @@ -0,0 +1,200 @@ +/* + * QEMU USB EHCI Emulation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or(at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/usb/hcd-ehci.h" +#include "hw/pci.h" + +typedef struct EHCIPCIState { + PCIDevice pcidev; + EHCIState ehci; +} EHCIPCIState; + +typedef struct EHCIPCIInfo { + const char *name; + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision; +} EHCIPCIInfo; + +static int usb_ehci_pci_initfn(PCIDevice *dev) +{ + EHCIPCIState *i = DO_UPCAST(EHCIPCIState, pcidev, dev); + EHCIState *s = &i->ehci; + uint8_t *pci_conf = dev->config; + + pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20); + + /* capabilities pointer */ + pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00); + /* pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50); */ + + pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); /* interrupt pin D */ + pci_set_byte(&pci_conf[PCI_MIN_GNT], 0); + pci_set_byte(&pci_conf[PCI_MAX_LAT], 0); + + /* pci_conf[0x50] = 0x01; *//* power management caps */ + + pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); /* release # (2.1.4) */ + pci_set_byte(&pci_conf[0x61], 0x20); /* frame length adjustment (2.1.5) */ + pci_set_word(&pci_conf[0x62], 0x00); /* port wake up capability (2.1.6) */ + + pci_conf[0x64] = 0x00; + pci_conf[0x65] = 0x00; + pci_conf[0x66] = 0x00; + pci_conf[0x67] = 0x00; + pci_conf[0x68] = 0x01; + pci_conf[0x69] = 0x00; + pci_conf[0x6a] = 0x00; + pci_conf[0x6b] = 0x00; /* USBLEGSUP */ + pci_conf[0x6c] = 0x00; + pci_conf[0x6d] = 0x00; + pci_conf[0x6e] = 0x00; + pci_conf[0x6f] = 0xc0; /* USBLEFCTLSTS */ + + s->caps[0x09] = 0x68; /* EECP */ + + s->irq = dev->irq[3]; + s->dma = pci_dma_context(dev); + + s->capsbase = 0x00; + s->opregbase = 0x20; + + usb_ehci_initfn(s, DEVICE(dev)); + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); + + return 0; +} + +static Property ehci_pci_properties[] = { + DEFINE_PROP_UINT32("maxframes", EHCIPCIState, ehci.maxframes, 128), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_ehci_pci = { + .name = "ehci", + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState), + VMSTATE_STRUCT(ehci, EHCIPCIState, 2, vmstate_ehci, EHCIState), + } +}; + +static void ehci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + EHCIPCIInfo *i = data; + + k->init = usb_ehci_pci_initfn; + k->vendor_id = i->vendor_id; + k->device_id = i->device_id; + k->revision = i->revision; + k->class_id = PCI_CLASS_SERIAL_USB; + dc->vmsd = &vmstate_ehci; + dc->props = ehci_pci_properties; +} + +static struct EHCIPCIInfo ehci_pci_info[] = { + { + .name = "usb-ehci", + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */ + .revision = 0x10, + },{ + .name = "ich9-usb-ehci1", /* 00:1d.7 */ + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1, + .revision = 0x03, + },{ + .name = "ich9-usb-ehci2", /* 00:1a.7 */ + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI2, + .revision = 0x03, + } +}; + +static void ehci_pci_register_types(void) +{ + TypeInfo ehci_type_info = { + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(EHCIPCIState), + .class_init = ehci_class_init, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(ehci_pci_info); i++) { + ehci_type_info.name = ehci_pci_info[i].name; + ehci_type_info.class_data = ehci_pci_info + i; + type_register(&ehci_type_info); + } +} + +type_init(ehci_pci_register_types) + +struct ehci_companions { + const char *name; + int func; + int port; +}; + +static const struct ehci_companions ich9_1d[] = { + { .name = "ich9-usb-uhci1", .func = 0, .port = 0 }, + { .name = "ich9-usb-uhci2", .func = 1, .port = 2 }, + { .name = "ich9-usb-uhci3", .func = 2, .port = 4 }, +}; + +static const struct ehci_companions ich9_1a[] = { + { .name = "ich9-usb-uhci4", .func = 0, .port = 0 }, + { .name = "ich9-usb-uhci5", .func = 1, .port = 2 }, + { .name = "ich9-usb-uhci6", .func = 2, .port = 4 }, +}; + +int ehci_create_ich9_with_companions(PCIBus *bus, int slot) +{ + const struct ehci_companions *comp; + PCIDevice *ehci, *uhci; + BusState *usbbus; + const char *name; + int i; + + switch (slot) { + case 0x1d: + name = "ich9-usb-ehci1"; + comp = ich9_1d; + break; + case 0x1a: + name = "ich9-usb-ehci2"; + comp = ich9_1a; + break; + default: + return -1; + } + + ehci = pci_create_multifunction(bus, PCI_DEVFN(slot, 7), true, name); + qdev_init_nofail(&ehci->qdev); + usbbus = QLIST_FIRST(&ehci->qdev.child_bus); + + for (i = 0; i < 3; i++) { + uhci = pci_create_multifunction(bus, PCI_DEVFN(slot, comp[i].func), + true, comp[i].name); + qdev_prop_set_string(&uhci->qdev, "masterbus", usbbus->name); + qdev_prop_set_uint32(&uhci->qdev, "firstport", comp[i].port); + qdev_init_nofail(&uhci->qdev); + } + return 0; +} diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c new file mode 100644 index 0000000000..1584079796 --- /dev/null +++ b/hw/usb/hcd-ehci-sysbus.c @@ -0,0 +1,77 @@ +/* + * QEMU USB EHCI Emulation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or(at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/usb/hcd-ehci.h" +#include "hw/sysbus.h" + +typedef struct EHCISysBusState { + SysBusDevice busdev; + EHCIState ehci; +} EHCISysBusState; + +static const VMStateDescription vmstate_ehci_sysbus = { + .name = "ehci-sysbus", + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(ehci, EHCISysBusState, 2, vmstate_ehci, EHCIState), + VMSTATE_END_OF_LIST() + } +}; + +static Property ehci_sysbus_properties[] = { + DEFINE_PROP_UINT32("maxframes", EHCISysBusState, ehci.maxframes, 128), + DEFINE_PROP_END_OF_LIST(), +}; + +static int usb_ehci_sysbus_initfn(SysBusDevice *dev) +{ + EHCISysBusState *i = FROM_SYSBUS(EHCISysBusState, dev); + EHCIState *s = &i->ehci; + + s->capsbase = 0x100; + s->opregbase = 0x140; + + usb_ehci_initfn(s, DEVICE(dev)); + sysbus_init_irq(dev, &s->irq); + sysbus_init_mmio(dev, &s->mem); + return 0; +} + +static void ehci_sysbus_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = usb_ehci_sysbus_initfn; + dc->vmsd = &vmstate_ehci_sysbus; + dc->props = ehci_sysbus_properties; +} + +TypeInfo ehci_xlnx_type_info = { + .name = "xlnx,ps7-usb", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(EHCISysBusState), + .class_init = ehci_sysbus_class_init, +}; + +static void ehci_sysbus_register_types(void) +{ + type_register_static(&ehci_xlnx_type_info); +} + +type_init(ehci_sysbus_register_types) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index f14f9d7018..d9dc576e7c 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -27,41 +27,21 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ -#include "hw/hw.h" -#include "qemu-timer.h" -#include "hw/usb.h" -#include "hw/pci.h" -#include "monitor.h" -#include "trace.h" -#include "dma.h" -#include "sysemu.h" - -#define EHCI_DEBUG 0 - -#if EHCI_DEBUG -#define DPRINTF printf -#else -#define DPRINTF(...) -#endif +#include "hw/usb/hcd-ehci.h" /* internal processing - reset HC to try and recover */ #define USB_RET_PROCERR (-99) -#define MMIO_SIZE 0x1000 - /* Capability Registers Base Address - section 2.2 */ -#define CAPREGBASE 0x0000 -#define CAPLENGTH CAPREGBASE + 0x0000 // 1-byte, 0x0001 reserved -#define HCIVERSION CAPREGBASE + 0x0002 // 2-bytes, i/f version # -#define HCSPARAMS CAPREGBASE + 0x0004 // 4-bytes, structural params -#define HCCPARAMS CAPREGBASE + 0x0008 // 4-bytes, capability params +#define CAPLENGTH 0x0000 /* 1-byte, 0x0001 reserved */ +#define HCIVERSION 0x0002 /* 2-bytes, i/f version # */ +#define HCSPARAMS 0x0004 /* 4-bytes, structural params */ +#define HCCPARAMS 0x0008 /* 4-bytes, capability params */ #define EECP HCCPARAMS + 1 -#define HCSPPORTROUTE1 CAPREGBASE + 0x000c -#define HCSPPORTROUTE2 CAPREGBASE + 0x0010 - -#define OPREGBASE 0x0020 // Operational Registers Base Address +#define HCSPPORTROUTE1 0x000c +#define HCSPPORTROUTE2 0x0010 -#define USBCMD OPREGBASE + 0x0000 +#define USBCMD 0x0000 #define USBCMD_RUNSTOP (1 << 0) // run / Stop #define USBCMD_HCRESET (1 << 1) // HC Reset #define USBCMD_FLS (3 << 2) // Frame List Size @@ -75,7 +55,7 @@ #define USBCMD_ITC (0x7f << 16) // Int Threshold Control #define USBCMD_ITC_SH 16 // Int Threshold Control Shift -#define USBSTS OPREGBASE + 0x0004 +#define USBSTS 0x0004 #define USBSTS_RO_MASK 0x0000003f #define USBSTS_INT (1 << 0) // USB Interrupt #define USBSTS_ERRINT (1 << 1) // Error Interrupt @@ -92,20 +72,17 @@ * Interrupt enable bits correspond to the interrupt active bits in USBSTS * so no need to redefine here. */ -#define USBINTR OPREGBASE + 0x0008 +#define USBINTR 0x0008 #define USBINTR_MASK 0x0000003f -#define FRINDEX OPREGBASE + 0x000c -#define CTRLDSSEGMENT OPREGBASE + 0x0010 -#define PERIODICLISTBASE OPREGBASE + 0x0014 -#define ASYNCLISTADDR OPREGBASE + 0x0018 +#define FRINDEX 0x000c +#define CTRLDSSEGMENT 0x0010 +#define PERIODICLISTBASE 0x0014 +#define ASYNCLISTADDR 0x0018 #define ASYNCLISTADDR_MASK 0xffffffe0 -#define CONFIGFLAG OPREGBASE + 0x0040 +#define CONFIGFLAG 0x0040 -#define PORTSC (OPREGBASE + 0x0044) -#define PORTSC_BEGIN PORTSC -#define PORTSC_END (PORTSC + 4 * NB_PORTS) /* * Bits that are reserved or are read-only are masked out of values * written to us by software @@ -137,7 +114,6 @@ #define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ) #define NB_MAXINTRATE 8 // Max rate at which controller issues ints -#define NB_PORTS 6 // Number of downstream ports #define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction #define MAX_QH 100 // Max allowable queue heads in a chain #define MIN_FR_PER_TICK 3 // Min frames to process when catching up @@ -174,278 +150,6 @@ typedef enum { #define NLPTR_TYPE_STITD 2 // split xaction, isoc xfer descriptor #define NLPTR_TYPE_FSTN 3 // frame span traversal node - -/* EHCI spec version 1.0 Section 3.3 - */ -typedef struct EHCIitd { - uint32_t next; - - uint32_t transact[8]; -#define ITD_XACT_ACTIVE (1 << 31) -#define ITD_XACT_DBERROR (1 << 30) -#define ITD_XACT_BABBLE (1 << 29) -#define ITD_XACT_XACTERR (1 << 28) -#define ITD_XACT_LENGTH_MASK 0x0fff0000 -#define ITD_XACT_LENGTH_SH 16 -#define ITD_XACT_IOC (1 << 15) -#define ITD_XACT_PGSEL_MASK 0x00007000 -#define ITD_XACT_PGSEL_SH 12 -#define ITD_XACT_OFFSET_MASK 0x00000fff - - uint32_t bufptr[7]; -#define ITD_BUFPTR_MASK 0xfffff000 -#define ITD_BUFPTR_SH 12 -#define ITD_BUFPTR_EP_MASK 0x00000f00 -#define ITD_BUFPTR_EP_SH 8 -#define ITD_BUFPTR_DEVADDR_MASK 0x0000007f -#define ITD_BUFPTR_DEVADDR_SH 0 -#define ITD_BUFPTR_DIRECTION (1 << 11) -#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff -#define ITD_BUFPTR_MAXPKT_SH 0 -#define ITD_BUFPTR_MULT_MASK 0x00000003 -#define ITD_BUFPTR_MULT_SH 0 -} EHCIitd; - -/* EHCI spec version 1.0 Section 3.4 - */ -typedef struct EHCIsitd { - uint32_t next; // Standard next link pointer - uint32_t epchar; -#define SITD_EPCHAR_IO (1 << 31) -#define SITD_EPCHAR_PORTNUM_MASK 0x7f000000 -#define SITD_EPCHAR_PORTNUM_SH 24 -#define SITD_EPCHAR_HUBADD_MASK 0x007f0000 -#define SITD_EPCHAR_HUBADDR_SH 16 -#define SITD_EPCHAR_EPNUM_MASK 0x00000f00 -#define SITD_EPCHAR_EPNUM_SH 8 -#define SITD_EPCHAR_DEVADDR_MASK 0x0000007f - - uint32_t uframe; -#define SITD_UFRAME_CMASK_MASK 0x0000ff00 -#define SITD_UFRAME_CMASK_SH 8 -#define SITD_UFRAME_SMASK_MASK 0x000000ff - - uint32_t results; -#define SITD_RESULTS_IOC (1 << 31) -#define SITD_RESULTS_PGSEL (1 << 30) -#define SITD_RESULTS_TBYTES_MASK 0x03ff0000 -#define SITD_RESULTS_TYBYTES_SH 16 -#define SITD_RESULTS_CPROGMASK_MASK 0x0000ff00 -#define SITD_RESULTS_CPROGMASK_SH 8 -#define SITD_RESULTS_ACTIVE (1 << 7) -#define SITD_RESULTS_ERR (1 << 6) -#define SITD_RESULTS_DBERR (1 << 5) -#define SITD_RESULTS_BABBLE (1 << 4) -#define SITD_RESULTS_XACTERR (1 << 3) -#define SITD_RESULTS_MISSEDUF (1 << 2) -#define SITD_RESULTS_SPLITXSTATE (1 << 1) - - uint32_t bufptr[2]; -#define SITD_BUFPTR_MASK 0xfffff000 -#define SITD_BUFPTR_CURROFF_MASK 0x00000fff -#define SITD_BUFPTR_TPOS_MASK 0x00000018 -#define SITD_BUFPTR_TPOS_SH 3 -#define SITD_BUFPTR_TCNT_MASK 0x00000007 - - uint32_t backptr; // Standard next link pointer -} EHCIsitd; - -/* EHCI spec version 1.0 Section 3.5 - */ -typedef struct EHCIqtd { - uint32_t next; // Standard next link pointer - uint32_t altnext; // Standard next link pointer - uint32_t token; -#define QTD_TOKEN_DTOGGLE (1 << 31) -#define QTD_TOKEN_TBYTES_MASK 0x7fff0000 -#define QTD_TOKEN_TBYTES_SH 16 -#define QTD_TOKEN_IOC (1 << 15) -#define QTD_TOKEN_CPAGE_MASK 0x00007000 -#define QTD_TOKEN_CPAGE_SH 12 -#define QTD_TOKEN_CERR_MASK 0x00000c00 -#define QTD_TOKEN_CERR_SH 10 -#define QTD_TOKEN_PID_MASK 0x00000300 -#define QTD_TOKEN_PID_SH 8 -#define QTD_TOKEN_ACTIVE (1 << 7) -#define QTD_TOKEN_HALT (1 << 6) -#define QTD_TOKEN_DBERR (1 << 5) -#define QTD_TOKEN_BABBLE (1 << 4) -#define QTD_TOKEN_XACTERR (1 << 3) -#define QTD_TOKEN_MISSEDUF (1 << 2) -#define QTD_TOKEN_SPLITXSTATE (1 << 1) -#define QTD_TOKEN_PING (1 << 0) - - uint32_t bufptr[5]; // Standard buffer pointer -#define QTD_BUFPTR_MASK 0xfffff000 -#define QTD_BUFPTR_SH 12 -} EHCIqtd; - -/* EHCI spec version 1.0 Section 3.6 - */ -typedef struct EHCIqh { - uint32_t next; // Standard next link pointer - - /* endpoint characteristics */ - uint32_t epchar; -#define QH_EPCHAR_RL_MASK 0xf0000000 -#define QH_EPCHAR_RL_SH 28 -#define QH_EPCHAR_C (1 << 27) -#define QH_EPCHAR_MPLEN_MASK 0x07FF0000 -#define QH_EPCHAR_MPLEN_SH 16 -#define QH_EPCHAR_H (1 << 15) -#define QH_EPCHAR_DTC (1 << 14) -#define QH_EPCHAR_EPS_MASK 0x00003000 -#define QH_EPCHAR_EPS_SH 12 -#define EHCI_QH_EPS_FULL 0 -#define EHCI_QH_EPS_LOW 1 -#define EHCI_QH_EPS_HIGH 2 -#define EHCI_QH_EPS_RESERVED 3 - -#define QH_EPCHAR_EP_MASK 0x00000f00 -#define QH_EPCHAR_EP_SH 8 -#define QH_EPCHAR_I (1 << 7) -#define QH_EPCHAR_DEVADDR_MASK 0x0000007f -#define QH_EPCHAR_DEVADDR_SH 0 - - /* endpoint capabilities */ - uint32_t epcap; -#define QH_EPCAP_MULT_MASK 0xc0000000 -#define QH_EPCAP_MULT_SH 30 -#define QH_EPCAP_PORTNUM_MASK 0x3f800000 -#define QH_EPCAP_PORTNUM_SH 23 -#define QH_EPCAP_HUBADDR_MASK 0x007f0000 -#define QH_EPCAP_HUBADDR_SH 16 -#define QH_EPCAP_CMASK_MASK 0x0000ff00 -#define QH_EPCAP_CMASK_SH 8 -#define QH_EPCAP_SMASK_MASK 0x000000ff -#define QH_EPCAP_SMASK_SH 0 - - uint32_t current_qtd; // Standard next link pointer - uint32_t next_qtd; // Standard next link pointer - uint32_t altnext_qtd; -#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e -#define QH_ALTNEXT_NAKCNT_SH 1 - - uint32_t token; // Same as QTD token - uint32_t bufptr[5]; // Standard buffer pointer -#define BUFPTR_CPROGMASK_MASK 0x000000ff -#define BUFPTR_FRAMETAG_MASK 0x0000001f -#define BUFPTR_SBYTES_MASK 0x00000fe0 -#define BUFPTR_SBYTES_SH 5 -} EHCIqh; - -/* EHCI spec version 1.0 Section 3.7 - */ -typedef struct EHCIfstn { - uint32_t next; // Standard next link pointer - uint32_t backptr; // Standard next link pointer -} EHCIfstn; - -typedef struct EHCIPacket EHCIPacket; -typedef struct EHCIQueue EHCIQueue; -typedef struct EHCIState EHCIState; - -enum async_state { - EHCI_ASYNC_NONE = 0, - EHCI_ASYNC_INITIALIZED, - EHCI_ASYNC_INFLIGHT, - EHCI_ASYNC_FINISHED, -}; - -struct EHCIPacket { - EHCIQueue *queue; - QTAILQ_ENTRY(EHCIPacket) next; - - EHCIqtd qtd; /* copy of current QTD (being worked on) */ - uint32_t qtdaddr; /* address QTD read from */ - - USBPacket packet; - QEMUSGList sgl; - int pid; - enum async_state async; - int usb_status; -}; - -struct EHCIQueue { - EHCIState *ehci; - QTAILQ_ENTRY(EHCIQueue) next; - uint32_t seen; - uint64_t ts; - int async; - int transact_ctr; - - /* cached data from guest - needs to be flushed - * when guest removes an entry (doorbell, handshake sequence) - */ - EHCIqh qh; /* copy of current QH (being worked on) */ - uint32_t qhaddr; /* address QH read from */ - uint32_t qtdaddr; /* address QTD read from */ - USBDevice *dev; - QTAILQ_HEAD(pkts_head, EHCIPacket) packets; -}; - -typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; - -struct EHCIState { - PCIDevice dev; - USBBus bus; - qemu_irq irq; - MemoryRegion mem; - MemoryRegion mem_caps; - MemoryRegion mem_opreg; - MemoryRegion mem_ports; - int companion_count; - - /* properties */ - uint32_t maxframes; - - /* - * EHCI spec version 1.0 Section 2.3 - * Host Controller Operational Registers - */ - uint8_t caps[OPREGBASE]; - union { - uint32_t opreg[(PORTSC_BEGIN-OPREGBASE)/sizeof(uint32_t)]; - struct { - uint32_t usbcmd; - uint32_t usbsts; - uint32_t usbintr; - uint32_t frindex; - uint32_t ctrldssegment; - uint32_t periodiclistbase; - uint32_t asynclistaddr; - uint32_t notused[9]; - uint32_t configflag; - }; - }; - uint32_t portsc[NB_PORTS]; - - /* - * Internal states, shadow registers, etc - */ - QEMUTimer *frame_timer; - QEMUBH *async_bh; - uint32_t astate; /* Current state in asynchronous schedule */ - uint32_t pstate; /* Current state in periodic schedule */ - USBPort ports[NB_PORTS]; - USBPort *companion_ports[NB_PORTS]; - uint32_t usbsts_pending; - uint32_t usbsts_frindex; - EHCIQueueHead aqueues; - EHCIQueueHead pqueues; - - /* which address to look at next */ - uint32_t a_fetch_addr; - uint32_t p_fetch_addr; - - USBPacket ipacket; - QEMUSGList isgl; - - uint64_t last_run_ns; - uint32_t async_stepdown; - bool int_req_by_async; -}; - #define SET_LAST_RUN_CLOCK(s) \ (s)->last_run_ns = qemu_get_clock_ns(vm_clock); @@ -506,8 +210,7 @@ static const char *state2str(uint32_t state) static const char *addr2str(hwaddr addr) { - return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), - addr + OPREGBASE); + return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr); } static void ehci_trace_usbsts(uint32_t mask, int state) @@ -1115,7 +818,7 @@ static uint64_t ehci_opreg_read(void *ptr, hwaddr addr, uint32_t val; val = s->opreg[addr >> 2]; - trace_usb_ehci_opreg_read(addr + OPREGBASE, addr2str(addr), val); + trace_usb_ehci_opreg_read(addr + s->opregbase, addr2str(addr), val); return val; } @@ -1211,9 +914,9 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, uint32_t old = *mmio; int i; - trace_usb_ehci_opreg_write(addr + OPREGBASE, addr2str(addr), val); + trace_usb_ehci_opreg_write(addr + s->opregbase, addr2str(addr), val); - switch (addr + OPREGBASE) { + switch (addr) { case USBCMD: if (val & USBCMD_HCRESET) { ehci_reset(s); @@ -1291,7 +994,8 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, } *mmio = val; - trace_usb_ehci_opreg_change(addr + OPREGBASE, addr2str(addr), *mmio, old); + trace_usb_ehci_opreg_change(addr + s->opregbase, addr2str(addr), + *mmio, old); } @@ -1304,7 +1008,7 @@ static inline int get_dwords(EHCIState *ehci, uint32_t addr, int i; for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - pci_dma_read(&ehci->dev, addr, buf, sizeof(*buf)); + dma_memory_read(ehci->dma, addr, buf, sizeof(*buf)); *buf = le32_to_cpu(*buf); } @@ -1319,7 +1023,7 @@ static inline int put_dwords(EHCIState *ehci, uint32_t addr, for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { uint32_t tmp = cpu_to_le32(*buf); - pci_dma_write(&ehci->dev, addr, &tmp, sizeof(tmp)); + dma_memory_write(ehci->dma, addr, &tmp, sizeof(tmp)); } return 1; @@ -1402,7 +1106,7 @@ static int ehci_init_transfer(EHCIPacket *p) cpage = get_field(p->qtd.token, QTD_TOKEN_CPAGE); bytes = get_field(p->qtd.token, QTD_TOKEN_TBYTES); offset = p->qtd.bufptr[0] & ~QTD_BUFPTR_MASK; - pci_dma_sglist_init(&p->sgl, &p->queue->ehci->dev, 5); + qemu_sglist_init(&p->sgl, 5, p->queue->ehci->dma); while (bytes > 0) { if (cpage > 4) { @@ -1647,7 +1351,7 @@ static int ehci_process_itd(EHCIState *ehci, return USB_RET_PROCERR; } - pci_dma_sglist_init(&ehci->isgl, &ehci->dev, 2); + qemu_sglist_init(&ehci->isgl, 2, ehci->dma); if (off + len > 4096) { /* transfer crosses page border */ uint32_t len2 = off + len - 4096; @@ -2402,7 +2106,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) } list |= ((ehci->frindex & 0x1ff8) >> 1); - pci_dma_read(&ehci->dev, list, &entry, sizeof entry); + dma_memory_read(ehci->dma, list, &entry, sizeof entry); entry = le32_to_cpu(entry); DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", @@ -2552,8 +2256,6 @@ static const MemoryRegionOps ehci_mmio_port_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static int usb_ehci_initfn(PCIDevice *dev); - static USBPortOps ehci_port_ops = { .attach = ehci_attach, .detach = ehci_detach, @@ -2612,13 +2314,12 @@ static void usb_ehci_vm_state_change(void *opaque, int running, RunState state) } } -static const VMStateDescription vmstate_ehci = { - .name = "ehci", +const VMStateDescription vmstate_ehci = { + .name = "ehci-core", .version_id = 2, .minimum_version_id = 1, .post_load = usb_ehci_post_load, .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, EHCIState), /* mmio registers */ VMSTATE_UINT32(usbcmd, EHCIState), VMSTATE_UINT32(usbsts, EHCIState), @@ -2649,90 +2350,12 @@ static const VMStateDescription vmstate_ehci = { } }; -static Property ehci_properties[] = { - DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ehci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_ehci_initfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801D; /* ich4 */ - k->revision = 0x10; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_ehci; - dc->props = ehci_properties; -} - -static TypeInfo ehci_info = { - .name = "usb-ehci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(EHCIState), - .class_init = ehci_class_init, -}; - -static void ich9_ehci_class_init(ObjectClass *klass, void *data) +void usb_ehci_initfn(EHCIState *s, DeviceState *dev) { - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_ehci_initfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1; - k->revision = 0x03; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_ehci; - dc->props = ehci_properties; -} - -static TypeInfo ich9_ehci_info = { - .name = "ich9-usb-ehci1", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(EHCIState), - .class_init = ich9_ehci_class_init, -}; - -static int usb_ehci_initfn(PCIDevice *dev) -{ - EHCIState *s = DO_UPCAST(EHCIState, dev, dev); - uint8_t *pci_conf = s->dev.config; int i; - pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20); - - /* capabilities pointer */ - pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00); - //pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50); - - pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); /* interrupt pin D */ - pci_set_byte(&pci_conf[PCI_MIN_GNT], 0); - pci_set_byte(&pci_conf[PCI_MAX_LAT], 0); - - // pci_conf[0x50] = 0x01; // power management caps - - pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); // release number (2.1.4) - pci_set_byte(&pci_conf[0x61], 0x20); // frame length adjustment (2.1.5) - pci_set_word(&pci_conf[0x62], 0x00); // port wake up capability (2.1.6) - - pci_conf[0x64] = 0x00; - pci_conf[0x65] = 0x00; - pci_conf[0x66] = 0x00; - pci_conf[0x67] = 0x00; - pci_conf[0x68] = 0x01; - pci_conf[0x69] = 0x00; - pci_conf[0x6a] = 0x00; - pci_conf[0x6b] = 0x00; // USBLEGSUP - pci_conf[0x6c] = 0x00; - pci_conf[0x6d] = 0x00; - pci_conf[0x6e] = 0x00; - pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS - /* 2.2 host controller interface version */ - s->caps[0x00] = (uint8_t) OPREGBASE; + s->caps[0x00] = (uint8_t)(s->opregbase - s->capsbase); s->caps[0x01] = 0x00; s->caps[0x02] = 0x00; s->caps[0x03] = 0x01; /* HC version */ @@ -2741,13 +2364,10 @@ static int usb_ehci_initfn(PCIDevice *dev) s->caps[0x06] = 0x00; s->caps[0x07] = 0x00; s->caps[0x08] = 0x80; /* We can cache whole frame, no 64-bit */ - s->caps[0x09] = 0x68; /* EECP */ s->caps[0x0a] = 0x00; s->caps[0x0b] = 0x00; - s->irq = s->dev.irq[3]; - - usb_bus_new(&s->bus, &ehci_bus_ops, &s->dev.qdev); + usb_bus_new(&s->bus, &ehci_bus_ops, dev); for(i = 0; i < NB_PORTS; i++) { usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops, USB_SPEED_MASK_HIGH); @@ -2765,29 +2385,18 @@ static int usb_ehci_initfn(PCIDevice *dev) memory_region_init(&s->mem, "ehci", MMIO_SIZE); memory_region_init_io(&s->mem_caps, &ehci_mmio_caps_ops, s, - "capabilities", OPREGBASE); + "capabilities", CAPA_SIZE); memory_region_init_io(&s->mem_opreg, &ehci_mmio_opreg_ops, s, - "operational", PORTSC_BEGIN - OPREGBASE); + "operational", PORTSC_BEGIN); memory_region_init_io(&s->mem_ports, &ehci_mmio_port_ops, s, "ports", PORTSC_END - PORTSC_BEGIN); - memory_region_add_subregion(&s->mem, 0, &s->mem_caps); - memory_region_add_subregion(&s->mem, OPREGBASE, &s->mem_opreg); - memory_region_add_subregion(&s->mem, PORTSC_BEGIN, &s->mem_ports); - - pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); - - return 0; + memory_region_add_subregion(&s->mem, s->capsbase, &s->mem_caps); + memory_region_add_subregion(&s->mem, s->opregbase, &s->mem_opreg); + memory_region_add_subregion(&s->mem, s->opregbase + PORTSC_BEGIN, + &s->mem_ports); } -static void ehci_register_types(void) -{ - type_register_static(&ehci_info); - type_register_static(&ich9_ehci_info); -} - -type_init(ehci_register_types) - /* * vim: expandtab ts=4 */ diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h new file mode 100644 index 0000000000..0ec675c352 --- /dev/null +++ b/hw/usb/hcd-ehci.h @@ -0,0 +1,320 @@ +/* + * QEMU USB EHCI Emulation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or(at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/hw.h" +#include "qemu-timer.h" +#include "hw/usb.h" +#include "monitor.h" +#include "trace.h" +#include "dma.h" +#include "sysemu.h" + +#ifndef EHCI_DEBUG +#define EHCI_DEBUG 0 +#endif + +#if EHCI_DEBUG +#define DPRINTF printf +#else +#define DPRINTF(...) +#endif + +#define MMIO_SIZE 0x1000 +#define CAPA_SIZE 0x10 + +#define PORTSC 0x0044 +#define PORTSC_BEGIN PORTSC +#define PORTSC_END (PORTSC + 4 * NB_PORTS) + +#define NB_PORTS 6 /* Number of downstream ports */ + +typedef struct EHCIPacket EHCIPacket; +typedef struct EHCIQueue EHCIQueue; +typedef struct EHCIState EHCIState; + +/* EHCI spec version 1.0 Section 3.3 + */ +typedef struct EHCIitd { + uint32_t next; + + uint32_t transact[8]; +#define ITD_XACT_ACTIVE (1 << 31) +#define ITD_XACT_DBERROR (1 << 30) +#define ITD_XACT_BABBLE (1 << 29) +#define ITD_XACT_XACTERR (1 << 28) +#define ITD_XACT_LENGTH_MASK 0x0fff0000 +#define ITD_XACT_LENGTH_SH 16 +#define ITD_XACT_IOC (1 << 15) +#define ITD_XACT_PGSEL_MASK 0x00007000 +#define ITD_XACT_PGSEL_SH 12 +#define ITD_XACT_OFFSET_MASK 0x00000fff + + uint32_t bufptr[7]; +#define ITD_BUFPTR_MASK 0xfffff000 +#define ITD_BUFPTR_SH 12 +#define ITD_BUFPTR_EP_MASK 0x00000f00 +#define ITD_BUFPTR_EP_SH 8 +#define ITD_BUFPTR_DEVADDR_MASK 0x0000007f +#define ITD_BUFPTR_DEVADDR_SH 0 +#define ITD_BUFPTR_DIRECTION (1 << 11) +#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff +#define ITD_BUFPTR_MAXPKT_SH 0 +#define ITD_BUFPTR_MULT_MASK 0x00000003 +#define ITD_BUFPTR_MULT_SH 0 +} EHCIitd; + +/* EHCI spec version 1.0 Section 3.4 + */ +typedef struct EHCIsitd { + uint32_t next; /* Standard next link pointer */ + uint32_t epchar; +#define SITD_EPCHAR_IO (1 << 31) +#define SITD_EPCHAR_PORTNUM_MASK 0x7f000000 +#define SITD_EPCHAR_PORTNUM_SH 24 +#define SITD_EPCHAR_HUBADD_MASK 0x007f0000 +#define SITD_EPCHAR_HUBADDR_SH 16 +#define SITD_EPCHAR_EPNUM_MASK 0x00000f00 +#define SITD_EPCHAR_EPNUM_SH 8 +#define SITD_EPCHAR_DEVADDR_MASK 0x0000007f + + uint32_t uframe; +#define SITD_UFRAME_CMASK_MASK 0x0000ff00 +#define SITD_UFRAME_CMASK_SH 8 +#define SITD_UFRAME_SMASK_MASK 0x000000ff + + uint32_t results; +#define SITD_RESULTS_IOC (1 << 31) +#define SITD_RESULTS_PGSEL (1 << 30) +#define SITD_RESULTS_TBYTES_MASK 0x03ff0000 +#define SITD_RESULTS_TYBYTES_SH 16 +#define SITD_RESULTS_CPROGMASK_MASK 0x0000ff00 +#define SITD_RESULTS_CPROGMASK_SH 8 +#define SITD_RESULTS_ACTIVE (1 << 7) +#define SITD_RESULTS_ERR (1 << 6) +#define SITD_RESULTS_DBERR (1 << 5) +#define SITD_RESULTS_BABBLE (1 << 4) +#define SITD_RESULTS_XACTERR (1 << 3) +#define SITD_RESULTS_MISSEDUF (1 << 2) +#define SITD_RESULTS_SPLITXSTATE (1 << 1) + + uint32_t bufptr[2]; +#define SITD_BUFPTR_MASK 0xfffff000 +#define SITD_BUFPTR_CURROFF_MASK 0x00000fff +#define SITD_BUFPTR_TPOS_MASK 0x00000018 +#define SITD_BUFPTR_TPOS_SH 3 +#define SITD_BUFPTR_TCNT_MASK 0x00000007 + + uint32_t backptr; /* Standard next link pointer */ +} EHCIsitd; + +/* EHCI spec version 1.0 Section 3.5 + */ +typedef struct EHCIqtd { + uint32_t next; /* Standard next link pointer */ + uint32_t altnext; /* Standard next link pointer */ + uint32_t token; +#define QTD_TOKEN_DTOGGLE (1 << 31) +#define QTD_TOKEN_TBYTES_MASK 0x7fff0000 +#define QTD_TOKEN_TBYTES_SH 16 +#define QTD_TOKEN_IOC (1 << 15) +#define QTD_TOKEN_CPAGE_MASK 0x00007000 +#define QTD_TOKEN_CPAGE_SH 12 +#define QTD_TOKEN_CERR_MASK 0x00000c00 +#define QTD_TOKEN_CERR_SH 10 +#define QTD_TOKEN_PID_MASK 0x00000300 +#define QTD_TOKEN_PID_SH 8 +#define QTD_TOKEN_ACTIVE (1 << 7) +#define QTD_TOKEN_HALT (1 << 6) +#define QTD_TOKEN_DBERR (1 << 5) +#define QTD_TOKEN_BABBLE (1 << 4) +#define QTD_TOKEN_XACTERR (1 << 3) +#define QTD_TOKEN_MISSEDUF (1 << 2) +#define QTD_TOKEN_SPLITXSTATE (1 << 1) +#define QTD_TOKEN_PING (1 << 0) + + uint32_t bufptr[5]; /* Standard buffer pointer */ +#define QTD_BUFPTR_MASK 0xfffff000 +#define QTD_BUFPTR_SH 12 +} EHCIqtd; + +/* EHCI spec version 1.0 Section 3.6 + */ +typedef struct EHCIqh { + uint32_t next; /* Standard next link pointer */ + + /* endpoint characteristics */ + uint32_t epchar; +#define QH_EPCHAR_RL_MASK 0xf0000000 +#define QH_EPCHAR_RL_SH 28 +#define QH_EPCHAR_C (1 << 27) +#define QH_EPCHAR_MPLEN_MASK 0x07FF0000 +#define QH_EPCHAR_MPLEN_SH 16 +#define QH_EPCHAR_H (1 << 15) +#define QH_EPCHAR_DTC (1 << 14) +#define QH_EPCHAR_EPS_MASK 0x00003000 +#define QH_EPCHAR_EPS_SH 12 +#define EHCI_QH_EPS_FULL 0 +#define EHCI_QH_EPS_LOW 1 +#define EHCI_QH_EPS_HIGH 2 +#define EHCI_QH_EPS_RESERVED 3 + +#define QH_EPCHAR_EP_MASK 0x00000f00 +#define QH_EPCHAR_EP_SH 8 +#define QH_EPCHAR_I (1 << 7) +#define QH_EPCHAR_DEVADDR_MASK 0x0000007f +#define QH_EPCHAR_DEVADDR_SH 0 + + /* endpoint capabilities */ + uint32_t epcap; +#define QH_EPCAP_MULT_MASK 0xc0000000 +#define QH_EPCAP_MULT_SH 30 +#define QH_EPCAP_PORTNUM_MASK 0x3f800000 +#define QH_EPCAP_PORTNUM_SH 23 +#define QH_EPCAP_HUBADDR_MASK 0x007f0000 +#define QH_EPCAP_HUBADDR_SH 16 +#define QH_EPCAP_CMASK_MASK 0x0000ff00 +#define QH_EPCAP_CMASK_SH 8 +#define QH_EPCAP_SMASK_MASK 0x000000ff +#define QH_EPCAP_SMASK_SH 0 + + uint32_t current_qtd; /* Standard next link pointer */ + uint32_t next_qtd; /* Standard next link pointer */ + uint32_t altnext_qtd; +#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e +#define QH_ALTNEXT_NAKCNT_SH 1 + + uint32_t token; /* Same as QTD token */ + uint32_t bufptr[5]; /* Standard buffer pointer */ +#define BUFPTR_CPROGMASK_MASK 0x000000ff +#define BUFPTR_FRAMETAG_MASK 0x0000001f +#define BUFPTR_SBYTES_MASK 0x00000fe0 +#define BUFPTR_SBYTES_SH 5 +} EHCIqh; + +/* EHCI spec version 1.0 Section 3.7 + */ +typedef struct EHCIfstn { + uint32_t next; /* Standard next link pointer */ + uint32_t backptr; /* Standard next link pointer */ +} EHCIfstn; + +enum async_state { + EHCI_ASYNC_NONE = 0, + EHCI_ASYNC_INITIALIZED, + EHCI_ASYNC_INFLIGHT, + EHCI_ASYNC_FINISHED, +}; + +struct EHCIPacket { + EHCIQueue *queue; + QTAILQ_ENTRY(EHCIPacket) next; + + EHCIqtd qtd; /* copy of current QTD (being worked on) */ + uint32_t qtdaddr; /* address QTD read from */ + + USBPacket packet; + QEMUSGList sgl; + int pid; + enum async_state async; + int usb_status; +}; + +struct EHCIQueue { + EHCIState *ehci; + QTAILQ_ENTRY(EHCIQueue) next; + uint32_t seen; + uint64_t ts; + int async; + int transact_ctr; + + /* cached data from guest - needs to be flushed + * when guest removes an entry (doorbell, handshake sequence) + */ + EHCIqh qh; /* copy of current QH (being worked on) */ + uint32_t qhaddr; /* address QH read from */ + uint32_t qtdaddr; /* address QTD read from */ + USBDevice *dev; + QTAILQ_HEAD(pkts_head, EHCIPacket) packets; +}; + +typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; + +struct EHCIState { + USBBus bus; + qemu_irq irq; + MemoryRegion mem; + DMAContext *dma; + MemoryRegion mem_caps; + MemoryRegion mem_opreg; + MemoryRegion mem_ports; + int companion_count; + uint16_t capsbase; + uint16_t opregbase; + + /* properties */ + uint32_t maxframes; + + /* + * EHCI spec version 1.0 Section 2.3 + * Host Controller Operational Registers + */ + uint8_t caps[CAPA_SIZE]; + union { + uint32_t opreg[PORTSC_BEGIN/sizeof(uint32_t)]; + struct { + uint32_t usbcmd; + uint32_t usbsts; + uint32_t usbintr; + uint32_t frindex; + uint32_t ctrldssegment; + uint32_t periodiclistbase; + uint32_t asynclistaddr; + uint32_t notused[9]; + uint32_t configflag; + }; + }; + uint32_t portsc[NB_PORTS]; + + /* + * Internal states, shadow registers, etc + */ + QEMUTimer *frame_timer; + QEMUBH *async_bh; + uint32_t astate; /* Current state in asynchronous schedule */ + uint32_t pstate; /* Current state in periodic schedule */ + USBPort ports[NB_PORTS]; + USBPort *companion_ports[NB_PORTS]; + uint32_t usbsts_pending; + uint32_t usbsts_frindex; + EHCIQueueHead aqueues; + EHCIQueueHead pqueues; + + /* which address to look at next */ + uint32_t a_fetch_addr; + uint32_t p_fetch_addr; + + USBPacket ipacket; + QEMUSGList isgl; + + uint64_t last_run_ns; + uint32_t async_stepdown; + bool int_req_by_async; +}; + +extern const VMStateDescription vmstate_ehci; + +void usb_ehci_initfn(EHCIState *s, DeviceState *dev); diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index b6b972fa92..f4b555addc 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -88,6 +88,23 @@ enum { typedef struct UHCIState UHCIState; typedef struct UHCIAsync UHCIAsync; typedef struct UHCIQueue UHCIQueue; +typedef struct UHCIInfo UHCIInfo; +typedef struct UHCIPCIDeviceClass UHCIPCIDeviceClass; + +struct UHCIInfo { + const char *name; + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision; + uint8_t irq_pin; + int (*initfn)(PCIDevice *dev); + bool unplug; +}; + +struct UHCIPCIDeviceClass { + PCIDeviceClass parent_class; + UHCIInfo info; +}; /* * Pending async transaction. @@ -718,9 +735,52 @@ static void uhci_read_td(UHCIState *s, UHCI_TD *td, uint32_t link) le32_to_cpus(&td->buffer); } +static int uhci_handle_td_error(UHCIState *s, UHCI_TD *td, uint32_t td_addr, + int status, uint32_t *int_mask) +{ + uint32_t queue_token = uhci_queue_token(td); + int ret; + + switch (status) { + case USB_RET_NAK: + td->ctrl |= TD_CTRL_NAK; + return TD_RESULT_NEXT_QH; + + case USB_RET_STALL: + td->ctrl |= TD_CTRL_STALL; + trace_usb_uhci_packet_complete_stall(queue_token, td_addr); + ret = TD_RESULT_NEXT_QH; + break; + + case USB_RET_BABBLE: + td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; + /* frame interrupted */ + trace_usb_uhci_packet_complete_babble(queue_token, td_addr); + ret = TD_RESULT_STOP_FRAME; + break; + + case USB_RET_IOERROR: + case USB_RET_NODEV: + default: + td->ctrl |= TD_CTRL_TIMEOUT; + td->ctrl &= ~(3 << TD_CTRL_ERROR_SHIFT); + trace_usb_uhci_packet_complete_error(queue_token, td_addr); + ret = TD_RESULT_NEXT_QH; + break; + } + + td->ctrl &= ~TD_CTRL_ACTIVE; + s->status |= UHCI_STS_USBERR; + if (td->ctrl & TD_CTRL_IOC) { + *int_mask |= 0x01; + } + uhci_update_irq(s); + return ret; +} + static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask) { - int len = 0, max_len, err, ret; + int len = 0, max_len, ret; uint8_t pid; max_len = ((td->token >> 21) + 1) & 0x7ff; @@ -731,8 +791,9 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; - if (ret < 0) - goto out; + if (ret < 0) { + return uhci_handle_td_error(s, td, async->td_addr, ret, int_mask); + } len = async->packet.result; td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); @@ -758,46 +819,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ trace_usb_uhci_packet_complete_success(async->queue->token, async->td_addr); return TD_RESULT_COMPLETE; - -out: - switch(ret) { - case USB_RET_NAK: - td->ctrl |= TD_CTRL_NAK; - return TD_RESULT_NEXT_QH; - - case USB_RET_STALL: - td->ctrl |= TD_CTRL_STALL; - trace_usb_uhci_packet_complete_stall(async->queue->token, - async->td_addr); - err = TD_RESULT_NEXT_QH; - break; - - case USB_RET_BABBLE: - td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; - /* frame interrupted */ - trace_usb_uhci_packet_complete_babble(async->queue->token, - async->td_addr); - err = TD_RESULT_STOP_FRAME; - break; - - case USB_RET_IOERROR: - case USB_RET_NODEV: - default: - td->ctrl |= TD_CTRL_TIMEOUT; - td->ctrl &= ~(3 << TD_CTRL_ERROR_SHIFT); - trace_usb_uhci_packet_complete_error(async->queue->token, - async->td_addr); - err = TD_RESULT_NEXT_QH; - break; - } - - td->ctrl &= ~TD_CTRL_ACTIVE; - s->status |= UHCI_STS_USBERR; - if (td->ctrl & TD_CTRL_IOC) { - *int_mask |= 0x01; - } - uhci_update_irq(s); - return err; } static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, @@ -875,6 +896,11 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, if (q == NULL) { USBDevice *dev = uhci_find_device(s, (td->token >> 8) & 0x7f); USBEndpoint *ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); + + if (ep == NULL) { + return uhci_handle_td_error(s, td, td_addr, USB_RET_NODEV, + int_mask); + } q = uhci_queue_new(s, qh_addr, td, ep); } async = uhci_async_alloc(q, td_addr); @@ -1208,6 +1234,7 @@ static USBBusOps uhci_bus_ops = { static int usb_uhci_common_initfn(PCIDevice *dev) { PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + UHCIPCIDeviceClass *u = container_of(pc, UHCIPCIDeviceClass, parent_class); UHCIState *s = DO_UPCAST(UHCIState, dev, dev); uint8_t *pci_conf = s->dev.config; int i; @@ -1216,20 +1243,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev) /* TODO: reset value should be 0. */ pci_conf[USB_SBRN] = USB_RELEASE_1; // release number - switch (pc->device_id) { - case PCI_DEVICE_ID_INTEL_82801I_UHCI1: - s->irq_pin = 0; /* A */ - break; - case PCI_DEVICE_ID_INTEL_82801I_UHCI2: - s->irq_pin = 1; /* B */ - break; - case PCI_DEVICE_ID_INTEL_82801I_UHCI3: - s->irq_pin = 2; /* C */ - break; - default: - s->irq_pin = 3; /* D */ - break; - } + s->irq_pin = u->info.irq_pin; pci_config_set_interrupt_pin(pci_conf, s->irq_pin + 1); if (s->masterbus) { @@ -1293,143 +1307,107 @@ static Property uhci_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void piix3_uhci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_uhci_common_initfn; - k->exit = usb_uhci_exit; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82371SB_2; - k->revision = 0x01; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_uhci; - dc->props = uhci_properties; -} - -static TypeInfo piix3_uhci_info = { - .name = "piix3-usb-uhci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_init = piix3_uhci_class_init, -}; - -static void piix4_uhci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_uhci_common_initfn; - k->exit = usb_uhci_exit; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82371AB_2; - k->revision = 0x01; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_uhci; - dc->props = uhci_properties; -} - -static TypeInfo piix4_uhci_info = { - .name = "piix4-usb-uhci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_init = piix4_uhci_class_init, -}; - -static void vt82c686b_uhci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_uhci_vt82c686b_initfn; - k->exit = usb_uhci_exit; - k->vendor_id = PCI_VENDOR_ID_VIA; - k->device_id = PCI_DEVICE_ID_VIA_UHCI; - k->revision = 0x01; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_uhci; - dc->props = uhci_properties; -} - -static TypeInfo vt82c686b_uhci_info = { - .name = "vt82c686b-usb-uhci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_init = vt82c686b_uhci_class_init, -}; - -static void ich9_uhci1_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_uhci_common_initfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1; - k->revision = 0x03; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_uhci; - dc->props = uhci_properties; -} - -static TypeInfo ich9_uhci1_info = { - .name = "ich9-usb-uhci1", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_init = ich9_uhci1_class_init, -}; - -static void ich9_uhci2_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_uhci_common_initfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2; - k->revision = 0x03; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_uhci; - dc->props = uhci_properties; -} - -static TypeInfo ich9_uhci2_info = { - .name = "ich9-usb-uhci2", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_init = ich9_uhci2_class_init, -}; - -static void ich9_uhci3_class_init(ObjectClass *klass, void *data) +static void uhci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_uhci_common_initfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3; - k->revision = 0x03; - k->class_id = PCI_CLASS_SERIAL_USB; + UHCIPCIDeviceClass *u = container_of(k, UHCIPCIDeviceClass, parent_class); + UHCIInfo *info = data; + + k->init = info->initfn ? info->initfn : usb_uhci_common_initfn; + k->exit = info->unplug ? usb_uhci_exit : NULL; + k->vendor_id = info->vendor_id; + k->device_id = info->device_id; + k->revision = info->revision; + k->class_id = PCI_CLASS_SERIAL_USB; dc->vmsd = &vmstate_uhci; dc->props = uhci_properties; + u->info = *info; } -static TypeInfo ich9_uhci3_info = { - .name = "ich9-usb-uhci3", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_init = ich9_uhci3_class_init, +static UHCIInfo uhci_info[] = { + { + .name = "piix3-usb-uhci", + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, + .revision = 0x01, + .irq_pin = 3, + .unplug = true, + },{ + .name = "piix4-usb-uhci", + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, + .revision = 0x01, + .irq_pin = 3, + .unplug = true, + },{ + .name = "vt82c686b-usb-uhci", + .vendor_id = PCI_VENDOR_ID_VIA, + .device_id = PCI_DEVICE_ID_VIA_UHCI, + .revision = 0x01, + .irq_pin = 3, + .initfn = usb_uhci_vt82c686b_initfn, + .unplug = true, + },{ + .name = "ich9-usb-uhci1", /* 00:1d.0 */ + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, + .revision = 0x03, + .irq_pin = 0, + .unplug = false, + },{ + .name = "ich9-usb-uhci2", /* 00:1d.1 */ + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, + .revision = 0x03, + .irq_pin = 1, + .unplug = false, + },{ + .name = "ich9-usb-uhci3", /* 00:1d.2 */ + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, + .revision = 0x03, + .irq_pin = 2, + .unplug = false, + },{ + .name = "ich9-usb-uhci4", /* 00:1a.0 */ + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI4, + .revision = 0x03, + .irq_pin = 0, + .unplug = false, + },{ + .name = "ich9-usb-uhci5", /* 00:1a.1 */ + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI5, + .revision = 0x03, + .irq_pin = 1, + .unplug = false, + },{ + .name = "ich9-usb-uhci6", /* 00:1a.2 */ + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI6, + .revision = 0x03, + .irq_pin = 2, + .unplug = false, + } }; static void uhci_register_types(void) { - type_register_static(&piix3_uhci_info); - type_register_static(&piix4_uhci_info); - type_register_static(&vt82c686b_uhci_info); - type_register_static(&ich9_uhci1_info); - type_register_static(&ich9_uhci2_info); - type_register_static(&ich9_uhci3_info); + TypeInfo uhci_type_info = { + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(UHCIState), + .class_size = sizeof(UHCIPCIDeviceClass), + .class_init = uhci_class_init, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(uhci_info); i++) { + uhci_type_info.name = uhci_info[i].name; + uhci_type_info.class_data = uhci_info + i; + type_register(&uhci_type_info); + } } type_init(uhci_register_types) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 7b65741dd5..900abf5566 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -146,6 +146,21 @@ typedef struct XHCITRB { bool ccs; } XHCITRB; +enum { + PLS_U0 = 0, + PLS_U1 = 1, + PLS_U2 = 2, + PLS_U3 = 3, + PLS_DISABLED = 4, + PLS_RX_DETECT = 5, + PLS_INACTIVE = 6, + PLS_POLLING = 7, + PLS_RECOVERY = 8, + PLS_HOT_RESET = 9, + PLS_COMPILANCE_MODE = 10, + PLS_TEST_MODE = 11, + PLS_RESUME = 15, +}; typedef enum TRBType { TRB_RESERVED = 0, @@ -287,6 +302,16 @@ typedef enum TRBCCode { typedef struct XHCIState XHCIState; +#define get_field(data, field) \ + (((data) >> field##_SHIFT) & field##_MASK) + +#define set_field(data, newval, field) do { \ + uint32_t val = *data; \ + val &= ~(field##_MASK << field##_SHIFT); \ + val |= ((newval) & field##_MASK) << field##_SHIFT; \ + *data = val; \ + } while (0) + typedef enum EPType { ET_INVALID = 0, ET_ISO_OUT, @@ -458,6 +483,8 @@ enum xhci_flags { static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid); +static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, + unsigned int epid); static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v); static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v); @@ -1050,8 +1077,7 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, slot = &xhci->slots[slotid-1]; if (slot->eps[epid-1]) { - fprintf(stderr, "xhci: slot %d ep %d already enabled!\n", slotid, epid); - return CC_TRB_ERROR; + xhci_disable_ep(xhci, slotid, epid); } epctx = g_malloc(sizeof(XHCIEPContext)); @@ -1894,6 +1920,9 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, } for (i = 0; i < xhci->numslots; i++) { + if (i == slotid-1) { + continue; + } if (xhci->slots[i].uport == uport) { fprintf(stderr, "xhci: port %s already assigned to slot %d\n", uport->path, i+1); @@ -1911,6 +1940,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, slot->devaddr = xhci->devaddr++; slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot->devaddr; DPRINTF("xhci: device address is %d\n", slot->devaddr); + usb_device_reset(dev); usb_device_handle_control(dev, NULL, DeviceOutRequest | USB_REQ_SET_ADDRESS, slot->devaddr, 0, 0, NULL); @@ -2312,35 +2342,86 @@ static void xhci_process_commands(XHCIState *xhci) } } -static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) +static bool xhci_port_have_device(XHCIPort *port) +{ + if (!port->uport->dev || !port->uport->dev->attached) { + return false; /* no device present */ + } + if (!((1 << port->uport->dev->speed) & port->speedmask)) { + return false; /* speed mismatch */ + } + return true; +} + +static void xhci_port_notify(XHCIPort *port, uint32_t bits) { + XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, + port->portnr << 24 }; + + if ((port->portsc & bits) == bits) { + return; + } + port->portsc |= bits; + if (!xhci_running(port->xhci)) { + return; + } + xhci_event(port->xhci, &ev, 0); +} + +static void xhci_port_update(XHCIPort *port, int is_detach) +{ + uint32_t pls = PLS_RX_DETECT; + port->portsc = PORTSC_PP; - if (port->uport->dev && port->uport->dev->attached && !is_detach && - (1 << port->uport->dev->speed) & port->speedmask) { + if (!is_detach && xhci_port_have_device(port)) { port->portsc |= PORTSC_CCS; switch (port->uport->dev->speed) { case USB_SPEED_LOW: port->portsc |= PORTSC_SPEED_LOW; + pls = PLS_POLLING; break; case USB_SPEED_FULL: port->portsc |= PORTSC_SPEED_FULL; + pls = PLS_POLLING; break; case USB_SPEED_HIGH: port->portsc |= PORTSC_SPEED_HIGH; + pls = PLS_POLLING; break; case USB_SPEED_SUPER: port->portsc |= PORTSC_SPEED_SUPER; + port->portsc |= PORTSC_PED; + pls = PLS_U0; break; } } + set_field(&port->portsc, pls, PORTSC_PLS); + trace_usb_xhci_port_link(port->portnr, pls); + xhci_port_notify(port, PORTSC_CSC); +} + +static void xhci_port_reset(XHCIPort *port) +{ + trace_usb_xhci_port_reset(port->portnr); - if (xhci_running(xhci)) { - port->portsc |= PORTSC_CSC; - XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, - port->portnr << 24}; - xhci_event(xhci, &ev, 0); - DPRINTF("xhci: port change event for port %d\n", port->portnr); + if (!xhci_port_have_device(port)) { + return; } + + usb_device_reset(port->uport->dev); + + switch (port->uport->dev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + set_field(&port->portsc, PLS_U0, PORTSC_PLS); + trace_usb_xhci_port_link(port->portnr, PLS_U0); + port->portsc |= PORTSC_PED; + break; + } + + port->portsc &= ~PORTSC_PR; + xhci_port_notify(port, PORTSC_PRC); } static void xhci_reset(DeviceState *dev) @@ -2368,7 +2449,7 @@ static void xhci_reset(DeviceState *dev) } for (i = 0; i < xhci->numports; i++) { - xhci_update_port(xhci, xhci->ports + i, 0); + xhci_port_update(xhci->ports + i, 0); } for (i = 0; i < xhci->numintrs; i++) { @@ -2499,19 +2580,18 @@ static void xhci_port_write(void *ptr, hwaddr reg, PORTSC_PRC|PORTSC_PLC|PORTSC_CEC)); if (val & PORTSC_LWS) { /* overwrite PLS only when LWS=1 */ - portsc &= ~(PORTSC_PLS_MASK << PORTSC_PLS_SHIFT); - portsc |= val & (PORTSC_PLS_MASK << PORTSC_PLS_SHIFT); + uint32_t pls = get_field(val, PORTSC_PLS); + set_field(&portsc, pls, PORTSC_PLS); + trace_usb_xhci_port_link(port->portnr, pls); } /* read/write bits */ portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE); portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE)); + port->portsc = portsc; /* write-1-to-start bits */ if (val & PORTSC_PR) { - DPRINTF("xhci: port %d reset\n", port); - usb_device_reset(port->uport->dev); - portsc |= PORTSC_PRC | PORTSC_PED; + xhci_port_reset(port); } - port->portsc = portsc; break; case 0x04: /* PORTPMSC */ case 0x08: /* PORTLI */ @@ -2815,7 +2895,7 @@ static void xhci_attach(USBPort *usbport) XHCIState *xhci = usbport->opaque; XHCIPort *port = xhci_lookup_port(xhci, usbport); - xhci_update_port(xhci, port, 0); + xhci_port_update(port, 0); } static void xhci_detach(USBPort *usbport) @@ -2823,27 +2903,19 @@ static void xhci_detach(USBPort *usbport) XHCIState *xhci = usbport->opaque; XHCIPort *port = xhci_lookup_port(xhci, usbport); - xhci_update_port(xhci, port, 1); + xhci_port_update(port, 1); } static void xhci_wakeup(USBPort *usbport) { XHCIState *xhci = usbport->opaque; XHCIPort *port = xhci_lookup_port(xhci, usbport); - XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, - port->portnr << 24}; - uint32_t pls; - pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; - if (pls != 3) { - return; - } - port->portsc |= 0xf << PORTSC_PLS_SHIFT; - if (port->portsc & PORTSC_PLC) { + if (get_field(port->portsc, PORTSC_PLS) != PLS_U3) { return; } - port->portsc |= PORTSC_PLC; - xhci_event(xhci, &ev, 0); + set_field(&port->portsc, PLS_RESUME, PORTSC_PLS); + xhci_port_notify(port, PORTSC_PLC); } static void xhci_complete(USBPort *port, USBPacket *packet) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 22f671b3b1..c5cfe0b313 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -29,6 +29,7 @@ #include "qemu-timer.h" #include "monitor.h" #include "sysemu.h" +#include "iov.h" #include <dirent.h> #include <sys/ioctl.h> @@ -105,6 +106,7 @@ struct USBRedirDevice { struct usb_redir_interface_info_header interface_info; struct usbredirfilter_rule *filter_rules; int filter_rules_count; + int compatible_speedmask; }; static void usbredir_hello(void *priv, struct usb_redir_hello_header *h); @@ -312,6 +314,11 @@ static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) { USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + if (p->combined) { + usb_combined_packet_cancel(udev, p); + return; + } + packet_id_queue_add(&dev->cancelled, p->id); usbredirparser_send_cancel_data_packet(dev->parser, p->id); usbredirparser_do_write(dev->parser); @@ -331,6 +338,10 @@ static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev, static USBPacket *p; QTAILQ_FOREACH(p, &ep->queue, queue) { + /* Skip combined packets, except for the first */ + if (p->combined && p != p->combined->first) { + continue; + } packet_id_queue_add(&dev->already_in_flight, p->id); } } @@ -565,26 +576,36 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, uint8_t ep) { struct usb_redir_bulk_packet_header bulk_packet; + size_t size = (p->combined) ? p->combined->iov.size : p->iov.size; - DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id); + DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, size, p->id); if (usbredir_already_in_flight(dev, p->id)) { return USB_RET_ASYNC; } bulk_packet.endpoint = ep; - bulk_packet.length = p->iov.size; + bulk_packet.length = size; bulk_packet.stream_id = 0; + bulk_packet.length_high = size >> 16; + assert(bulk_packet.length_high == 0 || + usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_32bits_bulk_length)); if (ep & USB_DIR_IN) { usbredirparser_send_bulk_packet(dev->parser, p->id, &bulk_packet, NULL, 0); } else { - uint8_t buf[p->iov.size]; - usb_packet_copy(p, buf, p->iov.size); - usbredir_log_data(dev, "bulk data out:", buf, p->iov.size); + uint8_t buf[size]; + if (p->combined) { + iov_to_buf(p->combined->iov.iov, p->combined->iov.niov, + 0, buf, size); + } else { + usb_packet_copy(p, buf, size); + } + usbredir_log_data(dev, "bulk data out:", buf, size); usbredirparser_send_bulk_packet(dev->parser, p->id, - &bulk_packet, buf, p->iov.size); + &bulk_packet, buf, size); } usbredirparser_do_write(dev->parser); return USB_RET_ASYNC; @@ -701,6 +722,10 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p) case USB_ENDPOINT_XFER_ISOC: return usbredir_handle_iso_data(dev, p, ep); case USB_ENDPOINT_XFER_BULK: + if (p->state == USB_PACKET_SETUP && p->pid == USB_TOKEN_IN && + p->ep->pipeline) { + return USB_RET_ADD_TO_QUEUE; + } return usbredir_handle_bulk_data(dev, p, ep); case USB_ENDPOINT_XFER_INT: return usbredir_handle_interrupt_data(dev, p, ep); @@ -711,6 +736,13 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p) } } +static void usbredir_flush_ep_queue(USBDevice *dev, USBEndpoint *ep) +{ + if (ep->pid == USB_TOKEN_IN && ep->pipeline) { + usb_ep_combine_input_packets(ep); + } +} + static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p, int config) { @@ -896,6 +928,7 @@ static void usbredir_create_parser(USBRedirDevice *dev) usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size); usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); + usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length); if (runstate_check(RUN_STATE_INMIGRATE)) { flags |= usbredirparser_fl_no_hello; @@ -930,6 +963,7 @@ static void usbredir_do_attach(void *opaque) } if (usb_device_attach(&dev->dev) != 0) { + WARNING("rejecting device due to speed mismatch\n"); usbredir_reject_device(dev); } } @@ -1036,6 +1070,9 @@ static int usbredir_initfn(USBDevice *udev) /* We'll do the attach once we receive the speed from the usb-host */ udev->auto_attach = 0; + /* Will be cleared during setup when we find conflicts */ + dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH; + /* Let the backend know we are ready */ qemu_chr_fe_open(dev->cs); qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, @@ -1176,10 +1213,13 @@ static void usbredir_device_connect(void *priv, case usb_redir_speed_low: speed = "low speed"; dev->dev.speed = USB_SPEED_LOW; + dev->compatible_speedmask &= ~USB_SPEED_MASK_FULL; + dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH; break; case usb_redir_speed_full: speed = "full speed"; dev->dev.speed = USB_SPEED_FULL; + dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH; break; case usb_redir_speed_high: speed = "high speed"; @@ -1209,7 +1249,7 @@ static void usbredir_device_connect(void *priv, device_connect->device_class); } - dev->dev.speedmask = (1 << dev->dev.speed); + dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask; dev->device_info = *device_connect; if (usbredir_check_filter(dev)) { @@ -1249,6 +1289,7 @@ static void usbredir_device_disconnect(void *priv) dev->interface_info.interface_count = NO_INTERFACE_INFO; dev->dev.addr = 0; dev->dev.speed = 0; + dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH; } static void usbredir_interface_info(void *priv, @@ -1270,6 +1311,12 @@ static void usbredir_interface_info(void *priv, } } +static void usbredir_mark_speed_incompatible(USBRedirDevice *dev, int speed) +{ + dev->compatible_speedmask &= ~(1 << speed); + dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask; +} + static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep) { if (uep->type != USB_ENDPOINT_XFER_BULK) { @@ -1278,27 +1325,64 @@ static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep) if (uep->pid == USB_TOKEN_OUT) { uep->pipeline = true; } + if (uep->pid == USB_TOKEN_IN && uep->max_packet_size != 0 && + usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_32bits_bulk_length)) { + uep->pipeline = true; + } +} + +static void usbredir_setup_usb_eps(USBRedirDevice *dev) +{ + struct USBEndpoint *usb_ep; + int i, pid; + + for (i = 0; i < MAX_ENDPOINTS; i++) { + pid = (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT; + usb_ep = usb_ep_get(&dev->dev, pid, i & 0x0f); + usb_ep->type = dev->endpoint[i].type; + usb_ep->ifnum = dev->endpoint[i].interface; + usb_ep->max_packet_size = dev->endpoint[i].max_packet_size; + usbredir_set_pipeline(dev, usb_ep); + } } static void usbredir_ep_info(void *priv, struct usb_redir_ep_info_header *ep_info) { USBRedirDevice *dev = priv; - struct USBEndpoint *usb_ep; int i; for (i = 0; i < MAX_ENDPOINTS; i++) { dev->endpoint[i].type = ep_info->type[i]; dev->endpoint[i].interval = ep_info->interval[i]; dev->endpoint[i].interface = ep_info->interface[i]; + if (usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_ep_info_max_packet_size)) { + dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i]; + } switch (dev->endpoint[i].type) { case usb_redir_type_invalid: break; case usb_redir_type_iso: + usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL); + usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH); + /* Fall through */ case usb_redir_type_interrupt: + if (!usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_ep_info_max_packet_size) || + ep_info->max_packet_size[i] > 64) { + usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL); + } + if (!usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_ep_info_max_packet_size) || + ep_info->max_packet_size[i] > 1024) { + usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH); + } if (dev->endpoint[i].interval == 0) { ERROR("Received 0 interval for isoc or irq endpoint\n"); - usbredir_device_disconnect(dev); + usbredir_reject_device(dev); + return; } /* Fall through */ case usb_redir_type_control: @@ -1308,21 +1392,19 @@ static void usbredir_ep_info(void *priv, break; default: ERROR("Received invalid endpoint type\n"); - usbredir_device_disconnect(dev); + usbredir_reject_device(dev); return; } - usb_ep = usb_ep_get(&dev->dev, - (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, - i & 0x0f); - usb_ep->type = dev->endpoint[i].type; - usb_ep->ifnum = dev->endpoint[i].interface; - if (usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_ep_info_max_packet_size)) { - dev->endpoint[i].max_packet_size = - usb_ep->max_packet_size = ep_info->max_packet_size[i]; - } - usbredir_set_pipeline(dev, usb_ep); } + /* The new ep info may have caused a speed incompatibility, recheck */ + if (dev->dev.attached && + !(dev->dev.port->speedmask & dev->dev.speedmask)) { + ERROR("Device no longer matches speed after endpoint info change, " + "disconnecting!\n"); + usbredir_reject_device(dev); + return; + } + usbredir_setup_usb_eps(dev); } static void usbredir_configuration_status(void *priv, uint64_t id, @@ -1427,6 +1509,17 @@ static void usbredir_control_packet(void *priv, uint64_t id, DPRINTF("ctrl-in status %d len %d id %"PRIu64"\n", control_packet->status, len, id); + /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices + * to work redirected to a not superspeed capable hcd */ + if (dev->dev.speed == USB_SPEED_SUPER && + !((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER)) && + control_packet->requesttype == 0x80 && + control_packet->request == 6 && + control_packet->value == 0x100 && control_packet->index == 0 && + data_len >= 18 && data[7] == 9) { + data[7] = 64; + } + p = usbredir_find_packet_by_id(dev, 0, id); if (p) { len = usbredir_handle_status(dev, control_packet->status, len); @@ -1452,7 +1545,7 @@ static void usbredir_bulk_packet(void *priv, uint64_t id, { USBRedirDevice *dev = priv; uint8_t ep = bulk_packet->endpoint; - int len = bulk_packet->length; + int len = (bulk_packet->length_high << 16) | bulk_packet->length; USBPacket *p; DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n", @@ -1460,11 +1553,17 @@ static void usbredir_bulk_packet(void *priv, uint64_t id, p = usbredir_find_packet_by_id(dev, ep, id); if (p) { + size_t size = (p->combined) ? p->combined->iov.size : p->iov.size; len = usbredir_handle_status(dev, bulk_packet->status, len); if (len > 0) { usbredir_log_data(dev, "bulk data in:", data, data_len); - if (data_len <= p->iov.size) { - usb_packet_copy(p, data, data_len); + if (data_len <= size) { + if (p->combined) { + iov_from_buf(p->combined->iov.iov, p->combined->iov.niov, + 0, data, data_len); + } else { + usb_packet_copy(p, data, data_len); + } } else { ERROR("bulk got more data then requested (%d > %zd)\n", data_len, p->iov.size); @@ -1472,7 +1571,11 @@ static void usbredir_bulk_packet(void *priv, uint64_t id, } } p->result = len; - usb_packet_complete(&dev->dev, p); + if (p->pid == USB_TOKEN_IN && p->ep->pipeline) { + usb_combined_input_packet_complete(&dev->dev, p); + } else { + usb_packet_complete(&dev->dev, p); + } } free(data); } @@ -1554,8 +1657,6 @@ static void usbredir_pre_save(void *priv) static int usbredir_post_load(void *priv, int version_id) { USBRedirDevice *dev = priv; - struct USBEndpoint *usb_ep; - int i; switch (dev->device_info.speed) { case usb_redir_speed_low: @@ -1575,15 +1676,8 @@ static int usbredir_post_load(void *priv, int version_id) } dev->dev.speedmask = (1 << dev->dev.speed); - for (i = 0; i < MAX_ENDPOINTS; i++) { - usb_ep = usb_ep_get(&dev->dev, - (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, - i & 0x0f); - usb_ep->type = dev->endpoint[i].type; - usb_ep->ifnum = dev->endpoint[i].interface; - usb_ep->max_packet_size = dev->endpoint[i].max_packet_size; - usbredir_set_pipeline(dev, usb_ep); - } + usbredir_setup_usb_eps(dev); + return 0; } @@ -1879,6 +1973,7 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data) uc->handle_reset = usbredir_handle_reset; uc->handle_data = usbredir_handle_data; uc->handle_control = usbredir_handle_control; + uc->flush_ep_queue = usbredir_flush_ep_queue; dc->vmsd = &usbredir_vmstate; dc->props = usbredir_properties; } diff --git a/hw/xilinx_zynq.c b/hw/xilinx_zynq.c index 00262350b6..1f12a3d1ad 100644 --- a/hw/xilinx_zynq.c +++ b/hw/xilinx_zynq.c @@ -166,6 +166,9 @@ static void zynq_init(QEMUMachineInitArgs *args) zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET], false); zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true); + sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]); + sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[75-IRQ_OFFSET]); + sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]); sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]); |