Module Name: src
Committed By: skrll
Date: Sun Oct 25 09:50:06 UTC 2015
Modified Files:
src/sys/dev/usb [nick-nhusb]: ehci.c ehcivar.h
Log Message:
Restructure the xfer methods so that (close to) minimal work in done in softint
context. Now all memory allocation is done in thread context.
Addresses kern/48308 for ehci(4), fixes a bunch of locking bugs around the
*TD free lists, and plugs some memory leaks on error conditions.
XXX revisit for isoc caching models (4.7.2.1)
To generate a diff of this commit:
cvs rdiff -u -r1.234.2.63 -r1.234.2.64 src/sys/dev/usb/ehci.c
cvs rdiff -u -r1.42.14.20 -r1.42.14.21 src/sys/dev/usb/ehcivar.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/ehci.c
diff -u src/sys/dev/usb/ehci.c:1.234.2.63 src/sys/dev/usb/ehci.c:1.234.2.64
--- src/sys/dev/usb/ehci.c:1.234.2.63 Sun Oct 25 09:28:41 2015
+++ src/sys/dev/usb/ehci.c Sun Oct 25 09:50:06 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: ehci.c,v 1.234.2.63 2015/10/25 09:28:41 skrll Exp $ */
+/* $NetBSD: ehci.c,v 1.234.2.64 2015/10/25 09:50:06 skrll Exp $ */
/*
* Copyright (c) 2004-2012 The NetBSD Foundation, Inc.
@@ -53,7 +53,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.234.2.63 2015/10/25 09:28:41 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.234.2.64 2015/10/25 09:50:06 skrll Exp $");
#include "ohci.h"
#include "uhci.h"
@@ -167,6 +167,7 @@ Static void ehci_pcd(void *);
Static struct usbd_xfer *
ehci_allocx(struct usbd_bus *, unsigned int);
Static void ehci_freex(struct usbd_bus *, struct usbd_xfer *);
+
Static void ehci_get_lock(struct usbd_bus *, kmutex_t **);
Static int ehci_roothub_ctrl(struct usbd_bus *,
usb_device_request_t *, void *, int);
@@ -177,30 +178,40 @@ Static void ehci_root_intr_abort(struct
Static void ehci_root_intr_close(struct usbd_pipe *);
Static void ehci_root_intr_done(struct usbd_xfer *);
+Static int ehci_device_ctrl_init(struct usbd_xfer *);
+Static void ehci_device_ctrl_fini(struct usbd_xfer *);
Static usbd_status ehci_device_ctrl_transfer(struct usbd_xfer *);
Static usbd_status ehci_device_ctrl_start(struct usbd_xfer *);
Static void ehci_device_ctrl_abort(struct usbd_xfer *);
Static void ehci_device_ctrl_close(struct usbd_pipe *);
Static void ehci_device_ctrl_done(struct usbd_xfer *);
+Static int ehci_device_bulk_init(struct usbd_xfer *);
+Static void ehci_device_bulk_fini(struct usbd_xfer *);
Static usbd_status ehci_device_bulk_transfer(struct usbd_xfer *);
Static usbd_status ehci_device_bulk_start(struct usbd_xfer *);
Static void ehci_device_bulk_abort(struct usbd_xfer *);
Static void ehci_device_bulk_close(struct usbd_pipe *);
Static void ehci_device_bulk_done(struct usbd_xfer *);
+Static int ehci_device_intr_init(struct usbd_xfer *);
+Static void ehci_device_intr_fini(struct usbd_xfer *);
Static usbd_status ehci_device_intr_transfer(struct usbd_xfer *);
Static usbd_status ehci_device_intr_start(struct usbd_xfer *);
Static void ehci_device_intr_abort(struct usbd_xfer *);
Static void ehci_device_intr_close(struct usbd_pipe *);
Static void ehci_device_intr_done(struct usbd_xfer *);
+Static int ehci_device_isoc_init(struct usbd_xfer *);
+Static void ehci_device_isoc_fini(struct usbd_xfer *);
Static usbd_status ehci_device_isoc_transfer(struct usbd_xfer *);
Static usbd_status ehci_device_isoc_start(struct usbd_xfer *);
Static void ehci_device_isoc_abort(struct usbd_xfer *);
Static void ehci_device_isoc_close(struct usbd_pipe *);
Static void ehci_device_isoc_done(struct usbd_xfer *);
+Static int ehci_device_fs_isoc_init(struct usbd_xfer *);
+Static void ehci_device_fs_isoc_fini(struct usbd_xfer *);
Static usbd_status ehci_device_fs_isoc_transfer(struct usbd_xfer *);
Static usbd_status ehci_device_fs_isoc_start(struct usbd_xfer *);
Static void ehci_device_fs_isoc_abort(struct usbd_xfer *);
@@ -217,25 +228,37 @@ Static void ehci_free_sqh(ehci_softc_t
Static ehci_soft_qtd_t *ehci_alloc_sqtd(ehci_softc_t *);
Static void ehci_free_sqtd(ehci_softc_t *, ehci_soft_qtd_t *);
-Static usbd_status ehci_alloc_sqtd_chain(struct ehci_pipe *,
- ehci_softc_t *, int, int, struct usbd_xfer *,
- ehci_soft_qtd_t **, ehci_soft_qtd_t **);
-Static void ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qtd_t *,
- ehci_soft_qtd_t *);
+Static usbd_status ehci_alloc_sqtd_chain(ehci_softc_t *, struct usbd_xfer *,
+ int, int, ehci_soft_qtd_t **, ehci_soft_qtd_t **);
+Static void ehci_free_sqtds(ehci_softc_t *, struct ehci_xfer *);
+
+Static void ehci_reset_sqtd_chain(ehci_softc_t *, struct usbd_xfer *,
+ int, int, int *, ehci_soft_qtd_t *, ehci_soft_qtd_t **);
Static ehci_soft_itd_t *ehci_alloc_itd(ehci_softc_t *);
Static ehci_soft_sitd_t *
ehci_alloc_sitd(ehci_softc_t *);
-Static void ehci_free_itd(ehci_softc_t *, ehci_soft_itd_t *);
-Static void ehci_free_sitd(ehci_softc_t *, ehci_soft_sitd_t *);
-Static void ehci_rem_free_itd_chain(ehci_softc_t *,
- struct ehci_xfer *);
-Static void ehci_rem_free_sitd_chain(ehci_softc_t *,
- struct ehci_xfer *);
-Static void ehci_abort_isoc_xfer(struct usbd_xfer *,
- usbd_status);
-Static usbd_status ehci_device_request(struct usbd_xfer *);
+Static void ehci_remove_itd_chain(ehci_softc_t *, ehci_soft_itd_t *);
+Static void ehci_remove_sitd_chain(ehci_softc_t *, ehci_soft_sitd_t *);
+Static void ehci_free_itd_chain(ehci_softc_t *, ehci_soft_itd_t *);
+Static void ehci_free_sitd_chain(ehci_softc_t *, ehci_soft_sitd_t *);
+
+static inline void
+ehci_free_itd_locked(ehci_softc_t *sc, ehci_soft_itd_t *itd)
+{
+
+ LIST_INSERT_HEAD(&sc->sc_freeitds, itd, free_list);
+}
+
+static inline void
+ehci_free_sitd_locked(ehci_softc_t *sc, ehci_soft_sitd_t *sitd)
+{
+
+ LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, free_list);
+}
+
+Static void ehci_abort_isoc_xfer(struct usbd_xfer *, usbd_status);
Static usbd_status ehci_device_setintr(ehci_softc_t *, ehci_soft_qh_t *,
int);
@@ -262,6 +285,7 @@ Static void ehci_dump_sqtd(ehci_soft_qt
Static void ehci_dump_qtd(ehci_qtd_t *);
Static void ehci_dump_sqh(ehci_soft_qh_t *);
Static void ehci_dump_sitd(struct ehci_soft_itd *);
+Static void ehci_dump_itds(ehci_soft_itd_t *);
Static void ehci_dump_itd(struct ehci_soft_itd *);
Static void ehci_dump_exfer(struct ehci_xfer *);
#endif
@@ -297,6 +321,8 @@ Static const struct usbd_pipe_methods eh
};
Static const struct usbd_pipe_methods ehci_device_ctrl_methods = {
+ .upm_init = ehci_device_ctrl_init,
+ .upm_fini = ehci_device_ctrl_fini,
.upm_transfer = ehci_device_ctrl_transfer,
.upm_start = ehci_device_ctrl_start,
.upm_abort = ehci_device_ctrl_abort,
@@ -306,6 +332,8 @@ Static const struct usbd_pipe_methods eh
};
Static const struct usbd_pipe_methods ehci_device_intr_methods = {
+ .upm_init = ehci_device_intr_init,
+ .upm_fini = ehci_device_intr_fini,
.upm_transfer = ehci_device_intr_transfer,
.upm_start = ehci_device_intr_start,
.upm_abort = ehci_device_intr_abort,
@@ -315,6 +343,8 @@ Static const struct usbd_pipe_methods eh
};
Static const struct usbd_pipe_methods ehci_device_bulk_methods = {
+ .upm_init = ehci_device_bulk_init,
+ .upm_fini = ehci_device_bulk_fini,
.upm_transfer = ehci_device_bulk_transfer,
.upm_start = ehci_device_bulk_start,
.upm_abort = ehci_device_bulk_abort,
@@ -324,6 +354,8 @@ Static const struct usbd_pipe_methods eh
};
Static const struct usbd_pipe_methods ehci_device_isoc_methods = {
+ .upm_init = ehci_device_isoc_init,
+ .upm_fini = ehci_device_isoc_fini,
.upm_transfer = ehci_device_isoc_transfer,
.upm_start = ehci_device_isoc_start,
.upm_abort = ehci_device_isoc_abort,
@@ -333,6 +365,8 @@ Static const struct usbd_pipe_methods eh
};
Static const struct usbd_pipe_methods ehci_device_fs_isoc_methods = {
+ .upm_init = ehci_device_fs_isoc_init,
+ .upm_fini = ehci_device_fs_isoc_fini,
.upm_transfer = ehci_device_fs_isoc_transfer,
.upm_start = ehci_device_fs_isoc_start,
.upm_abort = ehci_device_fs_isoc_abort,
@@ -783,22 +817,27 @@ ehci_softintr(void *v)
Static void
ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
{
- struct usbd_device *dev = ex->ex_xfer.ux_pipe->up_dev;
- int attr;
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
USBHIST_LOG(ehcidebug, "ex = %p", ex, 0, 0, 0);
KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock));
- attr = ex->ex_xfer.ux_pipe->up_endpoint->ue_edesc->bmAttributes;
- if (UE_GET_XFERTYPE(attr) == UE_ISOCHRONOUS) {
- if (dev->ud_speed == USB_SPEED_HIGH)
- ehci_check_itd_intr(sc, ex);
- else
- ehci_check_sitd_intr(sc, ex);
- } else
+ switch (ex->ex_type) {
+ case EX_CTRL:
+ case EX_BULK:
+ case EX_INTR:
ehci_check_qh_intr(sc, ex);
+ break;
+ case EX_ISOC:
+ ehci_check_itd_intr(sc, ex);
+ break;
+ case EX_FS_ISOC:
+ ehci_check_sitd_intr(sc, ex);
+ break;
+ default:
+ KASSERT(false);
+ }
return;
}
@@ -806,25 +845,23 @@ ehci_check_intr(ehci_softc_t *sc, struct
Static void
ehci_check_qh_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
{
- ehci_soft_qtd_t *sqtd, *lsqtd;
+ ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd;
uint32_t status;
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock));
- if (ex->ex_sqtdstart == NULL) {
- printf("ehci_check_qh_intr: not valid sqtd\n");
- return;
+ if (ex->ex_type == EX_CTRL) {
+ fsqtd = ex->ex_setup;
+ lsqtd = ex->ex_status;
+ } else {
+ fsqtd = ex->ex_sqtdstart;
+ lsqtd = ex->ex_sqtdend;
}
+ KASSERTMSG(fsqtd != NULL && lsqtd != NULL,
+ "xfer %p xt %d fsqtd %p lsqtd %p", ex, ex->ex_type, fsqtd, lsqtd);
- lsqtd = ex->ex_sqtdend;
-#ifdef DIAGNOSTIC
- if (lsqtd == NULL) {
- printf("ehci_check_qh_intr: lsqtd==0\n");
- return;
- }
-#endif
/*
* If the last TD is still active we need to check whether there
* is an error somewhere in the middle, or whether there was a
@@ -840,8 +877,7 @@ ehci_check_qh_intr(ehci_softc_t *sc, str
sizeof(lsqtd->qtd.qtd_status), BUS_DMASYNC_PREREAD);
if (status & EHCI_QTD_ACTIVE) {
USBHIST_LOGN(ehcidebug, 10, "active ex=%p", ex, 0, 0, 0);
- for (sqtd = ex->ex_sqtdstart; sqtd != lsqtd;
- sqtd = sqtd->nextqtd) {
+ for (sqtd = fsqtd; sqtd != lsqtd; sqtd = sqtd->nextqtd) {
usb_syncmem(&sqtd->dma,
sqtd->offs + offsetof(ehci_qtd_t, qtd_status),
sizeof(sqtd->qtd.qtd_status),
@@ -858,11 +894,6 @@ ehci_check_qh_intr(ehci_softc_t *sc, str
goto done;
/* Handle short packets */
if (EHCI_QTD_GET_BYTES(status) != 0) {
- struct usbd_pipe *pipe = ex->ex_xfer.ux_pipe;
- usb_endpoint_descriptor_t *ed =
- pipe->up_endpoint->ue_edesc;
- uint8_t xt = UE_GET_XFERTYPE(ed->bmAttributes);
-
/*
* If we get here for a control transfer then
* we need to let the hardware complete the
@@ -871,7 +902,7 @@ ehci_check_qh_intr(ehci_softc_t *sc, str
*
* Otherwise, we're done.
*/
- if (xt == UE_CONTROL) {
+ if (ex->ex_type == EX_CTRL) {
break;
}
goto done;
@@ -907,18 +938,10 @@ ehci_check_itd_intr(ehci_softc_t *sc, st
if (&ex->ex_xfer != SIMPLEQ_FIRST(&ex->ex_xfer.ux_pipe->up_queue))
return;
- if (ex->ex_itdstart == NULL) {
- printf("ehci_check_itd_intr: not valid itd\n");
- return;
- }
+ KASSERTMSG(ex->ex_itdstart != NULL && ex->ex_itdend != NULL,
+ "xfer %p fitd %p litd %p", ex, ex->ex_itdstart, ex->ex_itdend);
itd = ex->ex_itdend;
-#ifdef DIAGNOSTIC
- if (itd == NULL) {
- printf("ehci_check_itd_intr: itdend == 0\n");
- return;
- }
-#endif
/*
* check no active transfers in last itd, meaning we're finished
@@ -961,18 +984,10 @@ ehci_check_sitd_intr(ehci_softc_t *sc, s
if (&ex->ex_xfer != SIMPLEQ_FIRST(&ex->ex_xfer.ux_pipe->up_queue))
return;
- if (ex->ex_sitdstart == NULL) {
- printf("ehci_check_sitd_intr: not valid sitd\n");
- return;
- }
+ KASSERTMSG(ex->ex_sitdstart != NULL && ex->ex_sitdend != NULL,
+ "xfer %p fsitd %p lsitd %p", ex, ex->ex_sitdstart, ex->ex_sitdend);
sitd = ex->ex_sitdend;
-#ifdef DIAGNOSTIC
- if (sitd == NULL) {
- printf("ehci_check_sitd_intr: sitdend == 0\n");
- return;
- }
-#endif
/*
* check no active transfers in last sitd, meaning we're finished
@@ -982,12 +997,14 @@ ehci_check_sitd_intr(ehci_softc_t *sc, s
sizeof(sitd->sitd.sitd_trans),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
- if (le32toh(sitd->sitd.sitd_trans) & EHCI_SITD_ACTIVE)
- return;
+ bool active = ((le32toh(sitd->sitd.sitd_trans) & EHCI_SITD_ACTIVE) != 0);
usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_trans),
sizeof(sitd->sitd.sitd_trans), BUS_DMASYNC_PREREAD);
+ if (active)
+ return;
+
USBHIST_LOGN(ehcidebug, 10, "ex=%p done", ex, 0, 0, 0);
callout_stop(&(ex->ex_xfer.ux_callout));
ehci_idone(ex);
@@ -1000,9 +1017,9 @@ ehci_idone(struct ehci_xfer *ex)
struct usbd_xfer *xfer = &ex->ex_xfer;
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);
struct ehci_softc *sc = EHCI_XFER2SC(xfer);
- ehci_soft_qtd_t *sqtd, *lsqtd;
+ ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd;
uint32_t status = 0, nstatus = 0;
- int actlen;
+ int actlen = 0;
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
@@ -1031,19 +1048,19 @@ ehci_idone(struct ehci_xfer *ex)
USBHIST_LOG(ehcidebug, "xfer=%p, pipe=%p ready", xfer, epipe, 0, 0);
/* The transfer is done, compute actual length and status. */
-
- u_int xfertype, speed;
-
- xfertype = UE_GET_XFERTYPE(xfer->ux_pipe->up_endpoint->ue_edesc->bmAttributes);
- speed = xfer->ux_pipe->up_dev->ud_speed;
- if (xfertype == UE_ISOCHRONOUS && speed == USB_SPEED_HIGH) {
+ if (ex->ex_type == EX_ISOC) {
/* HS isoc transfer */
struct ehci_soft_itd *itd;
int i, nframes, len, uframes;
nframes = 0;
- actlen = 0;
+
+#ifdef EHCI_DEBUG
+ USBHIST_LOGN(ehcidebug, 5, "--- dump start ---", 0, 0, 0, 0);
+ ehci_dump_itds(ex->ex_itdstart);
+ USBHIST_LOGN(ehcidebug, 5, "--- dump end ---", 0, 0, 0, 0);
+#endif
i = xfer->ux_pipe->up_endpoint->ue_edesc->bInterval;
uframes = min(1 << (i - 1), USB_UFRAMES_PER_FRAME);
@@ -1084,15 +1101,12 @@ ehci_idone(struct ehci_xfer *ex)
xfer->ux_actlen = actlen;
xfer->ux_status = USBD_NORMAL_COMPLETION;
goto end;
- }
-
- if (xfertype == UE_ISOCHRONOUS && speed == USB_SPEED_FULL) {
+ } else if (ex->ex_type == EX_FS_ISOC) {
/* FS isoc transfer */
struct ehci_soft_sitd *sitd;
int nframes, len;
nframes = 0;
- actlen = 0;
for (sitd = ex->ex_sitdstart; sitd != NULL;
sitd = sitd->xfer_next) {
@@ -1139,20 +1153,24 @@ ehci_idone(struct ehci_xfer *ex)
xfer->ux_status = USBD_NORMAL_COMPLETION;
goto end;
}
- KASSERT(xfertype != UE_ISOCHRONOUS);
+ KASSERT(ex->ex_type == EX_CTRL || ex->ex_type == EX_INTR ||
+ ex->ex_type == EX_BULK);
/* Continue processing xfers using queue heads */
-
+ if (ex->ex_type == EX_CTRL) {
+ fsqtd = ex->ex_setup;
+ lsqtd = ex->ex_status;
+ } else {
+ fsqtd = ex->ex_sqtdstart;
+ lsqtd = ex->ex_sqtdend;
+ }
#ifdef EHCI_DEBUG
USBHIST_LOGN(ehcidebug, 5, "--- dump start ---", 0, 0, 0, 0);
- ehci_dump_sqtds(ex->ex_sqtdstart);
+ ehci_dump_sqtds(fsqtd);
USBHIST_LOGN(ehcidebug, 5, "--- dump end ---", 0, 0, 0, 0);
#endif
- lsqtd = ex->ex_sqtdend;
- actlen = 0;
- for (sqtd = ex->ex_sqtdstart; sqtd != lsqtd->nextqtd;
- sqtd = sqtd->nextqtd) {
+ for (sqtd = fsqtd; sqtd != lsqtd->nextqtd; sqtd = sqtd->nextqtd) {
usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd),
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
nstatus = le32toh(sqtd->qtd.qtd_status);
@@ -1286,7 +1304,6 @@ ehci_waitintr(ehci_softc_t *sc, struct u
mutex_enter(&sc->sc_lock);
usb_transfer_complete(xfer);
mutex_exit(&sc->sc_lock);
- /* XXX should free TD */
}
Static void
@@ -1535,12 +1552,16 @@ Static void
ehci_freex(struct usbd_bus *bus, struct usbd_xfer *xfer)
{
struct ehci_softc *sc = EHCI_BUS2SC(bus);
+ struct ehci_xfer *ex __diagused = EHCI_XFER2EXFER(xfer);
+
+ KASSERTMSG(xfer->ux_state == XFER_BUSY, "xfer %p state %d\n", xfer,
+ xfer->ux_state);
+ KASSERT(ex->ex_isdone);
- KASSERT(xfer->ux_state == XFER_BUSY);
- KASSERT(EHCI_XFER2EXFER(xfer)->ex_isdone);
#ifdef DIAGNOSTIC
xfer->ux_state = XFER_FREE;
#endif
+
pool_cache_put(sc->sc_xferpool, xfer);
}
@@ -1780,6 +1801,28 @@ ehci_dump_sqh(ehci_soft_qh_t *sqh)
}
Static void
+ehci_dump_itds(ehci_soft_itd_t *itd)
+{
+ USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+ int i;
+ uint32_t stop = 0;
+
+ for (i = 0; itd && i < 20 && !stop; itd = itd->xfer_next, i++) {
+ ehci_dump_itd(itd);
+ usb_syncmem(&itd->dma,
+ itd->offs + offsetof(ehci_itd_t, itd_next),
+ sizeof(itd->itd),
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+ stop = itd->itd.itd_next & htole32(EHCI_LINK_TERMINATE);
+ usb_syncmem(&itd->dma,
+ itd->offs + offsetof(ehci_itd_t, itd_next),
+ sizeof(itd->itd), BUS_DMASYNC_PREREAD);
+ }
+ if (!stop)
+ USBHIST_LOG(ehcidebug, "dump aborted, too many TDs", 0, 0, 0, 0);
+}
+
+Static void
ehci_dump_itd(struct ehci_soft_itd *itd)
{
ehci_isoc_trans_t t;
@@ -2025,8 +2068,11 @@ ehci_open(struct usbd_pipe *pipe)
return USBD_NORMAL_COMPLETION;
bad:
- if (sqh != NULL)
+ if (sqh != NULL) {
+ mutex_enter(&sc->sc_lock);
ehci_free_sqh(sc, sqh);
+ mutex_exit(&sc->sc_lock);
+ }
return err;
}
@@ -2158,17 +2204,14 @@ ehci_sync_hc(ehci_softc_t *sc)
}
Static void
-ehci_rem_free_itd_chain(ehci_softc_t *sc, struct ehci_xfer *exfer)
+ehci_remove_itd_chain(ehci_softc_t *sc, struct ehci_soft_itd *itd)
{
- struct ehci_soft_itd *itd, *prev;
- prev = NULL;
+ KASSERT(mutex_owned(&sc->sc_lock));
- if (exfer->ex_itdstart == NULL || exfer->ex_itdend == NULL)
- panic("ehci isoc xfer being freed, but with no itd chain");
+ for (; itd != NULL; itd = itd->xfer_next) {
+ struct ehci_soft_itd *prev = itd->frame_list.prev;
- for (itd = exfer->ex_itdstart; itd != NULL; itd = itd->xfer_next) {
- prev = itd->frame_list.prev;
/* Unlink itd from hardware chain, or frame array */
if (prev == NULL) { /* We're at the table head */
sc->sc_softitds[itd->slot] = itd->frame_list.next;
@@ -2192,31 +2235,31 @@ ehci_rem_free_itd_chain(ehci_softc_t *sc
itd->frame_list.next->frame_list.prev = prev;
}
}
+}
- prev = NULL;
- for (itd = exfer->ex_itdstart; itd != NULL; itd = itd->xfer_next) {
- if (prev != NULL)
- ehci_free_itd(sc, prev);
- prev = itd;
+Static void
+ehci_free_itd_chain(ehci_softc_t *sc, struct ehci_soft_itd *itd)
+{
+ struct ehci_soft_itd *next;
+
+ mutex_enter(&sc->sc_lock);
+ next = NULL;
+ for (; itd != NULL; itd = next) {
+ next = itd->xfer_next;
+ ehci_free_itd_locked(sc, itd);
}
- if (prev)
- ehci_free_itd(sc, prev);
- exfer->ex_itdstart = NULL;
- exfer->ex_itdend = NULL;
+ mutex_enter(&sc->sc_lock);
}
Static void
-ehci_rem_free_sitd_chain(ehci_softc_t *sc, struct ehci_xfer *exfer)
+ehci_remove_sitd_chain(ehci_softc_t *sc, struct ehci_soft_sitd *sitd)
{
- struct ehci_soft_sitd *sitd, *prev;
- prev = NULL;
+ KASSERT(mutex_owned(&sc->sc_lock));
- if (exfer->ex_sitdstart == NULL || exfer->ex_sitdend == NULL)
- panic("ehci isoc xfer being freed, but with no sitd chain\n");
+ for (; sitd != NULL; sitd = sitd->xfer_next) {
+ struct ehci_soft_sitd *prev = sitd->frame_list.prev;
- for (sitd = exfer->ex_sitdstart; sitd != NULL; sitd = sitd->xfer_next) {
- prev = sitd->frame_list.prev;
/* Unlink sitd from hardware chain, or frame array */
if (prev == NULL) { /* We're at the table head */
sc->sc_softsitds[sitd->slot] = sitd->frame_list.next;
@@ -2240,17 +2283,19 @@ ehci_rem_free_sitd_chain(ehci_softc_t *s
sitd->frame_list.next->frame_list.prev = prev;
}
}
+}
- prev = NULL;
- for (sitd = exfer->ex_sitdstart; sitd != NULL; sitd = sitd->xfer_next) {
- if (prev != NULL)
- ehci_free_sitd(sc, prev);
- prev = sitd;
- }
- if (prev)
- ehci_free_sitd(sc, prev);
- exfer->ex_sitdstart = NULL;
- exfer->ex_sitdend = NULL;
+Static void
+ehci_free_sitd_chain(ehci_softc_t *sc, struct ehci_soft_sitd *sitd)
+{
+
+ mutex_enter(&sc->sc_lock);
+ struct ehci_soft_sitd *next = NULL;
+ for (; sitd != NULL; sitd = next) {
+ next = sitd->xfer_next;
+ ehci_free_sitd_locked(sc, sitd);
+ }
+ mutex_exit(&sc->sc_lock);
}
/***********/
@@ -2673,8 +2718,11 @@ ehci_alloc_sqh(ehci_softc_t *sc)
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+ mutex_enter(&sc->sc_lock);
if (sc->sc_freeqhs == NULL) {
USBHIST_LOG(ehcidebug, "allocating chunk", 0, 0, 0, 0);
+ mutex_exit(&sc->sc_lock);
+
err = usb_allocmem(&sc->sc_bus, EHCI_SQH_SIZE * EHCI_SQH_CHUNK,
EHCI_PAGE_SIZE, &dma);
#ifdef EHCI_DEBUG
@@ -2683,6 +2731,8 @@ ehci_alloc_sqh(ehci_softc_t *sc)
#endif
if (err)
return NULL;
+
+ mutex_enter(&sc->sc_lock);
for (i = 0; i < EHCI_SQH_CHUNK; i++) {
offs = i * EHCI_SQH_SIZE;
sqh = KERNADDR(&dma, offs);
@@ -2695,6 +2745,8 @@ ehci_alloc_sqh(ehci_softc_t *sc)
}
sqh = sc->sc_freeqhs;
sc->sc_freeqhs = sqh->next;
+ mutex_exit(&sc->sc_lock);
+
memset(&sqh->qh, 0, sizeof(ehci_qh_t));
sqh->next = NULL;
return sqh;
@@ -2703,6 +2755,8 @@ ehci_alloc_sqh(ehci_softc_t *sc)
Static void
ehci_free_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh)
{
+ KASSERT(mutex_owned(&sc->sc_lock));
+
sqh->next = sc->sc_freeqhs;
sc->sc_freeqhs = sqh;
}
@@ -2717,8 +2771,10 @@ ehci_alloc_sqtd(ehci_softc_t *sc)
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+ mutex_enter(&sc->sc_lock);
if (sc->sc_freeqtds == NULL) {
USBHIST_LOG(ehcidebug, "allocating chunk", 0, 0, 0, 0);
+ mutex_exit(&sc->sc_lock);
err = usb_allocmem(&sc->sc_bus, EHCI_SQTD_SIZE*EHCI_SQTD_CHUNK,
EHCI_PAGE_SIZE, &dma);
@@ -2729,6 +2785,7 @@ ehci_alloc_sqtd(ehci_softc_t *sc)
if (err)
goto done;
+ mutex_enter(&sc->sc_lock);
for (i = 0; i < EHCI_SQTD_CHUNK; i++) {
offs = i * EHCI_SQTD_SIZE;
sqtd = KERNADDR(&dma, offs);
@@ -2743,6 +2800,8 @@ ehci_alloc_sqtd(ehci_softc_t *sc)
sqtd = sc->sc_freeqtds;
sc->sc_freeqtds = sqtd->nextqtd;
+ mutex_exit(&sc->sc_lock);
+
memset(&sqtd->qtd, 0, sizeof(ehci_qtd_t));
sqtd->nextqtd = NULL;
sqtd->xfer = NULL;
@@ -2755,17 +2814,17 @@ Static void
ehci_free_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd)
{
- KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock));
-
+ mutex_enter(&sc->sc_lock);
sqtd->nextqtd = sc->sc_freeqtds;
sc->sc_freeqtds = sqtd;
+ mutex_exit(&sc->sc_lock);
}
Static usbd_status
-ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
- int alen, int rd, struct usbd_xfer *xfer,
- ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep)
+ehci_alloc_sqtd_chain(ehci_softc_t *sc, struct usbd_xfer *xfer,
+ int alen, int rd, ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep)
{
+ struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
ehci_soft_qtd_t *next, *cur;
ehci_physaddr_t nextphys;
uint32_t qtdstatus;
@@ -2779,32 +2838,38 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *
paddr_t a;
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
-
USBHIST_LOG(ehcidebug, "start len=%d", alen, 0, 0, 0);
+ ASSERT_SLEEPABLE();
+ KASSERT(sp);
+ KASSERT(alen != 0 || (flags & USBD_FORCE_SHORT_XFER));
+
len = alen;
qtdstatus = EHCI_QTD_ACTIVE |
EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) |
EHCI_QTD_SET_CERR(3)
- /* IOC set below */
- /* BYTES set below */
;
- mps = UGETW(epipe->pipe.up_endpoint->ue_edesc->wMaxPacketSize);
- tog = epipe->nexttoggle;
- qtdstatus |= EHCI_QTD_SET_TOGGLE(tog);
+ size_t nsqtd = (flags & USBD_FORCE_SHORT_XFER) ? 1 : 0;
+ nsqtd += ((len + EHCI_QTD_MAXTRANSFER - 1) / EHCI_QTD_MAXTRANSFER);
+ exfer->ex_sqtds = kmem_alloc(sizeof(ehci_soft_qtd_t *) * nsqtd,
+ KM_SLEEP);
+ exfer->ex_nsqtd = nsqtd;
+
+ mps = UGETW(xfer->ux_pipe->up_endpoint->ue_edesc->wMaxPacketSize);
cur = ehci_alloc_sqtd(sc);
*sp = cur;
if (cur == NULL)
goto nomem;
- usb_syncmem(dma, 0, alen,
- rd ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
curoffs = 0;
- for (;;) {
+ for (size_t j = 0;;) {
+ KASSERT(j < nsqtd);
+ exfer->ex_sqtds[j++] = cur;
+
/* The EHCI hardware can handle at most 5 pages. */
- va_offs = (vaddr_t)KERNADDR(dma, curoffs);
- va_offs = EHCI_PAGE_OFFSET(va_offs);
+ va = (vaddr_t)KERNADDR(dma, curoffs);
+ va_offs = EHCI_PAGE_OFFSET(va);
if (len-curoffs < EHCI_QTD_MAXTRANSFER - va_offs) {
/* we can handle it in this QTD */
curlen = len - curoffs;
@@ -2851,15 +2916,14 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *
}
/* First buffer pointer requires a page offset to start at */
- va = (vaddr_t)KERNADDR(dma, curoffs);
- cur->qtd.qtd_buffer[0] |= htole32(EHCI_PAGE_OFFSET(va));
-
- cur->nextqtd = next;
+ cur->qtd.qtd_buffer[0] |= htole32(va_offs);
cur->qtd.qtd_next = cur->qtd.qtd_altnext = nextphys;
- cur->qtd.qtd_status =
- htole32(qtdstatus | EHCI_QTD_SET_BYTES(curlen));
+ cur->qtd.qtd_status = htole32(qtdstatus);
+ cur->nextqtd = next;
cur->xfer = xfer;
- cur->len = curlen;
+ cur->bufoff = curoffs;
+ cur->tdlen = curlen;
+ cur->len = 0;
USBHIST_LOG(ehcidebug, "cbp=0x%08zx end=0x%08zx",
curoffs, curoffs + curlen, 0, 0);
@@ -2874,44 +2938,126 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *
}
if (next == NULL)
break;
- usb_syncmem(&cur->dma, cur->offs, sizeof(cur->qtd),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
USBHIST_LOG(ehcidebug, "extend chain", 0, 0, 0, 0);
if (len)
curoffs += curlen;
cur = next;
}
- cur->qtd.qtd_status |= htole32(EHCI_QTD_IOC);
- usb_syncmem(&cur->dma, cur->offs, sizeof(cur->qtd),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- *ep = cur;
- epipe->nexttoggle = tog;
+ if (ep)
+ *ep = cur;
- USBHIST_LOG(ehcidebug, "return sqtd=%p sqtdend=%p", *sp, *ep, 0, 0);
+ USBHIST_LOG(ehcidebug, "return sqtd=%p sqtdend=%p", *sp, cur, 0, 0);
return USBD_NORMAL_COMPLETION;
nomem:
- /* XXX free chain */
+ ehci_free_sqtds(sc, exfer);
USBHIST_LOG(ehcidebug, "no memory", 0, 0, 0, 0);
return USBD_NOMEM;
}
Static void
-ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd,
- ehci_soft_qtd_t *sqtdend)
+ehci_free_sqtds(ehci_softc_t *sc, struct ehci_xfer *exfer)
{
- ehci_soft_qtd_t *p;
- int i;
-
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+ USBHIST_LOG(ehcidebug, "exfer=%p", exfer, 0, 0, 0);
+
+ mutex_enter(&sc->sc_lock);
+ for (size_t i = 0; i < exfer->ex_nsqtd; i++) {
+ exfer->ex_sqtds[i]->nextqtd = sc->sc_freeqtds;
+ sc->sc_freeqtds = exfer->ex_sqtds[i]->nextqtd;
+ }
+ mutex_exit(&sc->sc_lock);
+}
+
+Static void
+ehci_reset_sqtd_chain(ehci_softc_t *sc, struct usbd_xfer *xfer,
+ int length, int isread, int *toggle,
+ ehci_soft_qtd_t *fsqtd, ehci_soft_qtd_t **lsqtd)
+{
+ struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
+ ehci_soft_qtd_t *sqtd, *prev;
+ int tog = *toggle;
+ int mps = UGETW(xfer->ux_pipe->up_endpoint->ue_edesc->wMaxPacketSize);
+ int len = length;
+ size_t i;
+
+ USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+ USBHIST_LOG(ehcidebug, "xfer=%p len %d isread %d toggle %d", xfer,
+ len, isread, *toggle);
+
+ sqtd = prev = NULL;
+ for (i = 0; i < exfer->ex_nsqtd; i++, prev = sqtd) {
+ sqtd = exfer->ex_sqtds[i];
+ vaddr_t va = (vaddr_t)KERNADDR(&xfer->ux_dmabuf, sqtd->bufoff);
+ sqtd->len = sqtd->tdlen;
+ if (len < sqtd->len) {
+ sqtd->len = len;
+ }
+
+ USBHIST_LOG(ehcidebug, "sqtd[%d]=%p prev %p len %d", i, sqtd,
+ prev, sqtd->len);
+
+ if (prev) {
+ prev->nextqtd = sqtd;
+ prev->qtd.qtd_next = htole32(sqtd->physaddr);
+ prev->qtd.qtd_altnext = prev->qtd.qtd_next;
+ }
+ usb_syncmem(&sqtd->dma,
+ sqtd->offs + offsetof(ehci_qtd_t, qtd_status),
+ sizeof(sqtd->qtd.qtd_status),
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+ usb_syncmem(&sqtd->dma,
+ sqtd->offs + offsetof(ehci_qtd_t, qtd_buffer),
+ sizeof(sqtd->qtd.qtd_buffer[0]),
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+
+ sqtd->qtd.qtd_buffer[0] &= ~htole32(EHCI_PAGE_MASK);
+ sqtd->qtd.qtd_buffer[0] |= htole32(EHCI_PAGE_OFFSET(va));
+ /* Reset ... */
+ sqtd->qtd.qtd_status &= ~htole32(
+ EHCI_QTD_STATUS_MASK |
+ EHCI_QTD_PID_MASK |
+ EHCI_QTD_CERR_MASK |
+ EHCI_QTD_C_PAGE_MASK |
+ EHCI_QTD_BYTES_MASK |
+ EHCI_QTD_TOGGLE_MASK);
+ sqtd->qtd.qtd_status |= htole32(
+ EHCI_QTD_ACTIVE |
+ EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) |
+ EHCI_QTD_SET_BYTES(sqtd->len) |
+ EHCI_QTD_SET_CERR(3) |
+ EHCI_QTD_SET_TOGGLE(tog));
+
+ usb_syncmem(&sqtd->dma,
+ sqtd->offs + offsetof(ehci_qtd_t, qtd_status),
+ sizeof(sqtd->qtd.qtd_status),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ usb_syncmem(&sqtd->dma,
+ sqtd->offs + offsetof(ehci_qtd_t, qtd_buffer),
+ sizeof(sqtd->qtd.qtd_buffer[0]),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ if (((sqtd->len + mps - 1) / mps) & 1) {
+ tog ^= 1;
+ }
- USBHIST_LOG(ehcidebug, "sqtd=%p sqtdend=%p", sqtd, sqtdend, 0, 0);
+ len -= sqtd->len;
+ if (len == 0)
+ break;
+ }
+ KASSERTMSG(len == 0, "xfer %p olen %d len %d mps %d ex_nsqtd %zu i %zu",
+ xfer, length, len, mps, exfer->ex_nsqtd, i);
- for (i = 0; sqtd != sqtdend; sqtd = p, i++) {
- p = sqtd->nextqtd;
- ehci_free_sqtd(sc, sqtd);
+ if (i < exfer->ex_nsqtd) {
+ /*
+ * The full allocation chain wasn't used, so we need to
+ * terminate it.
+ */
+ sqtd->qtd.qtd_next = sqtd->qtd.qtd_altnext = EHCI_NULL;
}
+ *lsqtd = sqtd;
+ *toggle = tog;
}
Static ehci_soft_itd_t *
@@ -2919,46 +3065,27 @@ ehci_alloc_itd(ehci_softc_t *sc)
{
struct ehci_soft_itd *itd, *freeitd;
usbd_status err;
- int i, offs, frindex, previndex;
usb_dma_t dma;
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
mutex_enter(&sc->sc_lock);
- /*
- * Find an itd that wasn't freed this frame or last frame. This can
- * discard itds that were freed before frindex wrapped around
- * XXX - can this lead to thrashing? Could fix by enabling wrap-around
- * interrupt and fiddling with list when that happens
- */
- frindex = (EOREAD4(sc, EHCI_FRINDEX) + 1) >> 3;
- previndex = (frindex != 0) ? frindex - 1 : sc->sc_flsize;
-
- freeitd = NULL;
- LIST_FOREACH(itd, &sc->sc_freeitds, free_list) {
- if (itd == NULL)
- break;
- if (itd->slot != frindex && itd->slot != previndex) {
- freeitd = itd;
- break;
- }
- }
-
+ freeitd = LIST_FIRST(&sc->sc_freeitds);
if (freeitd == NULL) {
USBHIST_LOG(ehcidebug, "allocating chunk", 0, 0, 0, 0);
+ mutex_exit(&sc->sc_lock);
err = usb_allocmem(&sc->sc_bus, EHCI_ITD_SIZE * EHCI_ITD_CHUNK,
EHCI_PAGE_SIZE, &dma);
if (err) {
- USBHIST_LOG(ehcidebug,
- "alloc returned %d", err, 0, 0, 0);
- mutex_exit(&sc->sc_lock);
+ USBHIST_LOG(ehcidebug, "alloc returned %d", err, 0, 0, 0);
return NULL;
}
+ mutex_enter(&sc->sc_lock);
- for (i = 0; i < EHCI_ITD_CHUNK; i++) {
- offs = i * EHCI_ITD_SIZE;
+ for (int i = 0; i < EHCI_ITD_CHUNK; i++) {
+ int offs = i * EHCI_ITD_SIZE;
itd = KERNADDR(&dma, offs);
itd->physaddr = DMAADDR(&dma, offs);
itd->dma = dma;
@@ -2970,18 +3097,14 @@ ehci_alloc_itd(ehci_softc_t *sc)
itd = freeitd;
LIST_REMOVE(itd, free_list);
+ mutex_exit(&sc->sc_lock);
memset(&itd->itd, 0, sizeof(ehci_itd_t));
- usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_next),
- sizeof(itd->itd.itd_next),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
itd->frame_list.next = NULL;
itd->frame_list.prev = NULL;
itd->xfer_next = NULL;
itd->slot = 0;
- mutex_exit(&sc->sc_lock);
-
return itd;
}
@@ -2990,44 +3113,26 @@ ehci_alloc_sitd(ehci_softc_t *sc)
{
struct ehci_soft_sitd *sitd, *freesitd;
usbd_status err;
- int i, offs, frindex, previndex;
+ int i, offs;
usb_dma_t dma;
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
mutex_enter(&sc->sc_lock);
-
- /*
- * Find an sitd that wasn't freed this frame or last frame. This can
- * discard sitds that were freed before frindex wrapped around
- * XXX - can this lead to thrashing? Could fix by enabling wrap-around
- * interrupt and fiddling with list when that happens
- */
- frindex = (EOREAD4(sc, EHCI_FRINDEX) + 1) >> 3;
- previndex = (frindex != 0) ? frindex - 1 : sc->sc_flsize;
-
- freesitd = NULL;
- LIST_FOREACH(sitd, &sc->sc_freesitds, free_list) {
- if (sitd == NULL)
- break;
- if (sitd->slot != frindex && sitd->slot != previndex) {
- freesitd = sitd;
- break;
- }
- }
-
+ freesitd = LIST_FIRST(&sc->sc_freesitds);
if (freesitd == NULL) {
USBHIST_LOG(ehcidebug, "allocating chunk", 0, 0, 0, 0);
+ mutex_exit(&sc->sc_lock);
err = usb_allocmem(&sc->sc_bus, EHCI_SITD_SIZE * EHCI_SITD_CHUNK,
EHCI_PAGE_SIZE, &dma);
if (err) {
USBHIST_LOG(ehcidebug, "alloc returned %d", err, 0, 0,
0);
- mutex_exit(&sc->sc_lock);
return NULL;
}
+ mutex_enter(&sc->sc_lock);
for (i = 0; i < EHCI_SITD_CHUNK; i++) {
offs = i * EHCI_SITD_SIZE;
sitd = KERNADDR(&dma, offs);
@@ -3041,39 +3146,18 @@ ehci_alloc_sitd(ehci_softc_t *sc)
sitd = freesitd;
LIST_REMOVE(sitd, free_list);
+ mutex_exit(&sc->sc_lock);
+
memset(&sitd->sitd, 0, sizeof(ehci_sitd_t));
- usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_next),
- sizeof(sitd->sitd.sitd_next),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
sitd->frame_list.next = NULL;
sitd->frame_list.prev = NULL;
sitd->xfer_next = NULL;
sitd->slot = 0;
- mutex_exit(&sc->sc_lock);
-
return sitd;
}
-Static void
-ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd)
-{
-
- KASSERT(mutex_owned(&sc->sc_lock));
-
- LIST_INSERT_HEAD(&sc->sc_freeitds, itd, free_list);
-}
-
-Static void
-ehci_free_sitd(ehci_softc_t *sc, ehci_soft_sitd_t *sitd)
-{
-
- KASSERT(mutex_owned(&sc->sc_lock));
-
- LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, free_list);
-}
-
/****************/
/*
@@ -3360,6 +3444,8 @@ ehci_timeout(void *addr)
{
struct usbd_xfer *xfer = addr;
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
+ struct usbd_pipe *pipe = xfer->ux_pipe;
+ struct usbd_device *dev = pipe->up_dev;
ehci_softc_t *sc = EHCI_XFER2SC(xfer);
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
@@ -3367,7 +3453,7 @@ ehci_timeout(void *addr)
USBHIST_LOG(ehcidebug, "exfer %p", exfer, 0, 0, 0);
#ifdef EHCI_DEBUG
if (ehcidebug > 1)
- usbd_dump_pipe(xfer->ux_pipe);
+ usbd_dump_pipe(pipe);
#endif
if (sc->sc_dying) {
@@ -3378,10 +3464,9 @@ ehci_timeout(void *addr)
}
/* Execute the abort in a process context. */
- usb_init_task(&exfer->ex_aborttask, ehci_timeout_task, addr,
+ usb_init_task(&exfer->ex_aborttask, ehci_timeout_task, xfer,
USB_TASKQ_MPSAFE);
- usb_add_task(xfer->ux_pipe->up_dev, &exfer->ex_aborttask,
- USB_TASKQ_HC);
+ usb_add_task(dev, &exfer->ex_aborttask, USB_TASKQ_HC);
}
Static void
@@ -3401,220 +3486,243 @@ ehci_timeout_task(void *addr)
/************************/
-Static usbd_status
-ehci_device_ctrl_transfer(struct usbd_xfer *xfer)
+Static int
+ehci_device_ctrl_init(struct usbd_xfer *xfer)
{
+ struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
+ struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);
ehci_softc_t *sc = EHCI_XFER2SC(xfer);
- usbd_status err;
+ usb_device_request_t *req = &xfer->ux_request;
+ ehci_soft_qtd_t *setup, *status, *next;
+ int isread = req->bmRequestType & UT_READ;
+ int len = xfer->ux_bufsize;
+ int err;
- /* Insert last in queue. */
- mutex_enter(&sc->sc_lock);
- err = usb_insert_transfer(xfer);
- mutex_exit(&sc->sc_lock);
- if (err)
- return err;
+ exfer->ex_type = EX_CTRL;
+ exfer->ex_status = NULL;
+ exfer->ex_data = NULL;
+ exfer->ex_setup = ehci_alloc_sqtd(sc);
+ if (exfer->ex_setup == NULL) {
+ err = ENOMEM;
+ goto bad1;
+ }
+ exfer->ex_status = ehci_alloc_sqtd(sc);
+ if (exfer->ex_status == NULL) {
+ err = ENOMEM;
+ goto bad2;
+ }
+ setup = exfer->ex_setup;
+ status = exfer->ex_status;
+ exfer->ex_nsqtd = 0;
+ next = status;
+ /* Set up data transaction */
+ if (len != 0) {
+ ehci_soft_qtd_t *end;
+ err = ehci_alloc_sqtd_chain(sc, xfer, len, isread,
+ &exfer->ex_data, &end);
+ if (err)
+ goto bad3;
+ next = exfer->ex_data;
+ }
- /* Pipe isn't running, start first */
- return ehci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue));
-}
-
-Static usbd_status
-ehci_device_ctrl_start(struct usbd_xfer *xfer)
-{
- ehci_softc_t *sc = EHCI_XFER2SC(xfer);
- usbd_status err;
-
- if (sc->sc_dying)
- return USBD_IOERROR;
-
- KASSERT(xfer->ux_rqflags & URQ_REQUEST);
-
- err = ehci_device_request(xfer);
- if (err) {
- return err;
- }
-
- if (sc->sc_bus.ub_usepolling)
- ehci_waitintr(sc, xfer);
-
- return USBD_IN_PROGRESS;
+ /* Clear toggle */
+ setup->qtd.qtd_status = htole32(
+ EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) |
+ EHCI_QTD_SET_TOGGLE(0) |
+ EHCI_QTD_SET_BYTES(sizeof(*req))
+ );
+ setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->ctrl.reqdma, 0));
+ setup->qtd.qtd_buffer_hi[0] = 0;
+ setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr);
+ setup->nextqtd = next;
+ setup->xfer = xfer;
+ setup->tdlen = setup->len = sizeof(*req);
+
+ status->qtd.qtd_status = htole32(
+ EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) |
+ EHCI_QTD_SET_TOGGLE(1) |
+ EHCI_QTD_IOC
+ );
+ status->qtd.qtd_buffer[0] = 0;
+ status->qtd.qtd_buffer_hi[0] = 0;
+ status->qtd.qtd_next = status->qtd.qtd_altnext = EHCI_NULL;
+ status->nextqtd = NULL;
+ status->xfer = xfer;
+ status->tdlen = status->len = 0;
+
+ return 0;
+bad3:
+ ehci_free_sqtd(sc, exfer->ex_status);
+bad2:
+ ehci_free_sqtd(sc, exfer->ex_setup);
+bad1:
+ return err;
}
Static void
-ehci_device_ctrl_done(struct usbd_xfer *xfer)
+ehci_device_ctrl_fini(struct usbd_xfer *xfer)
{
- struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer);
ehci_softc_t *sc = EHCI_XFER2SC(xfer);
- struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);
- usb_device_request_t *req = &xfer->ux_request;
- int len = UGETW(req->wLength);
- int rd = req->bmRequestType & UT_READ;
-
- USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
-
- USBHIST_LOG(ehcidebug, "xfer=%p", xfer, 0, 0, 0);
-
- KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock));
- KASSERT(xfer->ux_rqflags & URQ_REQUEST);
-
- if (xfer->ux_status != USBD_NOMEM && ehci_active_intr_list(ex)) {
- ehci_del_intr_list(sc, ex); /* remove from active list */
- ehci_free_sqtd_chain(sc, ex->ex_sqtdstart, NULL);
- usb_syncmem(&epipe->ctrl.reqdma, 0, sizeof(*req),
- BUS_DMASYNC_POSTWRITE);
- if (len)
- usb_syncmem(&xfer->ux_dmabuf, 0, len,
- rd ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
- }
+ struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer);
- USBHIST_LOG(ehcidebug, "length=%d", xfer->ux_actlen, 0, 0, 0);
-}
+ KASSERT(ex->ex_type == EX_CTRL);
-/* Abort a device control request. */
-Static void
-ehci_device_ctrl_abort(struct usbd_xfer *xfer)
-{
- USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
-
- USBHIST_LOG(ehcidebug, "xfer=%p", xfer, 0, 0, 0);
- ehci_abort_xfer(xfer, USBD_CANCELLED);
+ ehci_free_sqtd(sc, ex->ex_setup);
+ ehci_free_sqtd(sc, ex->ex_status);
+ ehci_free_sqtds(sc, ex);
+ if (ex->ex_nsqtd)
+ kmem_free(ex->ex_sqtds, sizeof(ehci_soft_qtd_t *) * ex->ex_nsqtd);
}
-/* Close a device control pipe. */
-Static void
-ehci_device_ctrl_close(struct usbd_pipe *pipe)
+Static usbd_status
+ehci_device_ctrl_transfer(struct usbd_xfer *xfer)
{
- ehci_softc_t *sc = EHCI_PIPE2SC(pipe);
- /*struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe);*/
-
- USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
-
- KASSERT(mutex_owned(&sc->sc_lock));
+ ehci_softc_t *sc = EHCI_XFER2SC(xfer);
+ usbd_status err;
- USBHIST_LOG(ehcidebug, "pipe=%p", pipe, 0, 0, 0);
+ /* Insert last in queue. */
+ mutex_enter(&sc->sc_lock);
+ err = usb_insert_transfer(xfer);
+ mutex_exit(&sc->sc_lock);
+ if (err)
+ return err;
- ehci_close_pipe(pipe, sc->sc_async_head);
+ /* Pipe isn't running, start first */
+ return ehci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue));
}
Static usbd_status
-ehci_device_request(struct usbd_xfer *xfer)
+ehci_device_ctrl_start(struct usbd_xfer *xfer)
{
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
usb_device_request_t *req = &xfer->ux_request;
ehci_softc_t *sc = EHCI_XFER2SC(xfer);
- struct usbd_device *dev __diagused = epipe->pipe.up_dev;
- ehci_soft_qtd_t *setup, *stat, *next;
+ ehci_soft_qtd_t *setup, *status, *next;
ehci_soft_qh_t *sqh;
- int isread;
- int len;
- usbd_status err;
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
- isread = req->bmRequestType & UT_READ;
- len = UGETW(req->wLength);
+ KASSERT(xfer->ux_rqflags & URQ_REQUEST);
+
+ if (sc->sc_dying)
+ return USBD_IOERROR;
+
+ const int isread = req->bmRequestType & UT_READ;
+ const int len = UGETW(req->wLength);
USBHIST_LOG(ehcidebug, "type=0x%02x, request=0x%02x, "
"wValue=0x%04x, wIndex=0x%04x",
req->bmRequestType, req->bRequest, UGETW(req->wValue),
UGETW(req->wIndex));
USBHIST_LOG(ehcidebug, "len=%d, addr=%d, endpt=%d",
- len, dev->ud_addr,
+ len, epipe->pipe.up_dev->ud_addr,
epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress, 0);
- setup = ehci_alloc_sqtd(sc);
- if (setup == NULL) {
- err = USBD_NOMEM;
- goto bad1;
- }
- stat = ehci_alloc_sqtd(sc);
- if (stat == NULL) {
- err = USBD_NOMEM;
- goto bad2;
- }
-
- mutex_enter(&sc->sc_lock);
-
sqh = epipe->sqh;
- KASSERTMSG(EHCI_QH_GET_ADDR(le32toh(sqh->qh.qh_endp)) == dev->ud_addr,
+ KASSERTMSG(EHCI_QH_GET_ADDR(le32toh(sqh->qh.qh_endp)) == epipe->pipe.up_dev->ud_addr,
"address QH %" __PRIuBIT " pipe %d\n",
- EHCI_QH_GET_ADDR(le32toh(sqh->qh.qh_endp)), dev->ud_addr);
+ EHCI_QH_GET_ADDR(le32toh(sqh->qh.qh_endp)),
+ epipe->pipe.up_dev->ud_addr);
KASSERTMSG(EHCI_QH_GET_MPL(le32toh(sqh->qh.qh_endp)) ==
UGETW(epipe->pipe.up_endpoint->ue_edesc->wMaxPacketSize),
"MPS QH %" __PRIuBIT " pipe %d\n",
EHCI_QH_GET_MPL(le32toh(sqh->qh.qh_endp)),
UGETW(epipe->pipe.up_endpoint->ue_edesc->wMaxPacketSize));
- /* Set up data transaction */
- if (len != 0) {
- ehci_soft_qtd_t *end;
+ setup = exfer->ex_setup;
+ status = exfer->ex_status;
- /* Start toggle at 1. */
- epipe->nexttoggle = 1;
- err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
- &next, &end);
- if (err)
- goto bad3;
- end->qtd.qtd_status &= htole32(~EHCI_QTD_IOC);
- end->nextqtd = stat;
- end->qtd.qtd_next = end->qtd.qtd_altnext =
- htole32(stat->physaddr);
- usb_syncmem(&end->dma, end->offs, sizeof(end->qtd),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- } else {
- next = stat;
- }
+ USBHIST_LOG(ehcidebug, "setup %p status %p data %p",
+ setup, status, exfer->ex_data, 0);
+ KASSERTMSG(setup != NULL && status != NULL,
+ "Failed memory allocation, setup %p status %p",
+ setup, status);
memcpy(KERNADDR(&epipe->ctrl.reqdma, 0), req, sizeof(*req));
usb_syncmem(&epipe->ctrl.reqdma, 0, sizeof(*req), BUS_DMASYNC_PREWRITE);
/* Clear toggle */
- setup->qtd.qtd_status = htole32(
+ setup->qtd.qtd_status &= ~htole32(
+ EHCI_QTD_STATUS_MASK |
+ EHCI_QTD_BYTES_MASK |
+ EHCI_QTD_TOGGLE_MASK |
+ EHCI_QTD_CERR_MASK
+ );
+ setup->qtd.qtd_status |= htole32(
EHCI_QTD_ACTIVE |
- EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) |
EHCI_QTD_SET_CERR(3) |
EHCI_QTD_SET_TOGGLE(0) |
EHCI_QTD_SET_BYTES(sizeof(*req))
);
setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->ctrl.reqdma, 0));
setup->qtd.qtd_buffer_hi[0] = 0;
- setup->nextqtd = next;
- setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr);
- setup->xfer = xfer;
- setup->len = sizeof(*req);
- usb_syncmem(&setup->dma, setup->offs, sizeof(setup->qtd),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- stat->qtd.qtd_status = htole32(
+ next = status;
+ status->qtd.qtd_status &= ~htole32(
+ EHCI_QTD_STATUS_MASK |
+ EHCI_QTD_PID_MASK |
+ EHCI_QTD_BYTES_MASK |
+ EHCI_QTD_TOGGLE_MASK |
+ EHCI_QTD_CERR_MASK
+ );
+ status->qtd.qtd_status |= htole32(
EHCI_QTD_ACTIVE |
EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) |
EHCI_QTD_SET_CERR(3) |
EHCI_QTD_SET_TOGGLE(1) |
+ EHCI_QTD_SET_BYTES(0) |
EHCI_QTD_IOC
);
- stat->qtd.qtd_buffer[0] = 0; /* XXX not needed? */
- stat->qtd.qtd_buffer_hi[0] = 0; /* XXX not needed? */
- stat->nextqtd = NULL;
- stat->qtd.qtd_next = stat->qtd.qtd_altnext = EHCI_NULL;
- stat->xfer = xfer;
- stat->len = 0;
- usb_syncmem(&stat->dma, stat->offs, sizeof(stat->qtd),
+ KASSERT(status->qtd.qtd_status & htole32(EHCI_QTD_TOGGLE_MASK));
+
+ KASSERT(exfer->ex_isdone);
+#ifdef DIAGNOSTIC
+ exfer->ex_isdone = false;
+#endif
+
+ /* Set up data transaction */
+ if (len != 0) {
+ ehci_soft_qtd_t *end;
+
+ /* Start toggle at 1. */
+ int toggle = 1;
+ next = exfer->ex_data;
+ KASSERTMSG(next != NULL, "Failed memory allocation, "
+ "data %p", next);
+ ehci_reset_sqtd_chain(sc, xfer, len, isread, &toggle,
+ next, &end);
+ end->nextqtd = status;
+ end->qtd.qtd_next = end->qtd.qtd_altnext =
+ htole32(status->physaddr);
+
+ usb_syncmem(&end->dma, end->offs, sizeof(end->qtd),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ usb_syncmem(&xfer->ux_dmabuf, 0, len,
+ isread ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+ }
+
+ setup->nextqtd = next;
+ setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr);
+
+ usb_syncmem(&setup->dma, setup->offs, sizeof(setup->qtd),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ usb_syncmem(&status->dma, status->offs, sizeof(status->qtd),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ KASSERT(status->qtd.qtd_status & htole32(EHCI_QTD_TOGGLE_MASK));
+
#ifdef EHCI_DEBUG
- USBHIST_LOGN(ehcidebug, 5, "dump:", 0, 0, 0, 0);
+ USBHIST_LOGN(ehcidebug, 5, "--- dump start ---", 0, 0, 0, 0);
ehci_dump_sqh(sqh);
ehci_dump_sqtds(setup);
+ USBHIST_LOGN(ehcidebug, 5, "--- dump end ---", 0, 0, 0, 0);
#endif
- exfer->ex_sqtdstart = setup;
- exfer->ex_sqtdend = stat;
- KASSERT(exfer->ex_isdone);
-#ifdef DIAGNOSTIC
- exfer->ex_isdone = false;
-#endif
+ mutex_enter(&sc->sc_lock);
/* Insert qTD in QH list. */
ehci_set_qh_qtd(sqh, setup); /* also does usb_syncmem(sqh) */
@@ -3626,6 +3734,7 @@ ehci_device_request(struct usbd_xfer *xf
xfer->ux_status = USBD_IN_PROGRESS;
mutex_exit(&sc->sc_lock);
+#if 0
#ifdef EHCI_DEBUG
USBHIST_LOGN(ehcidebug, 10, "status=%x, dump:",
EOREAD4(sc, EHCI_USBSTS), 0, 0, 0);
@@ -3635,21 +3744,66 @@ ehci_device_request(struct usbd_xfer *xf
ehci_dump_sqh(sqh);
ehci_dump_sqtds(setup);
#endif
+#endif
- return USBD_NORMAL_COMPLETION;
+ if (sc->sc_bus.ub_usepolling)
+ ehci_waitintr(sc, xfer);
- bad3:
- mutex_exit(&sc->sc_lock);
- ehci_free_sqtd(sc, stat);
- bad2:
- ehci_free_sqtd(sc, setup);
- bad1:
- USBHIST_LOG(ehcidebug, "no memory", 0, 0, 0, 0);
- mutex_enter(&sc->sc_lock);
- xfer->ux_status = err;
- usb_transfer_complete(xfer);
- mutex_exit(&sc->sc_lock);
- return err;
+ return USBD_IN_PROGRESS;
+}
+
+Static void
+ehci_device_ctrl_done(struct usbd_xfer *xfer)
+{
+ struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer);
+ ehci_softc_t *sc = EHCI_XFER2SC(xfer);
+ struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);
+ usb_device_request_t *req = &xfer->ux_request;
+ int len = UGETW(req->wLength);
+ int rd = req->bmRequestType & UT_READ;
+
+ USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+ USBHIST_LOG(ehcidebug, "xfer=%p", xfer, 0, 0, 0);
+
+ KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock));
+ KASSERT(xfer->ux_rqflags & URQ_REQUEST);
+
+ if (xfer->ux_status != USBD_NOMEM && ehci_active_intr_list(ex)) {
+ ehci_del_intr_list(sc, ex); /* remove from active list */
+ usb_syncmem(&epipe->ctrl.reqdma, 0, sizeof(*req),
+ BUS_DMASYNC_POSTWRITE);
+ if (len)
+ usb_syncmem(&xfer->ux_dmabuf, 0, len,
+ rd ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
+ }
+
+ USBHIST_LOG(ehcidebug, "length=%d", xfer->ux_actlen, 0, 0, 0);
+}
+
+/* Abort a device control request. */
+Static void
+ehci_device_ctrl_abort(struct usbd_xfer *xfer)
+{
+ USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+
+ USBHIST_LOG(ehcidebug, "xfer=%p", xfer, 0, 0, 0);
+ ehci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/* Close a device control pipe. */
+Static void
+ehci_device_ctrl_close(struct usbd_pipe *pipe)
+{
+ ehci_softc_t *sc = EHCI_PIPE2SC(pipe);
+ /*struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe);*/
+
+ USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+
+ KASSERT(mutex_owned(&sc->sc_lock));
+
+ USBHIST_LOG(ehcidebug, "pipe=%p", pipe, 0, 0, 0);
+
+ ehci_close_pipe(pipe, sc->sc_async_head);
}
/*
@@ -3675,6 +3829,38 @@ ehci_intrlist_timeout(void *arg)
/************************/
+Static int
+ehci_device_bulk_init(struct usbd_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_XFER2SC(xfer);
+ struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
+ usb_endpoint_descriptor_t *ed = xfer->ux_pipe->up_endpoint->ue_edesc;
+ int endpt = ed->bEndpointAddress;
+ int isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ int len = xfer->ux_bufsize;
+ int err = 0;
+
+ exfer->ex_type = EX_BULK;
+ exfer->ex_nsqtd = 0;
+ err = ehci_alloc_sqtd_chain(sc, xfer, len, isread,
+ &exfer->ex_sqtdstart, &exfer->ex_sqtdend);
+
+ return err;
+}
+
+Static void
+ehci_device_bulk_fini(struct usbd_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_XFER2SC(xfer);
+ struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer);
+
+ KASSERT(ex->ex_type == EX_BULK);
+
+ ehci_free_sqtds(sc, ex);
+ if (ex->ex_nsqtd)
+ kmem_free(ex->ex_sqtds, sizeof(ehci_soft_qtd_t *) * ex->ex_nsqtd);
+}
+
Static usbd_status
ehci_device_bulk_transfer(struct usbd_xfer *xfer)
{
@@ -3698,9 +3884,8 @@ ehci_device_bulk_start(struct usbd_xfer
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
ehci_softc_t *sc = EHCI_XFER2SC(xfer);
- ehci_soft_qtd_t *data, *dataend;
ehci_soft_qh_t *sqh;
- usbd_status err;
+ ehci_soft_qtd_t *end;
int len, isread, endpt;
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
@@ -3712,40 +3897,39 @@ ehci_device_bulk_start(struct usbd_xfer
return USBD_IOERROR;
KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
-
- mutex_enter(&sc->sc_lock);
+ KASSERT(xfer->ux_length <= xfer->ux_bufsize);
len = xfer->ux_length;
endpt = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
isread = UE_GET_DIR(endpt) == UE_DIR_IN;
sqh = epipe->sqh;
- err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data,
- &dataend);
- if (err) {
- USBHIST_LOG(ehcidebug, "no memory", 0, 0, 0, 0);
- xfer->ux_status = err;
- usb_transfer_complete(xfer);
- mutex_exit(&sc->sc_lock);
- return err;
- }
+ KASSERT(exfer->ex_isdone);
+#ifdef DIAGNOSTIC
+ exfer->ex_isdone = false;
+#endif
+
+ /* Take lock here to protect nexttoggle */
+ mutex_enter(&sc->sc_lock);
+
+ ehci_reset_sqtd_chain(sc, xfer, len, isread, &epipe->nexttoggle,
+ exfer->ex_sqtdstart, &end);
+
+ exfer->ex_sqtdend = end;
+ end->qtd.qtd_status |= htole32(EHCI_QTD_IOC);
+ usb_syncmem(&end->dma, end->offs, sizeof(end->qtd),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
#ifdef EHCI_DEBUG
USBHIST_LOGN(ehcidebug, 5, "--- dump start ---", 0, 0, 0, 0);
ehci_dump_sqh(sqh);
- ehci_dump_sqtds(data);
+ ehci_dump_sqtds(exfer->ex_sqtdstart);
USBHIST_LOGN(ehcidebug, 5, "--- dump end ---", 0, 0, 0, 0);
#endif
- /* Set up interrupt info. */
- exfer->ex_sqtdstart = data;
- exfer->ex_sqtdend = dataend;
- KASSERT(exfer->ex_isdone);
-#ifdef DIAGNOSTIC
- exfer->ex_isdone = false;
-#endif
-
- ehci_set_qh_qtd(sqh, data); /* also does usb_syncmem(sqh) */
+ usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length,
+ isread ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+ ehci_set_qh_qtd(sqh, exfer->ex_sqtdstart); /* also does usb_syncmem(sqh) */
if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) {
callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout),
ehci_timeout, xfer);
@@ -3754,6 +3938,7 @@ ehci_device_bulk_start(struct usbd_xfer
xfer->ux_status = USBD_IN_PROGRESS;
mutex_exit(&sc->sc_lock);
+#if 0
#ifdef EHCI_DEBUG
USBHIST_LOGN(ehcidebug, 5, "data(2)", 0, 0, 0, 0);
// delay(10000);
@@ -3765,7 +3950,8 @@ ehci_device_bulk_start(struct usbd_xfer
#endif
USBHIST_LOG(ehcidebug, "sqh:", 0, 0, 0, 0);
ehci_dump_sqh(sqh);
- ehci_dump_sqtds(data);
+ ehci_dump_sqtds(exfer->ex_sqtdstart);
+#endif
#endif
if (sc->sc_bus.ub_usepolling)
@@ -3812,14 +3998,13 @@ ehci_device_bulk_done(struct usbd_xfer *
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
- USBHIST_LOG(ehcidebug, "xfer=%p, actlen=%d",
- xfer, xfer->ux_actlen, 0, 0);
+ USBHIST_LOG(ehcidebug, "xfer=%p, actlen=%d", xfer, xfer->ux_actlen,
+ 0, 0);
KASSERT(mutex_owned(&sc->sc_lock));
if (xfer->ux_status != USBD_NOMEM && ehci_active_intr_list(ex)) {
ehci_del_intr_list(sc, ex); /* remove from active list */
- ehci_free_sqtd_chain(sc, ex->ex_sqtdstart, NULL);
usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length,
rd ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
}
@@ -3854,6 +4039,47 @@ ehci_device_setintr(ehci_softc_t *sc, eh
return USBD_NORMAL_COMPLETION;
}
+
+Static int
+ehci_device_intr_init(struct usbd_xfer *xfer)
+{
+ struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
+ ehci_softc_t *sc = EHCI_XFER2SC(xfer);
+ usb_endpoint_descriptor_t *ed = xfer->ux_pipe->up_endpoint->ue_edesc;
+ int endpt = ed->bEndpointAddress;
+ int isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ int len = xfer->ux_bufsize;
+ int err;
+
+ USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+
+ USBHIST_LOG(ehcidebug, "xfer=%p len=%d flags=%d", xfer, xfer->ux_length,
+ xfer->ux_flags, 0);
+
+ KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
+ KASSERT(len != 0);
+
+ exfer->ex_type = EX_INTR;
+ exfer->ex_nsqtd = 0;
+ err = ehci_alloc_sqtd_chain(sc, xfer, len, isread,
+ &exfer->ex_sqtdstart, &exfer->ex_sqtdend);
+
+ return err;
+}
+
+Static void
+ehci_device_intr_fini(struct usbd_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_XFER2SC(xfer);
+ struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer);
+
+ KASSERT(ex->ex_type == EX_BULK);
+
+ ehci_free_sqtds(sc, ex);
+ if (ex->ex_nsqtd)
+ kmem_free(ex->ex_sqtds, sizeof(ehci_soft_qtd_t *) * ex->ex_nsqtd);
+}
+
Static usbd_status
ehci_device_intr_transfer(struct usbd_xfer *xfer)
{
@@ -3880,55 +4106,52 @@ ehci_device_intr_start(struct usbd_xfer
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
ehci_softc_t *sc = EHCI_XFER2SC(xfer);
- ehci_soft_qtd_t *data, *dataend;
+ ehci_soft_qtd_t *data, *end;
ehci_soft_qh_t *sqh;
- usbd_status err;
int len, isread, endpt;
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
- USBHIST_LOG(ehcidebug, "xfer=%p len=%d flags=%d",
- xfer, xfer->ux_length, xfer->ux_flags, 0);
+ USBHIST_LOG(ehcidebug, "xfer=%p len=%d flags=%d", xfer, xfer->ux_length,
+ xfer->ux_flags, 0);
if (sc->sc_dying)
return USBD_IOERROR;
KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
-
- mutex_enter(&sc->sc_lock);
+ KASSERT(xfer->ux_length <= xfer->ux_bufsize);
len = xfer->ux_length;
endpt = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
isread = UE_GET_DIR(endpt) == UE_DIR_IN;
sqh = epipe->sqh;
- epipe->intr.length = len;
+ data = exfer->ex_sqtdstart;
- err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data,
- &dataend);
- if (err) {
- USBHIST_LOG(ehcidebug, "no memory", 0, 0, 0, 0);
- xfer->ux_status = err;
- usb_transfer_complete(xfer);
- mutex_exit(&sc->sc_lock);
- return err;
- }
+ KASSERT(exfer->ex_isdone);
+#ifdef DIAGNOSTIC
+ exfer->ex_isdone = false;
+#endif
+
+ /* Take lock to protect nexttoggle */
+ mutex_enter(&sc->sc_lock);
+ ehci_reset_sqtd_chain(sc, xfer, len, isread, &epipe->nexttoggle,
+ data, &end);
+
+ end->qtd.qtd_status |= htole32(EHCI_QTD_IOC);
+ usb_syncmem(&end->dma, end->offs, sizeof(end->qtd),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ exfer->ex_sqtdend = end;
#ifdef EHCI_DEBUG
USBHIST_LOGN(ehcidebug, 5, "--- dump start ---", 0, 0, 0, 0);
ehci_dump_sqh(sqh);
- ehci_dump_sqtds(data);
+ ehci_dump_sqtds(exfer->ex_sqtdstart);
USBHIST_LOGN(ehcidebug, 5, "--- dump end ---", 0, 0, 0, 0);
#endif
- /* Set up interrupt info. */
- exfer->ex_sqtdstart = data;
- exfer->ex_sqtdend = dataend;
- KASSERT(exfer->ex_isdone);
-#ifdef DIAGNOSTIC
- exfer->ex_isdone = false;
-#endif
-
+ usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length,
+ isread ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
ehci_set_qh_qtd(sqh, data); /* also does usb_syncmem(sqh) */
if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) {
callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout),
@@ -3938,6 +4161,7 @@ ehci_device_intr_start(struct usbd_xfer
xfer->ux_status = USBD_IN_PROGRESS;
mutex_exit(&sc->sc_lock);
+#if 0
#ifdef EHCI_DEBUG
USBHIST_LOGN(ehcidebug, 5, "data(2)", 0, 0, 0, 0);
// delay(10000);
@@ -3947,6 +4171,7 @@ ehci_device_intr_start(struct usbd_xfer
ehci_dump_sqh(sqh);
ehci_dump_sqtds(data);
#endif
+#endif
if (sc->sc_bus.ub_usepolling)
ehci_waitintr(sc, xfer);
@@ -3989,45 +4214,41 @@ ehci_device_intr_done(struct usbd_xfer *
ehci_softc_t *sc = EHCI_XFER2SC(xfer);
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);
- ehci_soft_qtd_t *data, *dataend;
+ ehci_soft_qtd_t *data;
ehci_soft_qh_t *sqh;
- usbd_status err;
int len, isread, endpt;
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
- USBHIST_LOG(ehcidebug, "xfer=%p, actlen=%d",
- xfer, xfer->ux_actlen, 0, 0);
+ USBHIST_LOG(ehcidebug, "xfer=%p, actlen=%d", xfer, xfer->ux_actlen,
+ 0, 0);
KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock));
if (xfer->ux_pipe->up_repeat) {
- ehci_free_sqtd_chain(sc, exfer->ex_sqtdstart, NULL);
- len = epipe->intr.length;
- xfer->ux_length = len;
+ KASSERT(exfer->ex_isdone);
+#ifdef DIAGNOSTIC
+ exfer->ex_isdone = false;
+#endif
+
+ len = xfer->ux_length;
endpt = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
isread = UE_GET_DIR(endpt) == UE_DIR_IN;
usb_syncmem(&xfer->ux_dmabuf, 0, len,
isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
sqh = epipe->sqh;
- err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
- &data, &dataend);
- if (err) {
- USBHIST_LOG(ehcidebug, "no memory", 0, 0, 0, 0);
- xfer->ux_status = err;
- return;
- }
+ ehci_soft_qtd_t *end;
+ ehci_reset_sqtd_chain(sc, xfer, len, isread,
+ &epipe->nexttoggle, exfer->ex_sqtdstart, &end);
+ end->qtd.qtd_status |= htole32(EHCI_QTD_IOC);
+ usb_syncmem(&end->dma, end->offs, sizeof(end->qtd),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- /* Set up interrupt info. */
- exfer->ex_sqtdstart = data;
- exfer->ex_sqtdend = dataend;
- KASSERT(exfer->ex_isdone);
-#ifdef DIAGNOSTIC
- exfer->ex_isdone = false;
-#endif
+ exfer->ex_sqtdend = end;
+ data = exfer->ex_sqtdstart;
ehci_set_qh_qtd(sqh, data); /* also does usb_syncmem(sqh) */
if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) {
callout_reset(&xfer->ux_callout,
@@ -4037,7 +4258,6 @@ ehci_device_intr_done(struct usbd_xfer *
xfer->ux_status = USBD_IN_PROGRESS;
} else if (xfer->ux_status != USBD_NOMEM && ehci_active_intr_list(exfer)) {
ehci_del_intr_list(sc, exfer); /* remove from active list */
- ehci_free_sqtd_chain(sc, exfer->ex_sqtdstart, NULL);
endpt = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
isread = UE_GET_DIR(endpt) == UE_DIR_IN;
usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length,
@@ -4046,6 +4266,108 @@ ehci_device_intr_done(struct usbd_xfer *
}
/************************/
+Static int
+ehci_device_fs_isoc_init(struct usbd_xfer *xfer)
+{
+ struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(xfer->ux_pipe);
+ struct usbd_device *dev = xfer->ux_pipe->up_dev;
+ ehci_softc_t *sc = EHCI_XFER2SC(xfer);
+ struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
+ ehci_soft_sitd_t *sitd, *prev, *start, *stop;
+ int i, k, frames;
+ u_int huba, dir;
+ int err;
+
+ USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+
+ start = NULL;
+ sitd = NULL;
+
+ USBHIST_LOG(ehcidebug, "xfer %p len %d flags %d", xfer, xfer->ux_length,
+ xfer->ux_flags, 0);
+
+ KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
+ KASSERT(xfer->ux_nframes != 0);
+ KASSERT(exfer->ex_isdone);
+
+ exfer->ex_type = EX_FS_ISOC;
+ /*
+ * Step 1: Allocate and initialize sitds.
+ */
+ i = epipe->pipe.up_endpoint->ue_edesc->bInterval;
+ if (i > 16 || i == 0) {
+ /* Spec page 271 says intervals > 16 are invalid */
+ USBHIST_LOG(ehcidebug, "bInterval %d invalid", i, 0, 0, 0);
+
+ return EINVAL;
+ }
+
+ frames = xfer->ux_nframes;
+ for (i = 0, prev = NULL; i < frames; i++, prev = sitd) {
+ sitd = ehci_alloc_sitd(sc);
+ if (sitd == NULL) {
+ err = ENOMEM;
+ goto fail;
+ }
+
+ if (prev)
+ prev->xfer_next = sitd;
+ else
+ start = sitd;
+
+ huba = dev->ud_myhsport->up_parent->ud_addr;
+
+#if 0
+ if (sc->sc_flags & EHCIF_FREESCALE) {
+ // Set hub address to 0 if embedded TT is used.
+ if (huba == sc->sc_addr)
+ huba = 0;
+ }
+#endif
+
+ k = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
+ dir = UE_GET_DIR(k) ? 1 : 0;
+ sitd->sitd.sitd_endp =
+ htole32(EHCI_SITD_SET_ENDPT(UE_GET_ADDR(k)) |
+ EHCI_SITD_SET_DADDR(dev->ud_addr) |
+ EHCI_SITD_SET_PORT(dev->ud_myhsport->up_portno) |
+ EHCI_SITD_SET_HUBA(huba) |
+ EHCI_SITD_SET_DIR(dir));
+
+ sitd->sitd.sitd_back = htole32(EHCI_LINK_TERMINATE);
+ } /* End of frame */
+
+ sitd->sitd.sitd_trans |= htole32(EHCI_SITD_IOC);
+
+ stop = sitd;
+ stop->xfer_next = NULL;
+ exfer->ex_sitdstart = start;
+ exfer->ex_sitdend = stop;
+
+ return 0;
+
+fail:
+ mutex_enter(&sc->sc_lock);
+ ehci_soft_sitd_t *next;
+ for (sitd = start; sitd; sitd = next) {
+ next = sitd->xfer_next;
+ ehci_free_sitd_locked(sc, sitd);
+ }
+ mutex_exit(&sc->sc_lock);
+
+ return err;
+}
+
+Static void
+ehci_device_fs_isoc_fini(struct usbd_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_XFER2SC(xfer);
+ struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer);
+
+ KASSERT(ex->ex_type == EX_FS_ISOC);
+
+ ehci_free_sitd_chain(sc, ex->ex_sitdstart);
+}
Static usbd_status
ehci_device_fs_isoc_transfer(struct usbd_xfer *xfer)
@@ -4066,21 +4388,19 @@ ehci_device_fs_isoc_transfer(struct usbd
Static usbd_status
ehci_device_fs_isoc_start(struct usbd_xfer *xfer)
{
- struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);
- struct usbd_device *dev = xfer->ux_pipe->up_dev;
ehci_softc_t *sc = EHCI_XFER2SC(xfer);
+ struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);;
+ struct usbd_device *dev = xfer->ux_pipe->up_dev;;
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
- ehci_soft_sitd_t *sitd, *prev, *start, *stop;
+ ehci_soft_sitd_t *sitd;
usb_dma_t *dma_buf;
int i, j, k, frames;
int offs, total_length;
int frindex;
- u_int huba, dir;
+ u_int dir;
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
- start = NULL;
- prev = NULL;
sitd = NULL;
total_length = 0;
@@ -4091,8 +4411,9 @@ ehci_device_fs_isoc_start(struct usbd_xf
* in progress or not
*/
- if (exfer->ex_sitdstart != NULL)
+ if (exfer->ex_isrunning) {
return USBD_IN_PROGRESS;
+ }
USBHIST_LOG(ehcidebug, "xfer %p len %d flags %d",
xfer, xfer->ux_length, xfer->ux_flags, 0);
@@ -4112,55 +4433,30 @@ ehci_device_fs_isoc_start(struct usbd_xf
return USBD_INVAL;
}
+ KASSERT(xfer->ux_nframes != 0 && xfer->ux_frlengths);
KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
KASSERT(exfer->ex_isdone);
-
#ifdef DIAGNOSTIC
exfer->ex_isdone = false;
#endif
/*
- * Step 1: Allocate and initialize sitds.
+ * Step 1: Initialize sitds.
*/
- i = epipe->pipe.up_endpoint->ue_edesc->bInterval;
- if (i > 16 || i == 0) {
- /* Spec page 271 says intervals > 16 are invalid */
- USBHIST_LOG(ehcidebug, "bInterval %d invalid", 0, 0, 0, 0);
-
- return USBD_INVAL;
- }
-
frames = xfer->ux_nframes;
-
- if (frames == 0) {
- USBHIST_LOG(ehcidebug, "frames == 0", 0, 0, 0, 0);
-
- return USBD_INVAL;
- }
-
dma_buf = &xfer->ux_dmabuf;
offs = 0;
- for (i = 0; i < frames; i++) {
- sitd = ehci_alloc_sitd(sc);
-
- if (prev)
- prev->xfer_next = sitd;
- else
- start = sitd;
-
-#ifdef DIAGNOSTIC
- if (xfer->ux_frlengths[i] > 0x3ff) {
- printf("ehci: invalid frame length\n");
- xfer->ux_frlengths[i] = 0x3ff;
- }
-#endif
+ for (sitd = exfer->ex_sitdstart, i = 0; i < frames;
+ i++, sitd = sitd->xfer_next) {
+ KASSERT(sitd != NULL);
+ KASSERT(xfer->ux_frlengths[i] <= 0x3ff);
sitd->sitd.sitd_trans = htole32(EHCI_SITD_ACTIVE |
EHCI_SITD_SET_LEN(xfer->ux_frlengths[i]));
- /* Set page0 index and offset. */
+ /* Set page0 index and offset - TP and T-offset are set below */
sitd->sitd.sitd_buffer[0] = htole32(DMAADDR(dma_buf, offs));
total_length += xfer->ux_frlengths[i];
@@ -4169,7 +4465,7 @@ ehci_device_fs_isoc_start(struct usbd_xf
sitd->sitd.sitd_buffer[1] =
htole32(EHCI_SITD_SET_BPTR(DMAADDR(dma_buf, offs - 1)));
- huba = dev->ud_myhsport->up_parent->ud_addr;
+ u_int huba __diagused = dev->ud_myhsport->up_parent->ud_addr;
#if 0
if (sc->sc_flags & EHCIF_FREESCALE) {
@@ -4181,19 +4477,17 @@ ehci_device_fs_isoc_start(struct usbd_xf
k = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
dir = UE_GET_DIR(k) ? 1 : 0;
- sitd->sitd.sitd_endp =
- htole32(EHCI_SITD_SET_ENDPT(UE_GET_ADDR(k)) |
+ KASSERT(sitd->sitd.sitd_endp == htole32(
+ EHCI_SITD_SET_ENDPT(UE_GET_ADDR(k)) |
EHCI_SITD_SET_DADDR(dev->ud_addr) |
EHCI_SITD_SET_PORT(dev->ud_myhsport->up_portno) |
EHCI_SITD_SET_HUBA(huba) |
- EHCI_SITD_SET_DIR(dir));
-
- sitd->sitd.sitd_back = htole32(EHCI_LINK_TERMINATE);
+ EHCI_SITD_SET_DIR(dir)));
+ KASSERT(sitd->sitd.sitd_back == htole32(EHCI_LINK_TERMINATE));
- /* XXX */
- u_char sa, sb;
+ uint8_t sa = 0;
+ uint8_t sb = 0;
u_int temp, tlen;
- sa = 0;
if (dir == 0) { /* OUT */
temp = 0;
@@ -4227,24 +4521,22 @@ ehci_device_fs_isoc_start(struct usbd_xf
sb = 0xfc;
}
- sitd->sitd.sitd_sched = htole32(EHCI_SITD_SET_SMASK(sa) |
- EHCI_SITD_SET_CMASK(sb));
+ sitd->sitd.sitd_sched = htole32(
+ EHCI_SITD_SET_SMASK(sa) |
+ EHCI_SITD_SET_CMASK(sb)
+ );
usb_syncmem(&sitd->dma, sitd->offs, sizeof(ehci_sitd_t),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
-
- prev = sitd;
} /* End of frame */
+ sitd = exfer->ex_sitdend;
sitd->sitd.sitd_trans |= htole32(EHCI_SITD_IOC);
usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_trans),
sizeof(sitd->sitd.sitd_trans),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- stop = sitd;
- stop->xfer_next = NULL;
-
usb_syncmem(&exfer->ex_xfer.ux_dmabuf, 0, total_length,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
@@ -4272,10 +4564,9 @@ ehci_device_fs_isoc_start(struct usbd_xf
/* Whats the frame interval? */
i = epipe->pipe.up_endpoint->ue_edesc->bInterval;
- sitd = start;
- for (j = 0; j < frames; j++) {
- if (sitd == NULL)
- panic("ehci: unexpectedly ran out of isoc sitds\n");
+ for (sitd = exfer->ex_sitdstart, j = 0; j < frames;
+ j++, sitd = sitd->xfer_next) {
+ KASSERT(sitd);
usb_syncmem(&sc->sc_fldma,
sizeof(ehci_link_t) * frindex,
@@ -4313,15 +4604,12 @@ ehci_device_fs_isoc_start(struct usbd_xf
frindex += i;
if (frindex >= sc->sc_flsize)
frindex -= sc->sc_flsize;
-
- sitd = sitd->xfer_next;
}
epipe->isoc.cur_xfers++;
epipe->isoc.next_frame = frindex;
- exfer->ex_sitdstart = start;
- exfer->ex_sitdend = stop;
+ exfer->ex_isrunning = true;
ehci_add_intr_list(sc, exfer);
xfer->ux_status = USBD_IN_PROGRESS;
@@ -4365,12 +4653,124 @@ ehci_device_fs_isoc_done(struct usbd_xfe
epipe->isoc.cur_xfers--;
if (xfer->ux_status != USBD_NOMEM && ehci_active_intr_list(exfer)) {
ehci_del_intr_list(sc, exfer);
- ehci_rem_free_sitd_chain(sc, exfer);
+ ehci_remove_sitd_chain(sc, exfer->ex_itdstart);
+ exfer->ex_isrunning = false;
}
usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length,
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
}
+
+
+/************************/
+
+
+Static int
+ehci_device_isoc_init(struct usbd_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_XFER2SC(xfer);
+ struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);
+ struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
+ ehci_soft_itd_t *itd, *prev, *start, *stop;
+ int i, j, k;
+ int frames, ufrperframe;
+ int err;
+
+ USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+
+ start = NULL;
+ prev = NULL;
+ itd = NULL;
+
+ KASSERT(xfer->ux_nframes != 0);
+ KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
+ KASSERT(exfer->ex_isdone);
+
+ exfer->ex_type = EX_ISOC;
+
+ /*
+ * Step 1: Allocate and initialize itds, how many do we need?
+ * One per transfer if interval >= 8 microframes, less if we use
+ * multiple microframes per frame.
+ */
+ i = epipe->pipe.up_endpoint->ue_edesc->bInterval;
+ if (i > 16 || i == 0) {
+ /* Spec page 271 says intervals > 16 are invalid */
+ USBHIST_LOG(ehcidebug, "bInterval %d invalid", i, 0, 0, 0);
+ return USBD_INVAL;
+ }
+
+ ufrperframe = max(1, USB_UFRAMES_PER_FRAME / (1 << (i - 1)));
+ frames = (xfer->ux_nframes + (ufrperframe - 1)) / ufrperframe;
+
+ for (i = 0, prev = NULL; i < frames; i++, prev = itd) {
+ itd = ehci_alloc_itd(sc);
+ if (itd == NULL) {
+ err = ENOMEM;
+ goto fail;
+ }
+
+ if (prev != NULL) {
+ /* Maybe not as it's updated by the scheduling? */
+ prev->itd.itd_next =
+ htole32(itd->physaddr | EHCI_LINK_ITD);
+
+ prev->xfer_next = itd;
+ } else {
+ start = itd;
+ }
+
+ /*
+ * Other special values
+ */
+ k = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
+ itd->itd.itd_bufr[0] = htole32(
+ EHCI_ITD_SET_EP(UE_GET_ADDR(k)) |
+ EHCI_ITD_SET_DADDR(epipe->pipe.up_dev->ud_addr));
+
+ k = (UE_GET_DIR(epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress))
+ ? 1 : 0;
+ j = UGETW(epipe->pipe.up_endpoint->ue_edesc->wMaxPacketSize);
+ itd->itd.itd_bufr[1] |= htole32(
+ EHCI_ITD_SET_DIR(k) |
+ EHCI_ITD_SET_MAXPKT(UE_GET_SIZE(j)));
+
+ /* FIXME: handle invalid trans - should be done in openpipe */
+ itd->itd.itd_bufr[2] |=
+ htole32(EHCI_ITD_SET_MULTI(UE_GET_TRANS(j)+1));
+ } /* End of frame */
+
+ stop = itd;
+ stop->xfer_next = NULL;
+
+ exfer->ex_itdstart = start;
+ exfer->ex_itdend = stop;
+
+ return 0;
+fail:
+ mutex_enter(&sc->sc_lock);
+ ehci_soft_itd_t *next;
+ for (itd = start; itd; itd = next) {
+ next = itd->xfer_next;
+ ehci_free_itd_locked(sc, itd);
+ }
+ mutex_exit(&sc->sc_lock);
+
+ return err;
+
+}
+
+Static void
+ehci_device_isoc_fini(struct usbd_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_XFER2SC(xfer);
+ struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer);
+
+ KASSERT(ex->ex_type == EX_ISOC);
+
+ ehci_free_itd_chain(sc, ex->ex_itdstart);
+}
+
Static usbd_status
ehci_device_isoc_transfer(struct usbd_xfer *xfer)
{
@@ -4391,16 +4791,16 @@ ehci_device_isoc_start(struct usbd_xfer
{
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);
ehci_softc_t *sc = EHCI_XFER2SC(xfer);
- struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
- ehci_soft_itd_t *itd, *prev, *start, *stop;
+ struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer);
+ ehci_soft_itd_t *itd, *prev;
usb_dma_t *dma_buf;
- int i, j, k, frames, uframes, ufrperframe;
+ int i, j;
+ int frames, uframes, ufrperframe;
int trans_count, offs, total_length;
int frindex;
USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
- start = NULL;
prev = NULL;
itd = NULL;
trans_count = 0;
@@ -4413,11 +4813,11 @@ ehci_device_isoc_start(struct usbd_xfer
* in progress or not
*/
- if (exfer->ex_itdstart != NULL)
+ if (exfer->ex_isrunning) {
return USBD_IN_PROGRESS;
+ }
- USBHIST_LOG(ehcidebug, "xfer %p len %d flags %d",
- xfer, xfer->ux_length, xfer->ux_flags, 0);
+ USBHIST_LOG(ehcidebug, "xfer %p flags %d", xfer, xfer->ux_flags, 0, 0);
if (sc->sc_dying)
return USBD_IOERROR;
@@ -4435,6 +4835,7 @@ ehci_device_isoc_start(struct usbd_xfer
return USBD_INVAL;
}
+ KASSERT(xfer->ux_nframes != 0 && xfer->ux_frlengths);
KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
KASSERT(exfer->ex_isdone);
#ifdef DIAGNOSTIC
@@ -4442,9 +4843,7 @@ ehci_device_isoc_start(struct usbd_xfer
#endif
/*
- * Step 1: Allocate and initialize itds, how many do we need?
- * One per transfer if interval >= 8 microframes, fewer if we use
- * multiple microframes per frame.
+ * Step 1: Re-Initialize itds
*/
i = epipe->pipe.up_endpoint->ue_edesc->bInterval;
@@ -4466,26 +4865,22 @@ ehci_device_isoc_start(struct usbd_xfer
dma_buf = &xfer->ux_dmabuf;
offs = 0;
- for (i = 0; i < frames; i++) {
+ itd = exfer->ex_itdstart;
+ for (i = 0; i < frames; i++, itd = itd->xfer_next) {
int froffs = offs;
- itd = ehci_alloc_itd(sc);
if (prev != NULL) {
prev->itd.itd_next =
htole32(itd->physaddr | EHCI_LINK_ITD);
usb_syncmem(&prev->dma,
prev->offs + offsetof(ehci_itd_t, itd_next),
- sizeof(prev->itd.itd_next),
- BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
-
+ sizeof(prev->itd.itd_next), BUS_DMASYNC_POSTWRITE);
prev->xfer_next = itd;
- } else {
- start = itd;
}
/*
* Step 1.5, initialize uframes
- */
+ */
for (j = 0; j < EHCI_ITD_NUFRAMES; j += uframes) {
/* Calculate which page in the list this starts in */
int addr = DMAADDR(dma_buf, froffs);
@@ -4531,19 +4926,16 @@ ehci_device_isoc_start(struct usbd_xfer
if (page_offs >= dma_buf->udma_block->size)
break;
- unsigned long long page = DMAADDR(dma_buf, page_offs);
+ uint64_t page = DMAADDR(dma_buf, page_offs);
page = EHCI_PAGE(page);
- itd->itd.itd_bufr[j] =
- htole32(EHCI_ITD_SET_BPTR(page));
- itd->itd.itd_bufr_hi[j] =
- htole32(page >> 32);
+ itd->itd.itd_bufr[j] = htole32(EHCI_ITD_SET_BPTR(page));
+ itd->itd.itd_bufr_hi[j] = htole32(page >> 32);
}
-
/*
* Other special values
*/
- k = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
+ int k = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
itd->itd.itd_bufr[0] |= htole32(EHCI_ITD_SET_EP(UE_GET_ADDR(k)) |
EHCI_ITD_SET_DADDR(epipe->pipe.up_dev->ud_addr));
@@ -4563,9 +4955,6 @@ ehci_device_isoc_start(struct usbd_xfer
prev = itd;
} /* End of frame */
- stop = itd;
- stop->xfer_next = NULL;
-
usb_syncmem(&exfer->ex_xfer.ux_dmabuf, 0, total_length,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
@@ -4597,10 +4986,9 @@ ehci_device_isoc_start(struct usbd_xfer
else
i /= USB_UFRAMES_PER_FRAME;
- itd = start;
+ itd = exfer->ex_itdstart;
for (j = 0; j < frames; j++) {
- if (itd == NULL)
- panic("ehci: unexpectedly ran out of isoc itds, isoc_start");
+ KASSERTMSG(itd != NULL, "frame %d\n", j);
usb_syncmem(&sc->sc_fldma,
sizeof(ehci_link_t) * frindex,
@@ -4644,11 +5032,11 @@ ehci_device_isoc_start(struct usbd_xfer
epipe->isoc.cur_xfers++;
epipe->isoc.next_frame = frindex;
- exfer->ex_itdstart = start;
- exfer->ex_itdend = stop;
+ exfer->ex_isrunning = true;
ehci_add_intr_list(sc, exfer);
xfer->ux_status = USBD_IN_PROGRESS;
+
mutex_exit(&sc->sc_lock);
if (sc->sc_bus.ub_usepolling) {
@@ -4688,10 +5076,9 @@ ehci_device_isoc_done(struct usbd_xfer *
epipe->isoc.cur_xfers--;
if (xfer->ux_status != USBD_NOMEM && ehci_active_intr_list(exfer)) {
ehci_del_intr_list(sc, exfer);
- ehci_rem_free_itd_chain(sc, exfer);
+ ehci_remove_itd_chain(sc, exfer->ex_sitdstart);
+ exfer->ex_isrunning = false;
}
-
usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length,
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
-
}
Index: src/sys/dev/usb/ehcivar.h
diff -u src/sys/dev/usb/ehcivar.h:1.42.14.20 src/sys/dev/usb/ehcivar.h:1.42.14.21
--- src/sys/dev/usb/ehcivar.h:1.42.14.20 Sat Oct 24 10:37:22 2015
+++ src/sys/dev/usb/ehcivar.h Sun Oct 25 09:50:06 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: ehcivar.h,v 1.42.14.20 2015/10/24 10:37:22 skrll Exp $ */
+/* $NetBSD: ehcivar.h,v 1.42.14.21 2015/10/25 09:50:06 skrll Exp $ */
/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -41,7 +41,9 @@ typedef struct ehci_soft_qtd {
usb_dma_t dma; /* qTD's DMA infos */
int offs; /* qTD's offset in usb_dma_t */
struct usbd_xfer *xfer; /* xfer back pointer */
+ size_t bufoff; /* Offset into xfer buffer */
uint16_t len;
+ uint16_t tdlen;
} ehci_soft_qtd_t;
#define EHCI_SQTD_ALIGN MAX(EHCI_QTD_ALIGN, CACHE_LINE_SIZE)
#define EHCI_SQTD_SIZE ((sizeof(struct ehci_soft_qtd) + EHCI_SQTD_ALIGN - 1) & -EHCI_SQTD_ALIGN)
@@ -93,9 +95,32 @@ struct ehci_xfer {
struct usbd_xfer ex_xfer;
struct usb_task ex_aborttask;
TAILQ_ENTRY(ehci_xfer) ex_next; /* list of active xfers */
+ enum {
+ EX_NONE,
+ EX_CTRL,
+ EX_BULK,
+ EX_INTR,
+ EX_ISOC,
+ EX_FS_ISOC
+ } ex_type;
union {
/* ctrl/bulk/intr */
struct {
+ ehci_soft_qtd_t **ex_sqtds;
+ size_t ex_nsqtd;
+ };
+ /* isoc */
+ bool ex_isrunning;
+ };
+ union {
+ /* ctrl */
+ struct {
+ ehci_soft_qtd_t *ex_setup;
+ ehci_soft_qtd_t *ex_data;
+ ehci_soft_qtd_t *ex_status;
+ };
+ /* bulk/intr */
+ struct {
ehci_soft_qtd_t *ex_sqtdstart;
ehci_soft_qtd_t *ex_sqtdend;
};