This should fix the problem various people reported when using urtwn(4)
over xhci(4). This problem did not disappear but after iwm(4) got
imported bugs@ reports stopped ;)
It's a screw up in my initial understanding of the documentation. When
a multiple-TRB transfer descriptor span the end of the "ring", the link
TRB must include the Chain bit.
ok?
Index: xhci.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/xhci.c,v
retrieving revision 1.66
diff -u -p -r1.66 xhci.c
--- xhci.c 2 Dec 2015 09:23:23 -0000 1.66
+++ xhci.c 14 Jan 2016 20:47:11 -0000
@@ -91,7 +91,8 @@ int xhci_ring_alloc(struct xhci_softc *,
void xhci_ring_free(struct xhci_softc *, struct xhci_ring *);
void xhci_ring_reset(struct xhci_softc *, struct xhci_ring *);
struct xhci_trb *xhci_ring_consume(struct xhci_softc *, struct xhci_ring *);
-struct xhci_trb *xhci_ring_produce(struct xhci_softc *, struct xhci_ring *);
+struct xhci_trb *xhci_ring_produce(struct xhci_softc *, struct xhci_ring *,
+ int);
struct xhci_trb *xhci_xfer_get_trb(struct xhci_softc *, struct usbd_xfer*,
uint8_t *, int);
@@ -1499,7 +1500,7 @@ xhci_ring_consume(struct xhci_softc *sc,
}
struct xhci_trb*
-xhci_ring_produce(struct xhci_softc *sc, struct xhci_ring *ring)
+xhci_ring_produce(struct xhci_softc *sc, struct xhci_ring *ring, int last)
{
struct xhci_trb *trb = &ring->trbs[ring->index];
@@ -1517,6 +1518,15 @@ xhci_ring_produce(struct xhci_softc *sc,
bus_dmamap_sync(ring->dma.tag, ring->dma.map, TRBOFF(ring, lnk),
sizeof(struct xhci_trb), BUS_DMASYNC_POSTREAD);
+ /*
+ * Section 4.11.7 implies that the Chain bit should
+ * be added to the Link TRB if a TD is including it.
+ */
+ if (last)
+ lnk->trb_flags &= htole32(~XHCI_TRB_CHAIN);
+ else
+ lnk->trb_flags |= htole32(XHCI_TRB_CHAIN);
+
lnk->trb_flags ^= htole32(XHCI_TRB_CYCLE);
bus_dmamap_sync(ring->dma.tag, ring->dma.map, TRBOFF(ring, lnk),
@@ -1546,7 +1556,7 @@ xhci_xfer_get_trb(struct xhci_softc *sc,
xx->ntrb += 1;
*togglep = xp->ring.toggle;
- return (xhci_ring_produce(sc, &xp->ring));
+ return (xhci_ring_produce(sc, &xp->ring, last));
}
int
@@ -1559,7 +1569,7 @@ xhci_command_submit(struct xhci_softc *s
trb0->trb_flags |= htole32(sc->sc_cmd_ring.toggle);
- trb = xhci_ring_produce(sc, &sc->sc_cmd_ring);
+ trb = xhci_ring_produce(sc, &sc->sc_cmd_ring, 1);
if (trb == NULL)
return (EAGAIN);
memcpy(trb, trb0, sizeof(struct xhci_trb));