Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/dev/usb xhci(4): initial support for Isochronous pipes
details: https://anonhg.NetBSD.org/src/rev/351a0c2afe02
branches: trunk
changeset: 1013138:351a0c2afe02
user: jakllsch <jakllsch%NetBSD.org@localhost>
date: Fri Aug 21 20:46:03 2020 +0000
description:
xhci(4): initial support for Isochronous pipes
Needs more cleanup, and more work, particularly transaction scheduling
may need to be examined with bus analyzer (to check if too frequent/
infrequent) or various isoc pipe consumers (to check if too infrequent)
and fixed if incorrect.
Begins to address PR kern/55574.
diffstat:
sys/dev/usb/xhci.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++-
sys/dev/usb/xhcivar.h | 4 +-
2 files changed, 197 insertions(+), 6 deletions(-)
diffs (truncated from 330 to 300 lines):
diff -r da0bc1a5eaf9 -r 351a0c2afe02 sys/dev/usb/xhci.c
--- a/sys/dev/usb/xhci.c Fri Aug 21 20:44:38 2020 +0000
+++ b/sys/dev/usb/xhci.c Fri Aug 21 20:46:03 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: xhci.c,v 1.133 2020/08/21 20:16:39 jakllsch Exp $ */
+/* $NetBSD: xhci.c,v 1.134 2020/08/21 20:46:03 jakllsch Exp $ */
/*
* Copyright (c) 2013 Jonathan A. Kollasch
@@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.133 2020/08/21 20:16:39 jakllsch Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.134 2020/08/21 20:46:03 jakllsch Exp $");
#ifdef _KERNEL_OPT
#include "opt_usb.h"
@@ -129,6 +129,9 @@
struct xhci_pipe {
struct usbd_pipe xp_pipe;
struct usb_task xp_async_task;
+ int16_t xp_isoc_next; /* next frame */
+ uint8_t xp_maxb; /* max burst */
+ uint8_t xp_mult;
};
#define XHCI_COMMAND_RING_TRBS 256
@@ -199,6 +202,12 @@
static void xhci_device_ctrl_close(struct usbd_pipe *);
static void xhci_device_ctrl_done(struct usbd_xfer *);
+static usbd_status xhci_device_isoc_transfer(struct usbd_xfer *);
+static usbd_status xhci_device_isoc_enter(struct usbd_xfer *);
+static void xhci_device_isoc_abort(struct usbd_xfer *);
+static void xhci_device_isoc_close(struct usbd_pipe *);
+static void xhci_device_isoc_done(struct usbd_xfer *);
+
static usbd_status xhci_device_intr_transfer(struct usbd_xfer *);
static usbd_status xhci_device_intr_start(struct usbd_xfer *);
static void xhci_device_intr_abort(struct usbd_xfer *);
@@ -244,7 +253,11 @@
};
static const struct usbd_pipe_methods xhci_device_isoc_methods = {
+ .upm_transfer = xhci_device_isoc_transfer,
+ .upm_abort = xhci_device_isoc_abort,
+ .upm_close = xhci_device_isoc_close,
.upm_cleartoggle = xhci_noop,
+ .upm_done = xhci_device_isoc_done,
};
static const struct usbd_pipe_methods xhci_device_bulk_methods = {
@@ -1595,6 +1608,7 @@
xhci_open(struct usbd_pipe *pipe)
{
struct usbd_device * const dev = pipe->up_dev;
+ struct xhci_pipe * const xpipe = (struct xhci_pipe *)pipe;
struct xhci_softc * const sc = XHCI_BUS2SC(dev->ud_bus);
struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
usb_endpoint_descriptor_t * const ed = pipe->up_endpoint->ue_edesc;
@@ -1638,7 +1652,8 @@
break;
case UE_ISOCHRONOUS:
pipe->up_methods = &xhci_device_isoc_methods;
- return USBD_INVAL;
+ pipe->up_serialise = false;
+ xpipe->xp_isoc_next = -1;
break;
case UE_BULK:
pipe->up_methods = &xhci_device_bulk_methods;
@@ -1997,6 +2012,9 @@
return;
}
+ const uint8_t xfertype =
+ UE_GET_XFERTYPE(xfer->ux_pipe->up_endpoint->ue_edesc->bmAttributes);
+
/* 4.11.5.2 Event Data TRB */
if ((trb_3 & XHCI_TRB_3_ED_BIT) != 0) {
DPRINTFN(14, "transfer Event Data: 0x%016jx 0x%08jx"
@@ -2029,6 +2047,13 @@
* ctrl xfer uses EVENT_DATA, and others do not.
* Thus driver can switch the flow by checking ED bit.
*/
+ if (xfertype == UE_ISOCHRONOUS) {
+ xfer->ux_frlengths[xx->xx_isoc_done] -=
+ XHCI_TRB_2_REM_GET(trb_2);
+ xfer->ux_actlen += xfer->ux_frlengths[xx->xx_isoc_done];
+ if (++xx->xx_isoc_done < xfer->ux_nframes)
+ return;
+ } else
if ((trb_3 & XHCI_TRB_3_ED_BIT) == 0) {
if (xfer->ux_actlen == 0)
xfer->ux_actlen = xfer->ux_length -
@@ -2257,7 +2282,7 @@
XHCIHIST_FUNC(); XHCIHIST_CALLED();
- ntrbs = XHCI_XFER_NTRB;
+ ntrbs = uimax(3, nframes);
const size_t trbsz = sizeof(*xx->xx_trb) * ntrbs;
xx = pool_cache_get(sc->sc_xferpool, PR_WAITOK);
@@ -3360,6 +3385,7 @@
static void
xhci_setup_maxburst(struct usbd_pipe *pipe, uint32_t *cp)
{
+ struct xhci_pipe * const xpipe = (struct xhci_pipe *)pipe;
struct usbd_device *dev = pipe->up_dev;
usb_endpoint_descriptor_t * const ed = pipe->up_endpoint->ue_edesc;
const uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
@@ -3368,6 +3394,7 @@
uint32_t maxb = 0;
uint16_t mps = UGETW(ed->wMaxPacketSize);
uint8_t speed = dev->ud_speed;
+ uint8_t mult = 0;
uint8_t ep;
/* config desc is NULL when opening ep0 */
@@ -3401,6 +3428,7 @@
const usb_endpoint_ss_comp_descriptor_t * esscd =
(const usb_endpoint_ss_comp_descriptor_t *)cdcd;
maxb = esscd->bMaxBurst;
+ mult = UE_GET_SS_ISO_MULT(esscd->bmAttributes);
}
no_cdcd:
@@ -3438,6 +3466,8 @@
}
cp[1] |= XHCI_EPCTX_1_MAXB_SET(maxb);
}
+ xpipe->xp_maxb = maxb + 1;
+ xpipe->xp_mult = mult + 1;
}
/*
@@ -3986,6 +4016,167 @@
/* ------------------ */
/* device isochronous */
+static usbd_status
+xhci_device_isoc_transfer(struct usbd_xfer *xfer)
+{
+ struct xhci_softc * const sc = XHCI_XFER2SC(xfer);
+ usbd_status err;
+
+ XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+ /* Insert last in queue. */
+ mutex_enter(&sc->sc_lock);
+ err = usb_insert_transfer(xfer);
+ mutex_exit(&sc->sc_lock);
+ if (err)
+ return err;
+
+ return xhci_device_isoc_enter(xfer);
+}
+
+static usbd_status
+xhci_device_isoc_enter(struct usbd_xfer *xfer)
+{
+ struct xhci_softc * const sc = XHCI_XFER2SC(xfer);
+ struct xhci_slot * const xs = xfer->ux_pipe->up_dev->ud_hcpriv;
+ const u_int dci = xhci_ep_get_dci(xfer->ux_pipe->up_endpoint->ue_edesc);
+ struct xhci_ring * const tr = xs->xs_xr[dci];
+ struct xhci_xfer * const xx = XHCI_XFER2XXFER(xfer);
+ struct xhci_pipe * const xpipe = (struct xhci_pipe *)xfer->ux_pipe;
+ uint32_t len = xfer->ux_length;
+ usb_dma_t * const dma = &xfer->ux_dmabuf;
+ uint64_t parameter;
+ uint32_t status;
+ uint32_t control;
+ uint32_t mfindex;
+ uint32_t offs;
+ int i, ival;
+ const bool polling = xhci_polling_p(sc);
+ const uint16_t MPS = UGETW(xfer->ux_pipe->up_endpoint->ue_edesc->wMaxPacketSize);
+ const uint16_t mps = UE_GET_SIZE(MPS);
+ const uint8_t maxb = xpipe->xp_maxb;
+ u_int tdpc, tbc, tlbpc;
+
+ XHCIHIST_FUNC();
+ XHCIHIST_CALLARGS("%#jx slot %ju dci %ju",
+ (uintptr_t)xfer, xs->xs_idx, dci, 0);
+
+ if (sc->sc_dying)
+ return USBD_IOERROR;
+
+ KASSERT(xfer->ux_nframes != 0 && xfer->ux_frlengths);
+ KASSERT((xfer->ux_rqflags & URQ_REQUEST) == 0);
+
+ const bool isread = usbd_xfer_isread(xfer);
+ if (xfer->ux_length)
+ usb_syncmem(dma, 0, xfer->ux_length,
+ isread ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+
+ ival = xfer->ux_pipe->up_endpoint->ue_edesc->bInterval;
+ if (ival >= 1 && ival <= 16)
+ ival = 1 << (ival - 1);
+ else
+ ival = 1; /* fake something up */
+
+ if (xpipe->xp_isoc_next == -1) {
+ mfindex = xhci_rt_read_4(sc, XHCI_MFINDEX);
+ DPRINTF("mfindex %jx", (uintmax_t)mfindex, 0, 0, 0);
+ mfindex = XHCI_MFINDEX_GET(mfindex + 1);
+ mfindex /= USB_UFRAMES_PER_FRAME;
+ mfindex += 7; /* 7 frames is max possible IST */
+ xpipe->xp_isoc_next = roundup2(mfindex, ival);
+ }
+
+ offs = 0;
+ for (i = 0; i < xfer->ux_nframes; i++) {
+ len = xfer->ux_frlengths[i];
+
+ tdpc = howmany(len, mps);
+ tbc = howmany(tdpc, maxb) - 1;
+ tlbpc = tdpc % maxb;
+ tlbpc = tlbpc ? tlbpc - 1 : maxb - 1;
+
+ KASSERTMSG(len <= 0x10000, "len %d", len);
+ parameter = DMAADDR(dma, offs);
+ status = XHCI_TRB_2_IRQ_SET(0) |
+ XHCI_TRB_2_TDSZ_SET(0) |
+ XHCI_TRB_2_BYTES_SET(len);
+ control = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ISOCH) |
+ (isread ? XHCI_TRB_3_ISP_BIT : 0) |
+ XHCI_TRB_3_TBC_SET(tbc) |
+ XHCI_TRB_3_TLBPC_SET(tlbpc) |
+ XHCI_TRB_3_IOC_BIT;
+ if (XHCI_HCC_CFC(sc->sc_hcc)) {
+ control |= XHCI_TRB_3_FRID_SET(xpipe->xp_isoc_next);
+#if 0
+ } else if (xpipe->xp_isoc_next == -1) {
+ control |= XHCI_TRB_3_FRID_SET(xpipe->xp_isoc_next);
+#endif
+ } else {
+ control |= XHCI_TRB_3_ISO_SIA_BIT;
+ }
+#if 0
+ if (i != xfer->ux_nframes - 1)
+ control |= XHCI_TRB_3_BEI_BIT;
+#endif
+ xhci_xfer_put_trb(xx, i, parameter, status, control);
+
+ xpipe->xp_isoc_next += ival;
+ offs += len;
+ }
+
+ xx->xx_isoc_done = 0;
+
+ if (!polling)
+ mutex_enter(&tr->xr_lock);
+ xhci_ring_put_xfer(sc, tr, xx, i);
+ if (!polling)
+ mutex_exit(&tr->xr_lock);
+
+ if (!polling)
+ mutex_enter(&sc->sc_lock);
+ xfer->ux_status = USBD_IN_PROGRESS;
+ xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
+ usbd_xfer_schedule_timeout(xfer);
+ if (!polling)
+ mutex_exit(&sc->sc_lock);
+
+ return USBD_IN_PROGRESS;
+}
+
+static void
+xhci_device_isoc_abort(struct usbd_xfer *xfer)
+{
+ XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+ usbd_xfer_abort(xfer);
+}
+
+static void
+xhci_device_isoc_close(struct usbd_pipe *pipe)
+{
+ XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+ xhci_close_pipe(pipe);
+}
+
+static void
+xhci_device_isoc_done(struct usbd_xfer *xfer)
+{
+#ifdef USB_DEBUG
+ struct xhci_slot * const xs = xfer->ux_pipe->up_dev->ud_hcpriv;
+ const u_int dci = xhci_ep_get_dci(xfer->ux_pipe->up_endpoint->ue_edesc);
+#endif
+ const bool isread = usbd_xfer_isread(xfer);
+
+ XHCIHIST_FUNC();
+ XHCIHIST_CALLARGS("%#jx slot %ju dci %ju",
+ (uintptr_t)xfer, xs->xs_idx, dci, 0);
+
+ usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length,
+ isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
Home |
Main Index |
Thread Index |
Old Index