Module Name: src
Committed By: skrll
Date: Tue Nov 10 08:44:10 UTC 2015
Modified Files:
src/sys/dev/usb [nick-nhusb]: uhci.c uhcivar.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 uhci(4), fixes a bunch of 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.264.4.54 -r1.264.4.55 src/sys/dev/usb/uhci.c
cvs rdiff -u -r1.52.14.15 -r1.52.14.16 src/sys/dev/usb/uhcivar.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/uhci.c
diff -u src/sys/dev/usb/uhci.c:1.264.4.54 src/sys/dev/usb/uhci.c:1.264.4.55
--- src/sys/dev/usb/uhci.c:1.264.4.54 Mon Nov 9 08:35:23 2015
+++ src/sys/dev/usb/uhci.c Tue Nov 10 08:44:09 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: uhci.c,v 1.264.4.54 2015/11/09 08:35:23 skrll Exp $ */
+/* $NetBSD: uhci.c,v 1.264.4.55 2015/11/10 08:44:09 skrll Exp $ */
/*
* Copyright (c) 1998, 2004, 2011, 2012 The NetBSD Foundation, Inc.
@@ -42,7 +42,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.264.4.54 2015/11/09 08:35:23 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.264.4.55 2015/11/10 08:44:09 skrll Exp $");
#include "opt_usb.h"
@@ -139,20 +139,17 @@ struct uhci_pipe {
struct {
uhci_soft_qh_t *sqh;
usb_dma_t reqdma;
- uhci_soft_td_t *setup, *stat;
- u_int length;
+ uhci_soft_td_t *setup;
+ uhci_soft_td_t *stat;
} ctrl;
/* Interrupt pipe */
struct {
int npoll;
- int isread;
uhci_soft_qh_t **qhs;
} intr;
/* Bulk pipe */
struct {
uhci_soft_qh_t *sqh;
- u_int length;
- int isread;
} bulk;
/* Isochronous pipe */
struct isoc {
@@ -168,6 +165,7 @@ Static void uhci_reset(uhci_softc_t *);
Static usbd_status uhci_run(uhci_softc_t *, int, int);
Static uhci_soft_td_t *uhci_alloc_std(uhci_softc_t *);
Static void uhci_free_std(uhci_softc_t *, uhci_soft_td_t *);
+Static void uhci_free_std_locked(uhci_softc_t *, uhci_soft_td_t *);
Static uhci_soft_qh_t *uhci_alloc_sqh(uhci_softc_t *);
Static void uhci_free_sqh(uhci_softc_t *, uhci_soft_qh_t *);
#if 0
@@ -176,11 +174,15 @@ Static void uhci_enter_ctl_q(uhci_softc
Static void uhci_exit_ctl_q(uhci_softc_t *, uhci_soft_qh_t *);
#endif
-Static void uhci_free_std_chain(uhci_softc_t *,
- uhci_soft_td_t *, uhci_soft_td_t *);
-Static usbd_status uhci_alloc_std_chain(struct uhci_pipe *,
- uhci_softc_t *, int, int, uint16_t, usb_dma_t *,
- uhci_soft_td_t **, uhci_soft_td_t **);
+Static void uhci_free_std_chain(uhci_softc_t *, uhci_soft_td_t *,
+ uhci_soft_td_t *);
+Static usbd_status uhci_alloc_std_chain(uhci_softc_t *, struct usbd_xfer *,
+ int, int, uhci_soft_td_t **, uhci_soft_td_t **);
+Static void uhci_free_stds(uhci_softc_t *, struct uhci_xfer *);
+
+Static void uhci_reset_std_chain(uhci_softc_t *, struct usbd_xfer *,
+ int, int, int *, uhci_soft_td_t *, uhci_soft_td_t **);
+
Static void uhci_poll_hub(void *);
Static void uhci_waitintr(uhci_softc_t *, struct usbd_xfer *);
Static void uhci_check_intr(uhci_softc_t *, struct uhci_xfer *);
@@ -209,24 +211,32 @@ Static void uhci_get_lock(struct usbd_b
Static int uhci_roothub_ctrl(struct usbd_bus *,
usb_device_request_t *, void *, int);
+Static int uhci_device_ctrl_init(struct usbd_xfer *);
+Static void uhci_device_ctrl_fini(struct usbd_xfer *);
Static usbd_status uhci_device_ctrl_transfer(struct usbd_xfer *);
Static usbd_status uhci_device_ctrl_start(struct usbd_xfer *);
Static void uhci_device_ctrl_abort(struct usbd_xfer *);
Static void uhci_device_ctrl_close(struct usbd_pipe *);
Static void uhci_device_ctrl_done(struct usbd_xfer *);
+Static int uhci_device_intr_init(struct usbd_xfer *);
+Static void uhci_device_intr_fini(struct usbd_xfer *);
Static usbd_status uhci_device_intr_transfer(struct usbd_xfer *);
Static usbd_status uhci_device_intr_start(struct usbd_xfer *);
Static void uhci_device_intr_abort(struct usbd_xfer *);
Static void uhci_device_intr_close(struct usbd_pipe *);
Static void uhci_device_intr_done(struct usbd_xfer *);
+Static int uhci_device_bulk_init(struct usbd_xfer *);
+Static void uhci_device_bulk_fini(struct usbd_xfer *);
Static usbd_status uhci_device_bulk_transfer(struct usbd_xfer *);
Static usbd_status uhci_device_bulk_start(struct usbd_xfer *);
Static void uhci_device_bulk_abort(struct usbd_xfer *);
Static void uhci_device_bulk_close(struct usbd_pipe *);
Static void uhci_device_bulk_done(struct usbd_xfer *);
+Static int uhci_device_isoc_init(struct usbd_xfer *);
+Static void uhci_device_isoc_fini(struct usbd_xfer *);
Static usbd_status uhci_device_isoc_transfer(struct usbd_xfer *);
Static usbd_status uhci_device_isoc_start(struct usbd_xfer *);
Static void uhci_device_isoc_abort(struct usbd_xfer *);
@@ -332,6 +342,8 @@ const struct usbd_pipe_methods uhci_root
};
const struct usbd_pipe_methods uhci_device_ctrl_methods = {
+ .upm_init = uhci_device_ctrl_init,
+ .upm_fini = uhci_device_ctrl_fini,
.upm_transfer = uhci_device_ctrl_transfer,
.upm_start = uhci_device_ctrl_start,
.upm_abort = uhci_device_ctrl_abort,
@@ -341,6 +353,8 @@ const struct usbd_pipe_methods uhci_devi
};
const struct usbd_pipe_methods uhci_device_intr_methods = {
+ .upm_init = uhci_device_intr_init,
+ .upm_fini = uhci_device_intr_fini,
.upm_transfer = uhci_device_intr_transfer,
.upm_start = uhci_device_intr_start,
.upm_abort = uhci_device_intr_abort,
@@ -350,6 +364,8 @@ const struct usbd_pipe_methods uhci_devi
};
const struct usbd_pipe_methods uhci_device_bulk_methods = {
+ .upm_init = uhci_device_bulk_init,
+ .upm_fini = uhci_device_bulk_fini,
.upm_transfer = uhci_device_bulk_transfer,
.upm_start = uhci_device_bulk_start,
.upm_abort = uhci_device_bulk_abort,
@@ -359,6 +375,8 @@ const struct usbd_pipe_methods uhci_devi
};
const struct usbd_pipe_methods uhci_device_isoc_methods = {
+ .upm_init = uhci_device_isoc_init,
+ .upm_fini = uhci_device_isoc_fini,
.upm_transfer = uhci_device_isoc_transfer,
.upm_start = uhci_device_isoc_start,
.upm_abort = uhci_device_isoc_abort,
@@ -438,6 +456,10 @@ uhci_init(uhci_softc_t *sc)
UWRITE2(sc, UHCI_FRNUM, 0); /* set frame number to 0 */
UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0)); /* set frame list*/
+ /* Initialise mutex early for uhci_alloc_* */
+ mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB);
+ mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_USB);
+
/*
* Allocate a TD, inactive, that hangs from the last QH.
* This is to avoid a bug in the PIIX that makes it run berserk
@@ -457,7 +479,7 @@ uhci_init(uhci_softc_t *sc)
/* Allocate the dummy QH marking the end and used for looping the QHs.*/
lsqh = uhci_alloc_sqh(sc);
if (lsqh == NULL)
- return ENOMEM;
+ goto fail1;
lsqh->hlink = NULL;
lsqh->qh.qh_hlink = htole32(UHCI_PTR_T); /* end of QH chain */
lsqh->elink = std;
@@ -469,7 +491,7 @@ uhci_init(uhci_softc_t *sc)
/* Allocate the dummy QH where bulk traffic will be queued. */
bsqh = uhci_alloc_sqh(sc);
if (bsqh == NULL)
- return ENOMEM;
+ goto fail2;
bsqh->hlink = lsqh;
bsqh->qh.qh_hlink = htole32(lsqh->physaddr | UHCI_PTR_QH);
bsqh->elink = NULL;
@@ -481,7 +503,7 @@ uhci_init(uhci_softc_t *sc)
/* Allocate dummy QH where high speed control traffic will be queued. */
chsqh = uhci_alloc_sqh(sc);
if (chsqh == NULL)
- return ENOMEM;
+ goto fail3;
chsqh->hlink = bsqh;
chsqh->qh.qh_hlink = htole32(bsqh->physaddr | UHCI_PTR_QH);
chsqh->elink = NULL;
@@ -493,7 +515,7 @@ uhci_init(uhci_softc_t *sc)
/* Allocate dummy QH where control traffic will be queued. */
clsqh = uhci_alloc_sqh(sc);
if (clsqh == NULL)
- return ENOMEM;
+ goto fail4;
clsqh->hlink = chsqh;
clsqh->qh.qh_hlink = htole32(chsqh->physaddr | UHCI_PTR_QH);
clsqh->elink = NULL;
@@ -546,8 +568,6 @@ uhci_init(uhci_softc_t *sc)
callout_init(&sc->sc_poll_handle, CALLOUT_MPSAFE);
- mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB);
- mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_USB);
cv_init(&sc->sc_softwake_cv, "uhciab");
/* Set up the bus struct. */
@@ -563,6 +583,17 @@ uhci_init(uhci_softc_t *sc)
UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE |
UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* enable interrupts */
return err;
+
+fail4:
+ uhci_free_sqh(sc, chsqh);
+fail3:
+ uhci_free_sqh(sc, lsqh);
+fail2:
+ uhci_free_sqh(sc, lsqh);
+fail1:
+ uhci_free_std(sc, std);
+
+ return ENOMEM;
}
int
@@ -1385,7 +1416,7 @@ uhci_softintr(void *v)
void
uhci_check_intr(uhci_softc_t *sc, struct uhci_xfer *ux)
{
- uhci_soft_td_t *std, *lstd;
+ uhci_soft_td_t *std, *fstd = NULL, *lstd = NULL;
uint32_t status;
UHCIHIST_FUNC(); UHCIHIST_CALLED();
@@ -1400,9 +1431,23 @@ uhci_check_intr(uhci_softc_t *sc, struct
return;
}
- if (ux->ux_stdstart == NULL)
+ switch (ux->ux_type) {
+ case UX_CTRL:
+ fstd = ux->ux_setup;
+ lstd = ux->ux_stat;
+ break;
+ case UX_BULK:
+ case UX_INTR:
+ case UX_ISOC:
+ fstd = ux->ux_stdstart;
+ lstd = ux->ux_stdend;
+ break;
+ default:
+ KASSERT(false);
+ break;
+ }
+ if (fstd == NULL)
return;
- lstd = ux->ux_stdend;
KASSERT(lstd != NULL);
@@ -1432,7 +1477,7 @@ uhci_check_intr(uhci_softc_t *sc, struct
* short packet (SPD and not ACTIVE).
*/
DPRINTFN(12, "active ux=%p", ux, 0, 0, 0);
- for (std = ux->ux_stdstart; std != lstd; std = std->link.std) {
+ for (std = fstd; std != lstd; std = std->link.std) {
usb_syncmem(&std->dma,
std->offs + offsetof(uhci_td_t, td_status),
sizeof(std->td.td_status),
@@ -1457,10 +1502,8 @@ uhci_check_intr(uhci_softc_t *sc, struct
* If the data phase of a control transfer is short, we need
* to complete the status stage
*/
- usb_endpoint_descriptor_t *ed = xfer->ux_pipe->up_endpoint->ue_edesc;
- uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
- if ((status & UHCI_TD_SPD) && xfertype == UE_CONTROL) {
+ if ((status & UHCI_TD_SPD) && ux->ux_type == UX_CTRL) {
struct uhci_pipe *upipe =
UHCI_PIPE2UPIPE(xfer->ux_pipe);
uhci_soft_qh_t *sqh = upipe->ctrl.sqh;
@@ -1802,12 +1845,17 @@ uhci_alloc_std(uhci_softc_t *sc)
UHCIHIST_FUNC(); UHCIHIST_CALLED();
+ 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, UHCI_STD_SIZE * UHCI_STD_CHUNK,
UHCI_TD_ALIGN, &dma);
if (err)
return NULL;
+
+ mutex_enter(&sc->sc_lock);
for (i = 0; i < UHCI_STD_CHUNK; i++) {
offs = i * UHCI_STD_SIZE;
std = KERNADDR(&dma, offs);
@@ -1820,25 +1868,40 @@ uhci_alloc_std(uhci_softc_t *sc)
}
std = sc->sc_freetds;
sc->sc_freetds = std->link.std;
+ mutex_exit(&sc->sc_lock);
+
memset(&std->td, 0, sizeof(uhci_td_t));
+
return std;
}
+#define TD_IS_FREE 0x12345678
+
void
-uhci_free_std(uhci_softc_t *sc, uhci_soft_td_t *std)
+uhci_free_std_locked(uhci_softc_t *sc, uhci_soft_td_t *std)
{
+ KASSERT(mutex_owned(&sc->sc_lock));
+
#ifdef DIAGNOSTIC
-#define TD_IS_FREE 0x12345678
if (le32toh(std->td.td_token) == TD_IS_FREE) {
printf("uhci_free_std: freeing free TD %p\n", std);
return;
}
std->td.td_token = htole32(TD_IS_FREE);
#endif
+
std->link.std = sc->sc_freetds;
sc->sc_freetds = std;
}
+void
+uhci_free_std(uhci_softc_t *sc, uhci_soft_td_t *std)
+{
+ mutex_enter(&sc->sc_lock);
+ uhci_free_std_locked(sc, std);
+ mutex_exit(&sc->sc_lock);
+}
+
uhci_soft_qh_t *
uhci_alloc_sqh(uhci_softc_t *sc)
{
@@ -1849,12 +1912,17 @@ uhci_alloc_sqh(uhci_softc_t *sc)
UHCIHIST_FUNC(); UHCIHIST_CALLED();
+ mutex_enter(&sc->sc_lock);
if (sc->sc_freeqhs == NULL) {
DPRINTFN(2, "allocating chunk", 0, 0, 0, 0);
+ mutex_exit(&sc->sc_lock);
+
err = usb_allocmem(&sc->sc_bus, UHCI_SQH_SIZE * UHCI_SQH_CHUNK,
UHCI_QH_ALIGN, &dma);
if (err)
return NULL;
+
+ mutex_enter(&sc->sc_lock);
for (i = 0; i < UHCI_SQH_CHUNK; i++) {
offs = i * UHCI_SQH_SIZE;
sqh = KERNADDR(&dma, offs);
@@ -1867,13 +1935,18 @@ uhci_alloc_sqh(uhci_softc_t *sc)
}
sqh = sc->sc_freeqhs;
sc->sc_freeqhs = sqh->hlink;
+ mutex_exit(&sc->sc_lock);
+
memset(&sqh->qh, 0, sizeof(uhci_qh_t));
+
return sqh;
}
void
uhci_free_sqh(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
{
+ KASSERT(mutex_owned(&sc->sc_lock));
+
sqh->hlink = sc->sc_freeqhs;
sc->sc_freeqhs = sqh;
}
@@ -1917,89 +1990,226 @@ uhci_free_std_chain(uhci_softc_t *sc, uh
}
usbd_status
-uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len,
- int rd, uint16_t flags, usb_dma_t *dma,
- uhci_soft_td_t **sp, uhci_soft_td_t **ep)
+uhci_alloc_std_chain(uhci_softc_t *sc, struct usbd_xfer *xfer, int alen,
+ int rd, uhci_soft_td_t **sp, uhci_soft_td_t **ep)
{
uhci_soft_td_t *p, *lastp;
uhci_physaddr_t lastlink;
- int i, ntd, l, tog, maxp;
+ int i, l, maxp;
+ int len;
uint32_t status;
- int addr = upipe->pipe.up_dev->ud_addr;
- int endpt = upipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
+ struct uhci_xfer *uxfer = UHCI_XFER2UXFER(xfer);
+ int addr = xfer->ux_pipe->up_dev->ud_addr;
+ int endpt = xfer->ux_pipe->up_endpoint->ue_edesc->bEndpointAddress;
+ usb_dma_t *dma = &xfer->ux_dmabuf;
+ uint16_t flags = xfer->ux_flags;
UHCIHIST_FUNC(); UHCIHIST_CALLED();
+ DPRINTFN(8, "xfer=%p pipe=%p", xfer, xfer->ux_pipe, 0, 0);
DPRINTFN(8, "addr=%d endpt=%d len=%d speed=%d",
- addr, UE_GET_ADDR(endpt), len, upipe->pipe.up_dev->ud_speed);
+ addr, UE_GET_ADDR(endpt), alen, xfer->ux_pipe->up_dev->ud_speed);
- KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock));
+ ASSERT_SLEEPABLE();
+ KASSERT(sp);
- maxp = UGETW(upipe->pipe.up_endpoint->ue_edesc->wMaxPacketSize);
+ len = alen;
+ maxp = UGETW(xfer->ux_pipe->up_endpoint->ue_edesc->wMaxPacketSize);
if (maxp == 0) {
printf("uhci_alloc_std_chain: maxp=0\n");
return USBD_INVAL;
}
- ntd = (len + maxp - 1) / maxp;
+ size_t ntd = (len + maxp - 1) / maxp;
if ((flags & USBD_FORCE_SHORT_XFER) && len % maxp == 0)
ntd++;
- DPRINTFN(10, "maxp=%d ntd=%d",
- maxp, ntd, 0, 0);
+ DPRINTFN(10, "maxp=%d ntd=%d", maxp, ntd, 0, 0);
+ uxfer->ux_stds = NULL;
+ uxfer->ux_nstd = ntd;
if (ntd == 0) {
- *sp = *ep = NULL;
+ *sp = NULL;
+ if (ep)
+ *ep = NULL;
DPRINTF("ntd=0", 0, 0, 0, 0);
return USBD_NORMAL_COMPLETION;
}
- tog = upipe->nexttoggle;
- if (ntd % 2 == 0)
- tog ^= 1;
- upipe->nexttoggle = tog ^ 1;
+ uxfer->ux_stds = kmem_alloc(sizeof(uhci_soft_td_t *) * ntd,
+ KM_SLEEP);
+
lastp = NULL;
- lastlink = UHCI_PTR_T;
ntd--;
status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) | UHCI_TD_ACTIVE);
- if (upipe->pipe.up_dev->ud_speed == USB_SPEED_LOW)
+ if (xfer->ux_pipe->up_dev->ud_speed == USB_SPEED_LOW)
status |= UHCI_TD_LS;
if (flags & USBD_SHORT_XFER_OK)
status |= UHCI_TD_SPD;
- usb_syncmem(dma, 0, len,
- rd ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
for (i = ntd; i >= 0; i--) {
p = uhci_alloc_std(sc);
if (p == NULL) {
- KASSERT(lastp != NULL);
uhci_free_std_chain(sc, lastp, NULL);
return USBD_NOMEM;
}
- p->link.std = lastp;
- p->td.td_link = htole32(lastlink | UHCI_PTR_VF | UHCI_PTR_TD);
- lastp = p;
- lastlink = p->physaddr;
- p->td.td_status = htole32(status);
+ uxfer->ux_stds[i] = p;
if (i == ntd) {
/* last TD */
l = len % maxp;
if (l == 0 && !(flags & USBD_FORCE_SHORT_XFER))
l = maxp;
- *ep = p;
- } else
+ if (ep)
+ *ep = p;
+ lastlink = UHCI_PTR_T;
+ } else {
l = maxp;
- p->td.td_token =
- htole32(rd ? UHCI_TD_IN (l, endpt, addr, tog) :
- UHCI_TD_OUT(l, endpt, addr, tog));
+ lastlink = p->physaddr;
+ }
+ p->link.std = lastp;
+ p->td.td_link = htole32(lastlink | UHCI_PTR_VF | UHCI_PTR_TD);
+ p->td.td_status = htole32(status);
+ p->td.td_token = htole32(
+ (rd ? UHCI_TD_PID_IN : UHCI_TD_PID_OUT) |
+ UHCI_TD_SET_MAXLEN(l) |
+ UHCI_TD_SET_ENDPT(UE_GET_ADDR(endpt)) |
+ UHCI_TD_SET_DEVADDR(addr)
+ );
p->td.td_buffer = htole32(DMAADDR(dma, i * maxp));
- usb_syncmem(&p->dma, p->offs, sizeof(p->td),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- tog ^= 1;
+ DPRINTF("std %p link 0x%08x status 0x%08x token 0x%08x",
+ p, le32toh(p->td.td_link), le32toh(p->td.td_status),
+ le32toh(p->td.td_token));
+
+ lastp = p;
}
*sp = lastp;
- DPRINTFN(10, "nexttog=%d", upipe->nexttoggle,
- 0, 0, 0);
return USBD_NORMAL_COMPLETION;
}
+Static void
+uhci_free_stds(uhci_softc_t *sc, struct uhci_xfer *ux)
+{
+ UHCIHIST_FUNC(); UHCIHIST_CALLED();
+
+ DPRINTFN(8, "ux=%p", ux, 0, 0, 0);
+
+ mutex_enter(&sc->sc_lock);
+ for (size_t i = 0; i < ux->ux_nstd; i++) {
+ uhci_soft_td_t *std = ux->ux_stds[i];
+#ifdef DIAGNOSTIC
+ if (le32toh(std->td.td_token) == TD_IS_FREE) {
+ printf("uhci_free_std: freeing free TD %p\n", std);
+ return;
+ }
+ std->td.td_token = htole32(TD_IS_FREE);
+#endif
+ ux->ux_stds[i]->link.std = sc->sc_freetds;
+ sc->sc_freetds = std;
+ }
+ mutex_exit(&sc->sc_lock);
+}
+
+
+Static void
+uhci_reset_std_chain(uhci_softc_t *sc, struct usbd_xfer *xfer,
+ int length, int isread, int *toggle,
+ uhci_soft_td_t *fstd, uhci_soft_td_t **lstd)
+{
+ struct uhci_xfer *uxfer = UHCI_XFER2UXFER(xfer);
+ struct usbd_pipe *pipe = xfer->ux_pipe;
+ usb_dma_t *dma = &xfer->ux_dmabuf;
+ uint16_t flags = xfer->ux_flags;
+ uhci_soft_td_t *std, *prev;
+ int len = length;
+ int tog = *toggle;
+ int maxp;
+ uint32_t status;
+ size_t i;
+
+ UHCIHIST_FUNC(); UHCIHIST_CALLED();
+ DPRINTFN(8, "xfer=%p len %d isread %d toggle %d", xfer,
+ len, isread, *toggle);
+
+ KASSERT(len != 0 || (flags & USBD_FORCE_SHORT_XFER));
+
+ maxp = UGETW(pipe->up_endpoint->ue_edesc->wMaxPacketSize);
+ KASSERT(maxp != 0);
+
+ status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) | UHCI_TD_ACTIVE);
+ if (pipe->up_dev->ud_speed == USB_SPEED_LOW)
+ status |= UHCI_TD_LS;
+ if (flags & USBD_SHORT_XFER_OK)
+ status |= UHCI_TD_SPD;
+ usb_syncmem(dma, 0, len,
+ isread ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+
+ std = prev = NULL;
+ for (i = 0; i < uxfer->ux_nstd; i++, prev = std) {
+ int l = len;
+ std = uxfer->ux_stds[i];
+ if (l > maxp)
+ l = maxp;
+ if (l == 0 && !(flags & USBD_FORCE_SHORT_XFER))
+ break;
+
+ if (prev) {
+ prev->link.std = std;
+ prev->td.td_link = htole32(
+ std->physaddr | UHCI_PTR_VF | UHCI_PTR_TD
+ );
+ usb_syncmem(&prev->dma, prev->offs, sizeof(prev->td),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ }
+
+ int addr __diagused = xfer->ux_pipe->up_dev->ud_addr;
+ int endpt __diagused = xfer->ux_pipe->up_endpoint->ue_edesc->bEndpointAddress;
+ KASSERTMSG(UHCI_TD_GET_ENDPT(le32toh(std->td.td_token)) == UE_GET_ADDR(endpt),
+ "%" __PRIuBIT " vs %d (0x%08x) in %p",
+ UHCI_TD_GET_ENDPT(le32toh(std->td.td_token)),
+ UE_GET_ADDR(endpt), le32toh(std->td.td_token), std);
+ KASSERTMSG(UHCI_TD_GET_DEVADDR(le32toh(std->td.td_token)) == addr,
+ "%" __PRIuBIT " vs %d (0x%08x) in %p",
+ UHCI_TD_GET_DEVADDR(le32toh(std->td.td_token)), addr,
+ le32toh(std->td.td_token), std);
+
+ usb_syncmem(&std->dma, std->offs, sizeof(std->td),
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+
+ std->td.td_status = htole32(status);
+ std->td.td_token &= ~htole32(
+ UHCI_TD_PID_MASK |
+ UHCI_TD_DT_MASK |
+ UHCI_TD_MAXLEN_MASK
+ );
+ std->td.td_token |= htole32(
+ UHCI_TD_SET_PID(isread ? UHCI_TD_PID_IN : UHCI_TD_PID_OUT) |
+ UHCI_TD_SET_DT(tog) |
+ UHCI_TD_SET_MAXLEN(l)
+ );
+ std->td.td_link &= ~htole32(UHCI_PTR_T);
+
+ usb_syncmem(&std->dma, std->offs, sizeof(std->td),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ tog ^= 1;
+
+ len -= l;
+ if (len == 0)
+ break;
+ }
+ KASSERTMSG(len == 0, "xfer %p alen %d len %d mps %d ux_nqtd %zu i %zu",
+ xfer, length, len, maxp, uxfer->ux_nstd, i);
+
+ if (i < uxfer->ux_nstd) {
+ /*
+ * The full allocation chain wasn't used, so we need to
+ * terminate it.
+ */
+ std->link.std = NULL;
+ std->td.td_link = htole32(UHCI_PTR_T);
+ usb_syncmem(&std->dma, std->offs, sizeof(std->td),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ }
+ *lstd = std;
+ *toggle = tog;
+}
+
void
uhci_device_clear_toggle(struct usbd_pipe *pipe)
{
@@ -2012,6 +2222,56 @@ uhci_noop(struct usbd_pipe *pipe)
{
}
+int
+uhci_device_bulk_init(struct usbd_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_XFER2SC(xfer);
+ struct uhci_xfer *uxfer = UHCI_XFER2UXFER(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;
+
+
+ UHCIHIST_FUNC(); UHCIHIST_CALLED();
+ DPRINTFN(3, "xfer=%p len=%d flags=%d", xfer, len, xfer->ux_flags, 0);
+
+ if (sc->sc_dying)
+ return USBD_IOERROR;
+
+ KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
+
+ uxfer->ux_type = UX_BULK;
+ err = uhci_alloc_std_chain(sc, xfer, len, isread, &uxfer->ux_stdstart,
+ &uxfer->ux_stdend);
+ if (err)
+ return err;
+
+#ifdef UHCI_DEBUG
+ if (uhcidebug >= 10) {
+ DPRINTF("--- dump start ---", 0, 0, 0, 0);
+ uhci_dump_tds(uxfer->ux_stdstart);
+ DPRINTF("--- dump end ---", 0, 0, 0, 0);
+ }
+#endif
+
+ return 0;
+}
+
+Static void
+uhci_device_bulk_fini(struct usbd_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_XFER2SC(xfer);
+ struct uhci_xfer *ux = UHCI_XFER2UXFER(xfer);
+
+ KASSERT(ux->ux_type == UX_BULK);
+
+ uhci_free_stds(sc, ux);
+ if (ux->ux_nstd)
+ kmem_free(ux->ux_stds, sizeof(uhci_soft_td_t *) * ux->ux_nstd);
+}
+
usbd_status
uhci_device_bulk_transfer(struct usbd_xfer *xfer)
{
@@ -2040,54 +2300,48 @@ uhci_device_bulk_start(struct usbd_xfer
uhci_softc_t *sc = UHCI_XFER2SC(xfer);
uhci_soft_td_t *data, *dataend;
uhci_soft_qh_t *sqh;
- usbd_status err;
- int len, isread, endpt;
+ int len;
+ int endpt;
+ int isread;
UHCIHIST_FUNC(); UHCIHIST_CALLED();
- DPRINTFN(3, "xfer=%p len=%d flags=%d ux=%p",
- xfer, xfer->ux_length, xfer->ux_flags, ux);
+ DPRINTFN(3, "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 = upipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
isread = UE_GET_DIR(endpt) == UE_DIR_IN;
sqh = upipe->bulk.sqh;
- upipe->bulk.isread = isread;
- upipe->bulk.length = len;
+ /* Take lock here to protect nexttoggle */
+ mutex_enter(&sc->sc_lock);
- err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->ux_flags,
- &xfer->ux_dmabuf, &data, &dataend);
- if (err) {
- mutex_exit(&sc->sc_lock);
- return err;
- }
+ uhci_reset_std_chain(sc, xfer, len, isread, &upipe->nexttoggle,
+ ux->ux_stdstart, &dataend);
+
+ data = ux->ux_stdstart;
+ ux->ux_stdend = dataend;
dataend->td.td_status |= htole32(UHCI_TD_IOC);
usb_syncmem(&dataend->dma,
dataend->offs + offsetof(uhci_td_t, td_status),
sizeof(dataend->td.td_status),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
-
#ifdef UHCI_DEBUG
- if (uhcidebug >= 8) {
+ if (uhcidebug >= 10) {
DPRINTF("--- dump start ---", 0, 0, 0, 0);
- DPRINTFN(8, "data(1)", 0, 0, 0, 0);
- uhci_dump_tds(data);
+ DPRINTFN(10, "before transfer", 0, 0, 0, 0);
+ uhci_dump_tds(ux->ux_stdstart);
DPRINTF("--- dump end ---", 0, 0, 0, 0);
}
#endif
- /* Set up interrupt info. */
- ux->ux_stdstart = data;
- ux->ux_stdend = dataend;
-
KASSERT(ux->ux_isdone);
#ifdef DIAGNOSTIC
ux->ux_isdone = false;
@@ -2105,20 +2359,11 @@ uhci_device_bulk_start(struct usbd_xfer
uhci_timeout, xfer);
}
xfer->ux_status = USBD_IN_PROGRESS;
-
-#ifdef UHCI_DEBUG
- if (uhcidebug >= 10) {
- DPRINTF("--- dump start ---", 0, 0, 0, 0);
- DPRINTFN(10, "data(2)", 0, 0, 0, 0);
- uhci_dump_tds(data);
- DPRINTF("--- dump end ---", 0, 0, 0, 0);
- }
-#endif
+ mutex_exit(&sc->sc_lock);
if (sc->sc_bus.ub_usepolling)
uhci_waitintr(sc, xfer);
- mutex_exit(&sc->sc_lock);
return USBD_IN_PROGRESS;
}
@@ -2250,48 +2495,303 @@ uhci_device_bulk_close(struct usbd_pipe
pipe->up_endpoint->ue_toggle = upipe->nexttoggle;
}
-usbd_status
-uhci_device_ctrl_transfer(struct usbd_xfer *xfer)
+int
+uhci_device_ctrl_init(struct usbd_xfer *xfer)
{
- uhci_softc_t *sc = UHCI_XFER2SC(xfer);
+ struct uhci_xfer *uxfer = UHCI_XFER2UXFER(xfer);
+ struct uhci_pipe *upipe = UHCI_PIPE2UPIPE(xfer->ux_pipe);
+ usb_device_request_t *req = &xfer->ux_request;
+ struct usbd_device *dev = upipe->pipe.up_dev;
+ uhci_softc_t *sc = dev->ud_bus->ub_hcpriv;
+ int addr = dev->ud_addr;
+ int endpt = upipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
+ uhci_soft_td_t *setup, *data, *stat, *next, *dataend;
+ int len;
+ uint32_t ls;
usbd_status err;
+ int isread;
- /* Insert last in queue. */
- mutex_enter(&sc->sc_lock);
- err = usb_insert_transfer(xfer);
- mutex_exit(&sc->sc_lock);
- if (err)
- return err;
-
- /*
- * Pipe isn't running (otherwise err would be USBD_INPROG),
- * so start it first.
- */
- return uhci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue));
-}
+ UHCIHIST_FUNC(); UHCIHIST_CALLED();
+ DPRINTFN(3, "len=%d, addr=%d, endpt=%d", xfer->ux_bufsize,
+ dev->ud_addr, endpt, 0);
-usbd_status
-uhci_device_ctrl_start(struct usbd_xfer *xfer)
-{
- uhci_softc_t *sc = UHCI_XFER2SC(xfer);
- usbd_status err;
+ ls = dev->ud_speed == USB_SPEED_LOW ? UHCI_TD_LS : 0;
+ isread = req->bmRequestType & UT_READ;
+ len = xfer->ux_bufsize;
- if (sc->sc_dying)
- return USBD_IOERROR;
+ uxfer->ux_type = UX_CTRL;
+ setup = upipe->ctrl.setup;
+ stat = upipe->ctrl.stat;
- KASSERT(xfer->ux_rqflags & URQ_REQUEST);
+ /* Set up data transaction */
+ if (len != 0) {
+ err = uhci_alloc_std_chain(sc, xfer, len, isread, &data,
+ &dataend);
+ if (err)
+ return err;
+ next = data;
+ dataend->link.std = stat;
+ dataend->td.td_link = htole32(stat->physaddr | UHCI_PTR_TD);
+ } else {
+ next = stat;
+ }
- mutex_enter(&sc->sc_lock);
- err = uhci_device_request(xfer);
- mutex_exit(&sc->sc_lock);
- if (err)
- return err;
+ setup->link.std = next;
+ setup->td.td_link = htole32(next->physaddr | UHCI_PTR_TD);
+ setup->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls |
+ UHCI_TD_ACTIVE);
+ setup->td.td_token = htole32(UHCI_TD_SETUP(sizeof(*req), endpt, addr));
+ setup->td.td_buffer = htole32(DMAADDR(&upipe->ctrl.reqdma, 0));
- if (sc->sc_bus.ub_usepolling)
+ stat->link.std = NULL;
+ stat->td.td_link = htole32(UHCI_PTR_T);
+ stat->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls |
+ UHCI_TD_ACTIVE | UHCI_TD_IOC);
+ stat->td.td_token =
+ htole32(isread ? UHCI_TD_OUT(0, endpt, addr, 1) :
+ UHCI_TD_IN (0, endpt, addr, 1));
+ stat->td.td_buffer = htole32(0);
+
+ DPRINTFN(10, "--- dump start ---", 0, 0, 0, 0);
+#ifdef UHCI_DEBUG
+ if (uhcidebug >= 10) {
+ DPRINTFN(10, "before transfer", 0, 0, 0, 0);
+ uhci_dump_tds(setup);
+ }
+#endif
+ DPRINTFN(10, "--- dump end ---", 0, 0, 0, 0);
+
+ /* Set up interrupt info. */
+ uxfer->ux_setup = setup;
+ uxfer->ux_data = data;
+ uxfer->ux_stat = stat;
+
+ return 0;
+}
+
+Static void
+uhci_device_ctrl_fini(struct usbd_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_XFER2SC(xfer);
+ struct uhci_xfer *ux = UHCI_XFER2UXFER(xfer);
+
+ KASSERT(ux->ux_type == UX_CTRL);
+
+ uhci_free_stds(sc, ux);
+ if (ux->ux_nstd)
+ kmem_free(ux->ux_stds, sizeof(uhci_soft_td_t *) * ux->ux_nstd);
+}
+
+usbd_status
+uhci_device_ctrl_transfer(struct usbd_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_XFER2SC(xfer);
+ usbd_status err;
+
+ /* Insert last in queue. */
+ mutex_enter(&sc->sc_lock);
+ err = usb_insert_transfer(xfer);
+ mutex_exit(&sc->sc_lock);
+ if (err)
+ return err;
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return uhci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue));
+}
+
+usbd_status
+uhci_device_ctrl_start(struct usbd_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_XFER2SC(xfer);
+ struct uhci_xfer *uxfer = UHCI_XFER2UXFER(xfer);
+ struct uhci_pipe *upipe = UHCI_PIPE2UPIPE(xfer->ux_pipe);
+ usb_device_request_t *req = &xfer->ux_request;
+ struct usbd_device *dev = upipe->pipe.up_dev;
+ int addr = dev->ud_addr;
+ int endpt = upipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
+ uhci_soft_td_t *setup, *stat, *next, *dataend;
+ uhci_soft_qh_t *sqh;
+ int len;
+ uint32_t ls;
+ int isread;
+
+ UHCIHIST_FUNC(); UHCIHIST_CALLED();
+
+ if (sc->sc_dying)
+ return USBD_IOERROR;
+
+ KASSERT(xfer->ux_rqflags & URQ_REQUEST);
+
+ DPRINTFN(3, "type=0x%02x, request=0x%02x, "
+ "wValue=0x%04x, wIndex=0x%04x",
+ req->bmRequestType, req->bRequest, UGETW(req->wValue),
+ UGETW(req->wIndex));
+ DPRINTFN(3, "len=%d, addr=%d, endpt=%d",
+ UGETW(req->wLength), dev->ud_addr, endpt, 0);
+
+ ls = dev->ud_speed == USB_SPEED_LOW ? UHCI_TD_LS : 0;
+ isread = req->bmRequestType & UT_READ;
+ len = UGETW(req->wLength);
+
+ setup = upipe->ctrl.setup;
+ stat = upipe->ctrl.stat;
+ sqh = upipe->ctrl.sqh;
+
+ memcpy(KERNADDR(&upipe->ctrl.reqdma, 0), req, sizeof(*req));
+ usb_syncmem(&upipe->ctrl.reqdma, 0, sizeof(*req), BUS_DMASYNC_PREWRITE);
+
+ mutex_enter(&sc->sc_lock);
+
+ /* Set up data transaction */
+ if (len != 0) {
+ upipe->nexttoggle = 1;
+ next = uxfer->ux_data;
+ uhci_reset_std_chain(sc, xfer, len, isread,
+ &upipe->nexttoggle, next, &dataend);
+ dataend->link.std = stat;
+ dataend->td.td_link = htole32(stat->physaddr | UHCI_PTR_TD);
+ usb_syncmem(&dataend->dma,
+ dataend->offs + offsetof(uhci_td_t, td_link),
+ sizeof(dataend->td.td_link),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ } else {
+ next = stat;
+ }
+
+ setup->link.std = next;
+ setup->td.td_link = htole32(next->physaddr | UHCI_PTR_TD);
+ setup->td.td_status |= htole32(
+ UHCI_TD_SET_ERRCNT(3) |
+ ls |
+ UHCI_TD_ACTIVE
+ );
+ setup->td.td_token &= ~htole32(UHCI_TD_MAXLEN_MASK);
+ setup->td.td_token |= htole32(UHCI_TD_SET_MAXLEN(sizeof(*req)));
+ usb_syncmem(&setup->dma, setup->offs, sizeof(setup->td),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ stat->link.std = NULL;
+ stat->td.td_link = htole32(UHCI_PTR_T);
+ stat->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls |
+ UHCI_TD_ACTIVE | UHCI_TD_IOC);
+ stat->td.td_token =
+ htole32(isread ? UHCI_TD_OUT(0, endpt, addr, 1) :
+ UHCI_TD_IN (0, endpt, addr, 1));
+ stat->td.td_buffer = htole32(0);
+ usb_syncmem(&stat->dma, stat->offs, sizeof(stat->td),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+#ifdef UHCI_DEBUG
+ if (uhcidebug >= 10) {
+ DPRINTF("--- dump start ---", 0, 0, 0, 0);
+ DPRINTF("before transfer", 0, 0, 0, 0);
+ uhci_dump_tds(setup);
+ DPRINTF("--- dump end ---", 0, 0, 0, 0);
+ }
+#endif
+
+ /* Set up interrupt info. */
+ uxfer->ux_setup = setup;
+ uxfer->ux_stat = stat;
+ KASSERT(uxfer->ux_isdone);
+#ifdef DIAGNOSTIC
+ uxfer->ux_isdone = false;
+#endif
+
+ sqh->elink = setup;
+ sqh->qh.qh_elink = htole32(setup->physaddr | UHCI_PTR_TD);
+ /* uhci_add_?s_ctrl() will do usb_syncmem(sqh) */
+
+ if (dev->ud_speed == USB_SPEED_LOW)
+ uhci_add_ls_ctrl(sc, sqh);
+ else
+ uhci_add_hs_ctrl(sc, sqh);
+ uhci_add_intr_info(sc, uxfer);
+#ifdef UHCI_DEBUG
+ if (uhcidebug >= 12) {
+ uhci_soft_td_t *std;
+ uhci_soft_qh_t *xqh;
+ uhci_soft_qh_t *sxqh;
+ int maxqh = 0;
+ uhci_physaddr_t link;
+ DPRINTFN(12, "--- dump start ---", 0, 0, 0, 0);
+ DPRINTFN(12, "follow from [0]", 0, 0, 0, 0);
+ for (std = sc->sc_vframes[0].htd, link = 0;
+ (link & UHCI_PTR_QH) == 0;
+ std = std->link.std) {
+ link = le32toh(std->td.td_link);
+ uhci_dump_td(std);
+ }
+ sxqh = (uhci_soft_qh_t *)std;
+ uhci_dump_qh(sxqh);
+ for (xqh = sxqh;
+ xqh != NULL;
+ xqh = (maxqh++ == 5 || xqh->hlink == sxqh ||
+ xqh->hlink == xqh ? NULL : xqh->hlink)) {
+ uhci_dump_qh(xqh);
+ }
+ DPRINTFN(12, "Enqueued QH:", 0, 0, 0, 0);
+ uhci_dump_qh(sqh);
+ uhci_dump_tds(sqh->elink);
+ DPRINTF("--- dump end ---", 0, 0, 0, 0);
+ }
+#endif
+ if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) {
+ callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout),
+ uhci_timeout, xfer);
+ }
+ xfer->ux_status = USBD_IN_PROGRESS;
+ mutex_exit(&sc->sc_lock);
+
+ if (sc->sc_bus.ub_usepolling)
uhci_waitintr(sc, xfer);
+
return USBD_IN_PROGRESS;
}
+int
+uhci_device_intr_init(struct usbd_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_XFER2SC(xfer);
+ struct uhci_xfer *ux = UHCI_XFER2UXFER(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;
+
+ UHCIHIST_FUNC(); UHCIHIST_CALLED();
+
+ DPRINTFN(3, "xfer=%p len=%d flags=%d", xfer, xfer->ux_length,
+ xfer->ux_flags, 0);
+
+ KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
+ KASSERT(len != 0);
+
+ ux->ux_type = UX_INTR;
+ ux->ux_nstd = 0;
+ err = uhci_alloc_std_chain(sc, xfer, len, isread,
+ &ux->ux_stdstart, &ux->ux_stdend);
+
+ return err;
+}
+
+Static void
+uhci_device_intr_fini(struct usbd_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_XFER2SC(xfer);
+ struct uhci_xfer *ux = UHCI_XFER2UXFER(xfer);
+
+ KASSERT(ux->ux_type == UX_INTR);
+
+ uhci_free_stds(sc, ux);
+ if (ux->ux_nstd)
+ kmem_free(ux->ux_stds, sizeof(uhci_soft_td_t *) * ux->ux_nstd);
+}
+
usbd_status
uhci_device_intr_transfer(struct usbd_xfer *xfer)
{
@@ -2320,7 +2820,6 @@ uhci_device_intr_start(struct usbd_xfer
uhci_softc_t *sc = UHCI_XFER2SC(xfer);
uhci_soft_td_t *data, *dataend;
uhci_soft_qh_t *sqh;
- usbd_status err;
int isread, endpt;
int i;
@@ -2329,31 +2828,33 @@ uhci_device_intr_start(struct usbd_xfer
UHCIHIST_FUNC(); UHCIHIST_CALLED();
- DPRINTFN(3, "xfer=%p len=%d flags=%d",
- xfer, xfer->ux_length, xfer->ux_flags, 0);
+ DPRINTFN(3, "xfer=%p len=%d flags=%d", xfer, xfer->ux_length,
+ xfer->ux_flags, 0);
KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
-
- mutex_enter(&sc->sc_lock);
+ KASSERT(xfer->ux_length <= xfer->ux_bufsize);
endpt = upipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
isread = UE_GET_DIR(endpt) == UE_DIR_IN;
- upipe->intr.isread = isread;
+ data = ux->ux_stdstart;
- err = uhci_alloc_std_chain(upipe, sc, xfer->ux_length, isread,
- xfer->ux_flags, &xfer->ux_dmabuf, &data,
- &dataend);
- if (err) {
- mutex_exit(&sc->sc_lock);
- return err;
- }
+ KASSERT(ux->ux_isdone);
+#ifdef DIAGNOSTIC
+ ux->ux_isdone = false;
+#endif
+
+ /* Take lock to protect nexttoggle */
+ mutex_enter(&sc->sc_lock);
+ uhci_reset_std_chain(sc, xfer, xfer->ux_length, isread,
+ &upipe->nexttoggle, data, &dataend);
dataend->td.td_status |= htole32(UHCI_TD_IOC);
usb_syncmem(&dataend->dma,
dataend->offs + offsetof(uhci_td_t, td_status),
sizeof(dataend->td.td_status),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ ux->ux_stdend = dataend;
#ifdef UHCI_DEBUG
if (uhcidebug >= 10) {
@@ -2364,14 +2865,6 @@ uhci_device_intr_start(struct usbd_xfer
}
#endif
- /* Set up interrupt info. */
- ux->ux_stdstart = data;
- ux->ux_stdend = dataend;
- KASSERT(ux->ux_isdone);
-#ifdef DIAGNOSTIC
- ux->ux_isdone = false;
-#endif
-
DPRINTFN(10, "qhs[0]=%p", upipe->intr.qhs[0], 0, 0, 0);
for (i = 0; i < upipe->intr.npoll; i++) {
sqh = upipe->intr.qhs[i];
@@ -2414,6 +2907,13 @@ uhci_device_ctrl_abort(struct usbd_xfer
void
uhci_device_ctrl_close(struct usbd_pipe *pipe)
{
+ uhci_softc_t *sc = UHCI_PIPE2SC(pipe);
+ struct uhci_pipe *upipe = UHCI_PIPE2UPIPE(pipe);
+
+ uhci_free_sqh(sc, upipe->ctrl.sqh);
+ uhci_free_std_locked(sc, upipe->ctrl.setup);
+ uhci_free_std_locked(sc, upipe->ctrl.stat);
+
}
/* Abort a device interrupt request. */
@@ -2455,150 +2955,27 @@ uhci_device_intr_close(struct usbd_pipe
for (i = 0; i < npoll; i++)
uhci_free_sqh(sc, upipe->intr.qhs[i]);
kmem_free(upipe->intr.qhs, npoll * sizeof(uhci_soft_qh_t *));
-
- /* XXX free other resources */
}
-usbd_status
-uhci_device_request(struct usbd_xfer *xfer)
+int
+uhci_device_isoc_init(struct usbd_xfer *xfer)
{
- struct uhci_xfer *uxfer = UHCI_XFER2UXFER(xfer);
- struct uhci_pipe *upipe = UHCI_PIPE2UPIPE(xfer->ux_pipe);
- usb_device_request_t *req = &xfer->ux_request;
- struct usbd_device *dev = upipe->pipe.up_dev;
- uhci_softc_t *sc = UHCI_XFER2SC(xfer);
- int addr = dev->ud_addr;
- int endpt = upipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
- uhci_soft_td_t *setup, *data, *stat, *next, *dataend;
- uhci_soft_qh_t *sqh;
- int len;
- uint32_t ls;
- usbd_status err;
- int isread;
-
- KASSERT(mutex_owned(&sc->sc_lock));
-
- UHCIHIST_FUNC(); UHCIHIST_CALLED();
- DPRINTFN(3, "type=0x%02x, request=0x%02x, "
- "wValue=0x%04x, wIndex=0x%04x",
- req->bmRequestType, req->bRequest, UGETW(req->wValue),
- UGETW(req->wIndex));
- DPRINTFN(3, "len=%d, addr=%d, endpt=%d",
- UGETW(req->wLength), dev->ud_addr, endpt, 0);
-
- ls = dev->ud_speed == USB_SPEED_LOW ? UHCI_TD_LS : 0;
- isread = req->bmRequestType & UT_READ;
- len = UGETW(req->wLength);
-
- setup = upipe->ctrl.setup;
- stat = upipe->ctrl.stat;
- sqh = upipe->ctrl.sqh;
-
- /* Set up data transaction */
- if (len != 0) {
- upipe->nexttoggle = 1;
- err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->ux_flags,
- &xfer->ux_dmabuf, &data, &dataend);
- if (err)
- return err;
- next = data;
- dataend->link.std = stat;
- dataend->td.td_link = htole32(stat->physaddr | UHCI_PTR_TD);
- usb_syncmem(&dataend->dma,
- dataend->offs + offsetof(uhci_td_t, td_link),
- sizeof(dataend->td.td_link),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
- } else {
- next = stat;
- }
- upipe->ctrl.length = len;
-
- memcpy(KERNADDR(&upipe->ctrl.reqdma, 0), req, sizeof(*req));
- usb_syncmem(&upipe->ctrl.reqdma, 0, sizeof(*req), BUS_DMASYNC_PREWRITE);
-
- setup->link.std = next;
- setup->td.td_link = htole32(next->physaddr | UHCI_PTR_TD);
- setup->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls |
- UHCI_TD_ACTIVE);
- setup->td.td_token = htole32(UHCI_TD_SETUP(sizeof(*req), endpt, addr));
- setup->td.td_buffer = htole32(DMAADDR(&upipe->ctrl.reqdma, 0));
- usb_syncmem(&setup->dma, setup->offs, sizeof(setup->td),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
-
- stat->link.std = NULL;
- stat->td.td_link = htole32(UHCI_PTR_T);
- stat->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls |
- UHCI_TD_ACTIVE | UHCI_TD_IOC);
- stat->td.td_token =
- htole32(isread ? UHCI_TD_OUT(0, endpt, addr, 1) :
- UHCI_TD_IN (0, endpt, addr, 1));
- stat->td.td_buffer = htole32(0);
- usb_syncmem(&stat->dma, stat->offs, sizeof(stat->td),
- BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
-
-#ifdef UHCI_DEBUG
- if (uhcidebug >= 10) {
- DPRINTF("--- dump start ---", 0, 0, 0, 0);
- DPRINTF("before transfer", 0, 0, 0, 0);
- uhci_dump_tds(setup);
- DPRINTF("--- dump end ---", 0, 0, 0, 0);
- }
-#endif
-
- /* Set up interrupt info. */
- uxfer->ux_stdstart = setup;
- uxfer->ux_stdend = stat;
- KASSERT(uxfer->ux_isdone);
-#ifdef DIAGNOSTIC
- uxfer->ux_isdone = false;
-#endif
+ struct uhci_xfer *ux = UHCI_XFER2UXFER(xfer);
- sqh->elink = setup;
- sqh->qh.qh_elink = htole32(setup->physaddr | UHCI_PTR_TD);
- /* uhci_add_?s_ctrl() will do usb_syncmem(sqh) */
+ KASSERT(!(xfer->ux_rqflags & URQ_REQUEST));
+ KASSERT(xfer->ux_nframes != 0);
+ KASSERT(ux->ux_isdone);
- if (dev->ud_speed == USB_SPEED_LOW)
- uhci_add_ls_ctrl(sc, sqh);
- else
- uhci_add_hs_ctrl(sc, sqh);
- uhci_add_intr_info(sc, uxfer);
-#ifdef UHCI_DEBUG
- if (uhcidebug >= 12) {
- uhci_soft_td_t *std;
- uhci_soft_qh_t *xqh;
- uhci_soft_qh_t *sxqh;
- int maxqh = 0;
- uhci_physaddr_t link;
+ ux->ux_type = UX_ISOC;
+ return 0;
+}
- DPRINTF("--- dump start ---", 0, 0, 0, 0);
- DPRINTFN(12, "follow from [0]", 0, 0, 0, 0);
- for (std = sc->sc_vframes[0].htd, link = 0;
- (link & UHCI_PTR_QH) == 0;
- std = std->link.std) {
- link = le32toh(std->td.td_link);
- uhci_dump_td(std);
- }
- sxqh = (uhci_soft_qh_t *)std;
- uhci_dump_qh(sxqh);
- for (xqh = sxqh;
- xqh != NULL;
- xqh = (maxqh++ == 5 || xqh->hlink == sxqh ||
- xqh->hlink == xqh ? NULL : xqh->hlink)) {
- uhci_dump_qh(xqh);
- }
- DPRINTFN(12, "Enqueued QH:", 0, 0, 0, 0);
- uhci_dump_qh(sqh);
- uhci_dump_tds(sqh->elink);
- DPRINTF("--- dump end ---", 0, 0, 0, 0);
- }
-#endif
- if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) {
- callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout),
- uhci_timeout, xfer);
- }
- xfer->ux_status = USBD_IN_PROGRESS;
+Static void
+uhci_device_isoc_fini(struct usbd_xfer *xfer)
+{
+ struct uhci_xfer *ux = UHCI_XFER2UXFER(xfer);
- return USBD_NORMAL_COMPLETION;
+ KASSERT(ux->ux_type == UX_ISOC);
}
usbd_status
@@ -2634,8 +3011,8 @@ uhci_device_isoc_transfer(struct usbd_xf
void
uhci_device_isoc_enter(struct usbd_xfer *xfer)
{
- struct uhci_pipe *upipe = UHCI_PIPE2UPIPE(xfer->ux_pipe);
uhci_softc_t *sc = UHCI_XFER2SC(xfer);
+ struct uhci_pipe *upipe = UHCI_PIPE2UPIPE(xfer->ux_pipe);
struct isoc *isoc = &upipe->isoc;
uhci_soft_td_t *std;
uint32_t buf, len, status, offs;
@@ -2870,7 +3247,7 @@ uhci_device_isoc_close(struct usbd_pipe
vstd->offs + offsetof(uhci_td_t, td_link),
sizeof(vstd->td.td_link),
BUS_DMASYNC_PREWRITE);
- uhci_free_std(sc, std);
+ uhci_free_std_locked(sc, std);
}
kmem_free(isoc->stds, UHCI_VFRAMELIST_COUNT * sizeof(uhci_soft_td_t *));
@@ -2890,17 +3267,15 @@ uhci_setup_isoc(struct usbd_pipe *pipe)
int i;
isoc = &upipe->isoc;
- isoc->stds = kmem_alloc(UHCI_VFRAMELIST_COUNT *
- sizeof(uhci_soft_td_t *),
- KM_SLEEP);
+
+ isoc->stds = kmem_alloc(
+ UHCI_VFRAMELIST_COUNT * sizeof(uhci_soft_td_t *), KM_SLEEP);
if (isoc->stds == NULL)
return USBD_NOMEM;
token = rd ? UHCI_TD_IN (0, endpt, addr, 0) :
UHCI_TD_OUT(0, endpt, addr, 0);
- mutex_enter(&sc->sc_lock);
-
/* Allocate the TDs and mark as inactive; */
for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) {
std = uhci_alloc_std(sc);
@@ -2913,6 +3288,8 @@ uhci_setup_isoc(struct usbd_pipe *pipe)
isoc->stds[i] = std;
}
+ mutex_enter(&sc->sc_lock);
+
/* Insert TDs into schedule. */
for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) {
std = isoc->stds[i];
@@ -2944,7 +3321,6 @@ uhci_setup_isoc(struct usbd_pipe *pipe)
bad:
while (--i >= 0)
uhci_free_std(sc, isoc->stds[i]);
- mutex_exit(&sc->sc_lock);
kmem_free(isoc->stds, UHCI_VFRAMELIST_COUNT * sizeof(uhci_soft_td_t *));
return USBD_NOMEM;
}
@@ -3006,7 +3382,7 @@ uhci_device_intr_done(struct usbd_xfer *
struct uhci_xfer *ux = UHCI_XFER2UXFER(xfer);
struct uhci_pipe *upipe = UHCI_PIPE2UPIPE(xfer->ux_pipe);
uhci_soft_qh_t *sqh;
- int i, npoll, isread;
+ int i, npoll;
UHCIHIST_FUNC(); UHCIHIST_CALLED();
DPRINTFN(5, "length=%d", xfer->ux_actlen, 0, 0, 0);
@@ -3023,22 +3399,21 @@ uhci_device_intr_done(struct usbd_xfer *
sizeof(sqh->qh.qh_elink),
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
}
- uhci_free_std_chain(sc, ux->ux_stdstart, NULL);
- isread = UE_GET_DIR(upipe->pipe.up_endpoint->ue_edesc->bEndpointAddress) == UE_DIR_IN;
- usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length,
- isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
-
- /* XXX Wasteful. */
if (xfer->ux_pipe->up_repeat) {
uhci_soft_td_t *data, *dataend;
+ int endpt = upipe->pipe.up_endpoint->ue_edesc->bEndpointAddress;
+ int isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ KASSERT(ux->ux_isdone);
+#ifdef DIAGNOSTIC
+ ux->ux_isdone = false;
+#endif
DPRINTFN(5, "re-queueing", 0, 0, 0, 0);
- /* This alloc cannot fail since we freed the chain above. */
- uhci_alloc_std_chain(upipe, sc, xfer->ux_length,
- upipe->intr.isread, xfer->ux_flags,
- &xfer->ux_dmabuf, &data, &dataend);
+ data = ux->ux_stdstart;
+ uhci_reset_std_chain(sc, xfer, xfer->ux_length, isread,
+ &upipe->nexttoggle, data, &dataend);
dataend->td.td_status |= htole32(UHCI_TD_IOC);
usb_syncmem(&dataend->dma,
dataend->offs + offsetof(uhci_td_t, td_status),
@@ -3054,12 +3429,7 @@ uhci_device_intr_done(struct usbd_xfer *
}
#endif
- ux->ux_stdstart = data;
ux->ux_stdend = dataend;
- KASSERT(ux->ux_isdone);
-#ifdef DIAGNOSTIC
- ux->ux_isdone = false;
-#endif
for (i = 0; i < npoll; i++) {
sqh = upipe->intr.qhs[i];
sqh->elink = data;
@@ -3104,9 +3474,6 @@ uhci_device_ctrl_done(struct usbd_xfer *
else
uhci_remove_hs_ctrl(sc, upipe->ctrl.sqh);
- if (upipe->ctrl.length != 0)
- uhci_free_std_chain(sc, ux->ux_stdstart->link.std, ux->ux_stdend);
-
if (len) {
usb_syncmem(&xfer->ux_dmabuf, 0, len,
isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
@@ -3141,7 +3508,6 @@ uhci_device_bulk_done(struct usbd_xfer *
uhci_remove_bulk(sc, upipe->bulk.sqh);
- uhci_free_std_chain(sc, ux->ux_stdstart, NULL);
if (xfer->ux_length) {
usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length,
isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
@@ -3256,7 +3622,6 @@ uhci_device_setintr(uhci_softc_t *sc, st
}
}
DPRINTF("bw=%d offs=%d", bestbw, bestoffs, 0, 0);
- mutex_enter(&sc->sc_lock);
for (i = 0; i < npoll; i++) {
upipe->intr.qhs[i] = sqh = uhci_alloc_sqh(sc);
sqh->elink = NULL;
@@ -3269,6 +3634,7 @@ uhci_device_setintr(uhci_softc_t *sc, st
}
#undef MOD
+ mutex_enter(&sc->sc_lock);
/* Enter QHs into the controller data structures. */
for (i = 0; i < npoll; i++)
uhci_add_intr(sc, upipe->intr.qhs[i]);
Index: src/sys/dev/usb/uhcivar.h
diff -u src/sys/dev/usb/uhcivar.h:1.52.14.15 src/sys/dev/usb/uhcivar.h:1.52.14.16
--- src/sys/dev/usb/uhcivar.h:1.52.14.15 Sun Nov 8 14:59:13 2015
+++ src/sys/dev/usb/uhcivar.h Tue Nov 10 08:44:09 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: uhcivar.h,v 1.52.14.15 2015/11/08 14:59:13 skrll Exp $ */
+/* $NetBSD: uhcivar.h,v 1.52.14.16 2015/11/10 08:44:09 skrll Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -62,8 +62,32 @@ typedef union {
struct uhci_xfer {
struct usbd_xfer ux_xfer;
struct usb_task ux_aborttask;
- uhci_soft_td_t *ux_stdstart;
- uhci_soft_td_t *ux_stdend;
+ enum {
+ UX_NONE, UX_CTRL, UX_BULK, UX_INTR, UX_ISOC
+ } ux_type;
+ union {
+ /* ctrl/bulk/intr */
+ struct {
+ uhci_soft_td_t **ux_stds;
+ size_t ux_nstd;
+ };
+ /* isoc */
+ bool ux_isrunning;
+ };
+ union {
+ /* ctrl */
+ struct {
+ uhci_soft_td_t *ux_setup;
+ uhci_soft_td_t *ux_data;
+ uhci_soft_td_t *ux_stat;
+ };
+ /* bulk/intr/isoc */
+ struct {
+ uhci_soft_td_t *ux_stdstart;
+ uhci_soft_td_t *ux_stdend;
+ };
+ };
+
TAILQ_ENTRY(uhci_xfer) ux_list;
int ux_curframe;
bool ux_isdone; /* used only when DIAGNOSTIC is defined */