Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/dev/usb Add some interrupt processing.
details: https://anonhg.NetBSD.org/src/rev/5ae8cd8115ab
branches: trunk
changeset: 517917:5ae8cd8115ab
user: augustss <augustss%NetBSD.org@localhost>
date: Wed Nov 21 12:28:23 2001 +0000
description:
Add some interrupt processing.
diffstat:
sys/dev/usb/ehci.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++---
sys/dev/usb/ehcireg.h | 3 +-
sys/dev/usb/ehcivar.h | 29 ++--
3 files changed, 262 insertions(+), 35 deletions(-)
diffs (truncated from 556 to 300 lines):
diff -r 14f244260de0 -r 5ae8cd8115ab sys/dev/usb/ehci.c
--- a/sys/dev/usb/ehci.c Wed Nov 21 12:25:55 2001 +0000
+++ b/sys/dev/usb/ehci.c Wed Nov 21 12:28:23 2001 +0000
@@ -1,7 +1,7 @@
/* TODO
Add intrinfo.
*/
-/* $NetBSD: ehci.c,v 1.17 2001/11/21 08:18:39 augustss Exp $ */
+/* $NetBSD: ehci.c,v 1.18 2001/11/21 12:28:23 augustss Exp $ */
/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -50,7 +50,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.17 2001/11/21 08:18:39 augustss Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.18 2001/11/21 12:28:23 augustss Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -119,6 +119,8 @@
Static void ehci_softintr(void *);
Static int ehci_intr1(ehci_softc_t *);
Static void ehci_waitintr(ehci_softc_t *, usbd_xfer_handle);
+Static void ehci_check_intr(ehci_softc_t *, struct ehci_xfer *);
+Static void ehci_idone(struct ehci_xfer *);
Static void ehci_timeout(void *);
Static void ehci_timeout_task(void *);
@@ -181,6 +183,8 @@
Static usbd_status ehci_alloc_std_chain(struct ehci_pipe *,
ehci_softc_t *, int, int, usbd_xfer_handle,
ehci_soft_qtd_t **, ehci_soft_qtd_t **);
+Static void ehci_free_std_chain(ehci_softc_t *, ehci_soft_qtd_t *,
+ ehci_soft_qtd_t *);
Static usbd_status ehci_device_request(usbd_xfer_handle xfer);
@@ -193,7 +197,7 @@
Static void ehci_abort_xfer(usbd_xfer_handle, usbd_status);
#ifdef EHCI_DEBUG
-Static void ehci_dumpregs(ehci_softc_t *);
+Static void ehci_dump_regs(ehci_softc_t *);
Static void ehci_dump(void);
Static ehci_softc_t *theehci;
Static void ehci_dump_link(ehci_link_t, int);
@@ -201,6 +205,7 @@
Static void ehci_dump_sqtd(ehci_soft_qtd_t *);
Static void ehci_dump_qtd(ehci_qtd_t *);
Static void ehci_dump_sqh(ehci_soft_qh_t *);
+Static void ehci_dump_exfer(struct ehci_xfer *);
#endif
#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
@@ -209,6 +214,11 @@
#define EHCI_INTR_ENDPT 1
+#define ehci_add_intr_list(sc, ex) \
+ LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ex), inext);
+#define ehci_del_intr_list(ex) \
+ LIST_REMOVE((ex), inext)
+
Static struct usbd_bus_methods ehci_bus_methods = {
ehci_open,
ehci_softintr,
@@ -477,13 +487,10 @@
wakeup(&sc->sc_async_head);
eintrs &= ~EHCI_STS_INT;
}
- if (eintrs & EHCI_STS_INT) {
- DPRINTF(("ehci_intr1: something is done\n"));
- eintrs &= ~EHCI_STS_INT;
- }
- if (eintrs & EHCI_STS_ERRINT) {
- DPRINTF(("ehci_intr1: some error\n"));
- eintrs &= ~EHCI_STS_HSE;
+ if (eintrs & (EHCI_STS_INT | EHCI_STS_ERRINT)) {
+ DPRINTF(("ehci_intr1: INT/ERRINT\n"));
+ usb_schedsoftintr(&sc->sc_bus);
+ eintrs &= ~(EHCI_STS_INT | EHCI_STS_HSE);
}
if (eintrs & EHCI_STS_HSE) {
printf("%s: unrecoverable error, controller halted\n",
@@ -568,12 +575,168 @@
void
ehci_softintr(void *v)
{
- //ehci_softc_t *sc = v;
+ ehci_softc_t *sc = v;
+ struct ehci_xfer *ex;
+
+ DPRINTFN(10,("%s: ehci_softintr (%d)\n", USBDEVNAME(sc->sc_bus.bdev),
+ sc->sc_bus.intr_context));
+
+ sc->sc_bus.intr_context++;
+
+ /*
+ * The only explanation I can think of for why EHCI is as brain dead
+ * as UHCI interrupt-wise is that Intel was involved in both.
+ * An interrupt just tells us that something is done, we have no
+ * clue what, so we need to scan through all active transfers. :-(
+ */
+ for (ex = LIST_FIRST(&sc->sc_intrhead); ex; ex = LIST_NEXT(ex, inext))
+ ehci_check_intr(sc, ex);
+
+ sc->sc_bus.intr_context--;
+}
+
+/* Check for an interrupt. */
+void
+ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
+{
+ ehci_soft_qtd_t *sqtd, *lsqtd;
+ u_int32_t status;
+
+ DPRINTFN(15, ("ehci_check_intr: ex=%p\n", ex));
+
+ if (ex->sqtdstart == NULL) {
+ printf("ehci_check_intr: sqtdstart=NULL\n");
+ return;
+ }
+ lsqtd = ex->sqtdend;
+#ifdef DIAGNOSTIC
+ if (lsqtd == NULL) {
+ printf("ehci_check_intr: sqtd==0\n");
+ return;
+ }
+#endif
+ /*
+ * If the last TD is still active we need to check whether there
+ * is a an error somewhere in the middle, or whether there was a
+ * short packet (SPD and not ACTIVE).
+ */
+ if (le32toh(lsqtd->qtd.qtd_status) & EHCI_QTD_ACTIVE) {
+ DPRINTFN(12, ("ehci_check_intr: active ex=%p\n", ex));
+ for (sqtd = ex->sqtdstart; sqtd != lsqtd; sqtd=sqtd->nextqtd) {
+ status = le32toh(sqtd->qtd.qtd_status);
+ /* If there's an active QTD the xfer isn't done. */
+ if (status & EHCI_QTD_ACTIVE)
+ break;
+ /* Any kind of error makes the xfer done. */
+ if (status & EHCI_QTD_HALTED)
+ goto done;
+ /* We want short packets, and it is short: it's done */
+ if (EHCI_QTD_SET_BYTES(status) != 0)
+ goto done;
+ }
+ DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n",
+ ex, ex->sqtdstart));
+ return;
+ }
+ done:
+ DPRINTFN(12, ("ehci_check_intr: ex=%p done\n", ex));
+ usb_uncallout(ex->xfer.timeout_handle, ehci_timeout, ex);
+ ehci_idone(ex);
+}
+
+void
+ehci_idone(struct ehci_xfer *ex)
+{
+ usbd_xfer_handle xfer = &ex->xfer;
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+ ehci_soft_qtd_t *sqtd;
+ u_int32_t status = 0, nstatus;
+ int actlen;
+
+ DPRINTFN(12, ("ehci_idone: ex=%p\n", ex));
+#ifdef DIAGNOSTIC
+ {
+ int s = splhigh();
+ if (ex->isdone) {
+ splx(s);
+#ifdef EHCI_DEBUG
+ printf("ehci_idone: ex is done!\n ");
+ ehci_dump_exfer(ex);
+#else
+ printf("ehci_idone: ex=%p is done!\n", ex);
+#endif
+ return;
+ }
+ ex->isdone = 1;
+ splx(s);
+ }
+#endif
+
+ if (xfer->status == USBD_CANCELLED ||
+ xfer->status == USBD_TIMEOUT) {
+ DPRINTF(("ehci_idone: aborted xfer=%p\n", xfer));
+ return;
+ }
+
+#ifdef EHCI_DEBUG
+ DPRINTFN(10, ("ehci_idone: ex=%p, xfer=%p, pipe=%p ready\n",
+ ex, xfer, epipe));
+ if (ehcidebug > 10)
+ ehci_dump_sqtds(ex->sqtdstart);
+#endif
+
+ /* The transfer is done, compute actual length and status. */
+ actlen = 0;
+ for (sqtd = ex->sqtdstart; sqtd != NULL; sqtd = sqtd->nextqtd) {
+ nstatus = le32toh(sqtd->qtd.qtd_status);
+ if (nstatus & EHCI_QTD_ACTIVE)
+ break;
+
+ status = nstatus;
+ if (EHCI_QTD_GET_PID(status) != EHCI_QTD_PID_SETUP)
+ actlen += sqtd->len - EHCI_QTD_GET_BYTES(status);
+ }
+#if 0
+ /* If there are left over TDs we need to update the toggle. */
+ if (sqtd != NULL)
+ epipe->nexttoggle = EHCI_TD_GET_DT(le32toh(std->td.td_token));
+#endif
+
+ status &= EHCI_QTD_STATERRS;
+ DPRINTFN(10, ("ehci_idone: actlen=%d, status=0x%x\n", actlen, status));
+ xfer->actlen = actlen;
+ if (status != 0) {
+#ifdef EHCI_DEBUG
+ char sbuf[128];
+
+ bitmask_snprintf((u_int32_t)status,
+ "\20\2MISSEDMICRO\3XACT\4BABBLE\5BABBLE"
+ "\6HALTED",
+ sbuf, sizeof(sbuf));
+
+ DPRINTFN((status == EHCI_QTD_HALTED)*10,
+ ("ehci_idone: error, addr=%d, endpt=0x%02x, "
+ "status 0x%s\n",
+ xfer->pipe->device->address,
+ xfer->pipe->endpoint->edesc->bEndpointAddress,
+ sbuf));
+#endif
+
+ if (status == EHCI_QTD_HALTED)
+ xfer->status = USBD_STALLED;
+ else
+ xfer->status = USBD_IOERROR; /* more info XXX */
+ } else {
+ xfer->status = USBD_NORMAL_COMPLETION;
+ }
+
+ usb_transfer_complete(xfer);
+ DPRINTFN(12, ("ehci_idone: ex=%p done\n", ex));
}
/*
* Wait here until controller claims to have an interrupt.
- * Then call ohci_intr and return. Use timeout to avoid waiting
+ * Then call ehci_intr and return. Use timeout to avoid waiting
* too long.
*/
void
@@ -593,7 +756,7 @@
DPRINTFN(15,("ehci_waitintr: 0x%04x\n", intrs));
#ifdef OHCI_DEBUG
if (ehcidebug > 15)
- ehci_dumpregs(sc);
+ ehci_dump_regs(sc);
#endif
if (intrs) {
ehci_intr1(sc);
@@ -689,7 +852,7 @@
#ifdef EHCI_DEBUG
DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why));
- ehci_dumpregs(sc);
+ ehci_dump_regs(sc);
#endif
s = splhardusb();
@@ -787,8 +950,13 @@
SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, xfer, next);
else
xfer = malloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT);
- if (xfer != NULL)
+ if (xfer != NULL) {
memset(xfer, 0, sizeof (struct ehci_xfer));
+#ifdef DIAGNOSTIC
+ EXFER(xfer)->isdone = 1;
+ xfer->busy_free = XFER_BUSY;
+#endif
+ }
return (xfer);
}
@@ -797,6 +965,18 @@
{
struct ehci_softc *sc = (struct ehci_softc *)bus;
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ printf("ehci_freex: xfer=%p not busy, 0x%08x\n", xfer,
+ xfer->busy_free);
+ return;
+ }
Home |
Main Index |
Thread Index |
Old Index