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>
This commit is contained in:
Anthony Liguori 2012-11-01 14:34:13 -05:00
commit de0a36cd01
14 changed files with 1234 additions and 680 deletions

2
configure vendored
View File

@ -2788,7 +2788,7 @@ fi
# check for usbredirparser for usb network redirection support
if test "$usb_redir" != "no" ; then
if $pkg_config --atleast-version=0.5 libusbredirparser-0.5 >/dev/null 2>&1 ; then
if $pkg_config --atleast-version=0.5.3 libusbredirparser-0.5 >/dev/null 2>&1 ; then
usb_redir="yes"
usb_redir_cflags=$($pkg_config --cflags libusbredirparser-0.5 2>/dev/null)
usb_redir_libs=$($pkg_config --libs libusbredirparser-0.5 2>/dev/null)

View File

@ -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

View File

@ -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

182
hw/usb/combined-packet.c Normal file
View File

@ -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;
}
}
}

View File

@ -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);
}

200
hw/usb/hcd-ehci-pci.c Normal file
View File

@ -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;
}

77
hw/usb/hcd-ehci-sysbus.c Normal file
View File

@ -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)

View File

@ -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 HCSPPORTROUTE1 0x000c
#define HCSPPORTROUTE2 0x0010
#define OPREGBASE 0x0020 // Operational Registers Base Address
#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)
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_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)
{
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
*/

320
hw/usb/hcd-ehci.h Normal file
View File

@ -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);

View File

@ -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)
static void uhci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
UHCIPCIDeviceClass *u = container_of(k, UHCIPCIDeviceClass, parent_class);
UHCIInfo *info = data;
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;
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 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)
{
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;
dc->vmsd = &vmstate_uhci;
dc->props = uhci_properties;
}
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)

View File

@ -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);
}
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);
static void xhci_port_reset(XHCIPort *port)
{
trace_usb_xhci_port_reset(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) {
if (get_field(port->portsc, PORTSC_PLS) != PLS_U3) {
return;
}
port->portsc |= 0xf << PORTSC_PLS_SHIFT;
if (port->portsc & PORTSC_PLC) {
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)

View File

@ -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;
}

View File

@ -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]);

View File

@ -338,6 +338,8 @@ usb_xhci_irq_msix_use(uint32_t nr) "nr %d"
usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d"
usb_xhci_queue_event(uint32_t vector, uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "v %d, idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x"
usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x"
usb_xhci_port_reset(uint32_t port) "port %d"
usb_xhci_port_link(uint32_t port, uint32_t pls) "port %d, pls %d"
usb_xhci_slot_enable(uint32_t slotid) "slotid %d"
usb_xhci_slot_disable(uint32_t slotid) "slotid %d"
usb_xhci_slot_address(uint32_t slotid) "slotid %d"