usb-ehci: trace state machine changes

Add functions to get and set the current state of the state machine,
add tracepoints there to trace state transitions.  Add support for
traceing the queue heads and transfer descriptors as we look at them.

Drop a few DPRINTFs and all DPRINTF_ST lines, they are obsolete now.

No change in behavior.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Gerd Hoffmann 2011-05-18 14:23:35 +02:00
parent 439a97ccab
commit 26d53979db
2 changed files with 175 additions and 138 deletions

View file

@ -33,20 +33,13 @@
#include "trace.h" #include "trace.h"
#define EHCI_DEBUG 0 #define EHCI_DEBUG 0
#define STATE_DEBUG 0 /* state transitions */
#if EHCI_DEBUG || STATE_DEBUG #if EHCI_DEBUG
#define DPRINTF printf #define DPRINTF printf
#else #else
#define DPRINTF(...) #define DPRINTF(...)
#endif #endif
#if STATE_DEBUG
#define DPRINTF_ST DPRINTF
#else
#define DPRINTF_ST(...)
#endif
/* internal processing - reset HC to try and recover */ /* internal processing - reset HC to try and recover */
#define USB_RET_PROCERR (-99) #define USB_RET_PROCERR (-99)
@ -417,31 +410,57 @@ typedef struct {
*data = val; \ *data = val; \
} while(0) } while(0)
static const char *ehci_state_names[] = {
[ EST_INACTIVE ] = "INACTIVE",
[ EST_ACTIVE ] = "ACTIVE",
[ EST_EXECUTING ] = "EXECUTING",
[ EST_SLEEPING ] = "SLEEPING",
[ EST_WAITLISTHEAD ] = "WAITLISTHEAD",
[ EST_FETCHENTRY ] = "FETCH ENTRY",
[ EST_FETCHQH ] = "FETCH QH",
[ EST_FETCHITD ] = "FETCH ITD",
[ EST_ADVANCEQUEUE ] = "ADVANCEQUEUE",
[ EST_FETCHQTD ] = "FETCH QTD",
[ EST_EXECUTE ] = "EXECUTE",
[ EST_WRITEBACK ] = "WRITEBACK",
[ EST_HORIZONTALQH ] = "HORIZONTALQH",
};
static const char *ehci_mmio_names[] = {
[ CAPLENGTH ] = "CAPLENGTH",
[ HCIVERSION ] = "HCIVERSION",
[ HCSPARAMS ] = "HCSPARAMS",
[ HCCPARAMS ] = "HCCPARAMS",
[ USBCMD ] = "USBCMD",
[ USBSTS ] = "USBSTS",
[ USBINTR ] = "USBINTR",
[ FRINDEX ] = "FRINDEX",
[ PERIODICLISTBASE ] = "P-LIST BASE",
[ ASYNCLISTADDR ] = "A-LIST ADDR",
[ PORTSC_BEGIN ] = "PORTSC #0",
[ PORTSC_BEGIN + 4] = "PORTSC #1",
[ PORTSC_BEGIN + 8] = "PORTSC #2",
[ PORTSC_BEGIN + 12] = "PORTSC #3",
[ CONFIGFLAG ] = "CONFIGFLAG",
};
static const char *nr2str(const char **n, size_t len, uint32_t nr)
{
if (nr < len && n[nr] != NULL) {
return n[nr];
} else {
return "unknown";
}
}
static const char *state2str(uint32_t state)
{
return nr2str(ehci_state_names, ARRAY_SIZE(ehci_state_names), state);
}
static const char *addr2str(target_phys_addr_t addr) static const char *addr2str(target_phys_addr_t addr)
{ {
const char *r = "unknown"; return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr);
const char *n[] = {
[ CAPLENGTH ] = "CAPLENGTH",
[ HCIVERSION ] = "HCIVERSION",
[ HCSPARAMS ] = "HCSPARAMS",
[ HCCPARAMS ] = "HCCPARAMS",
[ USBCMD ] = "USBCMD",
[ USBSTS ] = "USBSTS",
[ USBINTR ] = "USBINTR",
[ FRINDEX ] = "FRINDEX",
[ PERIODICLISTBASE ] = "P-LIST BASE",
[ ASYNCLISTADDR ] = "A-LIST ADDR",
[ PORTSC_BEGIN ...
PORTSC_END ] = "PORTSC",
[ CONFIGFLAG ] = "CONFIGFLAG",
};
if (addr < ARRAY_SIZE(n) && n[addr] != NULL) {
return n[addr];
} else {
return r;
}
} }
static void ehci_trace_usbsts(uint32_t mask, int state) static void ehci_trace_usbsts(uint32_t mask, int state)
@ -528,6 +547,56 @@ static inline void ehci_commit_interrupt(EHCIState *s)
s->usbsts_pending = 0; s->usbsts_pending = 0;
} }
static void ehci_set_state(EHCIState *s, int async, int state)
{
if (async) {
trace_usb_ehci_state("async", state2str(state));
s->astate = state;
} else {
trace_usb_ehci_state("periodic", state2str(state));
s->pstate = state;
}
}
static int ehci_get_state(EHCIState *s, int async)
{
return async ? s->astate : s->pstate;
}
static void ehci_trace_qh(EHCIState *s, target_phys_addr_t addr, EHCIqh *qh)
{
trace_usb_ehci_qh(addr, qh->next,
qh->current_qtd, qh->next_qtd, qh->altnext_qtd,
get_field(qh->epchar, QH_EPCHAR_RL),
get_field(qh->epchar, QH_EPCHAR_MPLEN),
get_field(qh->epchar, QH_EPCHAR_EPS),
get_field(qh->epchar, QH_EPCHAR_EP),
get_field(qh->epchar, QH_EPCHAR_DEVADDR),
(bool)(qh->epchar & QH_EPCHAR_C),
(bool)(qh->epchar & QH_EPCHAR_H),
(bool)(qh->epchar & QH_EPCHAR_DTC),
(bool)(qh->epchar & QH_EPCHAR_I));
}
static void ehci_trace_qtd(EHCIState *s, target_phys_addr_t addr, EHCIqtd *qtd)
{
trace_usb_ehci_qtd(addr, qtd->next, qtd->altnext,
get_field(qtd->token, QTD_TOKEN_TBYTES),
get_field(qtd->token, QTD_TOKEN_CPAGE),
get_field(qtd->token, QTD_TOKEN_CERR),
get_field(qtd->token, QTD_TOKEN_PID),
(bool)(qtd->token & QTD_TOKEN_IOC),
(bool)(qtd->token & QTD_TOKEN_ACTIVE),
(bool)(qtd->token & QTD_TOKEN_HALT),
(bool)(qtd->token & QTD_TOKEN_BABBLE),
(bool)(qtd->token & QTD_TOKEN_XACTERR));
}
static void ehci_trace_itd(EHCIState *s, target_phys_addr_t addr, EHCIitd *itd)
{
trace_usb_ehci_itd(addr, itd->next);
}
/* Attach or detach a device on root hub */ /* Attach or detach a device on root hub */
static void ehci_attach(USBPort *port) static void ehci_attach(USBPort *port)
@ -1230,7 +1299,7 @@ static int ehci_process_itd(EHCIState *ehci,
/* This state is the entry point for asynchronous schedule /* This state is the entry point for asynchronous schedule
* processing. Entry here consitutes a EHCI start event state (4.8.5) * processing. Entry here consitutes a EHCI start event state (4.8.5)
*/ */
static int ehci_state_waitlisthead(EHCIState *ehci, int async, int *state) static int ehci_state_waitlisthead(EHCIState *ehci, int async)
{ {
EHCIqh *qh = &ehci->qh; EHCIqh *qh = &ehci->qh;
int i = 0; int i = 0;
@ -1245,32 +1314,28 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async, int *state)
/* Find the head of the list (4.9.1.1) */ /* Find the head of the list (4.9.1.1) */
for(i = 0; i < MAX_QH; i++) { for(i = 0; i < MAX_QH; i++) {
get_dwords(NLPTR_GET(entry), (uint32_t *) qh, sizeof(EHCIqh) >> 2); get_dwords(NLPTR_GET(entry), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
ehci_trace_qh(ehci, NLPTR_GET(entry), qh);
if (qh->epchar & QH_EPCHAR_H) { if (qh->epchar & QH_EPCHAR_H) {
DPRINTF_ST("WAITLISTHEAD: QH %08X is the HEAD of the list\n",
entry);
if (async) { if (async) {
entry |= (NLPTR_TYPE_QH << 1); entry |= (NLPTR_TYPE_QH << 1);
} }
ehci->fetch_addr = entry; ehci->fetch_addr = entry;
*state = EST_FETCHENTRY; ehci_set_state(ehci, async, EST_FETCHENTRY);
again = 1; again = 1;
goto out; goto out;
} }
DPRINTF_ST("WAITLISTHEAD: QH %08X is NOT the HEAD of the list\n",
entry);
entry = qh->next; entry = qh->next;
if (entry == ehci->asynclistaddr) { if (entry == ehci->asynclistaddr) {
DPRINTF("WAITLISTHEAD: reached beginning of QH list\n");
break; break;
} }
} }
/* no head found for list. */ /* no head found for list. */
*state = EST_ACTIVE; ehci_set_state(ehci, async, EST_ACTIVE);
out: out:
return again; return again;
@ -1280,7 +1345,7 @@ out:
/* This state is the entry point for periodic schedule processing as /* This state is the entry point for periodic schedule processing as
* well as being a continuation state for async processing. * well as being a continuation state for async processing.
*/ */
static int ehci_state_fetchentry(EHCIState *ehci, int async, int *state) static int ehci_state_fetchentry(EHCIState *ehci, int async)
{ {
int again = 0; int again = 0;
uint32_t entry = ehci->fetch_addr; uint32_t entry = ehci->fetch_addr;
@ -1298,7 +1363,7 @@ static int ehci_state_fetchentry(EHCIState *ehci, int async, int *state)
#endif #endif
if (entry < 0x1000) { if (entry < 0x1000) {
DPRINTF("fetchentry: entry invalid (0x%08x)\n", entry); DPRINTF("fetchentry: entry invalid (0x%08x)\n", entry);
*state = EST_ACTIVE; ehci_set_state(ehci, async, EST_ACTIVE);
goto out; goto out;
} }
@ -1310,15 +1375,13 @@ static int ehci_state_fetchentry(EHCIState *ehci, int async, int *state)
switch (NLPTR_TYPE_GET(entry)) { switch (NLPTR_TYPE_GET(entry)) {
case NLPTR_TYPE_QH: case NLPTR_TYPE_QH:
DPRINTF_ST("FETCHENTRY: entry %X is a Queue Head\n", entry); ehci_set_state(ehci, async, EST_FETCHQH);
*state = EST_FETCHQH;
ehci->qhaddr = entry; ehci->qhaddr = entry;
again = 1; again = 1;
break; break;
case NLPTR_TYPE_ITD: case NLPTR_TYPE_ITD:
DPRINTF_ST("FETCHENTRY: entry %X is an ITD\n", entry); ehci_set_state(ehci, async, EST_FETCHITD);
*state = EST_FETCHITD;
ehci->itdaddr = entry; ehci->itdaddr = entry;
again = 1; again = 1;
break; break;
@ -1334,13 +1397,14 @@ out:
return again; return again;
} }
static int ehci_state_fetchqh(EHCIState *ehci, int async, int *state) static int ehci_state_fetchqh(EHCIState *ehci, int async)
{ {
EHCIqh *qh = &ehci->qh; EHCIqh *qh = &ehci->qh;
int reload; int reload;
int again = 0; int again = 0;
get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2); get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
ehci_trace_qh(ehci, NLPTR_GET(ehci->qhaddr), qh);
if (async && (qh->epchar & QH_EPCHAR_H)) { if (async && (qh->epchar & QH_EPCHAR_H)) {
@ -1350,7 +1414,7 @@ static int ehci_state_fetchqh(EHCIState *ehci, int async, int *state)
} else { } else {
DPRINTF("FETCHQH: QH 0x%08x. H-bit set, reclamation status reset" DPRINTF("FETCHQH: QH 0x%08x. H-bit set, reclamation status reset"
" - done processing\n", ehci->qhaddr); " - done processing\n", ehci->qhaddr);
*state = EST_ACTIVE; ehci_set_state(ehci, async, EST_ACTIVE);
goto out; goto out;
} }
} }
@ -1368,25 +1432,21 @@ static int ehci_state_fetchqh(EHCIState *ehci, int async, int *state)
reload = get_field(qh->epchar, QH_EPCHAR_RL); reload = get_field(qh->epchar, QH_EPCHAR_RL);
if (reload) { if (reload) {
DPRINTF_ST("FETCHQH: reloading nakcnt to %d\n", reload);
set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT); set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
} }
if (qh->token & QTD_TOKEN_HALT) { if (qh->token & QTD_TOKEN_HALT) {
DPRINTF_ST("FETCHQH: QH Halted, go horizontal\n"); ehci_set_state(ehci, async, EST_HORIZONTALQH);
*state = EST_HORIZONTALQH;
again = 1; again = 1;
} else if ((qh->token & QTD_TOKEN_ACTIVE) && (qh->current_qtd > 0x1000)) { } else if ((qh->token & QTD_TOKEN_ACTIVE) && (qh->current_qtd > 0x1000)) {
DPRINTF_ST("FETCHQH: Active, !Halt, execute - fetch qTD\n");
ehci->qtdaddr = qh->current_qtd; ehci->qtdaddr = qh->current_qtd;
*state = EST_FETCHQTD; ehci_set_state(ehci, async, EST_FETCHQTD);
again = 1; again = 1;
} else { } else {
/* EHCI spec version 1.0 Section 4.10.2 */ /* EHCI spec version 1.0 Section 4.10.2 */
DPRINTF_ST("FETCHQH: !Active, !Halt, advance queue\n"); ehci_set_state(ehci, async, EST_ADVANCEQUEUE);
*state = EST_ADVANCEQUEUE;
again = 1; again = 1;
} }
@ -1394,14 +1454,13 @@ out:
return again; return again;
} }
static int ehci_state_fetchitd(EHCIState *ehci, int async, int *state) static int ehci_state_fetchitd(EHCIState *ehci, int async)
{ {
EHCIitd itd; EHCIitd itd;
get_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd, get_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd,
sizeof(EHCIitd) >> 2); sizeof(EHCIitd) >> 2);
DPRINTF_ST("FETCHITD: Fetched ITD at address %08X " "(next is %08X)\n", ehci_trace_itd(ehci, ehci->itdaddr, &itd);
ehci->itdaddr, itd.next);
if (ehci_process_itd(ehci, &itd) != 0) { if (ehci_process_itd(ehci, &itd) != 0) {
return -1; return -1;
@ -1410,13 +1469,13 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async, int *state)
put_dwords(NLPTR_GET(ehci->itdaddr), (uint32_t *) &itd, put_dwords(NLPTR_GET(ehci->itdaddr), (uint32_t *) &itd,
sizeof(EHCIitd) >> 2); sizeof(EHCIitd) >> 2);
ehci->fetch_addr = itd.next; ehci->fetch_addr = itd.next;
*state = EST_FETCHENTRY; ehci_set_state(ehci, async, EST_FETCHENTRY);
return 1; return 1;
} }
/* Section 4.10.2 - paragraph 3 */ /* Section 4.10.2 - paragraph 3 */
static int ehci_state_advqueue(EHCIState *ehci, int async, int *state) static int ehci_state_advqueue(EHCIState *ehci, int async)
{ {
#if 0 #if 0
/* TO-DO: 4.10.2 - paragraph 2 /* TO-DO: 4.10.2 - paragraph 2
@ -1424,7 +1483,7 @@ static int ehci_state_advqueue(EHCIState *ehci, int async, int *state)
* go to horizontal QH * go to horizontal QH
*/ */
if (I-bit set) { if (I-bit set) {
*state = EST_HORIZONTALQH; ehci_set_state(ehci, async, EST_HORIZONTALQH);
goto out; goto out;
} }
#endif #endif
@ -1435,71 +1494,63 @@ static int ehci_state_advqueue(EHCIState *ehci, int async, int *state)
if (((ehci->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) && if (((ehci->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
(ehci->qh.altnext_qtd > 0x1000) && (ehci->qh.altnext_qtd > 0x1000) &&
(NLPTR_TBIT(ehci->qh.altnext_qtd) == 0)) { (NLPTR_TBIT(ehci->qh.altnext_qtd) == 0)) {
DPRINTF_ST("ADVQUEUE: goto alt next qTD. "
"curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n",
ehci->qh.current_qtd, ehci->qh.altnext_qtd,
ehci->qh.next_qtd, ehci->qh.next);
ehci->qtdaddr = ehci->qh.altnext_qtd; ehci->qtdaddr = ehci->qh.altnext_qtd;
*state = EST_FETCHQTD; ehci_set_state(ehci, async, EST_FETCHQTD);
/* /*
* next qTD is valid * next qTD is valid
*/ */
} else if ((ehci->qh.next_qtd > 0x1000) && } else if ((ehci->qh.next_qtd > 0x1000) &&
(NLPTR_TBIT(ehci->qh.next_qtd) == 0)) { (NLPTR_TBIT(ehci->qh.next_qtd) == 0)) {
DPRINTF_ST("ADVQUEUE: next qTD. "
"curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n",
ehci->qh.current_qtd, ehci->qh.altnext_qtd,
ehci->qh.next_qtd, ehci->qh.next);
ehci->qtdaddr = ehci->qh.next_qtd; ehci->qtdaddr = ehci->qh.next_qtd;
*state = EST_FETCHQTD; ehci_set_state(ehci, async, EST_FETCHQTD);
/* /*
* no valid qTD, try next QH * no valid qTD, try next QH
*/ */
} else { } else {
DPRINTF_ST("ADVQUEUE: go to horizontal QH\n"); ehci_set_state(ehci, async, EST_HORIZONTALQH);
*state = EST_HORIZONTALQH;
} }
return 1; return 1;
} }
/* Section 4.10.2 - paragraph 4 */ /* Section 4.10.2 - paragraph 4 */
static int ehci_state_fetchqtd(EHCIState *ehci, int async, int *state) static int ehci_state_fetchqtd(EHCIState *ehci, int async)
{ {
EHCIqtd *qtd = &ehci->qtd; EHCIqtd *qtd = &ehci->qtd;
int again = 0; int again = 0;
get_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) qtd, sizeof(EHCIqtd) >> 2); get_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) qtd, sizeof(EHCIqtd) >> 2);
ehci_trace_qtd(ehci, NLPTR_GET(ehci->qtdaddr), qtd);
if (qtd->token & QTD_TOKEN_ACTIVE) { if (qtd->token & QTD_TOKEN_ACTIVE) {
*state = EST_EXECUTE; ehci_set_state(ehci, async, EST_EXECUTE);
again = 1; again = 1;
} else { } else {
*state = EST_HORIZONTALQH; ehci_set_state(ehci, async, EST_HORIZONTALQH);
again = 1; again = 1;
} }
return again; return again;
} }
static int ehci_state_horizqh(EHCIState *ehci, int async, int *state) static int ehci_state_horizqh(EHCIState *ehci, int async)
{ {
int again = 0; int again = 0;
if (ehci->fetch_addr != ehci->qh.next) { if (ehci->fetch_addr != ehci->qh.next) {
ehci->fetch_addr = ehci->qh.next; ehci->fetch_addr = ehci->qh.next;
*state = EST_FETCHENTRY; ehci_set_state(ehci, async, EST_FETCHENTRY);
again = 1; again = 1;
} else { } else {
*state = EST_ACTIVE; ehci_set_state(ehci, async, EST_ACTIVE);
} }
return again; return again;
} }
static int ehci_state_execute(EHCIState *ehci, int async, int *state) static int ehci_state_execute(EHCIState *ehci, int async)
{ {
EHCIqh *qh = &ehci->qh; EHCIqh *qh = &ehci->qh;
EHCIqtd *qtd = &ehci->qtd; EHCIqtd *qtd = &ehci->qtd;
@ -1507,13 +1558,6 @@ static int ehci_state_execute(EHCIState *ehci, int async, int *state)
int reload, nakcnt; int reload, nakcnt;
int smask; int smask;
if (async) {
DPRINTF_ST(">>>>> ASYNC STATE MACHINE execute QH 0x%08x, QTD 0x%08x\n",
ehci->qhaddr, ehci->qtdaddr);
} else {
DPRINTF_ST(">>>>> PERIODIC STATE MACHINE execute\n");
}
if (ehci_qh_do_overlay(ehci, qh, qtd) != 0) { if (ehci_qh_do_overlay(ehci, qh, qtd) != 0) {
return -1; return -1;
} }
@ -1524,8 +1568,7 @@ static int ehci_state_execute(EHCIState *ehci, int async, int *state)
reload = get_field(qh->epchar, QH_EPCHAR_RL); reload = get_field(qh->epchar, QH_EPCHAR_RL);
nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT); nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
if (reload && !nakcnt) { if (reload && !nakcnt) {
DPRINTF_ST("EXECUTE: RL != 0 but NakCnt == 0 -- no execute\n"); ehci_set_state(ehci, async, EST_HORIZONTALQH);
*state = EST_HORIZONTALQH;
again = 1; again = 1;
goto out; goto out;
} }
@ -1538,8 +1581,7 @@ static int ehci_state_execute(EHCIState *ehci, int async, int *state)
if (!async) { if (!async) {
int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT); int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
if (!transactCtr) { if (!transactCtr) {
DPRINTF("ZERO transactctr for int qh, go HORIZ\n"); ehci_set_state(ehci, async, EST_HORIZONTALQH);
*state = EST_HORIZONTALQH;
again = 1; again = 1;
goto out; goto out;
} }
@ -1554,7 +1596,7 @@ static int ehci_state_execute(EHCIState *ehci, int async, int *state)
again = -1; again = -1;
goto out; goto out;
} }
*state = EST_EXECUTING; ehci_set_state(ehci, async, EST_EXECUTING);
if (ehci->exec_status != USB_RET_ASYNC) { if (ehci->exec_status != USB_RET_ASYNC) {
again = 1; again = 1;
@ -1564,7 +1606,7 @@ out:
return again; return again;
} }
static int ehci_state_executing(EHCIState *ehci, int async, int *state) static int ehci_state_executing(EHCIState *ehci, int async)
{ {
EHCIqh *qh = &ehci->qh; EHCIqh *qh = &ehci->qh;
int again = 0; int again = 0;
@ -1596,12 +1638,8 @@ static int ehci_state_executing(EHCIState *ehci, int async, int *state)
if (nakcnt) { if (nakcnt) {
nakcnt--; nakcnt--;
} }
DPRINTF_ST("EXECUTING: Nak occured and RL != 0, dec NakCnt to %d\n",
nakcnt);
} else { } else {
nakcnt = reload; nakcnt = reload;
DPRINTF_ST("EXECUTING: Nak didn't occur, reloading to %d\n",
nakcnt);
} }
set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT); set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
} }
@ -1611,16 +1649,13 @@ static int ehci_state_executing(EHCIState *ehci, int async, int *state)
* in the EHCI spec but we need to do it since we don't share * in the EHCI spec but we need to do it since we don't share
* physical memory with our guest VM. * physical memory with our guest VM.
*/ */
DPRINTF("EXECUTING: write QH to VM memory: qhaddr 0x%x, next 0x%x\n",
ehci->qhaddr, qh->next);
put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2); put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
/* 4.10.5 */ /* 4.10.5 */
if ((ehci->exec_status == USB_RET_NAK) || (qh->token & QTD_TOKEN_ACTIVE)) { if ((ehci->exec_status == USB_RET_NAK) || (qh->token & QTD_TOKEN_ACTIVE)) {
*state = EST_HORIZONTALQH; ehci_set_state(ehci, async, EST_HORIZONTALQH);
} else { } else {
*state = EST_WRITEBACK; ehci_set_state(ehci, async, EST_WRITEBACK);
} }
again = 1; again = 1;
@ -1630,13 +1665,13 @@ out:
} }
static int ehci_state_writeback(EHCIState *ehci, int async, int *state) static int ehci_state_writeback(EHCIState *ehci, int async)
{ {
EHCIqh *qh = &ehci->qh; EHCIqh *qh = &ehci->qh;
int again = 0; int again = 0;
/* Write back the QTD from the QH area */ /* Write back the QTD from the QH area */
DPRINTF_ST("WRITEBACK: write QTD to VM memory\n"); ehci_trace_qtd(ehci, NLPTR_GET(ehci->qtdaddr), (EHCIqtd*) &qh->next_qtd);
put_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) &qh->next_qtd, put_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) &qh->next_qtd,
sizeof(EHCIqtd) >> 2); sizeof(EHCIqtd) >> 2);
@ -1644,10 +1679,10 @@ static int ehci_state_writeback(EHCIState *ehci, int async, int *state)
* but stop after one qtd if periodic * but stop after one qtd if periodic
*/ */
//if (async) { //if (async) {
*state = EST_ADVANCEQUEUE; ehci_set_state(ehci, async, EST_ADVANCEQUEUE);
again = 1; again = 1;
//} else { //} else {
// *state = EST_ACTIVE; // ehci_set_state(ehci, async, EST_ACTIVE);
//} //}
return again; return again;
} }
@ -1656,15 +1691,14 @@ static int ehci_state_writeback(EHCIState *ehci, int async, int *state)
* This is the state machine that is common to both async and periodic * This is the state machine that is common to both async and periodic
*/ */
static int ehci_advance_state(EHCIState *ehci, static void ehci_advance_state(EHCIState *ehci,
int async, int async)
int state)
{ {
int again; int again;
int iter = 0; int iter = 0;
do { do {
if (state == EST_FETCHQH) { if (ehci_get_state(ehci, async) == EST_FETCHQH) {
iter++; iter++;
/* if we are roaming a lot of QH without executing a qTD /* if we are roaming a lot of QH without executing a qTD
* something is wrong with the linked list. TO-DO: why is * something is wrong with the linked list. TO-DO: why is
@ -1672,50 +1706,50 @@ static int ehci_advance_state(EHCIState *ehci,
*/ */
if (iter > MAX_ITERATIONS) { if (iter > MAX_ITERATIONS) {
DPRINTF("\n*** advance_state: bailing on MAX ITERATIONS***\n"); DPRINTF("\n*** advance_state: bailing on MAX ITERATIONS***\n");
state = EST_ACTIVE; ehci_set_state(ehci, async, EST_ACTIVE);
break; break;
} }
} }
switch(state) { switch(ehci_get_state(ehci, async)) {
case EST_WAITLISTHEAD: case EST_WAITLISTHEAD:
again = ehci_state_waitlisthead(ehci, async, &state); again = ehci_state_waitlisthead(ehci, async);
break; break;
case EST_FETCHENTRY: case EST_FETCHENTRY:
again = ehci_state_fetchentry(ehci, async, &state); again = ehci_state_fetchentry(ehci, async);
break; break;
case EST_FETCHQH: case EST_FETCHQH:
again = ehci_state_fetchqh(ehci, async, &state); again = ehci_state_fetchqh(ehci, async);
break; break;
case EST_FETCHITD: case EST_FETCHITD:
again = ehci_state_fetchitd(ehci, async, &state); again = ehci_state_fetchitd(ehci, async);
break; break;
case EST_ADVANCEQUEUE: case EST_ADVANCEQUEUE:
again = ehci_state_advqueue(ehci, async, &state); again = ehci_state_advqueue(ehci, async);
break; break;
case EST_FETCHQTD: case EST_FETCHQTD:
again = ehci_state_fetchqtd(ehci, async, &state); again = ehci_state_fetchqtd(ehci, async);
break; break;
case EST_HORIZONTALQH: case EST_HORIZONTALQH:
again = ehci_state_horizqh(ehci, async, &state); again = ehci_state_horizqh(ehci, async);
break; break;
case EST_EXECUTE: case EST_EXECUTE:
iter = 0; iter = 0;
again = ehci_state_execute(ehci, async, &state); again = ehci_state_execute(ehci, async);
break; break;
case EST_EXECUTING: case EST_EXECUTING:
again = ehci_state_executing(ehci, async, &state); again = ehci_state_executing(ehci, async);
break; break;
case EST_WRITEBACK: case EST_WRITEBACK:
again = ehci_state_writeback(ehci, async, &state); again = ehci_state_writeback(ehci, async);
break; break;
default: default:
@ -1733,27 +1767,26 @@ static int ehci_advance_state(EHCIState *ehci,
while (again); while (again);
ehci_commit_interrupt(ehci); ehci_commit_interrupt(ehci);
return state;
} }
static void ehci_advance_async_state(EHCIState *ehci) static void ehci_advance_async_state(EHCIState *ehci)
{ {
EHCIqh qh; EHCIqh qh;
int state = ehci->astate; int async = 1;
switch(state) { switch(ehci_get_state(ehci, async)) {
case EST_INACTIVE: case EST_INACTIVE:
if (!(ehci->usbcmd & USBCMD_ASE)) { if (!(ehci->usbcmd & USBCMD_ASE)) {
break; break;
} }
ehci_set_usbsts(ehci, USBSTS_ASS); ehci_set_usbsts(ehci, USBSTS_ASS);
ehci->astate = EST_ACTIVE; ehci_set_state(ehci, async, EST_ACTIVE);
// No break, fall through to ACTIVE // No break, fall through to ACTIVE
case EST_ACTIVE: case EST_ACTIVE:
if ( !(ehci->usbcmd & USBCMD_ASE)) { if ( !(ehci->usbcmd & USBCMD_ASE)) {
ehci_clear_usbsts(ehci, USBSTS_ASS); ehci_clear_usbsts(ehci, USBSTS_ASS);
ehci->astate = EST_INACTIVE; ehci_set_state(ehci, async, EST_INACTIVE);
break; break;
} }
@ -1775,14 +1808,12 @@ static void ehci_advance_async_state(EHCIState *ehci)
break; break;
} }
DPRINTF_ST("ASYNC: waiting for listhead, starting at %08x\n",
ehci->asynclistaddr);
/* check that address register has been set */ /* check that address register has been set */
if (ehci->asynclistaddr == 0) { if (ehci->asynclistaddr == 0) {
break; break;
} }
state = EST_WAITLISTHEAD; ehci_set_state(ehci, async, EST_WAITLISTHEAD);
/* fall through */ /* fall through */
case EST_FETCHENTRY: case EST_FETCHENTRY:
@ -1791,14 +1822,14 @@ static void ehci_advance_async_state(EHCIState *ehci)
case EST_EXECUTING: case EST_EXECUTING:
get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) &qh, get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) &qh,
sizeof(EHCIqh) >> 2); sizeof(EHCIqh) >> 2);
ehci->astate = ehci_advance_state(ehci, 1, state); ehci_advance_state(ehci, async);
break; break;
default: default:
/* this should only be due to a developer mistake */ /* this should only be due to a developer mistake */
fprintf(stderr, "ehci: Bad asynchronous state %d. " fprintf(stderr, "ehci: Bad asynchronous state %d. "
"Resetting to active\n", ehci->astate); "Resetting to active\n", ehci->astate);
ehci->astate = EST_ACTIVE; ehci_set_state(ehci, async, EST_ACTIVE);
} }
} }
@ -1806,14 +1837,15 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
{ {
uint32_t entry; uint32_t entry;
uint32_t list; uint32_t list;
int async = 0;
// 4.6 // 4.6
switch(ehci->pstate) { switch(ehci_get_state(ehci, async)) {
case EST_INACTIVE: case EST_INACTIVE:
if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) { if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) {
ehci_set_usbsts(ehci, USBSTS_PSS); ehci_set_usbsts(ehci, USBSTS_PSS);
ehci->pstate = EST_ACTIVE; ehci_set_state(ehci, async, EST_ACTIVE);
// No break, fall through to ACTIVE // No break, fall through to ACTIVE
} else } else
break; break;
@ -1821,7 +1853,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
case EST_ACTIVE: case EST_ACTIVE:
if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) { if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) {
ehci_clear_usbsts(ehci, USBSTS_PSS); ehci_clear_usbsts(ehci, USBSTS_PSS);
ehci->pstate = EST_INACTIVE; ehci_set_state(ehci, async, EST_INACTIVE);
break; break;
} }
@ -1838,19 +1870,20 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n",
ehci->frindex / 8, list, entry); ehci->frindex / 8, list, entry);
ehci->fetch_addr = entry; ehci->fetch_addr = entry;
ehci->pstate = ehci_advance_state(ehci, 0, EST_FETCHENTRY); ehci_set_state(ehci, async, EST_FETCHENTRY);
ehci_advance_state(ehci, async);
break; break;
case EST_EXECUTING: case EST_EXECUTING:
DPRINTF("PERIODIC state adv for executing\n"); DPRINTF("PERIODIC state adv for executing\n");
ehci->pstate = ehci_advance_state(ehci, 0, EST_EXECUTING); ehci_advance_state(ehci, async);
break; break;
default: default:
/* this should only be due to a developer mistake */ /* this should only be due to a developer mistake */
fprintf(stderr, "ehci: Bad periodic state %d. " fprintf(stderr, "ehci: Bad periodic state %d. "
"Resetting to active\n", ehci->pstate); "Resetting to active\n", ehci->pstate);
ehci->pstate = EST_ACTIVE; ehci_set_state(ehci, async, EST_ACTIVE);
} }
} }
@ -1896,7 +1929,7 @@ static void ehci_frame_timer(void *opaque)
} else { } else {
// TODO could this cause periodic frames to get skipped if async // TODO could this cause periodic frames to get skipped if async
// active? // active?
if (ehci->astate != EST_EXECUTING) { if (ehci_get_state(ehci, 1) != EST_EXECUTING) {
ehci_advance_periodic_state(ehci); ehci_advance_periodic_state(ehci);
} }
} }
@ -1913,7 +1946,7 @@ static void ehci_frame_timer(void *opaque)
/* Async is not inside loop since it executes everything it can once /* Async is not inside loop since it executes everything it can once
* called * called
*/ */
if (ehci->pstate != EST_EXECUTING) { if (ehci_get_state(ehci, 0) != EST_EXECUTING) {
ehci_advance_async_state(ehci); ehci_advance_async_state(ehci);
} }

View file

@ -199,6 +199,10 @@ disable usb_ehci_reset(void) "=== RESET ==="
disable usb_ehci_mmio_readl(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x" disable usb_ehci_mmio_readl(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x"
disable usb_ehci_mmio_writel(uint32_t addr, const char *str, uint32_t val, uint32_t oldval) "wr mmio %04x [%s] = %x (old: %x)" disable usb_ehci_mmio_writel(uint32_t addr, const char *str, uint32_t val, uint32_t oldval) "wr mmio %04x [%s] = %x (old: %x)"
disable usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d" disable usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d"
disable usb_ehci_state(const char *schedule, const char *state) "%s schedule %s"
disable usb_ehci_qh(uint32_t addr, uint32_t next, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd, int rl, int mplen, int eps, int ep, int devaddr, int c, int h, int dtc, int i) "QH @ %08x: next %08x qtds %08x,%08x,%08x - rl %d, mplen %d, eps %d, ep %d, dev %d, c %d, h %d, dtc %d, i %d"
disable usb_ehci_qtd(uint32_t addr, uint32_t next, uint32_t altnext, int tbytes, int cpage, int cerr, int pid, int ioc, int active, int halt, int babble, int xacterr) "QH @ %08x: next %08x altnext %08x - tbytes %d, cpage %d, cerr %d, pid %d, ioc %d, active %d, halt %d, babble %d, xacterr %d"
disable usb_ehci_itd(uint32_t addr, uint32_t next) "ITD @ %08x: next %08x"
# hw/usb-desc.c # hw/usb-desc.c
disable usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d" disable usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"