diff options
author | Kenneth R Westerback <krw@cvs.openbsd.org> | 2020-01-22 02:25:12 +0000 |
---|---|---|
committer | Kenneth R Westerback <krw@cvs.openbsd.org> | 2020-01-22 02:25:12 +0000 |
commit | ac290f514c5b5000a98b9dda3fe137cb31b83790 (patch) | |
tree | e3414f227d154d9da519f8d026a8dbd6033200ab /sys/dev/usb/xhci.c | |
parent | 00d7baeb0ca89309267eda22a7378fc84f963ddf (diff) |
Add a zero length TD, rather than adding a zero length TRB to the
original TD, when a transfer is a multiple of the max packet size. The
zero length TD will have a NULL xfer pointer.
As a result "NULL xfer pointer" situations become perfectly normal. So
change the log_warnx() that issues that verbiage to a log_debug().
Note that the original transfer will complete and report its result up
the USB stack before the zero length transfer is executed.
Fixes (at least) urtwn(4) interfaces.
Feeback, cluebats, fixes and ok patrick@
Diffstat (limited to 'sys/dev/usb/xhci.c')
-rw-r--r-- | sys/dev/usb/xhci.c | 43 |
1 files changed, 32 insertions, 11 deletions
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c index 6b3306135b2..d6b62b1f997 100644 --- a/sys/dev/usb/xhci.c +++ b/sys/dev/usb/xhci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: xhci.c,v 1.110 2020/01/13 16:17:33 krw Exp $ */ +/* $OpenBSD: xhci.c,v 1.111 2020/01/22 02:25:11 krw Exp $ */ /* * Copyright (c) 2014-2015 Martin Pieuchot @@ -782,7 +782,7 @@ xhci_event_xfer(struct xhci_softc *sc, uint64_t paddr, uint32_t status, xfer = xp->pending_xfers[trb_idx]; if (xfer == NULL) { - printf("%s: NULL xfer pointer\n", DEVNAME(sc)); + DPRINTF(("%s: NULL xfer pointer\n", DEVNAME(sc))); return; } @@ -1799,15 +1799,25 @@ xhci_xfer_get_trb(struct xhci_softc *sc, struct usbd_xfer *xfer, struct xhci_xfer *xx = (struct xhci_xfer *)xfer; KASSERT(xp->free_trbs >= 1); - - /* Associate this TRB to our xfer. */ - xp->pending_xfers[xp->ring.index] = xfer; xp->free_trbs--; + *togglep = xp->ring.toggle; - xx->index = (last) ? xp->ring.index : -2; - xx->ntrb += 1; + switch (last) { + case -1: /* This will be a zero-length TD. */ + xp->pending_xfers[xp->ring.index] = NULL; + break; + case 0: /* This will be in a chain. */ + xp->pending_xfers[xp->ring.index] = xfer; + xx->index = -2; + xx->ntrb += 1; + break; + case 1: /* This will terminate a chain. */ + xp->pending_xfers[xp->ring.index] = xfer; + xx->index = xp->ring.index; + xx->ntrb += 1; + break; + } - *togglep = xp->ring.toggle; return (xhci_ring_produce(sc, &xp->ring)); } @@ -2899,7 +2909,7 @@ xhci_device_generic_start(struct usbd_xfer *xfer) uint32_t mps = UGETW(xfer->pipe->endpoint->edesc->wMaxPacketSize); uint64_t paddr = DMAADDR(&xfer->dmabuf, 0); uint8_t toggle; - int s, i, ntrb; + int s, i, ntrb, zerotd = 0; KASSERT(!(xfer->rqflags & URQ_REQUEST)); @@ -2919,9 +2929,9 @@ xhci_device_generic_start(struct usbd_xfer *xfer) /* If we need to append a zero length packet, we need one more. */ if ((xfer->flags & USBD_FORCE_SHORT_XFER || xfer->length == 0) && (xfer->length % UE_GET_SIZE(mps) == 0)) - ntrb++; + zerotd = 1; - if (xp->free_trbs < ntrb) + if (xp->free_trbs < (ntrb + zerotd)) return (USBD_NOMEM); /* We'll toggle the first TRB once we're finished with the chain. */ @@ -2970,6 +2980,17 @@ xhci_device_generic_start(struct usbd_xfer *xfer) paddr += len; } + /* Do we need to issue a zero length transfer? */ + if (zerotd == 1) { + trb = xhci_xfer_get_trb(sc, xfer, &toggle, -1); + trb->trb_paddr = 0; + trb->trb_status = 0; + trb->trb_flags = htole32(XHCI_TRB_TYPE_NORMAL | XHCI_TRB_IOC | toggle); + bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map, + TRBOFF(&xp->ring, trb), sizeof(struct xhci_trb), + BUS_DMASYNC_PREWRITE); + } + /* First TRB. */ trb0->trb_flags ^= htole32(XHCI_TRB_CYCLE); bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map, |