Hi,
on my i.MX8M machine which features a DWC3 xHCI 1.10 controller I have
seen this error while installing base64.tgz or running fsck:
umass0: Invalid CSW: sig 0x43425355 should be 0x53425355
As it turns out using a USB protocol analyzer, the transfers actually
seem fine, the USB mass storage is not responding nonsense. By further
looking into it I realized that our xhci(4) is told the transfer was
completed, even though the buffer had not been touched.
Further debugging revealed that the issue occured when a transfer that
spans multiple TRBs loops over the ring. This means one TRB is using
idx 254, the next TRB is the link TRB which does not contain data and
sits at idx 255, and the following data TRB is using idx 0.
Transfers that comprise of multiple TRBs must have the chain bit set in
all but the last TRB. Now in this case the link TRB which sits in the
middle does not get the chain bit set, and thus processing stops.
Somewhere I have seen code that always sets the chain bit in the link
TRB, so I'm wondering if we should follow that too. For now I think the
easiest fix for this is to set the chain bit in the link TRB if the
transfer spans multiple TRBs.
Feedback?
Patrick
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c
index 1078acb20c1..e6966655b48 100644
--- a/sys/dev/usb/xhci.c
+++ b/sys/dev/usb/xhci.c
@@ -91,7 +91,8 @@ int xhci_ring_alloc(struct xhci_softc *, struct xhci_ring
*, size_t,
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);
@@ -1584,7 +1585,7 @@ xhci_ring_consume(struct xhci_softc *sc, struct xhci_ring
*ring)
}
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];
@@ -1605,6 +1606,9 @@ xhci_ring_produce(struct xhci_softc *sc, struct xhci_ring
*ring)
BUS_DMASYNC_POSTWRITE);
lnk->trb_flags ^= htole32(XHCI_TRB_CYCLE);
+ lnk->trb_flags &= htole32(~XHCI_TRB_CHAIN);
+ if (!last)
+ lnk->trb_flags |= htole32(XHCI_TRB_CHAIN);
bus_dmamap_sync(ring->dma.tag, ring->dma.map, TRBOFF(ring, lnk),
sizeof(struct xhci_trb), BUS_DMASYNC_PREWRITE);
@@ -1633,7 +1637,7 @@ xhci_xfer_get_trb(struct xhci_softc *sc, struct usbd_xfer
*xfer,
xx->ntrb += 1;
*togglep = xp->ring.toggle;
- return (xhci_ring_produce(sc, &xp->ring));
+ return (xhci_ring_produce(sc, &xp->ring, last));
}
int
@@ -1646,7 +1650,7 @@ xhci_command_submit(struct xhci_softc *sc, struct
xhci_trb *trb0, int timeout)
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);
trb->trb_paddr = trb0->trb_paddr;