Module Name: src
Committed By: skrll
Date: Sun Dec 6 15:39:35 UTC 2015
Modified Files:
src/sys/dev/usb [nick-nhusb]: ohci.c ohcivar.h
Log Message:
Restructure the xfer methods of ohci(4) so that minimal work in done in
softint context. Now all memory allocation is done in thread context.
Addresses kern/48308 for ohci(4), might fix some locking bugs around the
*TD free lists, and plugs some memory leaks on error conditions.
To generate a diff of this commit:
cvs rdiff -u -r1.254.2.34 -r1.254.2.35 src/sys/dev/usb/ohci.c
cvs rdiff -u -r1.55.6.10 -r1.55.6.11 src/sys/dev/usb/ohcivar.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/usb/ohci.c
diff -u src/sys/dev/usb/ohci.c:1.254.2.34 src/sys/dev/usb/ohci.c:1.254.2.35
--- src/sys/dev/usb/ohci.c:1.254.2.34 Wed Dec 2 20:36:50 2015
+++ src/sys/dev/usb/ohci.c Sun Dec 6 15:39:35 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: ohci.c,v 1.254.2.34 2015/12/02 20:36:50 skrll Exp $ */
+/* $NetBSD: ohci.c,v 1.254.2.35 2015/12/06 15:39:35 skrll Exp $ */
/*
* Copyright (c) 1998, 2004, 2005, 2012 The NetBSD Foundation, Inc.
@@ -41,7 +41,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.254.2.34 2015/12/02 20:36:50 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.254.2.35 2015/12/06 15:39:35 skrll Exp $");
#include "opt_usb.h"
@@ -129,15 +129,19 @@ Static void ohci_free_sed(ohci_softc_t
Static ohci_soft_td_t *ohci_alloc_std(ohci_softc_t *);
Static void ohci_free_std(ohci_softc_t *, ohci_soft_td_t *);
+Static void ohci_free_std_locked(ohci_softc_t *, ohci_soft_td_t *);
Static ohci_soft_itd_t *ohci_alloc_sitd(ohci_softc_t *);
Static void ohci_free_sitd(ohci_softc_t *,ohci_soft_itd_t *);
+Static void ohci_free_sitd_locked(ohci_softc_t *,
+ ohci_soft_itd_t *);
-Static void ohci_free_std_chain(ohci_softc_t *, ohci_soft_td_t *,
- ohci_soft_td_t *);
-Static usbd_status ohci_alloc_std_chain(struct ohci_pipe *,
- ohci_softc_t *, int, int, struct usbd_xfer *,
- ohci_soft_td_t *, ohci_soft_td_t **);
+Static usbd_status ohci_alloc_std_chain(ohci_softc_t *, struct usbd_xfer *,
+ int, int);
+Static void ohci_free_stds(ohci_softc_t *, struct ohci_xfer *);
+
+Static void ohci_reset_std_chain(ohci_softc_t *, struct usbd_xfer *,
+ int, int, ohci_soft_td_t *, ohci_soft_td_t **);
Static usbd_status ohci_open(struct usbd_pipe *);
Static void ohci_poll(struct usbd_bus *);
@@ -174,24 +178,32 @@ Static void ohci_root_intr_abort(struct
Static void ohci_root_intr_close(struct usbd_pipe *);
Static void ohci_root_intr_done(struct usbd_xfer *);
+Static int ohci_device_ctrl_init(struct usbd_xfer *);
+Static void ohci_device_ctrl_fini(struct usbd_xfer *);
Static usbd_status ohci_device_ctrl_transfer(struct usbd_xfer *);
Static usbd_status ohci_device_ctrl_start(struct usbd_xfer *);
Static void ohci_device_ctrl_abort(struct usbd_xfer *);
Static void ohci_device_ctrl_close(struct usbd_pipe *);
Static void ohci_device_ctrl_done(struct usbd_xfer *);
+Static int ohci_device_bulk_init(struct usbd_xfer *);
+Static void ohci_device_bulk_fini(struct usbd_xfer *);
Static usbd_status ohci_device_bulk_transfer(struct usbd_xfer *);
Static usbd_status ohci_device_bulk_start(struct usbd_xfer *);
Static void ohci_device_bulk_abort(struct usbd_xfer *);
Static void ohci_device_bulk_close(struct usbd_pipe *);
Static void ohci_device_bulk_done(struct usbd_xfer *);
+Static int ohci_device_intr_init(struct usbd_xfer *);
+Static void ohci_device_intr_fini(struct usbd_xfer *);
Static usbd_status ohci_device_intr_transfer(struct usbd_xfer *);
Static usbd_status ohci_device_intr_start(struct usbd_xfer *);
Static void ohci_device_intr_abort(struct usbd_xfer *);
Static void ohci_device_intr_close(struct usbd_pipe *);
Static void ohci_device_intr_done(struct usbd_xfer *);
+Static int ohci_device_isoc_init(struct usbd_xfer *);
+Static void ohci_device_isoc_fini(struct usbd_xfer *);
Static usbd_status ohci_device_isoc_transfer(struct usbd_xfer *);
Static usbd_status ohci_device_isoc_start(struct usbd_xfer *);
Static void ohci_device_isoc_abort(struct usbd_xfer *);
@@ -289,6 +301,8 @@ Static const struct usbd_pipe_methods oh
};
Static const struct usbd_pipe_methods ohci_device_ctrl_methods = {
+ .upm_init = ohci_device_ctrl_init,
+ .upm_fini = ohci_device_ctrl_fini,
.upm_transfer = ohci_device_ctrl_transfer,
.upm_start = ohci_device_ctrl_start,
.upm_abort = ohci_device_ctrl_abort,
@@ -298,6 +312,8 @@ Static const struct usbd_pipe_methods oh
};
Static const struct usbd_pipe_methods ohci_device_intr_methods = {
+ .upm_init = ohci_device_intr_init,
+ .upm_fini = ohci_device_intr_fini,
.upm_transfer = ohci_device_intr_transfer,
.upm_start = ohci_device_intr_start,
.upm_abort = ohci_device_intr_abort,
@@ -307,6 +323,8 @@ Static const struct usbd_pipe_methods oh
};
Static const struct usbd_pipe_methods ohci_device_bulk_methods = {
+ .upm_init = ohci_device_bulk_init,
+ .upm_fini = ohci_device_bulk_fini,
.upm_transfer = ohci_device_bulk_transfer,
.upm_start = ohci_device_bulk_start,
.upm_abort = ohci_device_bulk_abort,
@@ -316,6 +334,8 @@ Static const struct usbd_pipe_methods oh
};
Static const struct usbd_pipe_methods ohci_device_isoc_methods = {
+ .upm_init = ohci_device_isoc_init,
+ .upm_fini = ohci_device_isoc_fini,
.upm_transfer = ohci_device_isoc_transfer,
.upm_start = ohci_device_isoc_start,
.upm_abort = ohci_device_isoc_abort,
@@ -387,12 +407,17 @@ ohci_alloc_sed(ohci_softc_t *sc)
OHCIHIST_FUNC(); OHCIHIST_CALLED();
+ mutex_enter(&sc->sc_lock);
if (sc->sc_freeeds == NULL) {
DPRINTFN(2, "allocating chunk", 0, 0, 0, 0);
+ mutex_exit(&sc->sc_lock);
+
err = usb_allocmem(&sc->sc_bus, OHCI_SED_SIZE * OHCI_SED_CHUNK,
OHCI_ED_ALIGN, &dma);
if (err)
return 0;
+
+ mutex_enter(&sc->sc_lock);
for (i = 0; i < OHCI_SED_CHUNK; i++) {
offs = i * OHCI_SED_SIZE;
sed = KERNADDR(&dma, offs);
@@ -405,18 +430,32 @@ ohci_alloc_sed(ohci_softc_t *sc)
}
sed = sc->sc_freeeds;
sc->sc_freeeds = sed->next;
+ mutex_exit(&sc->sc_lock);
+
memset(&sed->ed, 0, sizeof(ohci_ed_t));
sed->next = 0;
return sed;
}
-void
-ohci_free_sed(ohci_softc_t *sc, ohci_soft_ed_t *sed)
+static inline void
+ohci_free_sed_locked(ohci_softc_t *sc, ohci_soft_ed_t *sed)
{
+
+ KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock));
+
sed->next = sc->sc_freeeds;
sc->sc_freeeds = sed;
}
+void
+ohci_free_sed(ohci_softc_t *sc, ohci_soft_ed_t *sed)
+{
+
+ mutex_enter(&sc->sc_lock);
+ ohci_free_sed_locked(sc, sed);
+ mutex_exit(&sc->sc_lock);
+}
+
ohci_soft_td_t *
ohci_alloc_std(ohci_softc_t *sc)
{
@@ -427,14 +466,17 @@ ohci_alloc_std(ohci_softc_t *sc)
OHCIHIST_FUNC(); OHCIHIST_CALLED();
- KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock));
-
+ mutex_enter(&sc->sc_lock);
if (sc->sc_freetds == NULL) {
DPRINTFN(2, "allocating chunk", 0, 0, 0, 0);
+ mutex_exit(&sc->sc_lock);
+
err = usb_allocmem(&sc->sc_bus, OHCI_STD_SIZE * OHCI_STD_CHUNK,
OHCI_TD_ALIGN, &dma);
if (err)
return NULL;
+
+ mutex_enter(&sc->sc_lock);
for(i = 0; i < OHCI_STD_CHUNK; i++) {
offs = i * OHCI_STD_SIZE;
std = KERNADDR(&dma, offs);
@@ -448,59 +490,79 @@ ohci_alloc_std(ohci_softc_t *sc)
std = sc->sc_freetds;
sc->sc_freetds = std->nexttd;
+ mutex_exit(&sc->sc_lock);
+
memset(&std->td, 0, sizeof(ohci_td_t));
std->nexttd = NULL;
std->xfer = NULL;
- ohci_hash_add_td(sc, std);
return std;
}
void
-ohci_free_std(ohci_softc_t *sc, ohci_soft_td_t *std)
+ohci_free_std_locked(ohci_softc_t *sc, ohci_soft_td_t *std)
{
KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock));
- ohci_hash_rem_td(sc, std);
std->nexttd = sc->sc_freetds;
sc->sc_freetds = std;
}
-usbd_status
-ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc,
- int alen, int rd, struct usbd_xfer *xfer,
- ohci_soft_td_t *sp, ohci_soft_td_t **ep)
+void
+ohci_free_std(ohci_softc_t *sc, ohci_soft_td_t *std)
+{
+
+ mutex_enter(&sc->sc_lock);
+ ohci_free_std_locked(sc, std);
+ mutex_exit(&sc->sc_lock);
+}
+
+Static usbd_status
+ohci_alloc_std_chain(ohci_softc_t *sc, struct usbd_xfer *xfer, int alen, int rd)
{
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
+ struct usbd_pipe *pipe = xfer->ux_pipe;
ohci_soft_td_t *next, *cur;
ohci_physaddr_t dataphys, dataphysend;
uint32_t tdflags;
- int len, curlen;
+ int len = alen;
+ int curlen;
usb_dma_t *dma = &xfer->ux_dmabuf;
uint16_t flags = xfer->ux_flags;
OHCIHIST_FUNC(); OHCIHIST_CALLED();
- DPRINTF("start len=%d", alen, 0, 0, 0);
-
- KASSERT(mutex_owned(&sc->sc_lock));
DPRINTFN(8, "addr=%d endpt=%d len=%d speed=%d",
- opipe->pipe.up_dev->ud_addr,
- UE_GET_ADDR(opipe->pipe.up_endpoint->ue_edesc->bEndpointAddress),
- alen, opipe->pipe.up_dev->ud_speed);
+ pipe->up_dev->ud_addr,
+ UE_GET_ADDR(pipe->up_endpoint->ue_edesc->bEndpointAddress),
+ alen, pipe->up_dev->ud_speed);
+
+ ASSERT_SLEEPABLE();
+
+ size_t nstd = (flags & USBD_FORCE_SHORT_XFER) ? 1 : 0;
+ nstd += ((len + OHCI_PAGE_SIZE - 1) / OHCI_PAGE_SIZE);
+ ox->ox_stds = kmem_zalloc(sizeof(ohci_soft_td_t *) * nstd,
+ KM_SLEEP);
+ ox->ox_nstd = nstd;
+ int mps = UGETW(pipe->up_endpoint->ue_edesc->wMaxPacketSize);
+
+ DPRINTFN(8, "xfer %p nstd %d mps %d", xfer, nstd, mps, 0);
len = alen;
- cur = sp;
+ cur = ohci_alloc_std(sc);
+ if (cur == NULL)
+ goto nomem;
+
dataphys = DMAADDR(dma, 0);
dataphysend = OHCI_PAGE(dataphys + len - 1);
- usb_syncmem(dma, 0, len,
- rd ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
tdflags = HTOO32(
(rd ? OHCI_TD_IN : OHCI_TD_OUT) |
(flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0) |
OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY | OHCI_TD_NOINTR);
- for (;;) {
+ for (size_t j = 0;;) {
+ ox->ox_stds[j++] = cur;
next = ohci_alloc_std(sc);
if (next == NULL)
goto nomem;
@@ -515,7 +577,7 @@ ohci_alloc_std_chain(struct ohci_pipe *o
curlen = 2 * OHCI_PAGE_SIZE -
(dataphys & (OHCI_PAGE_SIZE-1));
/* the length must be a multiple of the max size */
- curlen -= curlen % UGETW(opipe->pipe.up_endpoint->ue_edesc->wMaxPacketSize);
+ curlen -= curlen % mps;
KASSERT(curlen != 0);
}
DPRINTFN(4, "dataphys=0x%08x dataphysend=0x%08x "
@@ -530,8 +592,7 @@ ohci_alloc_std_chain(struct ohci_pipe *o
cur->len = curlen;
cur->flags = OHCI_ADD_LEN;
cur->xfer = xfer;
- usb_syncmem(&cur->dma, cur->offs, sizeof(cur->td),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
DPRINTFN(10, "cbp=0x%08x be=0x%08x", dataphys,
dataphys + curlen - 1, 0, 0);
if (len == 0)
@@ -541,7 +602,7 @@ ohci_alloc_std_chain(struct ohci_pipe *o
cur = next;
}
if (!rd && (flags & USBD_FORCE_SHORT_XFER) &&
- alen % UGETW(opipe->pipe.up_endpoint->ue_edesc->wMaxPacketSize) == 0) {
+ alen % mps == 0) {
/* Force a 0 length transfer at the end. */
cur = next;
@@ -557,32 +618,141 @@ ohci_alloc_std_chain(struct ohci_pipe *o
cur->len = 0;
cur->flags = 0;
cur->xfer = xfer;
- usb_syncmem(&cur->dma, cur->offs, sizeof(cur->td),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
DPRINTFN(2, "add 0 xfer", 0, 0, 0, 0);
}
- *ep = cur;
return USBD_NORMAL_COMPLETION;
nomem:
-
- /* Don't free sp - let the caller do that */
- ohci_free_std_chain(sc, sp->nexttd, NULL);
+ ohci_free_stds(sc, ox);
return USBD_NOMEM;
}
Static void
-ohci_free_std_chain(ohci_softc_t *sc, ohci_soft_td_t *std,
- ohci_soft_td_t *stdend)
+ohci_free_stds(ohci_softc_t *sc, struct ohci_xfer *ox)
{
- ohci_soft_td_t *p;
+ OHCIHIST_FUNC(); OHCIHIST_CALLED();
+ DPRINTF("ox=%p", ox, 0, 0, 0);
- for (; std != stdend; std = p) {
- p = std->nexttd;
- ohci_free_std(sc, std);
+ mutex_enter(&sc->sc_lock);
+ for (size_t i = 0; i < ox->ox_nstd; i++) {
+ ohci_soft_td_t *std = ox->ox_stds[i];
+ if (std == NULL)
+ break;
+ ohci_free_std_locked(sc, std);
+ }
+ mutex_exit(&sc->sc_lock);
+}
+
+void
+ohci_reset_std_chain(ohci_softc_t *sc, struct usbd_xfer *xfer,
+ int alen, int rd, ohci_soft_td_t *sp, ohci_soft_td_t **ep)
+{
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
+ ohci_soft_td_t *next, *cur;
+ ohci_physaddr_t dataphys, dataphysend;
+ uint32_t tdflags;
+ int len, curlen;
+ usb_dma_t *dma = &xfer->ux_dmabuf;
+ uint16_t flags = xfer->ux_flags;
+
+ OHCIHIST_FUNC(); OHCIHIST_CALLED();
+ DPRINTF("start len=%d", alen, 0, 0, 0);
+
+ KASSERT(mutex_owned(&sc->sc_lock));
+
+ DPRINTFN(8, "addr=%d endpt=%d len=%d speed=%d",
+ xfer->ux_pipe->up_dev->ud_addr,
+ UE_GET_ADDR(xfer->ux_pipe->up_endpoint->ue_edesc->bEndpointAddress),
+ alen, xfer->ux_pipe->up_dev->ud_speed);
+
+ ASSERT_SLEEPABLE();
+ KASSERT(sp);
+
+ int mps = UGETW(xfer->ux_pipe->up_endpoint->ue_edesc->wMaxPacketSize);
+
+ len = alen;
+ cur = sp;
+
+ dataphys = DMAADDR(dma, 0);
+ dataphysend = OHCI_PAGE(dataphys + len - 1);
+ usb_syncmem(dma, 0, len,
+ rd ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+ tdflags = HTOO32(
+ (rd ? OHCI_TD_IN : OHCI_TD_OUT) |
+ (flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0) |
+ OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY | OHCI_TD_NOINTR);
+
+ for (size_t j = 1;;) {
+ if (j == ox->ox_nstd)
+ next = NULL;
+ else
+ next = ox->ox_stds[j++];
+ KASSERT(next != cur);
+
+ /* The OHCI hardware can handle at most one page crossing. */
+ if (OHCI_PAGE(dataphys) == dataphysend ||
+ OHCI_PAGE(dataphys) + OHCI_PAGE_SIZE == dataphysend) {
+ /* we can handle it in this TD */
+ curlen = len;
+ } else {
+ /* must use multiple TDs, fill as much as possible. */
+ curlen = 2 * OHCI_PAGE_SIZE -
+ (dataphys & (OHCI_PAGE_SIZE - 1));
+ /* the length must be a multiple of the max size */
+ curlen -= curlen % mps;
+ KASSERT(curlen != 0);
+ }
+ DPRINTFN(4, "dataphys=0x%08x dataphysend=0x%08x "
+ "len=%d curlen=%d", dataphys, dataphysend, len, curlen);
+ len -= curlen;
+
+ cur->td.td_flags = tdflags;
+ cur->td.td_cbp = HTOO32(dataphys);
+ cur->td.td_be = HTOO32(dataphys + curlen - 1);
+ cur->td.td_nexttd = (next != NULL) ? HTOO32(next->physaddr) : 0;
+ cur->nexttd = next;
+ cur->len = curlen;
+ cur->flags = OHCI_ADD_LEN;
+ cur->xfer = xfer;
+ ohci_hash_add_td(sc, cur);
+
+ usb_syncmem(&cur->dma, cur->offs, sizeof(cur->td),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ DPRINTFN(10, "cbp=0x%08x be=0x%08x", dataphys,
+ dataphys + curlen - 1, 0, 0);
+ if (len == 0)
+ break;
+ KASSERT(next != NULL);
+ DPRINTFN(10, "extend chain", 0, 0, 0, 0);
+ dataphys += curlen;
+ cur = next;
+ }
+ if (!rd &&
+ (flags & USBD_FORCE_SHORT_XFER) &&
+ alen % mps == 0) {
+ /* Force a 0 length transfer at the end. */
+
+ KASSERT(next != NULL);
+ cur = next;
+
+ cur->td.td_flags = tdflags;
+ cur->td.td_cbp = 0; /* indicate 0 length packet */
+ cur->td.td_nexttd = HTOO32(next->physaddr);
+ cur->td.td_be = ~0;
+ cur->nexttd = NULL;
+ cur->len = 0;
+ cur->flags = 0;
+ cur->xfer = xfer;
+ ohci_hash_add_td(sc, cur);
+
+ usb_syncmem(&cur->dma, cur->offs, sizeof(cur->td),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ DPRINTFN(2, "add 0 xfer", 0, 0, 0, 0);
}
+ *ep = cur;
}
ohci_soft_itd_t *
@@ -595,12 +765,16 @@ ohci_alloc_sitd(ohci_softc_t *sc)
OHCIHIST_FUNC(); OHCIHIST_CALLED();
+ mutex_enter(&sc->sc_lock);
if (sc->sc_freeitds == NULL) {
DPRINTFN(2, "allocating chunk", 0, 0, 0, 0);
+ mutex_exit(&sc->sc_lock);
+
err = usb_allocmem(&sc->sc_bus, OHCI_SITD_SIZE * OHCI_SITD_CHUNK,
OHCI_ITD_ALIGN, &dma);
if (err)
return NULL;
+ mutex_enter(&sc->sc_lock);
for(i = 0; i < OHCI_SITD_CHUNK; i++) {
offs = i * OHCI_SITD_SIZE;
sitd = KERNADDR(&dma, offs);
@@ -614,10 +788,11 @@ ohci_alloc_sitd(ohci_softc_t *sc)
sitd = sc->sc_freeitds;
sc->sc_freeitds = sitd->nextitd;
+ mutex_exit(&sc->sc_lock);
+
memset(&sitd->itd, 0, sizeof(ohci_itd_t));
sitd->nextitd = NULL;
sitd->xfer = NULL;
- ohci_hash_add_itd(sc, sitd);
#ifdef DIAGNOSTIC
sitd->isdone = false;
@@ -626,8 +801,8 @@ ohci_alloc_sitd(ohci_softc_t *sc)
return sitd;
}
-void
-ohci_free_sitd(ohci_softc_t *sc, ohci_soft_itd_t *sitd)
+Static void
+ohci_free_sitd_locked(ohci_softc_t *sc, ohci_soft_itd_t *sitd)
{
OHCIHIST_FUNC(); OHCIHIST_CALLED();
@@ -639,11 +814,21 @@ ohci_free_sitd(ohci_softc_t *sc, ohci_so
sitd->isdone = false;
#endif
- ohci_hash_rem_itd(sc, sitd);
sitd->nextitd = sc->sc_freeitds;
sc->sc_freeitds = sitd;
}
+void
+ohci_free_sitd(ohci_softc_t *sc, ohci_soft_itd_t *sitd)
+{
+
+ OHCIHIST_FUNC(); OHCIHIST_CALLED();
+
+ mutex_enter(&sc->sc_lock);
+ ohci_free_sitd_locked(sc, sitd);
+ mutex_exit(&sc->sc_lock);
+}
+
int
ohci_init(ohci_softc_t *sc)
{
@@ -1347,7 +1532,7 @@ ohci_softintr(void *v)
xfer->ux_status = USBD_NORMAL_COMPLETION;
usb_transfer_complete(xfer);
}
- ohci_free_std(sc, std);
+ ohci_hash_rem_td(sc, std);
} else {
/*
* Endpoint is halted. First unlink all the TDs
@@ -1360,10 +1545,10 @@ ohci_softintr(void *v)
DPRINTFN(15, "error cc=%d",
OHCI_TD_GET_CC(O32TOH(std->td.td_flags)), 0, 0, 0);
- /* remove TDs */
+ /* remove xfer's TDs from the hash */
for (p = std; p->xfer == xfer; p = n) {
n = p->nexttd;
- ohci_free_std(sc, p);
+ ohci_hash_rem_td(sc, p);
}
/* clear halt */
@@ -1440,9 +1625,10 @@ ohci_softintr(void *v)
}
if (sitd->flags & OHCI_CALL_DONE)
break;
- ohci_free_sitd(sc, sitd);
+ ohci_hash_rem_itd(sc, sitd);
+
}
- ohci_free_sitd(sc, sitd);
+ ohci_hash_rem_itd(sc, sitd);
if (uedir == UE_DIR_IN &&
xfer->ux_status == USBD_NORMAL_COMPLETION)
xfer->ux_actlen = actlen;
@@ -1484,10 +1670,10 @@ ohci_device_ctrl_done(struct usbd_xfer *
void
ohci_device_intr_done(struct usbd_xfer *xfer)
{
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
struct ohci_pipe *opipe = OHCI_PIPE2OPIPE(xfer->ux_pipe);
ohci_softc_t *sc = OHCI_XFER2SC(xfer);
ohci_soft_ed_t *sed = opipe->sed;
- ohci_soft_td_t *data, *tail;
int isread =
(UE_GET_DIR(xfer->ux_pipe->up_endpoint->ue_edesc->bEndpointAddress) == UE_DIR_IN);
@@ -1499,38 +1685,41 @@ ohci_device_intr_done(struct usbd_xfer *
usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length,
isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
if (xfer->ux_pipe->up_repeat) {
+ ohci_soft_td_t *data, *last, *tail;
+ int len = xfer->ux_length;
+
+ /* Use "tail" TD and loan our first TD to next transfer */
data = opipe->tail.td;
- tail = ohci_alloc_std(sc); /* XXX should reuse TD */
- if (tail == NULL) {
- xfer->ux_status = USBD_NOMEM;
- return;
- }
+ opipe->tail.td = ox->ox_stds[0];
+ ox->ox_stds[0] = data;
+ ohci_reset_std_chain(sc, xfer, len, isread, data, &last);
+
+ tail = opipe->tail.td; /* point at sentinel */
+ memset(&tail->td, 0, sizeof(tail->td));
+ tail->nexttd = NULL;
tail->xfer = NULL;
+ usb_syncmem(&tail->dma, tail->offs, sizeof(tail->td),
+ BUS_DMASYNC_PREWRITE);
- data->td.td_flags = HTOO32(
- OHCI_TD_IN | OHCI_TD_NOCC |
- OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY);
- if (xfer->ux_flags & USBD_SHORT_XFER_OK)
- data->td.td_flags |= HTOO32(OHCI_TD_R);
- data->td.td_cbp = HTOO32(DMAADDR(&xfer->ux_dmabuf, 0));
- data->td.td_nexttd = HTOO32(tail->physaddr);
- data->td.td_be = HTOO32(O32TOH(data->td.td_cbp) +
- xfer->ux_length - 1);
- data->nexttd = tail;
- data->len = xfer->ux_length;
- data->xfer = xfer;
- data->flags = OHCI_CALL_DONE | OHCI_ADD_LEN;
- usb_syncmem(&data->dma, data->offs, sizeof(data->td),
+ /* We want interrupt at the end of the transfer. */
+ last->td.td_flags &= HTOO32(~OHCI_TD_INTR_MASK);
+ last->td.td_flags |= HTOO32(OHCI_TD_SET_DI(1));
+
+ last->td.td_nexttd = HTOO32(tail->physaddr);
+ last->nexttd = tail;
+ last->flags |= OHCI_CALL_DONE;
+ usb_syncmem(&last->dma, last->offs, sizeof(last->td),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
xfer->ux_hcpriv = data;
xfer->ux_actlen = 0;
+ /* Insert ED in schedule */
sed->ed.ed_tailp = HTOO32(tail->physaddr);
usb_syncmem(&sed->dma,
sed->offs + offsetof(ohci_ed_t, ed_tailp),
sizeof(sed->ed.ed_tailp),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- opipe->tail.td = tail;
}
}
@@ -1642,8 +1831,6 @@ ohci_waitintr(ohci_softc_t *sc, struct u
xfer->ux_status = USBD_TIMEOUT;
usb_transfer_complete(xfer);
- /* XXX should free TD */
-
done:
mutex_exit(&sc->sc_lock);
}
@@ -2001,9 +2188,7 @@ ohci_open(struct usbd_pipe *pipe)
goto bad;
opipe->sed = sed;
if (xfertype == UE_ISOCHRONOUS) {
- mutex_enter(&sc->sc_lock);
sitd = ohci_alloc_sitd(sc);
- mutex_exit(&sc->sc_lock);
if (sitd == NULL)
goto bad;
@@ -2015,9 +2200,7 @@ ohci_open(struct usbd_pipe *pipe)
else
fmt |= OHCI_ED_DIR_OUT;
} else {
- mutex_enter(&sc->sc_lock);
std = ohci_alloc_std(sc);
- mutex_exit(&sc->sc_lock);
if (std == NULL)
goto bad;
@@ -2074,9 +2257,7 @@ ohci_open(struct usbd_pipe *pipe)
bad:
if (std != NULL) {
- mutex_enter(&sc->sc_lock);
ohci_free_std(sc, std);
- mutex_exit(&sc->sc_lock);
}
if (sed != NULL)
ohci_free_sed(sc, sed);
@@ -2125,7 +2306,7 @@ ohci_close_pipe(struct usbd_pipe *pipe,
usb_delay_ms(&sc->sc_bus, 1);
pipe->up_endpoint->ue_toggle =
(O32TOH(sed->ed.ed_headp) & OHCI_TOGGLECARRY) ? 1 : 0;
- ohci_free_sed(sc, opipe->sed);
+ ohci_free_sed_locked(sc, opipe->sed);
}
/*
@@ -2235,7 +2416,7 @@ ohci_abort_xfer(struct usbd_xfer *xfer,
for (; p->xfer == xfer; p = n) {
hit |= headp == p->physaddr;
n = p->nexttd;
- ohci_free_std(sc, p);
+ ohci_hash_rem_td(sc, p);
}
/* Zap headp register if hardware pointed inside the xfer. */
if (hit) {
@@ -2543,6 +2724,80 @@ ohci_root_intr_close(struct usbd_pipe *p
/************************/
+int
+ohci_device_ctrl_init(struct usbd_xfer *xfer)
+{
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
+ usb_device_request_t *req = &xfer->ux_request;
+ ohci_softc_t *sc = OHCI_XFER2SC(xfer);
+ ohci_soft_td_t *stat, *tail;
+ int isread = req->bmRequestType & UT_READ;
+ int len = xfer->ux_bufsize;
+ int err = ENOMEM;
+
+ OHCIHIST_FUNC(); OHCIHIST_CALLED();
+
+ /* The TD for setup will be a 'tail' from elsewhere */
+ stat = ohci_alloc_std(sc);
+ if (stat == NULL) {
+ goto bad1;
+ }
+ tail = ohci_alloc_std(sc);
+ if (tail == NULL) {
+ goto bad2;
+ }
+ tail->xfer = NULL;
+
+ ox->ox_stat = stat;
+ ox->ox_tdtail = tail;
+ ox->ox_nstd = 0;
+
+ /* Set up data transaction */
+ if (len != 0) {
+ err = ohci_alloc_std_chain(sc, xfer, len, isread);
+ if (err) {
+ goto bad3;
+ }
+ }
+ return 0;
+
+ bad3:
+ ohci_free_std(sc, tail);
+ bad2:
+ ohci_free_std(sc, stat);
+ bad1:
+ return err;
+}
+
+void
+ohci_device_ctrl_fini(struct usbd_xfer *xfer)
+{
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
+ ohci_softc_t *sc = OHCI_XFER2SC(xfer);
+ struct ohci_pipe *opipe = OHCI_PIPE2OPIPE(xfer->ux_pipe);
+
+ OHCIHIST_FUNC(); OHCIHIST_CALLED();
+ DPRINTFN(8, "xfer %p nstd %d", xfer, ox->ox_nstd, 0, 0);
+
+ mutex_enter(&sc->sc_lock);
+ if (ox->ox_tdtail != opipe->tail.td) {
+ ohci_free_std_locked(sc, ox->ox_tdtail);
+ }
+ for (size_t i = 0; i < ox->ox_nstd; i++) {
+ ohci_soft_td_t *std = ox->ox_stds[i];
+ if (std == NULL)
+ break;
+ ohci_free_std_locked(sc, std);
+ }
+ ohci_free_std_locked(sc, ox->ox_stat);
+ mutex_exit(&sc->sc_lock);
+
+ if (ox->ox_nstd) {
+ const size_t sz = sizeof(ohci_soft_td_t *) * ox->ox_nstd;
+ kmem_free(ox->ox_stds, sz);
+ }
+}
+
Static usbd_status
ohci_device_ctrl_transfer(struct usbd_xfer *xfer)
{
@@ -2564,6 +2819,7 @@ Static usbd_status
ohci_device_ctrl_start(struct usbd_xfer *xfer)
{
ohci_softc_t *sc = OHCI_XFER2SC(xfer);
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
struct ohci_pipe *opipe = OHCI_PIPE2OPIPE(xfer->ux_pipe);
usb_device_request_t *req = &xfer->ux_request;
struct usbd_device *dev __diagused = opipe->pipe.up_dev;
@@ -2571,8 +2827,6 @@ ohci_device_ctrl_start(struct usbd_xfer
ohci_soft_ed_t *sed;
int isread;
int len;
- usbd_status err;
-
OHCIHIST_FUNC(); OHCIHIST_CALLED();
@@ -2581,8 +2835,6 @@ ohci_device_ctrl_start(struct usbd_xfer
KASSERT(xfer->ux_rqflags & URQ_REQUEST);
- mutex_enter(&sc->sc_lock);
-
isread = req->bmRequestType & UT_READ;
len = UGETW(req->wLength);
@@ -2594,18 +2846,13 @@ ohci_device_ctrl_start(struct usbd_xfer
len, dev->ud_addr,
opipe->pipe.up_endpoint->ue_edesc->bEndpointAddress, 0);
+ /* Need to take lock here for pipe->tail.td */
+ mutex_enter(&sc->sc_lock);
+
setup = opipe->tail.td;
- stat = ohci_alloc_std(sc);
- if (stat == NULL) {
- err = USBD_NOMEM;
- goto bad1;
- }
- tail = ohci_alloc_std(sc);
- if (tail == NULL) {
- err = USBD_NOMEM;
- goto bad2;
- }
- tail->xfer = NULL;
+ stat = ox->ox_stat;
+ tail = ox->ox_tdtail;
+ opipe->tail.td = tail;
sed = opipe->sed;
@@ -2618,20 +2865,28 @@ ohci_device_ctrl_start(struct usbd_xfer
OHCI_ED_GET_MAXP(O32TOH(sed->ed.ed_flags)),
UGETW(opipe->pipe.up_endpoint->ue_edesc->wMaxPacketSize));
+ /* next will point to data if len != 0 */
next = stat;
/* Set up data transaction */
if (len != 0) {
- ohci_soft_td_t *std = stat;
+ ohci_soft_td_t *std;
+ ohci_soft_td_t *end;
- err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer,
- std, &stat);
- if (err) {
- /* stat is unchanged if error */
- goto bad3;
- }
- stat = stat->nexttd; /* point at free TD */
+ next = ox->ox_stds[0];
+ ohci_reset_std_chain(sc, xfer, len, isread, next, &end);
+ end->td.td_nexttd = HTOO32(stat->physaddr);
+ end->nexttd = stat;
+
+ usb_syncmem(&end->dma,
+ end->offs + offsetof(ohci_td_t, td_nexttd),
+ sizeof(end->td.td_nexttd),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ usb_syncmem(&xfer->ux_dmabuf, 0, len,
+ isread ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+ std = ox->ox_stds[0];
/* Start toggle at 1 and then use the carried toggle. */
std->td.td_flags &= HTOO32(~OHCI_TD_TOGGLE_MASK);
std->td.td_flags |= HTOO32(OHCI_TD_TOGGLE_1);
@@ -2641,6 +2896,10 @@ ohci_device_ctrl_start(struct usbd_xfer
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
}
+ DPRINTFN(8, "setup %p data %p stat %p tail %p", setup,
+ (len != 0 ? ox->ox_stds[0] : NULL), stat, tail);
+ KASSERT(opipe->tail.td == tail);
+
memcpy(KERNADDR(&opipe->ctrl.reqdma, 0), req, sizeof(*req));
usb_syncmem(&opipe->ctrl.reqdma, 0, sizeof(*req), BUS_DMASYNC_PREWRITE);
@@ -2653,6 +2912,8 @@ ohci_device_ctrl_start(struct usbd_xfer
setup->len = 0;
setup->xfer = xfer;
setup->flags = 0;
+ ohci_hash_add_td(sc, setup);
+
xfer->ux_hcpriv = setup;
usb_syncmem(&setup->dma, setup->offs, sizeof(setup->td),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
@@ -2667,9 +2928,19 @@ ohci_device_ctrl_start(struct usbd_xfer
stat->flags = OHCI_CALL_DONE;
stat->len = 0;
stat->xfer = xfer;
+ ohci_hash_add_td(sc, stat);
+
usb_syncmem(&stat->dma, stat->offs, sizeof(stat->td),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ memset(&tail->td, 0, sizeof(tail->td));
+ tail->nexttd = NULL;
+ tail->xfer = NULL;
+
+ usb_syncmem(&tail->dma, tail->offs, sizeof(tail->td),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+
#ifdef OHCI_DEBUG
USBHIST_LOGN(ohcidebug, 5, "--- dump start ---", 0, 0, 0, 0);
if (ohcidebug > 5) {
@@ -2685,7 +2956,6 @@ ohci_device_ctrl_start(struct usbd_xfer
sed->offs + offsetof(ohci_ed_t, ed_tailp),
sizeof(sed->ed.ed_tailp),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- opipe->tail.td = tail;
OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) {
callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout),
@@ -2714,15 +2984,6 @@ ohci_device_ctrl_start(struct usbd_xfer
ohci_waitintr(sc, xfer);
return USBD_IN_PROGRESS;
-
- bad3:
- ohci_free_std(sc, tail);
- bad2:
- ohci_free_std(sc, stat);
- bad1:
- mutex_exit(&sc->sc_lock);
-
- return err;
}
/* Abort a device control request. */
@@ -2750,7 +3011,7 @@ ohci_device_ctrl_close(struct usbd_pipe
OHCIHIST_FUNC(); OHCIHIST_CALLED();
DPRINTF("pipe=%p", pipe, 0, 0, 0);
ohci_close_pipe(pipe, sc->sc_ctrl_head);
- ohci_free_std(sc, opipe->tail.td);
+ ohci_free_std_locked(sc, opipe->tail.td);
}
/************************/
@@ -2769,6 +3030,57 @@ ohci_noop(struct usbd_pipe *pipe)
{
}
+Static int
+ohci_device_bulk_init(struct usbd_xfer *xfer)
+{
+ ohci_softc_t *sc = OHCI_XFER2SC(xfer);
+ int len = xfer->ux_bufsize;
+ int endpt = xfer->ux_pipe->up_endpoint->ue_edesc->bEndpointAddress;;
+ int isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ int err;
+
+ OHCIHIST_FUNC(); OHCIHIST_CALLED();
+
+ KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
+
+ DPRINTFN(4, "xfer=%p len=%d isread=%d flags=%d", xfer, len, isread,
+ xfer->ux_flags);
+ DPRINTFN(4, "endpt=%d", endpt, 0, 0, 0);
+
+ /* Allocate a chain of new TDs (including a new tail). */
+ err = ohci_alloc_std_chain(sc, xfer, len, isread);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+Static void
+ohci_device_bulk_fini(struct usbd_xfer *xfer)
+{
+ ohci_softc_t *sc = OHCI_XFER2SC(xfer);
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
+ struct ohci_pipe *opipe = OHCI_PIPE2OPIPE(xfer->ux_pipe);
+
+ OHCIHIST_FUNC(); OHCIHIST_CALLED();
+ DPRINTFN(8, "xfer %p nstd %d", xfer, ox->ox_nstd, 0, 0);
+
+ mutex_enter(&sc->sc_lock);
+ for (size_t i = 0; i < ox->ox_nstd; i++) {
+ ohci_soft_td_t *std = ox->ox_stds[i];
+ if (std == NULL)
+ break;
+ if (std != opipe->tail.td)
+ ohci_free_std_locked(sc, std);
+ }
+ mutex_exit(&sc->sc_lock);
+
+ if (ox->ox_nstd) {
+ const size_t sz = sizeof(ohci_soft_td_t *) * ox->ox_nstd;
+ kmem_free(ox->ox_stds, sz);
+ }
+}
+
Static usbd_status
ohci_device_bulk_transfer(struct usbd_xfer *xfer)
{
@@ -2789,14 +3101,13 @@ ohci_device_bulk_transfer(struct usbd_xf
Static usbd_status
ohci_device_bulk_start(struct usbd_xfer *xfer)
{
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
struct ohci_pipe *opipe = OHCI_PIPE2OPIPE(xfer->ux_pipe);
- struct usbd_device *dev = opipe->pipe.up_dev;
ohci_softc_t *sc = OHCI_XFER2SC(xfer);
- int addr = dev->ud_addr;
+ ohci_soft_td_t *last;
ohci_soft_td_t *data, *tail, *tdp;
ohci_soft_ed_t *sed;
int len, isread, endpt;
- usbd_status err;
OHCIHIST_FUNC(); OHCIHIST_CALLED();
@@ -2805,8 +3116,6 @@ ohci_device_bulk_start(struct usbd_xfer
KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
- mutex_enter(&sc->sc_lock);
-
len = xfer->ux_length;
endpt = xfer->ux_pipe->up_endpoint->ue_edesc->bEndpointAddress;
isread = UE_GET_DIR(endpt) == UE_DIR_IN;
@@ -2816,35 +3125,34 @@ ohci_device_bulk_start(struct usbd_xfer
xfer->ux_flags);
DPRINTFN(4, "endpt=%d", endpt, 0, 0, 0);
- usb_syncmem(&sed->dma, sed->offs, sizeof(sed->ed),
- BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
- /* Update device address */
- sed->ed.ed_flags = HTOO32(
- (O32TOH(sed->ed.ed_flags) & ~OHCI_ED_ADDRMASK) |
- OHCI_ED_SET_FA(addr));
+ mutex_enter(&sc->sc_lock);
- /* Allocate a chain of new TDs (including a new tail). */
+ /* Use "tail" TD and loan our first TD to next transfer */
data = opipe->tail.td;
- err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer,
- data, &tail);
- if (err)
- return err;
+ opipe->tail.td = ox->ox_stds[0];
+ ox->ox_stds[0] = data;
+ ohci_reset_std_chain(sc, xfer, len, isread, data, &last);
+
+ tail = opipe->tail.td; /* point at sentinel */
+ memset(&tail->td, 0, sizeof(tail->td));
+ tail->nexttd = NULL;
+ tail->xfer = NULL;
+ usb_syncmem(&tail->dma, tail->offs, sizeof(tail->td),
+ BUS_DMASYNC_PREWRITE);
+ xfer->ux_hcpriv = data;
+
+ DPRINTFN(8, "xfer %p data %p tail %p", xfer, ox->ox_stds[0], tail, 0);
+ KASSERT(opipe->tail.td == tail);
/* We want interrupt at the end of the transfer. */
- tail->td.td_flags &= HTOO32(~OHCI_TD_INTR_MASK);
- tail->td.td_flags |= HTOO32(OHCI_TD_SET_DI(1));
- tail->flags |= OHCI_CALL_DONE;
- tail = tail->nexttd; /* point at sentinel */
- usb_syncmem(&tail->dma, tail->offs + offsetof(ohci_td_t, td_flags),
- sizeof(tail->td.td_flags),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- if (err) {
- mutex_exit(&sc->sc_lock);
- return err;
- }
+ last->td.td_flags &= HTOO32(~OHCI_TD_INTR_MASK);
+ last->td.td_flags |= HTOO32(OHCI_TD_SET_DI(1));
- tail->xfer = NULL;
- xfer->ux_hcpriv = data;
+ last->td.td_nexttd = HTOO32(tail->physaddr);
+ last->nexttd = tail;
+ last->flags |= OHCI_CALL_DONE;
+ usb_syncmem(&last->dma, last->offs, sizeof(last->td),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
DPRINTFN(4, "ed_flags=0x%08x td_flags=0x%08x "
"td_cbp=0x%08x td_be=0x%08x",
@@ -2864,10 +3172,11 @@ ohci_device_bulk_start(struct usbd_xfer
/* Insert ED in schedule */
for (tdp = data; tdp != tail; tdp = tdp->nexttd) {
- tdp->xfer = xfer;
+ KASSERT(tdp->xfer == xfer);
}
+ usb_syncmem(&sed->dma, sed->offs, sizeof(sed->ed),
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
sed->ed.ed_tailp = HTOO32(tail->physaddr);
- opipe->tail.td = tail;
sed->ed.ed_flags &= HTOO32(~OHCI_ED_SKIP);
usb_syncmem(&sed->dma, sed->offs, sizeof(sed->ed),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
@@ -2909,11 +3218,66 @@ ohci_device_bulk_close(struct usbd_pipe
DPRINTF("pipe=%p", pipe, 0, 0, 0);
ohci_close_pipe(pipe, sc->sc_bulk_head);
- ohci_free_std(sc, opipe->tail.td);
+ ohci_free_std_locked(sc, opipe->tail.td);
}
/************************/
+Static int
+ohci_device_intr_init(struct usbd_xfer *xfer)
+{
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
+ ohci_softc_t *sc = OHCI_XFER2SC(xfer);
+ int len = xfer->ux_bufsize;
+ int endpt = xfer->ux_pipe->up_endpoint->ue_edesc->bEndpointAddress;;
+ int isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ int err;
+
+ OHCIHIST_FUNC(); OHCIHIST_CALLED();
+
+ KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
+ KASSERT(len != 0);
+
+ DPRINTFN(4, "xfer=%p len=%d isread=%d flags=%d", xfer, len, isread,
+ xfer->ux_flags);
+ DPRINTFN(4, "endpt=%d", endpt, 0, 0, 0);
+
+ ox->ox_nstd = 0;
+
+ err = ohci_alloc_std_chain(sc, xfer, len, isread);
+ if (err) {
+ return err;
+ }
+
+ return 0;
+}
+
+Static void
+ohci_device_intr_fini(struct usbd_xfer *xfer)
+{
+ ohci_softc_t *sc = OHCI_XFER2SC(xfer);
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
+ struct ohci_pipe *opipe = OHCI_PIPE2OPIPE(xfer->ux_pipe);
+
+ OHCIHIST_FUNC(); OHCIHIST_CALLED();
+ DPRINTFN(8, "xfer %p nstd %d", xfer, ox->ox_nstd, 0, 0);
+
+ mutex_enter(&sc->sc_lock);
+ for (size_t i = 0; i < ox->ox_nstd; i++) {
+ ohci_soft_td_t *std = ox->ox_stds[i];
+ if (std != NULL)
+ break;
+ if (std != opipe->tail.td)
+ ohci_free_std_locked(sc, std);
+ }
+ mutex_exit(&sc->sc_lock);
+
+ if (ox->ox_nstd) {
+ const size_t sz = sizeof(ohci_soft_td_t *) * ox->ox_nstd;
+ kmem_free(ox->ox_stds, sz);
+ }
+}
+
Static usbd_status
ohci_device_intr_transfer(struct usbd_xfer *xfer)
{
@@ -2934,10 +3298,11 @@ ohci_device_intr_transfer(struct usbd_xf
Static usbd_status
ohci_device_intr_start(struct usbd_xfer *xfer)
{
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
struct ohci_pipe *opipe = OHCI_PIPE2OPIPE(xfer->ux_pipe);
ohci_softc_t *sc = OHCI_XFER2SC(xfer);
ohci_soft_ed_t *sed = opipe->sed;
- ohci_soft_td_t *data, *tail;
+ ohci_soft_td_t *data, *last, *tail;
int len, isread, endpt;
OHCIHIST_FUNC(); OHCIHIST_CALLED();
@@ -2954,30 +3319,34 @@ ohci_device_intr_start(struct usbd_xfer
endpt = xfer->ux_pipe->up_endpoint->ue_edesc->bEndpointAddress;
isread = UE_GET_DIR(endpt) == UE_DIR_IN;
- data = opipe->tail.td;
mutex_enter(&sc->sc_lock);
- tail = ohci_alloc_std(sc);
- mutex_exit(&sc->sc_lock);
- if (tail == NULL)
- return USBD_NOMEM;
+
+ /* Use "tail" TD and loan our first TD to next transfer */
+ data = opipe->tail.td;
+ opipe->tail.td = ox->ox_stds[0];
+ ox->ox_stds[0] = data;
+ ohci_reset_std_chain(sc, xfer, len, isread, data, &last);
+
+ tail = opipe->tail.td; /* point at sentinel */
+ memset(&tail->td, 0, sizeof(tail->td));
+ tail->nexttd = NULL;
tail->xfer = NULL;
+ usb_syncmem(&tail->dma, tail->offs, sizeof(tail->td),
+ BUS_DMASYNC_PREWRITE);
+ xfer->ux_hcpriv = data;
+
+ DPRINTFN(8, "data %p tail %p", ox->ox_stds[0], tail, 0, 0);
+ KASSERT(opipe->tail.td == tail);
- data->td.td_flags = HTOO32(
- isread ? OHCI_TD_IN : OHCI_TD_OUT |
- OHCI_TD_NOCC |
- OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY);
- if (xfer->ux_flags & USBD_SHORT_XFER_OK)
- data->td.td_flags |= HTOO32(OHCI_TD_R);
- data->td.td_cbp = HTOO32(DMAADDR(&xfer->ux_dmabuf, 0));
- data->td.td_nexttd = HTOO32(tail->physaddr);
- data->td.td_be = HTOO32(O32TOH(data->td.td_cbp) + len - 1);
- data->nexttd = tail;
- data->len = len;
- data->xfer = xfer;
- data->flags = OHCI_CALL_DONE | OHCI_ADD_LEN;
- usb_syncmem(&data->dma, data->offs, sizeof(data->td),
+ /* We want interrupt at the end of the transfer. */
+ last->td.td_flags &= HTOO32(~OHCI_TD_INTR_MASK);
+ last->td.td_flags |= HTOO32(OHCI_TD_SET_DI(1));
+
+ last->td.td_nexttd = HTOO32(tail->physaddr);
+ last->nexttd = tail;
+ last->flags |= OHCI_CALL_DONE;
+ usb_syncmem(&last->dma, last->offs, sizeof(last->td),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- xfer->ux_hcpriv = data;
#ifdef OHCI_DEBUG
DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0);
@@ -2989,11 +3358,9 @@ ohci_device_intr_start(struct usbd_xfer
#endif
/* Insert ED in schedule */
- mutex_enter(&sc->sc_lock);
usb_syncmem(&sed->dma, sed->offs, sizeof(sed->ed),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
sed->ed.ed_tailp = HTOO32(tail->physaddr);
- opipe->tail.td = tail;
sed->ed.ed_flags &= HTOO32(~OHCI_ED_SKIP);
usb_syncmem(&sed->dma, sed->offs, sizeof(sed->ed),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
@@ -3053,8 +3420,8 @@ ohci_device_intr_close(struct usbd_pipe
for (j = 0; j < nslots; j++)
--sc->sc_bws[(pos * nslots + j) % OHCI_NO_INTRS];
- ohci_free_std(sc, opipe->tail.td);
- ohci_free_sed(sc, opipe->sed);
+ ohci_free_std_locked(sc, opipe->tail.td);
+ ohci_free_sed_locked(sc, opipe->sed);
}
Static usbd_status
@@ -3131,6 +3498,73 @@ ohci_device_setintr(ohci_softc_t *sc, st
/***********************/
+Static int
+ohci_device_isoc_init(struct usbd_xfer *xfer)
+{
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
+ ohci_softc_t *sc = OHCI_XFER2SC(xfer);
+ ohci_soft_itd_t *sitd;
+ size_t i;
+ int err;
+
+ OHCIHIST_FUNC(); OHCIHIST_CALLED();
+
+ DPRINTFN(1, "xfer %p len %d flags %d", xfer, xfer->ux_length,
+ xfer->ux_flags, 0);
+
+ const size_t nfsitd =
+ (xfer->ux_nframes + OHCI_ITD_NOFFSET - 1) / OHCI_ITD_NOFFSET;
+ const size_t nbsitd = xfer->ux_bufsize / OHCI_PAGE_SIZE;
+ const size_t nsitd = MAX(nfsitd, nbsitd) + 1;
+
+ ox->ox_sitds = kmem_zalloc(sizeof(ohci_soft_itd_t *) * nsitd,
+ KM_SLEEP);
+ ox->ox_nsitd = nsitd;
+
+ for (i = 0; i < nsitd; i++) {
+ /* Allocate next ITD */
+ sitd = ohci_alloc_sitd(sc);
+ if (sitd == NULL) {
+ err = ENOMEM;
+ goto fail;
+ }
+ ox->ox_sitds[i] = sitd;
+ sitd->xfer = xfer;
+ sitd->flags = 0;
+ }
+
+ return 0;
+fail:
+ for (; i > 0;) {
+ ohci_free_sitd(sc, ox->ox_sitds[--i]);
+ }
+ return err;
+}
+
+Static void
+ohci_device_isoc_fini(struct usbd_xfer *xfer)
+{
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
+ ohci_softc_t *sc = OHCI_XFER2SC(xfer);
+ struct ohci_pipe *opipe = OHCI_PIPE2OPIPE(xfer->ux_pipe);
+
+ OHCIHIST_FUNC(); OHCIHIST_CALLED();
+
+ mutex_enter(&sc->sc_lock);
+ for (size_t i = 0; i < ox->ox_nsitd; i++) {
+ if (ox->ox_sitds[i] != opipe->tail.itd) {
+ ohci_free_sitd_locked(sc, ox->ox_sitds[i]);
+ }
+ }
+ mutex_exit(&sc->sc_lock);
+
+ if (ox->ox_nsitd) {
+ const size_t sz = sizeof(ohci_soft_itd_t *) * ox->ox_nsitd;
+ kmem_free(ox->ox_sitds, sz);
+ }
+}
+
+
usbd_status
ohci_device_isoc_transfer(struct usbd_xfer *xfer)
{
@@ -3165,21 +3599,27 @@ ohci_device_isoc_transfer(struct usbd_xf
void
ohci_device_isoc_enter(struct usbd_xfer *xfer)
{
+ struct ohci_xfer *ox = OHCI_XFER2OXFER(xfer);
struct ohci_pipe *opipe = OHCI_PIPE2OPIPE(xfer->ux_pipe);
ohci_softc_t *sc = OHCI_XFER2SC(xfer);
ohci_soft_ed_t *sed = opipe->sed;
- struct isoc *isoc = &opipe->isoc;
ohci_soft_itd_t *sitd, *nsitd;
ohci_physaddr_t buf, offs, noffs, bp0;
int i, ncur, nframes;
OHCIHIST_FUNC(); OHCIHIST_CALLED();
- DPRINTFN(1, "used=%d next=%d xfer=%p nframes=%d",
- isoc->inuse, isoc->next, xfer, xfer->ux_nframes);
+ mutex_enter(&sc->sc_lock);
- if (sc->sc_dying)
+ if (sc->sc_dying) {
+ mutex_exit(&sc->sc_lock);
return;
+ }
+
+ struct isoc *isoc = &opipe->isoc;
+
+ DPRINTFN(1, "used=%d next=%d xfer=%p nframes=%d",
+ isoc->inuse, isoc->next, xfer, xfer->ux_nframes);
if (isoc->next == -1) {
/* Not in use yet, schedule it a few frames ahead. */
@@ -3188,26 +3628,24 @@ ohci_device_isoc_enter(struct usbd_xfer
}
sitd = opipe->tail.itd;
+ opipe->tail.itd = ox->ox_sitds[0];
+ ox->ox_sitds[0] = sitd;
+
buf = DMAADDR(&xfer->ux_dmabuf, 0);
bp0 = OHCI_PAGE(buf);
offs = OHCI_PAGE_OFFSET(buf);
nframes = xfer->ux_nframes;
xfer->ux_hcpriv = sitd;
+ size_t j = 1;
for (i = ncur = 0; i < nframes; i++, ncur++) {
noffs = offs + xfer->ux_frlengths[i];
if (ncur == OHCI_ITD_NOFFSET || /* all offsets used */
OHCI_PAGE(buf + noffs) > bp0 + OHCI_PAGE_SIZE) { /* too many page crossings */
/* Allocate next ITD */
- mutex_enter(&sc->sc_lock);
- nsitd = ohci_alloc_sitd(sc);
- mutex_exit(&sc->sc_lock);
- if (nsitd == NULL) {
- /* XXX what now? */
- printf("%s: isoc TD alloc failed\n",
- device_xname(sc->sc_dev));
- return;
- }
+ nsitd = ox->ox_sitds[j++];
+ KASSERT(nsitd != NULL);
+ KASSERT(j < ox->ox_nsitd);
/* Fill current ITD */
sitd->itd.itd_flags = HTOO32(
@@ -3221,6 +3659,10 @@ ohci_device_isoc_enter(struct usbd_xfer
sitd->nextitd = nsitd;
sitd->xfer = xfer;
sitd->flags = 0;
+#ifdef DIAGNOSTIC
+ sitd->isdone = false;
+#endif
+ ohci_hash_add_itd(sc, sitd);
usb_syncmem(&sitd->dma, sitd->offs, sizeof(sitd->itd),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
@@ -3232,15 +3674,13 @@ ohci_device_isoc_enter(struct usbd_xfer
sitd->itd.itd_offset[ncur] = HTOO16(OHCI_ITD_MK_OFFS(offs));
offs = noffs;
}
- mutex_enter(&sc->sc_lock);
- nsitd = ohci_alloc_sitd(sc);
- mutex_exit(&sc->sc_lock);
- if (nsitd == NULL) {
- /* XXX what now? */
- printf("%s: isoc TD alloc failed\n",
- device_xname(sc->sc_dev));
- return;
- }
+ nsitd = ox->ox_sitds[j++];
+ KASSERT(nsitd != NULL);
+ KASSERT(j <= ox->ox_nsitd);
+
+ memset(&nsitd->itd, 0, sizeof(nsitd->itd));
+ nsitd->nextitd = NULL;
+ nsitd->xfer = NULL;
/* Fixup last used ITD */
sitd->itd.itd_flags = HTOO32(
OHCI_ITD_NOCC |
@@ -3253,6 +3693,10 @@ ohci_device_isoc_enter(struct usbd_xfer
sitd->nextitd = nsitd;
sitd->xfer = xfer;
sitd->flags = OHCI_CALL_DONE;
+#ifdef DIAGNOSTIC
+ sitd->isdone = false;
+#endif
+ ohci_hash_add_itd(sc, sitd);
usb_syncmem(&sitd->dma, sitd->offs, sizeof(sitd->itd),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
@@ -3272,7 +3716,6 @@ ohci_device_isoc_enter(struct usbd_xfer
}
#endif
- mutex_enter(&sc->sc_lock);
usb_syncmem(&sed->dma, sed->offs, sizeof(sed->ed),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
sed->ed.ed_tailp = HTOO32(nsitd->physaddr);
@@ -3415,5 +3858,5 @@ ohci_device_isoc_close(struct usbd_pipe
#ifdef DIAGNOSTIC
opipe->tail.itd->isdone = true;
#endif
- ohci_free_sitd(sc, opipe->tail.itd);
+ ohci_free_sitd_locked(sc, opipe->tail.itd);
}
Index: src/sys/dev/usb/ohcivar.h
diff -u src/sys/dev/usb/ohcivar.h:1.55.6.10 src/sys/dev/usb/ohcivar.h:1.55.6.11
--- src/sys/dev/usb/ohcivar.h:1.55.6.10 Wed Dec 2 20:07:08 2015
+++ src/sys/dev/usb/ohcivar.h Sun Dec 6 15:39:35 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: ohcivar.h,v 1.55.6.10 2015/12/02 20:07:08 skrll Exp $ */
+/* $NetBSD: ohcivar.h,v 1.55.6.11 2015/12/06 15:39:35 skrll Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -146,6 +146,28 @@ typedef struct ohci_softc {
struct ohci_xfer {
struct usbd_xfer xfer;
struct usb_task abort_task;
+ /*
+ * The TD/iTD that is used to terminate the chain and is borrowed
+ * by the next transfer for its first TD
+ */
+ union {
+ ohci_soft_td_t *ox_tdtail;
+ ohci_soft_itd_t *ox_itdtail;
+ };
+ union {
+ /* ctrl/bulk/intr */
+ struct {
+ ohci_soft_td_t **ox_stds;
+ size_t ox_nstd;
+ };
+ /* isoc */
+ struct {
+ ohci_soft_itd_t **ox_sitds;
+ size_t ox_nsitd;
+ };
+ };
+ /* ctrl */
+ ohci_soft_td_t *ox_stat;
};
#define OHCI_BUS2SC(bus) ((bus)->ub_hcpriv)