Applied current state of porting EHCI from Qemu. No functional changes yet.

This commit is contained in:
Volker Ruppert 2016-02-09 17:11:59 +00:00
parent 60d5aaaacf
commit ce7afe050f
2 changed files with 837 additions and 22 deletions

View File

@ -45,6 +45,7 @@
#include "pci.h"
#include "usb_common.h"
#include "uhci_core.h"
#include "qemu-queue.h"
#include "usb_ehci.h"
#define LOG_THIS theUSB_EHCI->
@ -67,6 +68,41 @@ bx_usb_ehci_c* theUSB_EHCI = NULL;
#define FRAME_TIMER_FREQ 1000
#define FRAME_TIMER_USEC (1000000 / FRAME_TIMER_FREQ)
#define MAX_QH 100 // Max allowable queue heads in a chain
#define MIN_FR_PER_TICK 3 // Min frames to process when catching up
/* Internal periodic / asynchronous schedule state machine states
*/
typedef enum {
EST_INACTIVE = 1000,
EST_ACTIVE,
EST_EXECUTING,
EST_SLEEPING,
/* The following states are internal to the state machine function
*/
EST_WAITLISTHEAD,
EST_FETCHENTRY,
EST_FETCHQH,
EST_FETCHITD,
EST_FETCHSITD,
EST_ADVANCEQUEUE,
EST_FETCHQTD,
EST_EXECUTE,
EST_WRITEBACK,
EST_HORIZONTALQH
} EHCI_STATES;
/* macros for accessing fields within next link pointer entry */
#define NLPTR_GET(x) ((x) & 0xffffffe0)
#define NLPTR_TYPE_GET(x) (((x) >> 1) & 3)
#define NLPTR_TBIT(x) ((x) & 1) // 1=invalid, 0=valid
/* link pointer types */
#define NLPTR_TYPE_ITD 0 // isoc xfer descriptor
#define NLPTR_TYPE_QH 1 // queue head
#define NLPTR_TYPE_STITD 2 // split xaction, isoc xfer descriptor
#define NLPTR_TYPE_FSTN 3 // frame span traversal node
// builtin configuration handling functions
Bit32s usb_ehci_options_parser(const char *context, int num_params, char *params[])
@ -233,6 +269,7 @@ void bx_usb_ehci_c::init(void)
// register handler for correct device connect handling after runtime config
BX_EHCI_THIS rt_conf_id = SIM->register_runtime_config_handler(BX_EHCI_THIS_PTR, runtime_config_handler);
BX_EHCI_THIS device_change = 0;
BX_EHCI_THIS maxframes = 128;
BX_INFO(("USB EHCI initialized"));
}
@ -315,7 +352,10 @@ void bx_usb_ehci_c::register_state(void)
hub = new bx_list_c(list, "hub");
BXRS_DEC_PARAM_FIELD(hub, usbsts_pending, BX_EHCI_THIS hub.usbsts_pending);
BXRS_DEC_PARAM_FIELD(hub, usbsts_frindex, BX_EHCI_THIS hub.usbsts_frindex);
BXRS_DEC_PARAM_FIELD(hub, pstate, BX_EHCI_THIS hub.pstate);
BXRS_DEC_PARAM_FIELD(hub, astate, BX_EHCI_THIS hub.astate);
BXRS_DEC_PARAM_FIELD(hub, last_run_usec, BX_EHCI_THIS hub.last_run_usec);
BXRS_DEC_PARAM_FIELD(hub, async_stepdown, BX_EHCI_THIS hub.async_stepdown);
op_regs = new bx_list_c(hub, "op_regs");
reg = new bx_list_c(op_regs, "UsbCmd");
BXRS_HEX_PARAM_FIELD(reg, itc, BX_EHCI_THIS hub.op_regs.UsbCmd.itc);
@ -422,6 +462,8 @@ void bx_usb_ehci_c::reset_hc()
BX_EHCI_THIS hub.usbsts_pending = 0;
BX_EHCI_THIS hub.usbsts_frindex = 0;
BX_EHCI_THIS hub.astate = EST_INACTIVE;
BX_EHCI_THIS hub.pstate = EST_INACTIVE;
BX_EHCI_THIS update_irq();
}
@ -584,18 +626,6 @@ void bx_usb_ehci_c::change_port_owner(int port)
}
}
int bx_usb_ehci_c::broadcast_packet(USBPacket *p, const int port)
{
int ret = USB_RET_NODEV;
if ((BX_EHCI_THIS hub.usb_port[port].device != NULL) &&
!BX_EHCI_THIS hub.usb_port[port].portsc.po &&
!BX_EHCI_THIS hub.usb_port[port].portsc.sus) {
ret = BX_EHCI_THIS hub.usb_port[port].device->handle_packet(p);
}
return ret;
}
bx_bool bx_usb_ehci_c::read_handler(bx_phy_address addr, unsigned len, void *data, void *param)
{
Bit32u val = 0, val_hi = 0;
@ -731,12 +761,13 @@ bx_bool bx_usb_ehci_c::write_handler(bx_phy_address addr, unsigned len, void *da
BX_EHCI_THIS hub.op_regs.UsbCmd.itc = (value >> 16) & 0x7f;
BX_EHCI_THIS hub.op_regs.UsbCmd.iaad = (value >> 6) & 1;
BX_EHCI_THIS hub.op_regs.UsbCmd.ase = (value >> 5) & 1;
BX_EHCI_THIS hub.op_regs.UsbCmd.pse = (value >> 4) & 1;
BX_EHCI_THIS hub.op_regs.UsbCmd.hcreset = (value >> 1) & 1;
BX_EHCI_THIS hub.op_regs.UsbCmd.rs = (value & 1);
if (!BX_EHCI_THIS hub.op_regs.UsbCmd.pse) {
BX_EHCI_THIS hub.last_run_usec = bx_pc_system.time_usec();
if (BX_EHCI_THIS hub.op_regs.UsbCmd.iaad) {
BX_EHCI_THIS hub.async_stepdown = 0;
// TODO
}
BX_EHCI_THIS hub.op_regs.UsbCmd.pse = (value >> 4) & 1;
if (BX_EHCI_THIS hub.op_regs.UsbCmd.hcreset) {
BX_EHCI_THIS reset_hc();
BX_EHCI_THIS hub.op_regs.UsbCmd.hcreset = 0;
@ -862,6 +893,490 @@ void bx_usb_ehci_c::commit_irq(void)
BX_EHCI_THIS update_irq();
}
void bx_usb_ehci_c::update_halt(void)
{
if (BX_EHCI_THIS hub.op_regs.UsbCmd.rs) {
BX_EHCI_THIS hub.op_regs.UsbSts.hchalted = 0;
} else {
if (BX_EHCI_THIS hub.astate == EST_INACTIVE && BX_EHCI_THIS hub.pstate == EST_INACTIVE) {
BX_EHCI_THIS hub.op_regs.UsbSts.hchalted = 1;
}
}
}
void bx_usb_ehci_c::set_state(int async, int state)
{
if (async) {
BX_EHCI_THIS hub.astate = state;
if (BX_EHCI_THIS hub.astate == EST_INACTIVE) {
BX_EHCI_THIS hub.op_regs.UsbSts.ass = 0;
BX_EHCI_THIS update_halt();
} else {
BX_EHCI_THIS hub.op_regs.UsbSts.ass = 1;
}
} else {
BX_EHCI_THIS hub.pstate = state;
if (BX_EHCI_THIS hub.pstate == EST_INACTIVE) {
BX_EHCI_THIS hub.op_regs.UsbSts.pss = 0;
BX_EHCI_THIS update_halt();
} else {
BX_EHCI_THIS hub.op_regs.UsbSts.pss = 1;
}
}
}
int bx_usb_ehci_c::get_state(int async)
{
return async ? BX_EHCI_THIS hub.astate : BX_EHCI_THIS hub.pstate;
}
void bx_usb_ehci_c::set_fetch_addr(int async, Bit32u addr)
{
if (async) {
BX_EHCI_THIS hub.a_fetch_addr = addr;
} else {
BX_EHCI_THIS hub.p_fetch_addr = addr;
}
}
int bx_usb_ehci_c::get_fetch_addr(int async)
{
// TODO
return 0;
}
EHCIPacket *bx_usb_ehci_c::alloc_packet(EHCIQueue *q)
{
// TODO
return NULL;
}
void bx_usb_ehci_c::free_packet(EHCIPacket *p)
{
// TODO
}
EHCIQueue *bx_usb_ehci_c::alloc_queue(Bit32u addr, int async)
{
// TODO
return NULL;
}
int bx_usb_ehci_c::cancel_queue(EHCIQueue *q)
{
EHCIPacket *p;
int packets = 0;
p = QTAILQ_FIRST(&q->packets);
if (p == NULL) {
return 0;
}
do {
free_packet(p);
packets++;
} while ((p = QTAILQ_FIRST(&q->packets)) != NULL);
return packets;
}
int bx_usb_ehci_c::reset_queue(EHCIQueue *q)
{
// TODO
return 0;
}
void bx_usb_ehci_c::free_queue(EHCIQueue *q, const char *warn)
{
EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
int cancelled;
cancelled = BX_EHCI_THIS cancel_queue(q);
if (warn && cancelled > 0) {
// ehci_trace_guest_bug(q->ehci, warn);
}
QTAILQ_REMOVE(head, q, next);
free(q);
}
EHCIQueue *bx_usb_ehci_c::find_queue_by_qh(Bit32u addr, int async)
{
// TODO
return NULL;
}
void bx_usb_ehci_c::queues_rip_unused(int async)
{
EHCIQueueHead *head = async ? &BX_EHCI_THIS hub.aqueues : &BX_EHCI_THIS hub.pqueues;
const char *warn = async ? "guest unlinked busy QH" : NULL;
Bit64u maxage = FRAME_TIMER_USEC * BX_EHCI_THIS maxframes * 4;
EHCIQueue *q, *tmp;
QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
if (q->seen) {
q->seen = 0;
q->ts = BX_EHCI_THIS hub.last_run_usec;
continue;
}
if (BX_EHCI_THIS hub.last_run_usec < q->ts + maxage) {
continue;
}
BX_EHCI_THIS free_queue(q, warn);
}
}
void bx_usb_ehci_c::queues_rip_unseen(int async)
{
// TODO
}
void bx_usb_ehci_c::queues_rip_device(usb_device_c *dev, int async)
{
// TODO
}
void bx_usb_ehci_c::queues_rip_all(int async)
{
// TODO
}
usb_device_c *bx_usb_ehci_c::find_device(Bit8u addr)
{
// TODO
return NULL;
}
void bx_usb_ehci_c::flush_qh(EHCIQueue *q)
{
// TODO
}
int bx_usb_ehci_c::qh_do_overlay(EHCIQueue *q)
{
// TODO
return 0;
}
int bx_usb_ehci_c::init_transfer(EHCIPacket *p)
{
// TODO
return 0;
}
void bx_usb_ehci_c::finish_transfer(EHCIQueue *q, int status)
{
// TODO
}
void bx_usb_ehci_c::async_complete_packet(USBPacket *packet)
{
// TODO
}
void bx_usb_ehci_c::execute_complete(EHCIQueue *q)
{
// TODO
}
int bx_usb_ehci_c::execute(EHCIPacket *p, const char *action)
{
// TODO
return 0;
}
int bx_usb_ehci_c::process_itd(EHCIitd *itd, Bit32u addr)
{
// TODO
return 0;
}
int bx_usb_ehci_c::state_waitlisthead(int async)
{
EHCIqh qh;
int i = 0;
int again = 0;
Bit32u entry = BX_EHCI_THIS hub.op_regs.AsyncListAddr;
/* set reclamation flag at start event (4.8.6) */
if (async) {
BX_EHCI_THIS hub.op_regs.UsbSts.recl = 1;
}
BX_EHCI_THIS queues_rip_unused(async);
/* Find the head of the list (4.9.1.1) */
for (i = 0; i < MAX_QH; i++) {
// TODO
// get_dwords(NLPTR_GET(entry), (Bit32u *) &qh,
// sizeof(EHCIqh) >> 2);
if (qh.epchar & QH_EPCHAR_H) {
if (async) {
entry |= (NLPTR_TYPE_QH << 1);
}
BX_EHCI_THIS set_fetch_addr(async, entry);
BX_EHCI_THIS set_state(async, EST_FETCHENTRY);
again = 1;
goto out;
}
entry = qh.next;
if (entry == BX_EHCI_THIS hub.op_regs.AsyncListAddr) {
break;
}
}
/* no head found for list. */
BX_EHCI_THIS set_state(async, EST_ACTIVE);
out:
return again;
}
int bx_usb_ehci_c::state_fetchentry(int async)
{
// TODO
return 0;
}
EHCIQueue *bx_usb_ehci_c::state_fetchqh(int async)
{
// TODO
return NULL;
}
int bx_usb_ehci_c::state_fetchitd(int async)
{
// TODO
return 0;
}
int bx_usb_ehci_c::state_fetchsitd(int async)
{
// TODO
return 0;
}
int bx_usb_ehci_c::state_advqueue(EHCIQueue *q)
{
// TODO
return 0;
}
int bx_usb_ehci_c::state_fetchqtd(EHCIQueue *q)
{
// TODO
return 0;
}
int bx_usb_ehci_c::state_horizqh(EHCIQueue *q)
{
// TODO
return 0;
}
int bx_usb_ehci_c::fill_queue(EHCIPacket *p)
{
// TODO
return 0;
}
int bx_usb_ehci_c::state_execute(EHCIQueue *q)
{
// TODO
return 0;
}
int bx_usb_ehci_c::state_executing(EHCIQueue *q)
{
// TODO
return 0;
}
int bx_usb_ehci_c::state_writeback(EHCIQueue *q)
{
// TODO
return 0;
}
void bx_usb_ehci_c::advance_state(int async)
{
EHCIQueue *q = NULL;
int again;
do {
switch (BX_EHCI_THIS get_state(async)) {
case EST_WAITLISTHEAD:
again = BX_EHCI_THIS state_waitlisthead(async);
break;
case EST_FETCHENTRY:
again = BX_EHCI_THIS state_fetchentry(async);
break;
case EST_FETCHQH:
q = BX_EHCI_THIS state_fetchqh(async);
if (q != NULL) {
assert(q->async == async);
again = 1;
} else {
again = 0;
}
break;
case EST_FETCHITD:
again = BX_EHCI_THIS state_fetchitd(async);
break;
case EST_FETCHSITD:
again = BX_EHCI_THIS state_fetchsitd(async);
break;
case EST_ADVANCEQUEUE:
again = BX_EHCI_THIS state_advqueue(q);
break;
case EST_FETCHQTD:
again = BX_EHCI_THIS state_fetchqtd(q);
break;
case EST_HORIZONTALQH:
again = BX_EHCI_THIS state_horizqh(q);
break;
case EST_EXECUTE:
again = BX_EHCI_THIS state_execute(q);
if (async) {
BX_EHCI_THIS hub.async_stepdown = 0;
}
break;
case EST_EXECUTING:
assert(q != NULL);
if (async) {
BX_EHCI_THIS hub.async_stepdown = 0;
}
again = BX_EHCI_THIS state_executing(q);
break;
case EST_WRITEBACK:
assert(q != NULL);
again = BX_EHCI_THIS state_writeback(q);
break;
default:
BX_ERROR(("Bad state!"));
again = -1;
break;
}
if (again < 0) {
BX_ERROR(("processing error - resetting ehci HC"));
BX_EHCI_THIS reset_hc();
again = 0;
}
} while (again);
}
void bx_usb_ehci_c::advance_async_state(void)
{
const int async = 1;
switch (BX_EHCI_THIS get_state(async)) {
case EST_INACTIVE:
if (!BX_EHCI_THIS hub.op_regs.UsbCmd.ase) {
break;
}
BX_EHCI_THIS set_state(async, EST_ACTIVE);
// No break, fall through to ACTIVE
case EST_ACTIVE:
if (!BX_EHCI_THIS hub.op_regs.UsbCmd.ase) {
BX_EHCI_THIS queues_rip_all(async);
BX_EHCI_THIS set_state(async, EST_INACTIVE);
break;
}
/* make sure guest has acknowledged the doorbell interrupt */
/* TO-DO: is this really needed? */
if (BX_EHCI_THIS hub.op_regs.UsbSts.inti & USBSTS_IAA) {
BX_DEBUG(("IAA status bit still set."));
break;
}
/* check that address register has been set */
if (BX_EHCI_THIS hub.op_regs.AsyncListAddr == 0) {
break;
}
BX_EHCI_THIS set_state(async, EST_WAITLISTHEAD);
BX_EHCI_THIS advance_state(async);
/* If the doorbell is set, the guest wants to make a change to the
* schedule. The host controller needs to release cached data.
* (section 4.8.2)
*/
if (BX_EHCI_THIS hub.op_regs.UsbCmd.iaad) {
/* Remove all unseen qhs from the async qhs queue */
BX_EHCI_THIS queues_rip_unseen(async);
BX_EHCI_THIS hub.op_regs.UsbCmd.iaad = 0;
BX_EHCI_THIS raise_irq(USBSTS_IAA);
}
break;
default:
/* this should only be due to a developer mistake */
BX_PANIC(("Bad asynchronous state %d. Resetting to active", BX_EHCI_THIS hub.astate));
}
}
void bx_usb_ehci_c::advance_periodic_state(void)
{
Bit32u entry;
Bit32u list;
const int async = 0;
// 4.6
switch (BX_EHCI_THIS get_state(async)) {
case EST_INACTIVE:
if (!(BX_EHCI_THIS hub.op_regs.FrIndex & 7) && BX_EHCI_THIS hub.op_regs.UsbCmd.pse) {
BX_EHCI_THIS set_state(async, EST_ACTIVE);
// No break, fall through to ACTIVE
} else
break;
case EST_ACTIVE:
if (!(BX_EHCI_THIS hub.op_regs.FrIndex & 7) && !BX_EHCI_THIS hub.op_regs.UsbCmd.pse) {
BX_EHCI_THIS queues_rip_all(async);
BX_EHCI_THIS set_state(async, EST_INACTIVE);
break;
}
list = BX_EHCI_THIS hub.op_regs.PeriodicListBase & 0xfffff000;
/* check that register has been set */
if (list == 0) {
break;
}
list |= ((BX_EHCI_THIS hub.op_regs.FrIndex & 0x1ff8) >> 1);
DEV_MEM_READ_PHYSICAL(list, 4, (Bit8u*)&entry);
BX_DEBUG(("PERIODIC state adv fr=%d. [%08X] -> %08X",
BX_EHCI_THIS hub.op_regs.FrIndex / 8, list, entry));
BX_EHCI_THIS set_fetch_addr(async, entry);
BX_EHCI_THIS set_state(async, EST_FETCHENTRY);
BX_EHCI_THIS advance_state(async);
BX_EHCI_THIS queues_rip_unused(async);
break;
default:
/* this should only be due to a developer mistake */
BX_PANIC(("Bad periodic state %d. Resetting to active", BX_EHCI_THIS hub.pstate));
}
}
void bx_usb_ehci_c::update_frindex(int frames)
{
int i;
@ -889,6 +1404,11 @@ void bx_usb_ehci_c::update_frindex(int frames)
}
}
void bx_usb_ehci_c::async_bh(void)
{
// TODO
}
void bx_usb_ehci_c::ehci_frame_handler(void *this_ptr)
{
bx_usb_ehci_c *class_ptr = (bx_usb_ehci_c *) this_ptr;
@ -900,15 +1420,51 @@ void bx_usb_ehci_c::ehci_frame_timer(void)
{
Bit64u t_now;
Bit64u usec_elapsed;
int frames;
int frames, skipped_frames;
int i;
t_now = bx_pc_system.time_usec();
usec_elapsed = t_now - BX_EHCI_THIS hub.last_run_usec;
frames = usec_elapsed / FRAME_TIMER_USEC;
BX_EHCI_THIS update_frindex(frames);
BX_EHCI_THIS hub.last_run_usec += frames;
// TODO
if (BX_EHCI_THIS hub.op_regs.UsbCmd.rs || (BX_EHCI_THIS hub.pstate != EST_INACTIVE)) {
BX_EHCI_THIS hub.async_stepdown = 0;
if (frames > (int)BX_EHCI_THIS maxframes) {
skipped_frames = frames - BX_EHCI_THIS maxframes;
BX_EHCI_THIS update_frindex(skipped_frames);
BX_EHCI_THIS hub.last_run_usec += FRAME_TIMER_USEC * skipped_frames;
frames -= skipped_frames;
BX_DEBUG(("WARNING - EHCI skipped %d frames", skipped_frames));
}
for (i = 0; i < frames; i++) {
if (i >= MIN_FR_PER_TICK) {
BX_EHCI_THIS commit_irq();
if ((BX_EHCI_THIS hub.op_regs.UsbSts.inti & BX_EHCI_THIS hub.op_regs.UsbIntr) > 0){
break;
}
}
BX_EHCI_THIS update_frindex(1);
BX_EHCI_THIS advance_periodic_state();
BX_EHCI_THIS hub.last_run_usec += FRAME_TIMER_USEC;
}
} else {
if (BX_EHCI_THIS hub.async_stepdown < BX_EHCI_THIS maxframes / 2) {
BX_EHCI_THIS hub.async_stepdown++;
}
BX_EHCI_THIS update_frindex(frames);
BX_EHCI_THIS hub.last_run_usec += FRAME_TIMER_USEC * frames;
}
if (BX_EHCI_THIS hub.op_regs.UsbCmd.rs || (BX_EHCI_THIS hub.astate != EST_INACTIVE)) {
BX_EHCI_THIS advance_async_state();
}
BX_EHCI_THIS commit_irq();
if (BX_EHCI_THIS hub.usbsts_pending) {
BX_EHCI_THIS hub.async_stepdown = 0;
}
}
void bx_usb_ehci_c::runtime_config_handler(void *this_ptr)

View File

@ -46,12 +46,184 @@
#define USB_EHCI_PORTS 6
/* EHCI spec version 1.0 Section 3.3
*/
typedef struct EHCIitd {
Bit32u next;
Bit32u 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
Bit32u 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 {
Bit32u next; // Standard next link pointer
Bit32u 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
Bit32u uframe;
#define SITD_UFRAME_CMASK_MASK 0x0000ff00
#define SITD_UFRAME_CMASK_SH 8
#define SITD_UFRAME_SMASK_MASK 0x000000ff
Bit32u 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)
Bit32u 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
Bit32u backptr; // Standard next link pointer
} EHCIsitd;
/* EHCI spec version 1.0 Section 3.5
*/
typedef struct EHCIqtd {
Bit32u next; // Standard next link pointer
Bit32u altnext; // Standard next link pointer
Bit32u 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)
Bit32u 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 {
Bit32u next; // Standard next link pointer
/* endpoint characteristics */
Bit32u 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 */
Bit32u 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
Bit32u current_qtd; // Standard next link pointer
Bit32u next_qtd; // Standard next link pointer
Bit32u altnext_qtd;
#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e
#define QH_ALTNEXT_NAKCNT_SH 1
Bit32u token; // Same as QTD token
Bit32u 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;
typedef struct EHCIPacket EHCIPacket;
typedef struct EHCIQueue EHCIQueue;
typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
typedef struct {
int frame_timer_index;
Bit8u usbsts_pending;
Bit32u usbsts_frindex;
EHCIQueueHead aqueues;
EHCIQueueHead pqueues;
Bit32u pstate, astate;
/* which address to look at next */
Bit32u a_fetch_addr;
Bit32u p_fetch_addr;
Bit64u last_run_usec;
Bit32u async_stepdown;
struct {
Bit8u CapLength;
@ -112,6 +284,45 @@ typedef struct {
} bx_usb_ehci_t;
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) */
Bit32u qtdaddr; /* address QTD read from */
USBPacket packet;
// QEMUSGList sgl;
int pid;
Bit32u tbytes;
enum async_state async;
int usb_status;
};
struct EHCIQueue {
bx_usb_ehci_t *ehci;
QTAILQ_ENTRY(EHCIQueue) next;
Bit32u seen;
Bit64u ts;
int async;
/* 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) */
Bit32u qhaddr; /* address QH read from */
Bit32u qtdaddr; /* address QTD read from */
usb_device_c *dev;
QTAILQ_HEAD(, EHCIPacket) packets;
};
class bx_usb_ehci_c : public bx_devmodel_c, public bx_pci_device_stub_c {
public:
bx_usb_ehci_c();
@ -133,21 +344,69 @@ private:
Bit8u device_change;
int rt_conf_id;
Bit8u *device_buffer;
Bit32u maxframes;
void reset_hc();
void reset_hc(void);
void reset_port(int);
static void init_device(Bit8u port, bx_list_c *portconf);
static void remove_device(Bit8u port);
static void set_connect_status(Bit8u port, int type, bx_bool connected);
static void change_port_owner(int port);
static int broadcast_packet(USBPacket *p, const int port);
// methods ported from QEMU EHCI
void update_irq(void);
void raise_irq(Bit8u intr);
void commit_irq(void);
void update_frindex(int frames);
void update_halt(void);
void set_state(int async, int state);
int get_state(int async);
void set_fetch_addr(int async, Bit32u addr);
int get_fetch_addr(int async);
EHCIPacket *alloc_packet(EHCIQueue *q);
void free_packet(EHCIPacket *p);
EHCIQueue *alloc_queue(Bit32u addr, int async);
int cancel_queue(EHCIQueue *q);
int reset_queue(EHCIQueue *q);
void free_queue(EHCIQueue *q, const char *warn);
EHCIQueue *find_queue_by_qh(Bit32u addr, int async);
void queues_rip_unused(int async);
void queues_rip_unseen(int async);
void queues_rip_device(usb_device_c *dev, int async);
void queues_rip_all(int async);
usb_device_c *find_device(Bit8u addr);
void flush_qh(EHCIQueue *q);
int qh_do_overlay(EHCIQueue *q);
int init_transfer(EHCIPacket *p);
void finish_transfer(EHCIQueue *q, int status);
void async_complete_packet(USBPacket *packet);
void execute_complete(EHCIQueue *q);
int execute(EHCIPacket *p, const char *action);
int process_itd(EHCIitd *itd, Bit32u addr);
int state_waitlisthead(int async);
int state_fetchentry(int async);
EHCIQueue *state_fetchqh(int async);
int state_fetchitd(int async);
int state_fetchsitd(int async);
int state_advqueue(EHCIQueue *q);
int state_fetchqtd(EHCIQueue *q);
int state_horizqh(EHCIQueue *q);
int fill_queue(EHCIPacket *p);
int state_execute(EHCIQueue *q);
int state_executing(EHCIQueue *q);
int state_writeback(EHCIQueue *q);
void advance_state(int async);
void advance_async_state(void);
void advance_periodic_state(void);
void update_frindex(int frames);
void async_bh(void);
// EHCI frame timer
static void ehci_frame_handler(void *);
void ehci_frame_timer(void);