usb/ehci: split into multiple source files
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
569c7fc840
commit
0bf96f9457
@ -1,6 +1,6 @@
|
||||
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
|
||||
common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o
|
||||
common-obj-y += libhw.o
|
||||
|
||||
|
138
hw/usb/hcd-ehci-pci.c
Normal file
138
hw/usb/hcd-ehci-pci.c
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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);
|
||||
|
||||
k->init = usb_ehci_pci_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_pci_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_pci_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_pci_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 void ehci_pci_register_types(void)
|
||||
{
|
||||
type_register_static(&ehci_info);
|
||||
type_register_static(&ich9_ehci_info);
|
||||
}
|
||||
|
||||
type_init(ehci_pci_register_types)
|
@ -27,31 +27,11 @@
|
||||
* 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"
|
||||
|
||||
#ifndef EHCI_DEBUG
|
||||
#define EHCI_DEBUG 0
|
||||
#endif
|
||||
|
||||
#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
|
||||
#define CAPA_SIZE 0x10
|
||||
|
||||
/* Capability Registers Base Address - section 2.2 */
|
||||
#define CAPLENGTH 0x0000 /* 1-byte, 0x0001 reserved */
|
||||
#define HCIVERSION 0x0002 /* 2-bytes, i/f version # */
|
||||
@ -103,9 +83,6 @@
|
||||
|
||||
#define CONFIGFLAG 0x0040
|
||||
|
||||
#define PORTSC 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,285 +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 {
|
||||
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;
|
||||
};
|
||||
|
||||
typedef struct EHCIPCIState {
|
||||
PCIDevice pcidev;
|
||||
EHCIState ehci;
|
||||
} EHCIPCIState;
|
||||
|
||||
#define SET_LAST_RUN_CLOCK(s) \
|
||||
(s)->last_run_ns = qemu_get_clock_ns(vm_clock);
|
||||
|
||||
@ -2559,8 +2256,6 @@ static const MemoryRegionOps ehci_mmio_port_ops = {
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static int usb_ehci_pci_initfn(PCIDevice *dev);
|
||||
|
||||
static USBPortOps ehci_port_ops = {
|
||||
.attach = ehci_attach,
|
||||
.detach = ehci_detach,
|
||||
@ -2619,7 +2314,7 @@ static void usb_ehci_vm_state_change(void *opaque, int running, RunState state)
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_ehci = {
|
||||
const VMStateDescription vmstate_ehci = {
|
||||
.name = "ehci-core",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 1,
|
||||
@ -2655,65 +2350,7 @@ static const VMStateDescription vmstate_ehci = {
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_ehci_pci = {
|
||||
.name = "ehci",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = usb_ehci_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState),
|
||||
VMSTATE_STRUCT(ehci, EHCIPCIState, 2, vmstate_ehci, EHCIState),
|
||||
}
|
||||
};
|
||||
|
||||
static Property ehci_pci_properties[] = {
|
||||
DEFINE_PROP_UINT32("maxframes", EHCIPCIState, ehci.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_pci_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_pci_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_pci_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_pci_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 void usb_ehci_initfn(EHCIState *s, DeviceState *dev)
|
||||
void usb_ehci_initfn(EHCIState *s, DeviceState *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -2760,63 +2397,6 @@ static void usb_ehci_initfn(EHCIState *s, DeviceState *dev)
|
||||
&s->mem_ports);
|
||||
}
|
||||
|
||||
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 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
320
hw/usb/hcd-ehci.h
Normal 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);
|
Loading…
Reference in New Issue
Block a user