@@ -28,14 +28,11 @@
#include "usb-ehci.h"
#include "monitor.h"
-#define EHCI_DEBUG 1
-#define TDEBUG 0
-#define DEBUG_PACKET 0
-#define STATE_DEBUG 0
-
-#if EHCI_DEBUG || TDEBUG || DEBUG_PACKET || STATE_DEBUG
-//#define DPRINTF printf
-#define DPRINTF timed_printf
+#define EHCI_DEBUG 0
+#define STATE_DEBUG 0 /* state transitions */
+
+#if EHCI_DEBUG || STATE_DEBUG
+#define DPRINTF printf
#else
#define DPRINTF(...)
#endif
@@ -46,16 +43,17 @@
#define DPRINTF_ST(...)
#endif
-#define ASSERT(x) { if (!(x)) { printf("Assertion failed in usb-echi.c line %d\n", __LINE__); exit(1); } }
+/* internal processing - reset HC to try and recover */
+#define USB_RET_PROCERR (-99)
#define MMIO_SIZE 0x1000
-#define CAPREGBASE 0x0000 // Capability Registers Base Address
-
-#define CAPLENGTH CAPREGBASE + 0x0000
-#define HCIVERSION CAPREGBASE + 0x0002
-#define HCSPARAMS CAPREGBASE + 0x0004
-#define HCCPARAMS CAPREGBASE + 0x0008
+/* 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 EECP HCCPARAMS + 1
#define HCSPPORTROUTE1 CAPREGBASE + 0x000c
#define HCSPPORTROUTE2 CAPREGBASE + 0x0010
@@ -83,7 +81,7 @@
#define USBSTS_PCD (1 << 2) // Port Change Detect
#define USBSTS_FLR (1 << 3) // Frame List Rollover
#define USBSTS_HSE (1 << 4) // Host System Error
-#define USBSTS_IAA (1 << 5) // Interrupt Async Advance
+#define USBSTS_IAA (1 << 5) // Interrupt on Async Advance
#define USBSTS_HALT (1 << 12) // HC Halted
#define USBSTS_REC (1 << 13) // Reclamation
#define USBSTS_PSS (1 << 14) // Periodic Schedule Status
@@ -137,7 +135,7 @@
//#define EHCI_NOMICROFRAMES
#ifdef EHCI_NOMICROFRAMES
-#define FRAME_TIMER_FREQ 2000
+#define FRAME_TIMER_FREQ 1000
#else
#define FRAME_TIMER_FREQ 8000
#endif
@@ -145,9 +143,9 @@
#define NB_MAXINTRATE 8 // Max rate at which controller issues ints
#define NB_PORTS 4 // Number of downstream ports
-#define BUFF_SIZE 20480 // Max bytes to transfer per transaction
-#define MAX_ITERATIONS 1000 // Max number of states before we abort
-#define MAX_QH 1000 // Max allowable queue heads in a chain
+#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction
+#define MAX_ITERATIONS 50 // Max number of states before we abort
+#define MAX_QH 100 // Max allowable queue heads in a chain
/* Internal periodic / asynchronous schedule state machine states
*/
@@ -159,8 +157,6 @@ typedef enum {
/* The following states are internal to the state machine function
*/
EST_WAITLISTHEAD,
- EST_DORELOAD,
- EST_WAITSTARTEVENT,
EST_FETCHENTRY,
EST_FETCHQH,
EST_FETCHITD,
@@ -171,17 +167,22 @@ typedef enum {
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
+
+
/* EHCI spec version 1.0 Section 3.3
*/
typedef struct EHCIitd {
uint32_t next;
-#define NLPTR_GET(x) ((x) & 0xffffffe0)
-#define NLPTR_TYPE_GET(x) (((x) >> 1) & 3)
-#define NLPTR_TYPE_ITD 0
-#define NLPTR_TYPE_QH 1
-#define NLPTR_TYPE_STITD 2
-#define NLPTR_TYPE_FSTN 3
-#define NLPTR_TBIT(x) ((x) & 1)
uint32_t transact[8];
#define ITD_XACT_ACTIVE (1 << 31)
@@ -201,8 +202,10 @@ typedef struct EHCIitd {
#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
} EHCIitd;
@@ -241,11 +244,11 @@ typedef struct EHCIsitd {
#define SITD_RESULTS_SPLITXSTATE (1 << 1)
uint32_t bufptr[2];
-#define BUFPTR_MASK 0xfffff000
-#define BUFPTR_CURROFF_MASK 0x00000fff
-#define BUFPTR_TPOS_MASK 0x00000018
-#define BUFPTR_TPOS_SH 3
-#define BUFPTR_TCNT_MASK 0x00000007
+#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;
@@ -276,6 +279,7 @@ typedef struct EHCIqtd {
#define QTD_TOKEN_PING (1 << 0)
uint32_t bufptr[5]; // Standard buffer pointer
+#define QTD_BUFPTR_MASK 0xfffff000
} EHCIqtd;
/* EHCI spec version 1.0 Section 3.6
@@ -283,6 +287,7 @@ typedef struct EHCIqtd {
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
@@ -293,11 +298,18 @@ typedef struct EHCIqh {
#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
@@ -308,10 +320,11 @@ typedef struct EHCIqh {
#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; // Standard next link pointer
- uint32_t qtdnext; // Standard next link pointer
- uint32_t altnext;
+ 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
@@ -365,16 +378,27 @@ typedef struct {
int astate; // Current state in asynchronous schedule
int pstate; // Current state in periodic schedule
USBPort ports[NB_PORTS];
- unsigned char buffer[BUFF_SIZE];
- EHCIqh qh;
- EHCIqtd qtd;
+ uint8_t buffer[BUFF_SIZE];
+
+ int more;
+
+ /* 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
+
+ EHCIqtd qtd; // copy of current QTD (being worked on)
+ uint32_t qtdaddr; // address QTD read from
+
+ uint32_t itdaddr; // current ITD
+
+ uint32_t fetch_addr; // which address to look at next
+
USBBus bus;
USBPacket usb_packet;
int async_port_in_progress;
int async_complete;
- uint32_t qhaddr;
- uint32_t itdaddr;
- uint32_t qtdaddr;
uint32_t tbytes;
int pid;
int exec_status;
@@ -383,46 +407,22 @@ typedef struct {
uint32_t frame_end_usec;
} EHCIState;
-#define SET_LAST_RUN_CLOCK(s) (s)->last_run_usec = qemu_get_clock(vm_clock) / 1000;
-
-static inline uint32_t get_field(uint32_t data, uint32_t mask, int shift)
-{
- return((data & mask) >> shift);
-}
-
-static inline void set_field(uint32_t *data, uint32_t val,
- uint32_t mask, int shift)
-{
- *data &= ~mask;
- *data |=(val << shift);
-}
-
-#if EHCI_DEBUG
-static int timed_printf(const char *fmt, ...)
-{
- int msec, usec;
- static int usec_last;
- va_list ap;
+#define SET_LAST_RUN_CLOCK(s) \
+ (s)->last_run_usec = qemu_get_clock(vm_clock) / 1000;
- usec = qemu_get_clock(vm_clock) / 1000;
+/* nifty macros from Arnon's EHCI version */
+#define get_field(data, field) \
+ (((data) & field##_MASK) >> field##_SH)
- msec = usec - usec_last;
- usec_last = usec;
- usec = msec;
+#define set_field(data, newval, field) do { \
+ uint32_t val = *data; \
+ val &= ~ field##_MASK; \
+ val |= ((newval) << field##_SH) & field##_MASK; \
+ *data = val; \
+ } while(0)
- msec /= 1000;
- msec %= 1000;
-
- usec %= 1000;
-
- va_start(ap, fmt);
- printf("%03d.%03d ", msec, usec);
- vprintf(fmt, ap);
- va_end(ap);
-
- return 0;
-}
+#if EHCI_DEBUG
static const char *addr2str(unsigned addr)
{
const char *r = " unknown";
@@ -459,186 +459,22 @@ static const char *addr2str(unsigned addr)
case FRINDEX:
r = " FRAME IDX";
break;
- }
-
- return r;
-}
-
-static void dump_ptr(const char *s, uint32_t ptr, int has_type)
-{
- int t = NLPTR_TYPE_GET(ptr);
- DPRINTF("%s%08X", s, NLPTR_GET(ptr));
-
- if (has_type) {
- DPRINTF("(PTR type is %s)",
- t == NLPTR_TYPE_ITD ? "ITD" :
- (t == NLPTR_TYPE_QH ? "QH" :
- (t == NLPTR_TYPE_STITD ? "STITD" :
- (t == NLPTR_TYPE_FSTN ? "FSTN" : "****"))));
- }
-
- DPRINTF("%s\n", NLPTR_TBIT(ptr) ? " TBIT set" : "");
-}
-#else
-static inline void dump_ptr(const char *s, uint32_t ptr, int has_type)
-{
-}
-#endif
-
-#if EHCI_DEBUG || DEBUG_PACKET
-static void dump_qtd(EHCIqtd *qtd, uint32_t qtdaddr)
-{
- int pid;
-
- pid =(qtd->token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
-
- printf(" QTD analysis %08X\n"
- " === ======== ========\n", qtdaddr);
-
- printf(" NakCnt: %d\n",
- (qtd->altnext & QH_ALTNEXT_NAKCNT_MASK) >> QH_ALTNEXT_NAKCNT_SH);
- dump_ptr(" Next: ", qtd->next, 0);
- dump_ptr(" Alternate: ", qtd->altnext, 0);
- printf(" Data Toggle: %s ",
- qtd->token & QTD_TOKEN_DTOGGLE ? "Yes " : "No ");
- printf(" Total Bytes: %d\n",
- (qtd->token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH);
- printf(" IOC: %s ",
- qtd->token & QTD_TOKEN_IOC ? "Yes " : "No ");
- printf(" C_Page: %d\n",
- (qtd->token & QTD_TOKEN_CPAGE_MASK) >> QTD_TOKEN_CPAGE_SH);
- printf(" CErr: %-4d ",
- (qtd->token & QTD_TOKEN_CERR_MASK) >> QTD_TOKEN_CERR_SH);
- printf(" PID code: %s\n",
- pid == 0 ? "OUT" :
- (pid == 1 ? "IN" :
- (pid == 2 ? "SETUP" : "****")));
- printf(" Flags: %s%s%s%s%s%s%s%s\n",
- qtd->token & QTD_TOKEN_ACTIVE ? "ACTIVE " : "",
- qtd->token & QTD_TOKEN_HALT ? "HALT " : "",
- qtd->token & QTD_TOKEN_DBERR ? "DBERR " : "",
- qtd->token & QTD_TOKEN_BABBLE ? "BABBLE " : "",
- qtd->token & QTD_TOKEN_XACTERR ? "XACTERR " : "",
- qtd->token & QTD_TOKEN_MISSEDUF ? "MISSEDUF " : "",
- qtd->token & QTD_TOKEN_SPLITXSTATE ? "SPLITXSTATE " : "",
- qtd->token & QTD_TOKEN_PING ? "PING " : "");
- printf(" Current Offset %d\n",
- qtd->bufptr[0] & BUFPTR_CURROFF_MASK);
- printf(" === ======== ========\n");
-}
-#endif
-#if EHCI_DEBUG
-static void dump_qh(EHCIqh *qh, uint32_t qhaddr)
-{
- int speed =(qh->epchar & QH_EPCHAR_EPS_MASK) >> QH_EPCHAR_EPS_SH;
-
- printf("QH analysis %08X\n"
- "== ======== ========\n", qhaddr);
-
- dump_ptr("Horizontal: ", qh->next, 1);
- printf("Nak Count Reload: %d\n",
- (qh->epchar & QH_EPCHAR_RL_MASK) >> QH_EPCHAR_RL_SH);
- printf("Max Pkt Len: %d\n",
- (qh->epchar & QH_EPCHAR_MPLEN_MASK) >> QH_EPCHAR_MPLEN_SH);
- printf("Control Endpoint: %s ",
- (qh->epchar & QH_EPCHAR_C) ? "Yes " : "No ");
- printf("H-bit: %s\n",
- (qh->epchar & QH_EPCHAR_H) ? "Yes " : "No ");
- printf("DTC: %s ",
- (qh->epchar & QH_EPCHAR_DTC) ? "Yes " : "No ");
- printf("EndPoint Speed: %s\n",
- speed == 0 ? "Full" :
- (speed == 1 ? "Low " :
- (speed == 2 ? "High" : "****")));
- printf("EndPoint: %-4d ",
- (qh->epchar & QH_EPCHAR_EP_MASK) >> QH_EPCHAR_EP_SH);
- printf("Inactive on next: %s\n",
- (qh->epchar & QH_EPCHAR_I) ? "Yes" : "No");
- printf("DevAddr: %-4d ",
- qh->epchar & QH_EPCHAR_DEVADDR_MASK);
- printf("Mult: %-4d\n",
- (qh->epcap & QH_EPCAP_MULT_MASK) >> QH_EPCAP_MULT_SH);
- printf("PortNum: %-4d ",
- (qh->epcap & QH_EPCAP_PORTNUM_MASK) >> QH_EPCAP_PORTNUM_SH);
- printf("HubAddr: %d\n",
- (qh->epcap & QH_EPCAP_HUBADDR_MASK) >> QH_EPCAP_HUBADDR_SH);
- printf("C-mask: %-4d ",
- (qh->epcap & QH_EPCAP_CMASK_MASK) >> QH_EPCAP_CMASK_SH);
- printf("S-mask: %d\n",
- qh->epcap & QH_EPCAP_SMASK_MASK);
- dump_ptr("Current: ", qh->current, 0);
-
- dump_qtd((EHCIqtd *)&qh->qtdnext, qhaddr + 16);
-
- printf("C-prog mask: %d\n",
- qh->bufptr[1] & BUFPTR_CPROGMASK_MASK);
- printf("S-bytes: %d\n",
- qh->bufptr[2] & BUFPTR_FRAMETAG_MASK);
- printf("FrameTag: %d\n",
- (qh->bufptr[2] & BUFPTR_SBYTES_MASK) >> BUFPTR_SBYTES_SH);
- printf("== ======== ========\n");
-}
-#else
-static inline void dump_qh(EHCIqh *qh, uint32_t qhaddr)
-{
-}
-#endif
-
-#if DEBUG_PACKET
-
-static void dump_itd(EHCIitd *itd, uint32_t addr)
-{
- int i;
-
- printf("ITD analysis %08X\n"
- "=== ======== ========\n", addr);
-
- dump_ptr("Horizontal: ", itd->next, 1);
-
- for(i = 0; i < 8; i++) {
- printf("Trans Desc %d, len %5d, off %03X, page sel %d, ioc:%s ",
- i,
- get_field(itd->transact[i], ITD_XACT_LENGTH_MASK,
- ITD_XACT_LENGTH_SH),
- get_field(itd->transact[i], ITD_XACT_OFFSET_MASK, 0),
- get_field(itd->transact[i], ITD_XACT_PGSEL_MASK,
- ITD_XACT_PGSEL_SH),
- itd->transact[i] & ITD_XACT_IOC ? "Yes" : "No ");
-
- if (itd->transact[i] & ITD_XACT_ACTIVE)
- printf("ACTIVE ");
-
- if (itd->transact[i] & ITD_XACT_DBERROR)
- printf("DATAERR ");
-
- if (itd->transact[i] & ITD_XACT_BABBLE)
- printf("BABBLE ");
-
- if (itd->transact[i] & ITD_XACT_XACTERR)
- printf("XACTERR ");
+
+ case PERIODICLISTBASE:
+ r = "P-LIST BASE";
+ break;
+
+ case ASYNCLISTADDR:
+ r = "A-LIST ADDR";
+ break;
- printf("\n");
+ case PORTSC_BEGIN ... PORTSC_END:
+ r = "PORT STATUS";
+ break;
}
- printf("Device: %d\n",
- get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR_MASK, 0));
-
- printf("Endpoint: %d\n",
- get_field(itd->bufptr[0], ITD_BUFPTR_EP_MASK, ITD_BUFPTR_EP_SH));
-
- printf("Direction: %s\n",
- itd->bufptr[1] & ITD_BUFPTR_DIRECTION ? "IN" : "OUT");
-
- printf("Max Packet: %d\n",
- get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT_MASK, 0));
-
- printf("Mult: %d\n",
- get_field(itd->bufptr[2], ITD_BUFPTR_MULT_MASK, 0));
-
- for(i = 0; i < 7; i++)
- printf("Buf Ptr %d: %05X\n", i, itd->bufptr[i] >> 12);
+ return r;
}
-
#endif
@@ -653,9 +489,6 @@ static inline void ehci_set_interrupt(EHCIState *s, int intr)
if ((s->usbsts & USBINTR_MASK) & s->usbintr)
level = 1;
- DPRINTF("ehci_set_interrupt: intr x%x, STS x%x, INTR x%x, level %d\n",
- intr, s->usbsts & USBINTR_MASK, s->usbintr, level);
-
qemu_set_irq(s->irq, level);
}
@@ -701,6 +534,7 @@ static void ehci_attach(USBPort *port, USBDevice *dev)
}
}
+/* 4.1 host controller initialization */
static void ehci_reset(void *opaque)
{
EHCIState *s = opaque;
@@ -735,8 +569,6 @@ static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
uint32_t val;
val = s->mmio[addr];
- DPRINTF("ehci_mem_readb: %s (addr 0x%08X), val 0x%02X\n",
- addr2str((unsigned) addr), (unsigned) addr, val);
return val;
}
@@ -746,9 +578,7 @@ static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr)
EHCIState *s = ptr;
uint32_t val;
- val = s->mmio[addr] |(s->mmio[addr+1] << 8);
- DPRINTF("ehci_mem_readw: %s (addr 0x%08X), val 0x%04X\n",
- addr2str((unsigned) addr), (unsigned) addr, val);
+ val = s->mmio[addr] | (s->mmio[addr+1] << 8);
return val;
}
@@ -758,12 +588,8 @@ static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr)
EHCIState *s = ptr;
uint32_t val;
- val = s->mmio[addr] |(s->mmio[addr+1] << 8) |
- (s->mmio[addr+2] << 16) |(s->mmio[addr+3] << 24);
-
- if (addr != USBSTS)
- DPRINTF("ehci_mem_readl: %s (addr 0x%08X), val 0x%08X\n",
- addr2str((unsigned) addr), (unsigned) addr, val);
+ val = s->mmio[addr] | (s->mmio[addr+1] << 8) |
+ (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24);
return val;
}
@@ -833,6 +659,9 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
{
EHCIState *s = ptr;
int i;
+#if EHCI_DEBUG
+ const char *str;
+#endif
/* Only aligned reads are allowed on OHCI */
if (addr & 3) {
@@ -846,14 +675,25 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
return;
}
- /* Do any register specific pre-write processing here. */
+ if (addr < OPREGBASE) {
+ fprintf(stderr, "usb-ehci: write attempt to read-only register"
+ TARGET_FMT_plx "\n", addr);
+ return;
+ }
+
+ /* Do any register specific pre-write processing here. */
+#if EHCI_DEBUG
+ str = addr2str((unsigned) addr);
+#endif
switch(addr)
{
case USBCMD:
- DPRINTF("ehci_mem_writel: USBCMD val=0x%08X\n", val);
+ DPRINTF("ehci_mem_writel: USBCMD val=0x%08X, current cmd=0x%08X\n",
+ val, s->usbcmd);
+
if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) {
- DPRINTF(" run, clear halt\n");
+ DPRINTF("ehci_mem_writel: %s run, clear halt\n", str);
qemu_mod_timer(s->frame_timer, qemu_get_clock(vm_clock));
SET_LAST_RUN_CLOCK(s);
s->usbsts &= ~USBSTS_HALT;
@@ -867,41 +707,90 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
}
if (val & USBCMD_HCRESET) {
- DPRINTF(" resetting ...\n");
+ DPRINTF("ehci_mem_writel: %s run, resetting\n", str);
ehci_reset(s);
- DPRINTF(" reset done, clear reset request bit\n");
val &= ~USBCMD_HCRESET;
}
+ /* not supporting dynamic frame list size at the moment */
+ if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) {
+ fprintf(stderr, "attempt to set frame list size -- value %d\n",
+ val & USBCMD_FLS);
+ val &= ~USBCMD_FLS;
+ }
+#if EHCI_DEBUG
+ if ((val & USBCMD_PSE) && !(s->usbcmd & USBCMD_PSE)) {
+ DPRINTF("periodic scheduling enabled\n");
+ }
+ if (!(val & USBCMD_PSE) && (s->usbcmd & USBCMD_PSE)) {
+ DPRINTF("periodic scheduling disabled\n");
+ }
+ if ((val & USBCMD_ASE) && !(s->usbcmd & USBCMD_ASE)) {
+ DPRINTF("asynchronous scheduling enabled\n");
+ }
+ if (!(val & USBCMD_ASE) && (s->usbcmd & USBCMD_ASE)) {
+ DPRINTF("asynchronous scheduling disabled\n");
+ }
+ if ((val & USBCMD_IAAD) && !(s->usbcmd & USBCMD_IAAD)) {
+ DPRINTF("doorbell request received\n");
+ }
+ if ((val & USBCMD_LHCR) && !(s->usbcmd & USBCMD_LHCR)) {
+ DPRINTF("light host controller reset received\n");
+ }
+ if ((val & USBCMD_ITC) != (s->usbcmd & USBCMD_ITC)) {
+ DPRINTF("interrupt threshold control set to %x\n",
+ (val & USBCMD_ITC)>>USBCMD_ITC_SH);
+ }
+#endif
break;
+
case USBSTS:
val &= USBSTS_RO_MASK; // bits 6 thru 31 are RO
- DPRINTF("mem_writel : USBSTS RWC set to 0x%08X\n", val);
+ DPRINTF("ehci_mem_writel: %s RWC set to 0x%08X\n", str, val);
+
+ val = (s->usbsts &= ~val); // bits 0 thru 5 are R/WC
- val =(s->usbsts &= ~val); // bits 0 thru 5 are R/WC
- DPRINTF("mem_writel : USBSTS updating interrupt condition\n");
+ DPRINTF("ehci_mem_writel: %s updating interrupt condition\n", str);
ehci_set_interrupt(s, 0);
break;
+
case USBINTR:
val &= USBINTR_MASK;
- DPRINTF("ehci_mem_writel: USBINTR set to 0x%08X\n", val);
+ DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
break;
case FRINDEX:
s->sofv = val >> 3;
- DPRINTF("ehci_mem_writel: FRAME index set to 0x%08X\n",(unsigned) addr, val);
+ DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
break;
case CONFIGFLAG:
- DPRINTF("ehci_mem_writel: CONFIGFLAG set to 0x%08X\n",(unsigned) addr, val);
+ DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
val &= 0x1;
if (val) {
for(i = 0; i < NB_PORTS; i++)
s->portsc[i] &= ~PORTSC_POWNER;
}
+ break;
+ case PERIODICLISTBASE:
+ if (val & USBCMD_PSE) {
+ fprintf(stderr, "Guest OS should not be setting the periodic"
+ " list base register while periodic schedule is enabled\n");
+ return;
+ }
+ DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
+ break;
+
+ case ASYNCLISTADDR:
+ if (val & USBCMD_ASE) {
+ fprintf(stderr, "Guest OS should not be setting the async list"
+ " address register while async schedule is enabled\n");
+ return;
+ }
+ DPRINTF("ehci_mem_writel: A-LIST ADDR set to 0x%08X\n", val);
break;
}
@@ -939,38 +828,44 @@ static inline int put_dwords(uint32_t addr, uint32_t *buf, int num)
// 4.10.2
-static void ehci_qh_do_overlay(EHCIState *ehci, EHCIqh *qh, EHCIqtd *qtd)
+static int ehci_qh_do_overlay(EHCIState *ehci, EHCIqh *qh, EHCIqtd *qtd)
{
int i;
int dtoggle;
int ping;
+ int eps;
+ int reload;
+
+ if (ehci->qtdaddr < 0x1000) {
+ fprintf(stderr, "invalid address for qTD. resetting\n");
+ return USB_RET_PROCERR;
+ }
// remember values in fields to preserve in qh after overlay
dtoggle = qh->token & QTD_TOKEN_DTOGGLE;
- ping = qh->token & QTD_TOKEN_PING;
+ ping = qh->token & QTD_TOKEN_PING;
- DPRINTF("setting qh.current from %08X to 0x%08X\n", qh->current,
+ DPRINTF("setting qh.current from %08X to 0x%08X\n", qh->current_qtd,
ehci->qtdaddr);
- qh->current = ehci->qtdaddr;
- qh->qtdnext = qtd->next;
- qh->altnext = qtd->altnext;
- qh->token = qtd->token;
-
- if (qh->current < 0x1000) {
-#if DEBUG_PACKET
- dump_qh(qh, qh->current);
-#endif
- ASSERT(1==2);
- }
+ qh->current_qtd = ehci->qtdaddr;
+ qh->next_qtd = qtd->next;
+ qh->altnext_qtd = qtd->altnext;
+ qh->token = qtd->token;
- if (((qh->epchar & QH_EPCHAR_EPS_MASK) >> QH_EPCHAR_EPS_SH) == 2) {
+
+ eps = get_field(qh->epchar, QH_EPCHAR_EPS);
+ if (eps == EHCI_QH_EPS_HIGH) {
qh->token &= ~QTD_TOKEN_PING;
qh->token |= ping;
}
- for (i = 0; i < 5; i++)
+ reload = get_field(qh->epchar, QH_EPCHAR_RL);
+ set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
+
+ for (i = 0; i < 5; i++) {
qh->bufptr[i] = qtd->bufptr[i];
+ }
if (!(qh->epchar & QH_EPCHAR_DTC)) {
// preserve QH DT bit
@@ -981,80 +876,102 @@ static void ehci_qh_do_overlay(EHCIState *ehci, EHCIqh *qh, EHCIqtd *qtd)
qh->bufptr[1] &= ~BUFPTR_CPROGMASK_MASK;
qh->bufptr[2] &= ~BUFPTR_FRAMETAG_MASK;
- // TODO NakCnt
+ put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+ return 0;
}
-static void ehci_buffer_rw(EHCIState *ehci, EHCIqh *qh, int bytes, int rw)
+static int ehci_buffer_rw(uint8_t *buffer, EHCIqh *qh, int bytes, int rw)
{
int bufpos = 0;
- int cpage;
+ int cpage, offset;
uint32_t head;
uint32_t tail;
- cpage = get_field(qh->token, QTD_TOKEN_CPAGE_MASK, QTD_TOKEN_CPAGE_SH);
- ASSERT(cpage == 0);
-
- DPRINTF("exec: %sing %d bytes to/from %08x\n",
- rw ? "writ" : "read", bytes, qh->bufptr[0]);
if (!bytes)
- return;
+ return 0;
+
+ cpage = get_field(qh->token, QTD_TOKEN_CPAGE);
+ if (cpage > 4) {
+ fprintf(stderr, "cpage out of range (%d)\n", cpage);
+ return USB_RET_PROCERR;
+ }
+
+ offset = qh->bufptr[0] & ~QTD_BUFPTR_MASK;
+ DPRINTF("ehci_buffer_rw: %sing %d bytes %08x cpage %d offset\n",
+ rw ? "writ" : "read", bytes, qh->bufptr[0], cpage, offset);
do {
- head = qh->bufptr[cpage];
- tail =(qh->bufptr[cpage] & 0xfffff000) + 0x1000;
+ /* start and end of this page */
+ head = qh->bufptr[cpage] & QTD_BUFPTR_MASK;
+ tail = head + ~QTD_BUFPTR_MASK + 1;
+ /* add offset into page */
+ head |= offset;
- if (bytes <=(tail - head))
+ if (bytes <= (tail - head)) {
tail = head + bytes;
+ }
DPRINTF("DATA %s cpage:%d head:%08X tail:%08X target:%08X\n",
rw ? "WRITE" : "READ ", cpage, head, tail, bufpos);
- ASSERT(bufpos + tail - head <= BUFF_SIZE);
- ASSERT(tail - head > 0);
-
- cpu_physical_memory_rw(qh->bufptr[cpage], &ehci->buffer[bufpos],
- tail - head, rw);
-
- bufpos +=(tail - head);
- bytes -=(tail - head);
+ cpu_physical_memory_rw(head, &buffer[bufpos], tail - head, rw);
- qh->bufptr[cpage] +=(tail - head);
+ bufpos += (tail - head);
+ bytes -= (tail - head);
- if (bytes > 0)
+ if (bytes > 0) {
cpage++;
+ offset = 0;
+ }
} while (bytes > 0);
- set_field(&qh->token, cpage, QTD_TOKEN_CPAGE_MASK, QTD_TOKEN_CPAGE_SH);
+ /* save cpage */
+ set_field(&qh->token, cpage, QTD_TOKEN_CPAGE);
+
+ /* save offset into cpage */
+ offset = tail - head;
+ qh->bufptr[0] &= ~QTD_BUFPTR_MASK;
+ qh->bufptr[0] |= offset;
+
+ return 0;
}
static void ehci_async_complete_packet(USBPacket *packet, void *opaque)
{
EHCIState *ehci = opaque;
-#if DEBUG_PACKET
+
DPRINTF("Async packet complete\n");
-#endif
ehci->async_complete = 1;
ehci->exec_status = packet->len;
}
-#if TDEBUG
-static int transactid;
-#endif
-
static int ehci_execute_complete(EHCIState *ehci,
EHCIqh *qh,
int ret)
{
+ int i, c_err, reload;
+
if (ret == USB_RET_ASYNC && !ehci->async_complete) {
DPRINTF("not done yet\n");
return ret;
}
ehci->async_complete = 0;
+ i = ehci->async_port_in_progress;
ehci->async_port_in_progress = -1;
+ DPRINTF("execute_complete: qhaddr 0x%x, qtdaddr 0x%x, status %d\n",
+ ehci->qhaddr, ehci->qtdaddr, ret);
+
if (ret < 0) {
+err:
+ /* TO-DO: put this is in a function that can be invoked below as well */
+ c_err = get_field(qh->token, QTD_TOKEN_CERR);
+ c_err--;
+ set_field(&qh->token, c_err, QTD_TOKEN_CERR);
+
switch(ret) {
case USB_RET_NODEV:
fprintf(stderr, "USB no device\n");
@@ -1064,154 +981,135 @@ static int ehci_execute_complete(EHCIState *ehci,
qh->token |= QTD_TOKEN_HALT;
break;
case USB_RET_NAK:
- DPRINTF("USBTRAN RSP NAK, returning without clear active\n");
+ reload = get_field(qh->epchar, QH_EPCHAR_RL);
+ if ((ehci->pid == USB_TOKEN_IN) && reload) {
+ int nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
+ nakcnt--;
+ set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
+ }
return USB_RET_NAK;
break;
case USB_RET_BABBLE:
fprintf(stderr, "USB babble TODO\n");
- ASSERT(ret >= 0);
+ qh->token |= QTD_TOKEN_BABBLE;
break;
default:
fprintf(stderr, "USB invalid response %d to handle\n", ret);
- ASSERT(ret >= 0);
+ /* TO-DO: transaction error */
+ ret = USB_RET_PROCERR;
break;
}
} else {
- // if (ret < maxpkt)
- // {
- // DPRINTF("Short packet condition\n");
- // // TODO check 4.12 for splits
- // }
+ // DPRINTF("Short packet condition\n");
+ // TODO check 4.12 for splits
+
+ /* see if a follow-up rquest is needed - request for
+ * 20k yet OS only allows 16k requests at a time
+ */
+ if ((ret > 0) && (ehci->tbytes > 16384) && !ehci->more) {
+ /* TO-DO: put this in a function for use here and by execute */
+ USBPort *port = &ehci->ports[i];
+ USBDevice *dev = port->dev;
+
+ ehci->more = ret;
+
+ ehci->usb_packet.pid = ehci->pid;
+ ehci->usb_packet.devaddr = get_field(qh->epchar, QH_EPCHAR_DEVADDR);
+ ehci->usb_packet.devep = get_field(qh->epchar, QH_EPCHAR_EP);
+ ehci->usb_packet.data = ehci->buffer + ret;
+ /* linux devio.c limits max to 16k */
+ ehci->usb_packet.len = ehci->tbytes - ret;
+ ehci->usb_packet.complete_cb = ehci_async_complete_packet;
+ ehci->usb_packet.complete_opaque = ehci;
+
+ ret = dev->info->handle_packet(dev, &ehci->usb_packet);
+
+ DPRINTF("submit followup: qh %x qtd %x pid %x len %d ret %d\n",
+ ehci->qhaddr, ehci->qtdaddr, ehci->pid,
+ ehci->usb_packet.len, ret);
+
+ if (ret == USB_RET_ASYNC) {
+ ehci->async_port_in_progress = i;
+ ehci->async_complete = 0;
+ return ret;
+ }
+ if (ret < 0) {
+ goto err;
+ }
+ }
+
+ ret += ehci->more;
+
+ if (ret > ehci->tbytes) {
+ ret = USB_RET_BABBLE;
+ goto err;
+ }
if (ehci->tbytes && ehci->pid == USB_TOKEN_IN) {
- ASSERT(ret > 0);
- ehci_buffer_rw(ehci, qh, ret, 1);
-#if TDEBUG
- printf("Data after execution:\n");
- // dump_data(ehci->buffer, ehci->tbytes < 64 ? ehci->tbytes : 64);
- // decode_data(ehci->pid, ehci->buffer, ret);
-#endif
+ if (ehci_buffer_rw(ehci->buffer, qh, ret, 1) != 0) {
+ return USB_RET_PROCERR;
+ }
ehci->tbytes -= ret;
- } else
+ } else {
ehci->tbytes = 0;
+ }
- ASSERT(ehci->tbytes >= 0);
-
- set_field(&qh->token, ehci->tbytes,
- QTD_TOKEN_TBYTES_MASK, QTD_TOKEN_TBYTES_SH);
+ DPRINTF("updating tbytes to %d\n", ehci->tbytes);
+ set_field(&qh->token, ehci->tbytes, QTD_TOKEN_TBYTES);
}
qh->token ^= QTD_TOKEN_DTOGGLE;
qh->token &= ~QTD_TOKEN_ACTIVE;
- if (qh->token & QTD_TOKEN_IOC) {
+ if ((ret >= 0) && (qh->token & QTD_TOKEN_IOC)) {
// TODO should do this after writeback to memory
ehci_set_interrupt(ehci, USBSTS_INT);
}
-#if DEBUG_PACKET
- DPRINTF("QH after execute:-\n");
- dump_qh(qh, NLPTR_GET(ehci->qhaddr));
-#endif
-#if TDEBUG
- DPRINTF("USBTRAN RSP %3d return:(%5d) ",
- transactid,
- ret);
-
- if (ehci->pid == USB_TOKEN_IN) {
- DPRINTF("[%02X %02X %02X %02X ...]\n",
- *ehci->buffer, *(ehci->buffer+1),
- *(ehci->buffer+2), *(ehci->buffer+3));
- }
- else
- DPRINTF("\n");
-#endif
return ret;
}
// 4.10.3
-static int ehci_execute(EHCIState *ehci,
- uint32_t qhaddr,
- EHCIqh *qh)
+static int ehci_execute(EHCIState *ehci, EHCIqh *qh)
{
USBPort *port;
USBDevice *dev;
- int smask;
- int maxpkt;
int ret;
int i;
int endp;
int devadr;
- smask = QH_EPCAP_SMASK_MASK & qh->epcap;
- ehci->tbytes =(qh->token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
- ehci->pid =(qh->token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
- maxpkt = get_field(qh->epchar, QH_EPCHAR_MPLEN_MASK, QH_EPCHAR_MPLEN_SH);
- endp = get_field(qh->epchar, QH_EPCHAR_EP_MASK, QH_EPCHAR_EP_SH);
- devadr = get_field(qh->epchar, QH_EPCHAR_DEVADDR_MASK, 0);
-
if ( !(qh->token & QTD_TOKEN_ACTIVE)) {
fprintf(stderr, "Attempting to execute inactive QH\n");
- exit(1);;
+ return USB_RET_PROCERR;
}
- if (smask) {
- DPRINTF("active interrupt transfer frindex %d for dev %d EP %d\n",
- ehci->frindex, devadr, endp);
- // TODO are interrupt always IN ?
- ehci->pid = USB_TOKEN_IN;
- } else {
- DPRINTF("Active non-interrupt QH, executing\n");
-
- DPRINTF("pid is %2X\n", ehci->pid);
+ ehci->tbytes = (qh->token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
+ if (ehci->tbytes > BUFF_SIZE) {
+ fprintf(stderr, "Request for more bytes than allowed\n");
+ return USB_RET_PROCERR;
+ }
- switch(ehci->pid) {
+ ehci->pid = (qh->token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
+ switch(ehci->pid) {
case 0: ehci->pid = USB_TOKEN_OUT; break;
case 1: ehci->pid = USB_TOKEN_IN; break;
case 2: ehci->pid = USB_TOKEN_SETUP; break;
default: fprintf(stderr, "bad token\n"); break;
- }
}
- // TODO set reclam
-
-#if DEBUG_PACKET
- DPRINTF("QH before execute:-\n");
- dump_qh(qh, NLPTR_GET(qhaddr));
-#endif
-
- if (ehci->tbytes && ehci->pid != USB_TOKEN_IN) {
- ehci_buffer_rw(ehci, qh, ehci->tbytes, 0);
-#if TDEBUG
- DPRINTF("Data before execution:\n");
- // dump_data(ehci->buffer, ehci->tbytes < 64 ? ehci->tbytes : 64);
- // decode_data(ehci->pid, ehci->buffer, ehci->tbytes);
-#endif
+ if ((ehci->tbytes && ehci->pid != USB_TOKEN_IN) &&
+ (ehci_buffer_rw(ehci->buffer, qh, ehci->tbytes, 0) != 0)) {
+ return USB_RET_PROCERR;
}
-#if TDEBUG
- DPRINTF("\nUSBTRAN REQ %3d dev:%d ep:%d pid:%02X %s bytes:(%5d) ",
- ++transactid,
- devadr,
- endp,
- ehci->pid,
- (ehci->pid == USB_TOKEN_SETUP ? "(SETUP)" :
- (ehci->pid == USB_TOKEN_IN ? "(IN) " :
- (ehci->pid == USB_TOKEN_OUT ? "(OUT) " : "(*****)"))),
- ehci->tbytes);
-
- if (ehci->pid != USB_TOKEN_IN) {
- DPRINTF("[%02X %02X %02X %02X ...]\n",
- *ehci->buffer, *(ehci->buffer+1),
- *(ehci->buffer+2), *(ehci->buffer+3));
- }
- else
- DPRINTF("\n");
-#endif
+ endp = get_field(qh->epchar, QH_EPCHAR_EP);
+ devadr = get_field(qh->epchar, QH_EPCHAR_DEVADDR);
ret = USB_RET_NODEV;
+ // TO-DO: associating device with ehci port
for(i = 0; i < NB_PORTS; i++) {
port = &ehci->ports[i];
dev = port->dev;
@@ -1228,23 +1126,28 @@ static int ehci_execute(EHCIState *ehci,
ehci->usb_packet.devaddr = devadr;
ehci->usb_packet.devep = endp;
ehci->usb_packet.data = ehci->buffer;
- ehci->usb_packet.len = ehci->tbytes;
+ /* linux devio.c limits max to 16k */
+ if (ehci->tbytes > 16384) {
+ ehci->usb_packet.len = 16384;
+ } else {
+ ehci->usb_packet.len = ehci->tbytes;
+ }
ehci->usb_packet.complete_cb = ehci_async_complete_packet;
ehci->usb_packet.complete_opaque = ehci;
- DPRINTF("calling dev->info->handle_packet\n");
ret = dev->info->handle_packet(dev, &ehci->usb_packet);
+ DPRINTF("submit: qh %x qtd %x pid %x len %d (total %d) endp %x ret %d\n",
+ ehci->qhaddr, ehci->qtdaddr, ehci->pid,
+ ehci->usb_packet.len, ehci->tbytes, endp, ret);
+
if (ret != USB_RET_NODEV)
break;
}
- DPRINTF("exit loop dev->info->handle_packet\n");
-
- if (ret > BUFF_SIZE || ehci->tbytes > BUFF_SIZE) {
- fprintf(stderr, "bogus QH byte count\n");
- dump_qh(qh, NLPTR_GET(qhaddr));
- ASSERT(ret <= BUFF_SIZE && ehci->tbytes <= BUFF_SIZE);
+ if (ret > BUFF_SIZE) {
+ fprintf(stderr, "ret from handle packet > BUFF_SIZE\n");
+ return USB_RET_PROCERR;
}
if (ret == USB_RET_ASYNC) {
@@ -1258,7 +1161,7 @@ static int ehci_execute(EHCIState *ehci,
/* 4.7.2
*/
-static void ehci_process_itd(EHCIState *ehci,
+static int ehci_process_itd(EHCIState *ehci,
EHCIitd *itd)
{
USBPort *port;
@@ -1275,11 +1178,9 @@ static void ehci_process_itd(EHCIState *ehci,
int maxpkt;
dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
- devadr = get_field(itd->bufptr[0],
- ITD_BUFPTR_DEVADDR_MASK, 0);
- endp = get_field(itd->bufptr[0],
- ITD_BUFPTR_EP_MASK, ITD_BUFPTR_EP_SH);
- maxpkt = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT_MASK, 0);
+ devadr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR);
+ endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP);
+ maxpkt = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT);
#ifdef EHCI_NOMICROFRAMES
for(i = 0; i < 8; i++) {
@@ -1291,14 +1192,14 @@ static void ehci_process_itd(EHCIState *ehci,
DPRINTF("ISOCHRONOUS active for frame %d, interval %d\n",
ehci->frindex >> 3, i);
- pg = get_field(itd->transact[i], ITD_XACT_PGSEL_MASK,
- ITD_XACT_PGSEL_SH);
- ptr =(itd->bufptr[pg] & ITD_BUFPTR_MASK) |
- (itd->transact[i] & ITD_XACT_OFFSET_MASK);
- len = get_field(itd->transact[i], ITD_XACT_LENGTH_MASK,
- ITD_XACT_LENGTH_SH);
+ pg = get_field(itd->transact[i], ITD_XACT_PGSEL);
+ ptr = (itd->bufptr[pg] & ITD_BUFPTR_MASK) |
+ (itd->transact[i] & ITD_XACT_OFFSET_MASK);
+ len = get_field(itd->transact[i], ITD_XACT_LENGTH);
- ASSERT(len <= BUFF_SIZE);
+ if (len > BUFF_SIZE) {
+ return USB_RET_PROCERR;
+ }
DPRINTF("ISOCH: buffer %08X len %d\n", ptr, len);
@@ -1346,13 +1247,13 @@ static void ehci_process_itd(EHCIState *ehci,
if (ehci->isoch_pause > 0) {
DPRINTF("ISOCH: received a NAK but paused so returning\n");
ehci->isoch_pause--;
- return;
+ return 0;
} else if (ehci->isoch_pause == -1) {
DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n");
// Pause frindex for up to 50 msec waiting for data from
// remote
ehci->isoch_pause = 50;
- return;
+ return 0;
} else {
DPRINTF("ISOCH: isoch pause timeout! return 0\n");
ret = 0;
@@ -1377,10 +1278,7 @@ static void ehci_process_itd(EHCIState *ehci,
if (ret != len) {
DPRINTF("ISOCH IN expected %d, got %d\n",
len, ret);
- set_field(&itd->transact[i],
- ret,
- ITD_XACT_LENGTH_MASK,
- ITD_XACT_LENGTH_SH);
+ set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
}
}
}
@@ -1388,405 +1286,505 @@ static void ehci_process_itd(EHCIState *ehci,
#ifdef EHCI_NOMICROFRAMES
}
#endif
+ return 0;
}
-/* This is the state machine that is common to both async and periodic */
-
-static int ehci_advance_state(EHCIState *ehci,
- int async,
- int state,
- uint32_t entry)
+/* This state is the entry point for asynchronous schedule
+ * processing. Entry here consitutes a EHCI start event state (4.8.5)
+ */
+static int ehci_state_waitlisthead(EHCIState *ehci, int async, int *state)
{
EHCIqh *qh = &ehci->qh;
- EHCIqtd *qtd = &ehci->qtd;
- EHCIitd itd;
+ int i = 0;
int again = 0;
- int loopcount = 0;
- int transactCtr;
- int smask;
- int reload;
- int nakcnt;
+ uint32_t entry = ehci->asynclistaddr;
- do {
- DPRINTF_ST("advance_state: again=%d\n", again);
- again = 0;
- // ASSERT(loopcount++ < MAX_ITERATIONS);
+ /* set reclamation flag at start event (4.8.6) */
+ if (async) {
+ ehci->usbsts |= USBSTS_REC;
+ }
- switch(state) {
- /* This state is the entry point for asynchronous schedule
- * processing. Entry here consitutes a EHCI start event state(4.8.5)
- */
- case EST_WAITLISTHEAD:
- DPRINTF_ST("WAITLISTHEAD\n");
+ /* Find the head of the list (4.9.1.1) */
+ for(i = 0; i < MAX_QH; i++) {
+ get_dwords(NLPTR_GET(entry), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+ if (qh->epchar & QH_EPCHAR_H) {
+ DPRINTF_ST("WAITLISTHEAD: QH %08X is the HEAD of the list\n",
+ entry);
if (async)
- ehci->usbsts |= USBSTS_REC;
+ entry |= (NLPTR_TYPE_QH << 1);
- /* Find the head of the list
- */
+ ehci->fetch_addr = entry;
+ *state = EST_FETCHENTRY;
+ again = 1;
+ goto out;
+ }
- for(loopcount = 0; loopcount < MAX_QH; loopcount++) {
- get_dwords(NLPTR_GET(entry),(uint32_t *) qh,
- sizeof(EHCIqh) >> 2);
+ DPRINTF_ST("WAITLISTHEAD: QH %08X is NOT the HEAD of the list\n",
+ entry);
+ entry = qh->next;
+ if (entry == ehci->asynclistaddr) {
+ DPRINTF("WAITLISTHEAD: reached beginning of QH list\n");
+ break;
+ }
+ }
- if (qh->epchar & QH_EPCHAR_H) {
- DPRINTF_ST("QH %08X is the HEAD of the list\n", entry);
- break;
- }
+ /* no head found for list. */
- DPRINTF_ST("QH %08X is NOT the HEAD of the list\n", entry);
- entry = qh->next;
- }
+ *state = EST_ACTIVE;
- entry |=(NLPTR_TYPE_QH << 1);
- ASSERT(loopcount < MAX_QH);
- loopcount = 0;
+out:
+ return again;
+}
- state = EST_FETCHENTRY;
- again = 1;
- break;
- /* This state is the entry point for periodic schedule
- * processing as well as being a continuation state for async
- * processing.
- */
- case EST_FETCHENTRY:
- DPRINTF_ST("FETCHENTRY\n");
-
- if (qemu_get_clock(vm_clock) / 1000 > ehci->frame_end_usec) {
- if (async) {
- DPRINTF_ST("FRAME timer elapsed, exit state machine\n");
- state = EST_ACTIVE;
- break;
- } else
- DPRINTF("WARNING - frame timer elapsed during periodic\n");
- }
+/* This state is the entry point for periodic schedule processing as
+ * well as being a continuation state for async processing.
+ */
+static int ehci_state_fetchentry(EHCIState *ehci, int async, int *state)
+{
+ int again = 0;
+ uint32_t entry = ehci->fetch_addr;
- if (NLPTR_TBIT(entry)) {
- state = EST_ACTIVE;
- break;
- }
+#if 0
+ if (qemu_get_clock(vm_clock) / 1000 > ehci->frame_end_usec) {
+ if (async) {
+/* TO-DO: supposed to be saving the horizontal QH and picking up
+ * from there on next frame
+ */
+ printf("FETCHENTRY: FRAME timer elapsed, exit state machine\n");
+ *state = EST_ACTIVE;
+ goto out;
+ } else {
+ DPRINTF("FETCHENTRY: WARNING "
+ "- frame timer elapsed during periodic\n");
+ }
+ }
+#endif
- if (NLPTR_TYPE_GET(entry) == NLPTR_TYPE_QH) {
- state = EST_FETCHQH;
- ehci->qhaddr = entry;
- again = 1;
- break;
- }
+ /* section 4.8, only QH in async schedule */
+ if (async && (NLPTR_TYPE_GET(entry) != NLPTR_TYPE_QH)) {
+ fprintf(stderr, "non queue head request in async schedule\n");
+ return -1;
+ }
- if (NLPTR_TYPE_GET(entry) == NLPTR_TYPE_ITD) {
- state = EST_FETCHITD;
- ehci->itdaddr = entry;
- again = 1;
- break;
- }
+ switch (NLPTR_TYPE_GET(entry)) {
+ case NLPTR_TYPE_QH:
+ DPRINTF_ST("FETCHENTRY: entry %X is a Queue Head\n", *entry);
+ *state = EST_FETCHQH;
+ ehci->qhaddr = entry;
+ again = 1;
+ break;
- // TODO handle types other than QH
- ASSERT(NLPTR_TYPE_GET(entry) == NLPTR_TYPE_QH);
- break;
+ case NLPTR_TYPE_ITD:
+ DPRINTF_ST("FETCHENTRY: entry %X is an ITD\n", *entry);
+ *state = EST_FETCHITD;
+ ehci->itdaddr = entry;
+ again = 1;
+ break;
- case EST_FETCHQH:
- get_dwords(NLPTR_GET(ehci->qhaddr),(uint32_t *) qh,
- sizeof(EHCIqh) >> 2);
- DPRINTF_ST("FETCHQH: Fetched QH at address %08X "
- "(next is %08X, h-bit is %d)\n",
- ehci->qhaddr, qh->next, qh->epchar & QH_EPCHAR_H);
-
-#if DEBUG_PACKET
- dump_qh(qh, NLPTR_GET(ehci->qhaddr));
-#endif
+ default:
+ // TODO: handle siTD and FSTN types
+ fprintf(stderr, "FETCHENTRY: entry at %X is of type %d "
+ "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry));
+ return -1;
+ }
- if (async) {
- /* EHCI spec version 1.0 Section 4.8.3
- */
- if (qh->epchar & QH_EPCHAR_H) {
- DPRINTF_ST("h-bit set\n");
-
- if (!(ehci->usbsts & USBSTS_REC)) {
- DPRINTF_ST("h-bit and !reclam, done\n");
- state = EST_ACTIVE;
- break;
- }
- }
- /* EHCI spec version 1.0 Section 4.10.1
- */
- if ( !(qh->epcap & QH_EPCAP_SMASK_MASK)) {
- DPRINTF_ST("not intr, clear reclam\n");
- ehci->usbsts &= ~USBSTS_REC;
- }
- } else {
- DPRINTF_ST("exec: qh check, frindex is %d,%d\n",
- (ehci->frindex >> 3),(ehci->frindex & 7));
- }
+ return again;
+}
- reload = get_field(qh->epchar, QH_EPCHAR_RL_MASK, QH_EPCHAR_RL_SH);
+static int ehci_state_fetchqh(EHCIState *ehci, int async, int *state)
+{
+ EHCIqh *qh = &ehci->qh;
+ int reload;
+ int again = 0;
- if (reload) {
- DPRINTF_ST("reloading nakcnt to %d\n",
- reload);
- set_field(&qh->altnext, reload, QH_ALTNEXT_NAKCNT_MASK,
- QH_ALTNEXT_NAKCNT_SH);
- }
+ get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
- if (qh->token & QTD_TOKEN_ACTIVE) {
- if ((qh->token & QTD_TOKEN_HALT)) {
- fprintf(stderr, "Active, Halt, ** ILLEGAL **\n");
- state = EST_ACTIVE;
- } else {
- DPRINTF_ST("Active, !Halt, execute - fetchqtd\n");
- ehci->qtdaddr = qh->current;
- state = EST_FETCHQTD;
- again = 1;
- }
- } else {
- if (qh->token & QTD_TOKEN_HALT) {
- DPRINTF_ST("!Active, Halt, go horiz\n");
- state = EST_HORIZONTALQH;
- again = 1;
- } else {
- /* EHCI spec version 1.0 Section 4.10.2
- */
- DPRINTF_ST("!Active, !Halt, adv q\n");
- state = EST_ADVANCEQUEUE;
- again = 1;
- }
- }
+ if (async && (qh->epchar & QH_EPCHAR_H)) {
- break;
+ /* EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */
+ if (ehci->usbsts & USBSTS_REC) {
+ ehci->usbsts &= ~USBSTS_REC;
+ } else {
+ DPRINTF("FETCHQH: QH 0x%08x. H-bit set, reclamation status reset"
+ " - done processing\n", ehci->qhaddr);
+ *state = EST_ACTIVE;
+ goto out;
+ }
+ }
- case EST_FETCHITD:
- get_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd,
- sizeof(EHCIitd) >> 2);
- DPRINTF_ST("FETCHITD: Fetched ITD at address %08X "
- "(next is %08X)\n",
- ehci->itdaddr, itd.next);
-
-#if DEBUG_PACKET
- dump_itd(&itd, NLPTR_GET(ehci->itdaddr));
+#if 0
+ if (ehci->qhaddr != qh->next) {
+ DPRINTF("FETCHQH: QH 0x%08x (h %x halt %x active %x) next 0x%08x\n",
+ ehci->qhaddr,
+ qh->epchar & QH_EPCHAR_H,
+ qh->token & QTD_TOKEN_HALT,
+ qh->token & QTD_TOKEN_ACTIVE,
+ qh->next);
+ }
#endif
- ehci_process_itd(ehci, &itd);
-#if DEBUG_PACKET
- dump_itd(&itd, NLPTR_GET(ehci->itdaddr));
-#endif
- put_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd,
- sizeof(EHCIitd) >> 2);
- entry = itd.next;
- state = EST_FETCHENTRY;
- again = 1;
- break;
+ reload = get_field(qh->epchar, QH_EPCHAR_RL);
+ if (reload) {
+ DPRINTF_ST("FETCHQH: reloading nakcnt to %d\n", reload);
+ set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
+ }
- case EST_ADVANCEQUEUE:
- DPRINTF_ST("ADVANCEQUEUE\n");
- if ((qh->token & QTD_TOKEN_TBYTES_MASK) != 0 &&
- NLPTR_TBIT(qh->altnext) == 0) {
- ehci->qtdaddr = qh->altnext;
- DPRINTF_ST("tbytes!=0 and tbit = 0, go with altnext\n");
- } else {
- if (NLPTR_TBIT(qh->qtdnext)) {
- state = EST_HORIZONTALQH;
- again = 1;
- break;
- }
+ if (qh->token & QTD_TOKEN_HALT) {
+ DPRINTF_ST("FETCHQH: QH Halted, go horizontal\n");
+ *state = EST_HORIZONTALQH;
+ again = 1;
- ehci->qtdaddr = qh->qtdnext;
- }
- state = EST_FETCHQTD;
- again = 1;
- break;
+ } else if (qh->token & QTD_TOKEN_ACTIVE) {
+ DPRINTF_ST("FETCHQH: Active, !Halt, execute - fetch qTD\n");
+ ehci->qtdaddr = qh->current_qtd;
+ *state = EST_FETCHQTD;
+ again = 1;
- case EST_FETCHQTD:
- DPRINTF_ST("FETCHQTD: Fetching QTD at address %08X\n", ehci->qtdaddr);
- get_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) qtd,
- sizeof(EHCIqtd) >> 2);
+ } else {
+ /* EHCI spec version 1.0 Section 4.10.2 */
+ DPRINTF_ST("FETCHQH: !Active, !Halt, advance queue\n");
+ *state = EST_ADVANCEQUEUE;
+ again = 1;
+ }
- if (qtd->token & QTD_TOKEN_ACTIVE) {
- state = EST_EXECUTE;
- again = 1;
- break;
- } else {
- DPRINTF_ST("abort advance, not active\n");
- state = EST_HORIZONTALQH;
- again = 1;
- break;
- }
+out:
+ return again;
+}
- break;
+static int ehci_state_fetchitd(EHCIState *ehci, int async, int *state)
+{
+ EHCIitd itd;
- case EST_HORIZONTALQH:
- entry = qh->next;
- state = EST_FETCHENTRY;
- again = 1;
- break;
+ get_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd,
+ sizeof(EHCIitd) >> 2);
+ DPRINTF_ST("FETCHITD: Fetched ITD at address %08X " "(next is %08X)\n",
+ ehci->itdaddr, itd.next);
- case EST_EXECUTE:
- if (async) {
- DPRINTF("\n\n>>>>> ASYNC STATE MACHINE execute\n");
- } else {
- DPRINTF("\n\n>>>>> PERIODIC STATE MACHINE execute\n");
- }
+ if (ehci_process_itd(ehci, &itd) != 0)
+ return -1;
-#if DEBUG_PACKET
- dump_qh(qh, NLPTR_GET(ehci->qhaddr));
- dump_qtd(qtd, NLPTR_GET(ehci->qtdaddr));
+ put_dwords(NLPTR_GET(ehci->itdaddr), (uint32_t *) &itd,
+ sizeof(EHCIitd) >> 2);
+ ehci->itdaddr = itd.next;
+ *state = EST_FETCHITD;
+
+ return 1;
+}
+
+/* Section 4.10.2 - paragraph 3 */
+static int ehci_state_advqueue(EHCIState *ehci, int async, int *state)
+{
+#if 0
+ /* TO-DO: 4.10.2 - paragraph 2
+ * if I-bit is set to 1 and QH is not active
+ * go to horizontal QH
+ */
+ if (I-bit set) {
+ *state = EST_HORIZONTALQH;
+ goto out;
+ }
#endif
- smask = QH_EPCAP_SMASK_MASK & qh->epcap;
+ /*
+ * want data and alt-next qTD is valid
+ */
+ if (((ehci->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
+ (NLPTR_TBIT(ehci->qh.altnext_qtd) == 0)) {
+ DPRINTF_ST("ADVQUEUE: goto alt next qTD. "
+ "curr 0x%08x next 0x%08x alt 0x%08x\n",
+ ehci->qh.current_qtd, ehci->qh.altnext_qtd,
+ ehci->qh.next_qtd);
+ ehci->qtdaddr = ehci->qh.altnext_qtd;
+ *state = EST_FETCHQTD;
+
+ /*
+ * next qTD is valid
+ */
+ } else if (NLPTR_TBIT(ehci->qh.next_qtd) == 0) {
+ DPRINTF_ST("ADVQUEUE: next qTD. curr 0x%08x next 0x%08x alt 0x%08x\n",
+ ehci->qh.current_qtd, ehci->qh.altnext_qtd,
+ ehci->qh.next_qtd);
+ ehci->qtdaddr = ehci->qh.next_qtd;
+ *state = EST_FETCHQTD;
+ /*
+ * no valid qTD, try next QH
+ */
+ } else {
+ DPRINTF_ST("ADVQUEUE: go to horizontal QH\n");
+ *state = EST_HORIZONTALQH;
+ }
+
+ return 1;
+}
+
+/* Section 4.10.2 - paragraph 4 */
+static int ehci_state_fetchqtd(EHCIState *ehci, int async, int *state)
+{
+ EHCIqtd *qtd = &ehci->qtd;
+ int again = 0;
+
+ get_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) qtd, sizeof(EHCIqtd) >> 2);
+
+ if (qtd->token & QTD_TOKEN_ACTIVE) {
+ *state = EST_EXECUTE;
+ again = 1;
+ } else {
+ *state = EST_HORIZONTALQH;
+ again = 1;
+ }
+
+ return again;
+}
+
+static int ehci_state_horizqh(EHCIState *ehci, int async, int *state)
+{
+ ehci->fetch_addr = ehci->qh.next;
+ *state = EST_FETCHENTRY;
+
+ return 1;
+}
+
+static int ehci_state_execute(EHCIState *ehci, int async, int *state)
+{
+ EHCIqh *qh = &ehci->qh;
+ EHCIqtd *qtd = &ehci->qtd;
+ int again = 0;
+ int reload, nakcnt;
+ 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)
+ return -1;
+
+ smask = get_field(qh->epcap, QH_EPCAP_SMASK);
#ifndef EHCI_NOMICROFRAMES
- if (smask &&(smask &(1 <<(ehci->frindex & 7))) == 0) {
- DPRINTF("PERIODIC active not interval: "
- "mask is %d, "
- "frindex is %d,%d\n",
- smask,
- (ehci->frindex >> 3),(ehci->frindex & 7));
-
- state = EST_HORIZONTALQH;
- again = 1;
- break;
- }
+ if (smask && (smask & (1 << (ehci->frindex & 7))) == 0) {
+ DPRINTF_ST("PERIODIC active not interval: mask %x, frindex %d,%d\n",
+ smask, (ehci->frindex >> 3),(ehci->frindex & 7));
+
+ *state = EST_HORIZONTALQH;
+ again = 1;
+ goto out;
+ }
#endif
- if (smask) {
- DPRINTF("PERIODIC active !!! "
- "mask is %d, "
- "frindex is %d,%d\n",
- smask,
- (ehci->frindex >> 3),(ehci->frindex & 7));
- }
+ if (!smask) {
+ reload = get_field(qh->epchar, QH_EPCHAR_RL);
+ nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
+ if (reload && !nakcnt) {
+ DPRINTF_ST("EXECUTE: RL != 0 but NakCnt == 0 -- no execute\n");
+ *state = EST_HORIZONTALQH;
+ again = 1;
+ goto out;
+ }
+ }
- reload = get_field(qh->epchar, QH_EPCHAR_RL_MASK, QH_EPCHAR_RL_SH);
- nakcnt = get_field(qh->altnext, QH_ALTNEXT_NAKCNT_MASK,
- QH_ALTNEXT_NAKCNT_SH);
- if (reload && !nakcnt) {
- DPRINTF("RL != 0 but NakCnt == 0, no execute\n");
- state = EST_HORIZONTALQH;
- again = 1;
- break;
- }
+ // TODO verify enough time remains in the uframe as in 4.4.1.1
- transactCtr = get_field(qh->epcap,
- QH_EPCAP_MULT_MASK, QH_EPCAP_MULT_SH);
+ // TODO write back ptr to async list when done or out of time
- // TODO verify enough time remains in the uframe as in 4.4.1.1
+ // TODO Windows does not seem to ever set the MULT field
- // TODO write back ptr to async list when done or out of time
- // TODO Windows does not seem to ever set the MULT field
+ if (!async)
+ {
+ int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
+ if (!transactCtr) {
+ DPRINTF("ZERO transactctr for int qh, go HORIZ\n");
+ *state = EST_HORIZONTALQH;
+ again = 1;
+ goto out;
+ }
+ }
-#if 0
- if (!transactCtr &&(qh->epcap & QH_EPCAP_SMASK_MASK) > 0)
- {
- DPRINTF("ZERO transactctr for int qh, go HORIZ\n");
- *state = EST_HORIZONTALQH;
- again = 1;
- break;
- }
-#endif
- if (!transactCtr) {
- transactCtr = 1; // TODO - check at what level do we repeat
+ if (async)
+ ehci->usbsts |= USBSTS_REC;
+ ehci->more = 0;
+ ehci->exec_status = ehci_execute(ehci, qh);
+ if (ehci->exec_status == USB_RET_PROCERR) {
+ again = -1;
+ goto out;
+ }
+ *state = EST_EXECUTING;
+
+ if (ehci->exec_status != USB_RET_ASYNC)
+ again = 1;
- if (qh->epcap & QH_EPCAP_SMASK_MASK)
- DPRINTF("WARN - ZERO transactctr force to 1 for intr\n");
+out:
+ return again;
+}
+
+static int ehci_state_executing(EHCIState *ehci, int async, int *state)
+{
+ EHCIqh *qh = &ehci->qh;
+ int again = 0;
+ int reload, nakcnt;
+
+ ehci->exec_status = ehci_execute_complete(ehci, qh, ehci->exec_status);
+ if (ehci->exec_status == USB_RET_ASYNC) {
+ goto out;
+ }
+ if (ehci->exec_status == USB_RET_PROCERR) {
+ again = -1;
+ goto out;
+ }
+
+ // 4.10.3
+ if (!async) {
+ int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
+ transactCtr--;
+ set_field(&qh->epcap, transactCtr, QH_EPCAP_MULT);
+ // 4.10.3, bottom of page 82, should exit this state when transaction
+ // counter decrements to 0
+ }
+
+
+ reload = get_field(qh->epchar, QH_EPCHAR_RL);
+ if (reload) {
+ nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
+ if (ehci->exec_status == USB_RET_NAK) {
+ if (nakcnt) {
+ nakcnt--;
}
+ DPRINTF_ST("EXECUTING: Nak occured and RL != 0, dec NakCnt to %d\n",
+ nakcnt);
+ } else {
+ nakcnt = reload;
+ DPRINTF_ST("EXECUTING: Nak didn't occur, reloading to %d\n",
+ nakcnt);
+ }
+ set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
+ }
- DPRINTF("exec: ctr is %d\n", transactCtr);
- DPRINTF("exec: frindex is %d,%d\n",
- (ehci->frindex >> 3),(ehci->frindex & 7));
+ /*
+ * Write the qh back to guest physical memory. This step isn't
+ * in the EHCI spec but we need to do it since we don't share
+ * physical memory with our guest VM.
+ */
- ehci_qh_do_overlay(ehci, qh, qtd);
- ehci->exec_status = ehci_execute(ehci, ehci->qhaddr, qh);
- state = EST_EXECUTING;
+ 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);
- if (ehci->exec_status != USB_RET_ASYNC)
- again = 1;
+ /* 4.10.5 */
+ if ((ehci->exec_status == USB_RET_NAK) || (qh->token & QTD_TOKEN_ACTIVE)) {
+ *state = EST_HORIZONTALQH;
+ } else {
+ *state = EST_WRITEBACK;
+ }
- break;
+ again = 1;
- case EST_EXECUTING:
- DPRINTF("Enter EXECUTING\n");
- ehci->exec_status = ehci_execute_complete(ehci, qh,
- ehci->exec_status);
+out:
+ return again;
+}
- if (ehci->exec_status == USB_RET_ASYNC)
- break;
- DPRINTF("finishing exec\n");
- transactCtr = get_field(qh->epcap,
- QH_EPCAP_MULT_MASK, QH_EPCAP_MULT_SH);
+static int ehci_state_writeback(EHCIState *ehci, int async, int *state)
+{
+ EHCIqh *qh = &ehci->qh;
+ int again = 0;
- if (transactCtr)
- transactCtr--;
+ /* Write back the QTD from the QH area */
+ DPRINTF_ST("WRITEBACK: write QTD to VM memory\n");
+ put_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) &qh->next_qtd,
+ sizeof(EHCIqtd) >> 2);
- set_field(&qh->epcap, transactCtr,
- QH_EPCAP_MULT_MASK, QH_EPCAP_MULT_SH);
+ /* TODO confirm next state. For now, keep going if async
+ * but stop after one qtd if periodic
+ */
+ //if (async) {
+ *state = EST_ADVANCEQUEUE;
+ again = 1;
+ //} else {
+ // *state = EST_ACTIVE;
+ //}
+ return again;
+}
- reload = get_field(qh->epchar, QH_EPCHAR_RL_MASK, QH_EPCHAR_RL_SH);
- nakcnt = get_field(qh->altnext, QH_ALTNEXT_NAKCNT_MASK,
- QH_ALTNEXT_NAKCNT_SH);
+/*
+ * This is the state machine that is common to both async and periodic
+ */
- if (reload != 0) {
- if (ehci->exec_status == USB_RET_NAK) {
- nakcnt--;
+static int ehci_advance_state(EHCIState *ehci,
+ int async,
+ int state)
+{
+ int again;
+ int iter = 0;
- DPRINTF("Nak occured and RL != 0, dec NakCnt to %d\n",
- nakcnt);
- } else {
- nakcnt = reload;
+ do {
+ iter++;
+ if (iter > MAX_ITERATIONS) {
+ DPRINTF("\n*** advance_state: bailing on MAX ITERATIONS***\n\n");
+ state = EST_ACTIVE;
+ break;
+ }
+ switch(state) {
+ case EST_WAITLISTHEAD:
+ again = ehci_state_waitlisthead(ehci, async, &state);
+ break;
- DPRINTF("Nak didn't occur, reloading to %d\n",
- nakcnt);
- }
+ case EST_FETCHENTRY:
+ again = ehci_state_fetchentry(ehci, async, &state);
+ break;
- set_field(&qh->altnext, nakcnt, QH_ALTNEXT_NAKCNT_MASK,
- QH_ALTNEXT_NAKCNT_SH);
- }
+ case EST_FETCHQH:
+ again = ehci_state_fetchqh(ehci, async, &state);
+ break;
- /*
- * Write the qh back to guest physical memory. This step isn't
- * in the EHCI spec but we need to do it since we don't share
- * physical memory with our guest VM.
- */
+ case EST_FETCHITD:
+ again = ehci_state_fetchitd(ehci, async, &state);
+ break;
- DPRINTF("write QH to VM memory\n");
-#if DEBUG_PACKET
- dump_qh(qh, NLPTR_GET(ehci->qhaddr));
-#endif
- put_dwords(NLPTR_GET(ehci->qhaddr),(uint32_t *) qh,
- sizeof(EHCIqh) >> 2);
+ case EST_ADVANCEQUEUE:
+ again = ehci_state_advqueue(ehci, async, &state);
+ break;
- // 4.10.5
+ case EST_FETCHQTD:
+ again = ehci_state_fetchqtd(ehci, async, &state);
+ break;
- if (qh->token & QTD_TOKEN_ACTIVE)
- state = EST_HORIZONTALQH;
- else
- state = EST_WRITEBACK;
+ case EST_HORIZONTALQH:
+ again = ehci_state_horizqh(ehci, async, &state);
+ break;
- again = 1;
+ case EST_EXECUTE:
+ iter = 0;
+ again = ehci_state_execute(ehci, async, &state);
+ break;
+
+ case EST_EXECUTING:
+ iter = 0;
+ again = ehci_state_executing(ehci, async, &state);
break;
case EST_WRITEBACK:
- /* Write back the QTD from the QH area */
- DPRINTF_ST("write QTD to VM memory\n");
- put_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) &qh->qtdnext,
- sizeof(EHCIqtd) >> 2);
- /* TODO confirm next state. For now, keep going if async
- * but stop after one qtd if periodic
- */
- // if (async)
- // {
- state = EST_FETCHQH;
- again = 1;
- // }
- // else
- // state = EST_ACTIVE;
+ iter = 0;
+ again = ehci_state_writeback(ehci, async, &state);
break;
default:
fprintf(stderr, "Bad state!\n");
+ again = -1;
break;
- }
+ }
+
+ if (again < 0) {
+ fprintf(stderr, "processing error - resetting ehci HC\n");
+ ehci_reset(ehci);
+ again = 0;
+ }
}
while (again);
@@ -1795,47 +1793,58 @@ static int ehci_advance_state(EHCIState *ehci,
static void ehci_advance_async_state(EHCIState *ehci)
{
+ EHCIqh qh;
+
switch(ehci->astate) {
case EST_INACTIVE:
- if (ehci->usbcmd & USBCMD_ASE) {
- DPRINTF("ASYNC going active\n");
- ehci->usbsts |= USBSTS_ASS;
- ehci->astate = EST_ACTIVE;
- // No break, fall through to ACTIVE
- } else
+ if (!(ehci->usbcmd & USBCMD_ASE)) {
break;
+ }
+ ehci->usbsts |= USBSTS_ASS;
+ ehci->astate = EST_ACTIVE;
+ // No break, fall through to ACTIVE
case EST_ACTIVE:
if ( !(ehci->usbcmd & USBCMD_ASE)) {
- DPRINTF("ASYNC going inactive\n");
ehci->usbsts &= ~USBSTS_ASS;
ehci->astate = EST_INACTIVE;
break;
}
- DPRINTF_ST("\n === === === === === ===\n\n");
+ /* 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 (ehci->usbcmd & USBCMD_IAAD) {
- /* Async advance doorbell interrupted requested
- */
+ DPRINTF("ASYNC: doorbell request acknowledged\n");
ehci->usbcmd &= ~USBCMD_IAAD;
ehci_set_interrupt(ehci, USBSTS_IAA);
+ break;
}
- ehci->astate = ehci_advance_state(ehci, 1,
- EST_WAITLISTHEAD,
- ehci->asynclistaddr);
+ /* make sure guest has acknowledged */
+ /* TO-DO: is this really needed? */
+ if (ehci->usbsts & USBSTS_IAA) {
+ DPRINTF("IAA status bit still set.\n");
+ break;
+ }
+
+ DPRINTF_ST("ASYNC: waiting for listhead, starting at %08x\n",
+ ehci->asynclistaddr);
+ ehci->astate = ehci_advance_state(ehci, 1, EST_WAITLISTHEAD);
break;
case EST_EXECUTING:
- DPRINTF("async state adv for executing\n");
- ehci->astate = ehci_advance_state(ehci, 1,
- EST_EXECUTING, ehci->qhaddr);
+ get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) &qh,
+ sizeof(EHCIqh) >> 2);
+ ehci->astate = ehci_advance_state(ehci, 1, EST_EXECUTING);
break;
default:
- fprintf(stderr, "Bad asynchronous state %d\n",
- ehci->astate);
- ASSERT(1==2);
+ /* this should only be due to a developer mistake */
+ fprintf(stderr, "ehci: Bad asynchronous state %d. "
+ "Resetting to active\n", ehci->astate);
+ ehci->astate = EST_ACTIVE;
}
}
@@ -1848,7 +1857,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
switch(ehci->pstate) {
case EST_INACTIVE:
- if ( !(ehci->frindex & 7) &&(ehci->usbcmd & USBCMD_PSE)) {
+ if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) {
DPRINTF("PERIODIC going active\n");
ehci->usbsts |= USBSTS_PSS;
ehci->pstate = EST_ACTIVE;
@@ -1865,27 +1874,27 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
}
list = ehci->periodiclistbase & 0xfffff000;
- list |=((ehci->frindex & 0x1ff8) >> 1);
+ list |= ((ehci->frindex & 0x1ff8) >> 1);
- cpu_physical_memory_rw(list,(uint8_t *) &entry, sizeof entry, 0);
+ cpu_physical_memory_rw(list, (uint8_t *) &entry, sizeof entry, 0);
entry = le32_to_cpu(entry);
- DPRINTF("periodic state adv fr=%d. [%08X] -> %08X\n",
+ DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n",
ehci->frindex / 8, list, entry);
- ehci->pstate = ehci_advance_state(ehci, 0,
- EST_FETCHENTRY, entry);
+ ehci->fetch_addr = entry;
+ ehci->pstate = ehci_advance_state(ehci, 0, EST_FETCHENTRY);
break;
case EST_EXECUTING:
- DPRINTF("periodic state adv for executing\n");
- ehci->pstate = ehci_advance_state(ehci, 0,
- EST_EXECUTING, ehci->qhaddr);
+ DPRINTF("PERIODIC state adv for executing\n");
+ ehci->pstate = ehci_advance_state(ehci, 0, EST_EXECUTING);
break;
default:
- fprintf(stderr, "Bad periodic state %d\n",
- ehci->pstate);
- ASSERT(1==2);
+ /* this should only be due to a developer mistake */
+ fprintf(stderr, "ehci: Bad periodic state %d. "
+ "Resetting to active\n", ehci->pstate);
+ ehci->pstate = EST_ACTIVE;
}
}
@@ -1910,11 +1919,6 @@ static void ehci_frame_timer(void *opaque)
frames = usec_elapsed / FRAME_TIMER_USEC;
ehci->frame_end_usec = usec_now + FRAME_TIMER_USEC;
-#if TDEBUG
- DPRINTF("Frame timer, usec elapsed since last %d, frames %d\n",
- usec_elapsed, frames);
-#endif
-
for(i = 0; i < frames; i++) {
if ( !(ehci->usbsts & USBSTS_HALT)) {
if (ehci->isoch_pause <= 0) {
@@ -1927,13 +1931,10 @@ static void ehci_frame_timer(void *opaque)
if (ehci->frindex > 0x00001fff) {
ehci->frindex = 0;
-#if TDEBUG
- DPRINTF("PERIODIC frindex rollover\n");
-#endif
ehci_set_interrupt(ehci, USBSTS_FLR);
}
- ehci->sofv =(ehci->frindex - 1) >> 3;
+ ehci->sofv = (ehci->frindex - 1) >> 3;
ehci->sofv &= 0x000003ff;
}
@@ -1961,11 +1962,6 @@ static void ehci_frame_timer(void *opaque)
ehci_advance_async_state(ehci);
qemu_mod_timer(ehci->frame_timer, expire_time);
-
-#if TDEBUG
- usec_elapsed = qemu_get_clock(vm_clock) / 1000 - usec_now;
- DPRINTF("TIMING: frame_timer used %d usec\n", usec_elapsed);
-#endif
}
static CPUReadMemoryFunc *ehci_readfn[3]={
@@ -2021,8 +2017,8 @@ static int usb_ehci_initfn(PCIDevice *dev)
//pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50);
pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); // interrupt pin 3
- pci_set_byte(&pci_conf[PCI_MIN_GNT], 0); // MaxLat
- pci_set_byte(&pci_conf[PCI_MAX_LAT], 0); // MinGnt
+ 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
@@ -2043,17 +2039,19 @@ static int usb_ehci_initfn(PCIDevice *dev)
pci_conf[0x6e] = 0x00;
pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS
- // 2.2.2 host controller interface version
- pci_set_byte(&s->mmio[CAPLENGTH], OPREGBASE);
- pci_set_word(&s->mmio[HCIVERSION], 0x0100);
-
- // 2.2.3 host controller structural parameters
- pci_set_word(&s->mmio[HCSPARAMS], NB_PORTS);
-
- // 2.2.4 host controller capability parameters
- // - 0x80 = can cache whole frame, not 64-bit capable
- pci_set_word(&s->mmio[HCCPARAMS], 0x00000080);
-
+ // 2.2 host controller interface version
+ s->mmio[0x00] = (uint8_t) OPREGBASE;
+ s->mmio[0x01] = 0x00;
+ s->mmio[0x02] = 0x00;
+ s->mmio[0x03] = 0x01; // HC version
+ s->mmio[0x04] = NB_PORTS; // Number of downstream ports
+ s->mmio[0x05] = 0x00; // No companion ports at present
+ s->mmio[0x06] = 0x00;
+ s->mmio[0x07] = 0x00;
+ s->mmio[0x08] = 0x80; // We can cache whole frame, not 64-bit capable
+ s->mmio[0x09] = 0x68; // EECP
+ s->mmio[0x0a] = 0x00;
+ s->mmio[0x0b] = 0x00;
s->irq = s->dev.irq[3];
@@ -2075,7 +2073,6 @@ static int usb_ehci_initfn(PCIDevice *dev)
s->frame_timer = qemu_new_timer(vm_clock, ehci_frame_timer, s);
- DPRINTF("ehci_init: calling ehci_reset\n");
qemu_register_reset(ehci_reset, s);
s->mem = cpu_register_io_memory(ehci_readfn, ehci_writefn, s);