summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authoraugustss <augustss@NetBSD.org>2001-11-19 02:57:16 +0000
committeraugustss <augustss@NetBSD.org>2001-11-19 02:57:16 +0000
commit2db9b8d2c856ff01a378bcb4d89f00ae73c81a61 (patch)
treed399d67bf33072c69a06cf09028bcf3596736c72 /sys
parent75824c636a6fa83c38fd0bfd8a3e00d86f5f28b7 (diff)
Start of pipe open routine.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/usb/ehci.c321
-rw-r--r--sys/dev/usb/ehcireg.h6
-rw-r--r--sys/dev/usb/ehcivar.h4
3 files changed, 258 insertions, 73 deletions
diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c
index 3df1b3a81e5..43822f83394 100644
--- a/sys/dev/usb/ehci.c
+++ b/sys/dev/usb/ehci.c
@@ -1,4 +1,4 @@
-/* $NetBSD: ehci.c,v 1.9 2001/11/18 00:39:46 augustss Exp $ */
+/* $NetBSD: ehci.c,v 1.10 2001/11/19 02:57:16 augustss Exp $ */
/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -47,7 +47,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.9 2001/11/18 00:39:46 augustss Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.10 2001/11/19 02:57:16 augustss Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -82,6 +82,26 @@ int ehcidebug = 0;
struct ehci_pipe {
struct usbd_pipe pipe;
+ ehci_soft_qh_t *sqh;
+ union {
+ ehci_soft_qtd_t *qtd;
+ /* ehci_soft_itd_t *itd; */
+ } tail;
+ union {
+ /* Control pipe */
+ struct {
+ usb_dma_t reqdma;
+ u_int length;
+ ehci_soft_qtd_t *setup, *data, *stat;
+ } ctl;
+ /* Interrupt pipe */
+ /* Bulk pipe */
+ struct {
+ u_int length;
+ int isread;
+ } bulk;
+ /* Iso pipe */
+ } u;
};
Static void ehci_shutdown(void *);
@@ -151,6 +171,12 @@ Static void ehci_free_sqtd(ehci_softc_t *, ehci_soft_qtd_t *);
Static void ehci_hash_add_qtd(ehci_softc_t *, ehci_soft_qtd_t *);
Static void ehci_hash_rem_qtd(ehci_softc_t *, ehci_soft_qtd_t *);
Static ehci_soft_qtd_t *ehci_hash_find_qtd(ehci_softc_t *, ehci_physaddr_t);
+Static void ehci_add_qh(ehci_soft_qh_t *, ehci_soft_qh_t *);
+Static void ehci_rem_qh(ehci_softc_t *, ehci_soft_qh_t *,
+ ehci_soft_qh_t *);
+
+Static void ehci_close_pipe(usbd_pipe_handle, ehci_soft_qh_t *);
+Static void ehci_abort_xfer(usbd_xfer_handle, usbd_status);
#ifdef EHCI_DEBUG
Static void ehci_dumpregs(ehci_softc_t *);
@@ -318,6 +344,7 @@ ehci_init(ehci_softc_t *sc)
goto bad1;
}
memset(&sc->sc_bulk_head->qh, 0, sizeof(ehci_qtd_t));
+ sc->sc_bulk_head->qh.qh_curqtd = htole32(EHCI_LINK_TERMINATE);
sc->sc_bulk_head->qh.qh_qtd.qtd_status =
htole32(EHCI_QTD_SET_STATUS(EHCI_QTD_HALTED));
sc->sc_bulk_head->qh.qh_link =
@@ -336,6 +363,7 @@ ehci_init(ehci_softc_t *sc)
goto bad2;
}
memset(&sc->sc_ctrl_head->qh, 0, sizeof(ehci_qtd_t));
+ sc->sc_ctrl_head->qh.qh_curqtd = htole32(EHCI_LINK_TERMINATE);
sc->sc_ctrl_head->qh.qh_qtd.qtd_status =
htole32(EHCI_QTD_SET_STATUS(EHCI_QTD_HALTED));
sc->sc_ctrl_head->qh.qh_endp = htole32(EHCI_QH_HRECL);
@@ -354,6 +382,8 @@ ehci_init(ehci_softc_t *sc)
usb_callout_init(sc->sc_tmo_pcd);
+ lockinit(&sc->sc_doorbell_lock, PZERO, "ehcidb", 0, 0);
+
/* Enable interrupts */
EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
@@ -361,7 +391,7 @@ ehci_init(ehci_softc_t *sc)
EOWRITE4(sc, EHCI_USBCMD,
EHCI_CMD_ITC_8 | /* 8 microframes */
(EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) |
- /* EHCI_CMD_ASE | */
+ EHCI_CMD_ASE |
/* EHCI_CMD_PSE | */
EHCI_CMD_RS);
@@ -440,6 +470,11 @@ ehci_intr1(ehci_softc_t *sc)
sc->sc_bus.intr_context++;
sc->sc_bus.no_intrs++;
+ if (eintrs & EHCI_STS_IAA) {
+ DPRINTF(("ehci_intr1: door bell\n"));
+ wakeup(&sc->sc_bulk_head);
+ eintrs &= ~EHCI_STS_INT;
+ }
if (eintrs & EHCI_STS_INT) {
DPRINTF(("ehci_intr1: something is done\n"));
eintrs &= ~EHCI_STS_INT;
@@ -821,18 +856,19 @@ ehci_open(usbd_pipe_handle pipe)
ehci_softc_t *sc = (ehci_softc_t *)dev->bus;
usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
u_int8_t addr = dev->address;
-#if 0
u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE;
struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
- ehci_soft_ed_t *sqh;
+ ehci_soft_qh_t *sqh;
ehci_soft_qtd_t *sqtd;
+ usbd_status err;
+#if 0
ehci_soft_itd_t *sitd;
ehci_physaddr_t tdphys;
u_int32_t fmt;
- usbd_status err;
- int s;
int ival;
#endif
+ int s;
+ int speed, naks;
DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n",
pipe, addr, ed->bEndpointAddress, sc->sc_addr));
@@ -848,80 +884,131 @@ ehci_open(usbd_pipe_handle pipe)
default:
return (USBD_INVAL);
}
- } else {
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ speed = EHCI_QH_SPEED_HIGH; /* XXX */
+ naks = 8; /* XXX */
+ sqh = ehci_alloc_sqh(sc);
+ if (sqh == NULL)
+ goto bad0;
+ /* qh_link filled when the QH is added */
+ sqh->qh.qh_endp = htole32(
+ EHCI_QH_SET_ADDR(addr) |
+ EHCI_QH_SET_ENDPT(ed->bEndpointAddress) |
+ EHCI_QH_SET_EPS(speed) | /* XXX */
+ /* XXX EHCI_QH_DTC ? */
+ EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) |
+ (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ?
+ EHCI_QH_CTL : 0) |
+ EHCI_QH_SET_NRL(naks)
+ );
+ sqh->qh.qh_endphub = htole32(
+ EHCI_QH_SET_MULT(1)
+ );
+ sqh->qh.qh_curqtd = htole32(EHCI_LINK_TERMINATE);
+
+ epipe->sqh = sqh;
#if 0
- sqtd = NULL;
- sqh = NULL;
-
- sqh = ehci_alloc_sqh(sc);
- if (sqh == NULL)
- goto bad0;
- epipe->sqh = sqh;
- if (xfertype == UE_ISOCHRONOUS || xfertype == UE_INTERRUPT) {
- return (USBD_IOERROR);
- } else {
- sqtd = ehci_alloc_sqtd(sc);
- if (sqtd == NULL) {
- ehci_free_sqtd(sc, sqtd);
- goto bad1;
- }
- epipe->tail.qtd = sqtd;
- tdphys = sqtd->physaddr;
- fmt = EHCI_ED_FORMAT_GEN | EHCI_ED_DIR_TD;
+ if (xfertype == UE_CONTROL || xfertype == UE_BULK) {
+ sqtd = ehci_alloc_sqtd(sc);
+ if (sqtd == NULL) {
+ ehci_free_sqtd(sc, sqtd);
+ goto bad1;
}
- sqh->ed.ed_flags = htole32(
- EHCI_ED_SET_FA(addr) |
- EHCI_ED_SET_EN(ed->bEndpointAddress) |
- (dev->lowspeed ? EHCI_ED_SPEED : 0) | fmt |
- EHCI_ED_SET_MAXP(UGETW(ed->wMaxPacketSize)));
- sqh->ed.ed_headp = sqh->ed.ed_tailp = htole32(tdphys);
-
- switch (xfertype) {
- case UE_CONTROL:
- pipe->methods = &ehci_device_ctrl_methods;
- err = usb_allocmem(&sc->sc_bus,
- sizeof(usb_device_request_t),
- 0, &epipe->u.ctl.reqdma);
- if (err)
- goto bad;
- s = splusb();
- ehci_add_ed(sqh, sc->sc_ctrl_head);
- splx(s);
- break;
- case UE_INTERRUPT:
- pipe->methods = &ehci_device_intr_methods;
- ival = pipe->interval;
- if (ival == USBD_DEFAULT_INTERVAL)
- ival = ed->bInterval;
- return (ehci_device_setintr(sc, epipe, ival));
- case UE_ISOCHRONOUS:
- pipe->methods = &ehci_device_isoc_methods;
- return (ehci_setup_isoc(pipe));
- case UE_BULK:
- pipe->methods = &ehci_device_bulk_methods;
- s = splusb();
- ehci_add_ed(sqh, sc->sc_bulk_head);
- splx(s);
- break;
- }
-#else
- return (USBD_IOERROR);
+ epipe->tail.qtd = sqtd;
+ tdphys = sqtd->physaddr;
+ } else
+ sqtd = NULL;
#endif
+
+ switch (xfertype) {
+ case UE_CONTROL:
+ pipe->methods = &ehci_device_ctrl_methods;
+ err = usb_allocmem(&sc->sc_bus,
+ sizeof(usb_device_request_t),
+ 0, &epipe->u.ctl.reqdma);
+ if (err)
+ goto bad;
+ s = splusb();
+ ehci_add_qh(sqh, sc->sc_ctrl_head);
+ splx(s);
+ break;
+ case UE_BULK:
+ pipe->methods = &ehci_device_bulk_methods;
+ s = splusb();
+ ehci_add_qh(sqh, sc->sc_bulk_head);
+ splx(s);
+ break;
+ default:
+ return (USBD_INVAL);
}
return (USBD_NORMAL_COMPLETION);
-#if 0
bad:
if (sqtd != NULL)
ehci_free_sqtd(sc, sqtd);
- bad1:
+/*bad1:*/
if (sqh != NULL)
ehci_free_sqh(sc, sqh);
bad0:
return (USBD_NOMEM);
+}
+
+/*
+ * Add an ED to the schedule. Called at splusb().
+ */
+void
+ehci_add_qh(ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
+{
+ SPLUSBCHECK;
+
+ sqh->next = head->next;
+ sqh->qh.qh_link = head->qh.qh_link;
+ head->next = sqh;
+ head->qh.qh_link = htole32(sqh->physaddr);
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 0) {
+ printf("ehci_add_qh:\n");
+ ehci_dump_sqh(sqh);
+ }
#endif
}
+/*
+ * Remove an ED from the schedule. Called at splusb().
+ */
+void
+ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
+{
+ ehci_soft_qh_t *p;
+ int s;
+
+ SPLUSBCHECK;
+ /* XXX */
+ for (p = head; p == NULL && p->next != sqh; p = p->next)
+ ;
+ if (p == NULL)
+ panic("ehci_rem_qh: ED not found\n");
+ p->next = sqh->next;
+ p->qh.qh_link = sqh->qh.qh_link;
+
+ /*
+ * Now we must ensure that the HC has released all references to the
+ * QH. We do this by asking for a Async Advance Doorbell interrupt
+ * and then we wait for the interrupt.
+ * To make this easier we first obtain exclusive use ofthe doorbell.
+ */
+ lockmgr(&sc->sc_doorbell_lock, LK_EXCLUSIVE, NULL); /* get doorbell */
+ s = splhardusb();
+ /* ask for doorbell */
+ EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD);
+ tsleep(&sc->sc_bulk_head, PZERO, "ehcidi", 0); /* wait for doorbell */
+ splx(s);
+ lockmgr(&sc->sc_doorbell_lock, LK_RELEASE, NULL); /* release doorbell */
+}
+
/***********/
/*
@@ -1598,13 +1685,105 @@ ehci_hash_find_qtd(ehci_softc_t *sc, ehci_physaddr_t a)
return (NULL);
}
+/*
+ * Close a reqular pipe.
+ * Assumes that there are no pending transactions.
+ */
+void
+ehci_close_pipe(usbd_pipe_handle pipe, ehci_soft_qh_t *head)
+{
+ struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+ ehci_soft_qh_t *sqh = epipe->sqh;
+ int s;
+
+ s = splusb();
+ ehci_rem_qh(sc, sqh, head);
+ splx(s);
+ ehci_free_sqh(sc, epipe->sqh);
+}
+
+/*
+ * Abort a device request.
+ * If this routine is called at splusb() it guarantees that the request
+ * will be removed from the hardware scheduling and that the callback
+ * for it will be called with USBD_CANCELLED status.
+ * It's impossible to guarantee that the requested transfer will not
+ * have happened since the hardware runs concurrently.
+ * If the transaction has already happened we rely on the ordinary
+ * interrupt processing to process it.
+ */
+void
+ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
+{
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+ ehci_soft_qh_t *sqh = epipe->sqh;
+#if 0
+ ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus;
+ ehci_soft_td_t *p, *n;
+ ehci_physaddr_t headp;
+ int s, hit;
+#endif
+
+ DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p sqh=%p\n", xfer, epipe,sqh));
+
+ if (xfer->device->bus->intr_context || !curproc)
+ panic("ehci_abort_xfer: not in process context\n");
+
+}
+
/************************/
-Static usbd_status ehci_device_ctrl_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; }
+Static usbd_status
+ehci_device_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ehci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
+}
+
Static usbd_status ehci_device_ctrl_start(usbd_xfer_handle xfer) { return USBD_IOERROR; }
-Static void ehci_device_ctrl_abort(usbd_xfer_handle xfer) { }
-Static void ehci_device_ctrl_close(usbd_pipe_handle pipe) { }
-Static void ehci_device_ctrl_done(usbd_xfer_handle xfer) { }
+
+void
+ehci_device_ctrl_done(usbd_xfer_handle xfer)
+{
+ DPRINTFN(10,("ehci_ctrl_done: xfer=%p\n", xfer));
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST)) {
+ panic("ehci_ctrl_done: not a request\n");
+ }
+#endif
+ xfer->hcpriv = NULL;
+}
+
+/* Abort a device control request. */
+Static void
+ehci_device_ctrl_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("ehci_device_ctrl_abort: xfer=%p\n", xfer));
+ ehci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/* Close a device control pipe. */
+Static void
+ehci_device_ctrl_close(usbd_pipe_handle pipe)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+ /*struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;*/
+
+ DPRINTF(("ehci_device_ctrl_close: pipe=%p\n", pipe));
+ ehci_close_pipe(pipe, sc->sc_ctrl_head);
+ /*ehci_free_std(sc, epipe->tail.td);*/
+}
+
+/************************/
Static usbd_status ehci_device_bulk_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; }
Static usbd_status ehci_device_bulk_start(usbd_xfer_handle xfer) { return USBD_IOERROR; }
@@ -1612,12 +1791,16 @@ Static void ehci_device_bulk_abort(usbd_xfer_handle xfer) { }
Static void ehci_device_bulk_close(usbd_pipe_handle pipe) { }
Static void ehci_device_bulk_done(usbd_xfer_handle xfer) { }
+/************************/
+
Static usbd_status ehci_device_intr_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; }
Static usbd_status ehci_device_intr_start(usbd_xfer_handle xfer) { return USBD_IOERROR; }
Static void ehci_device_intr_abort(usbd_xfer_handle xfer) { }
Static void ehci_device_intr_close(usbd_pipe_handle pipe) { }
Static void ehci_device_intr_done(usbd_xfer_handle xfer) { }
+/************************/
+
Static usbd_status ehci_device_isoc_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; }
Static usbd_status ehci_device_isoc_start(usbd_xfer_handle xfer) { return USBD_IOERROR; }
Static void ehci_device_isoc_abort(usbd_xfer_handle xfer) { }
diff --git a/sys/dev/usb/ehcireg.h b/sys/dev/usb/ehcireg.h
index 4391a3993ff..46da3d3a283 100644
--- a/sys/dev/usb/ehcireg.h
+++ b/sys/dev/usb/ehcireg.h
@@ -1,4 +1,4 @@
-/* $NetBSD: ehcireg.h,v 1.9 2001/11/18 00:39:46 augustss Exp $ */
+/* $NetBSD: ehcireg.h,v 1.10 2001/11/19 02:57:16 augustss Exp $ */
/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -122,7 +122,7 @@
#define EHCI_STS_PCD 0x00000004 /* RWC port change detect */
#define EHCI_STS_ERRINT 0x00000002 /* RWC error interrupt */
#define EHCI_STS_INT 0x00000001 /* RWC interrupt */
-#define EHCI_STS_INTRS(x) ((x) & 0x2f)
+#define EHCI_STS_INTRS(x) ((x) & 0x3f)
#define EHCI_NORMAL_INTRS (EHCI_STS_IAA | EHCI_STS_HSE | EHCI_STS_PCD | EHCI_STS_ERRINT | EHCI_STS_INT)
@@ -236,7 +236,7 @@ typedef struct {
#define EHCI_QH_INACT 0x00000080
#define EHCI_QH_GET_ENDPT(x) (((x) >> 8) & 0x0f) /* endpoint no */
#define EHCI_QH_SET_ENDPT(x) ((x) << 8)
-#define EHCI_QH_GET_EPS(x) (((x) >> 12) & 0x03) /* enspoint speed */
+#define EHCI_QH_GET_EPS(x) (((x) >> 12) & 0x03) /* endpoint speed */
#define EHCI_QH_SET_EPS(x) ((x) << 12)
#define EHCI_QH_SPEED_FULL 0x0
#define EHCI_QH_SPEED_LOW 0x1
diff --git a/sys/dev/usb/ehcivar.h b/sys/dev/usb/ehcivar.h
index 344c484df8d..4b9ddcda768 100644
--- a/sys/dev/usb/ehcivar.h
+++ b/sys/dev/usb/ehcivar.h
@@ -1,4 +1,4 @@
-/* $NetBSD: ehcivar.h,v 1.7 2001/11/18 00:39:46 augustss Exp $ */
+/* $NetBSD: ehcivar.h,v 1.8 2001/11/19 02:57:16 augustss Exp $ */
/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -101,6 +101,8 @@ typedef struct ehci_softc {
SIMPLEQ_HEAD(, usbd_xfer) sc_free_xfers; /* free xfers */
+ struct lock sc_doorbell_lock;
+
usb_callout_t sc_tmo_pcd;
device_ptr_t sc_child; /* /dev/usb# device */