diff options
author | Patrick Wildt <patrick@cvs.openbsd.org> | 2020-04-03 20:11:48 +0000 |
---|---|---|
committer | Patrick Wildt <patrick@cvs.openbsd.org> | 2020-04-03 20:11:48 +0000 |
commit | 8baf0724dd1235767b077ddafd31352ebaa84da7 (patch) | |
tree | 7644ae616139685ec29125a4be5f5332efcee34f /sys/dev/usb/ohci.c | |
parent | 56dc09f718254cb856d837908bef7cfc15f3077e (diff) |
Move the responsibility of syncing the data buffers from the USB
stack into the USB controller driver, since not all of the xfers
actually use DMA and some invalidations might even be harmful.
The important part is to sync before handing the buffer to the
controller, and to sync on a successful transfer before handing
it back to the USB stack. For isoc transfers it's easier to sync
the complete length of the transfer, since the buffer to flush is
not filled in a contiguous fashion.
For dwc2 there's a common point which takes care of the start
of transfers from or to devices, where we can make sure that our
buffers are synced. On completion, there's a common point before
handing it off to the USB stack.
For ehci there are three places which take care of the start
of transfers from or to devices, where one already does the sync,
while the two other places still need the sync. There are two
completion handler (isoc and non-isoc), where one already has a
comment asking for the need of a sync. The done handler for intr
xfers does a sync that is not needed anymore after adding the sync
in the completion handler.
For ohci there are three places which take care of the start
of transfers from or to devices, where all of them were still in
need of the sync. For completion, there is one place for isoc
xfers and two places for handling successful generic xfers.
For uhci there are two places which take care of the start
of transfers from or to device, where all of them were still in
need of the sync. For completion, there is one handler for both
isoc and non-isoc xfers where syncs need to occur.
For xhci there are three places which take care of the start
of transfers from or to device, where all of them were still in
need of the sync. For completion, there is one handler for isoc
and one for non-isoc xfers where syncs need to occur.
With this we can revert the workaround that implicitly allocated
buffers are COHERENT, since now control transfers fulfilled by the
driver code (instead of the controller doing DMA) are not flushed
into oblivion anymore.
Tested by Janne Johansson with dwc2 (octeon)
Tested by kettenis@ with xhci(4) (octeon)
Tested by patrick@ with ehci(4) on a Cubox-i (armv7)
Tested by patrick@ with xhci(4) on an i.MX8MQ (arm64)
Tested by tobhe@ with dwc2 on a rPi 3b (arm64)
ok kettenis@
Diffstat (limited to 'sys/dev/usb/ohci.c')
-rw-r--r-- | sys/dev/usb/ohci.c | 37 |
1 files changed, 34 insertions, 3 deletions
diff --git a/sys/dev/usb/ohci.c b/sys/dev/usb/ohci.c index 7aa2303eabd..3155068633e 100644 --- a/sys/dev/usb/ohci.c +++ b/sys/dev/usb/ohci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ohci.c,v 1.160 2020/03/21 12:08:31 patrick Exp $ */ +/* $OpenBSD: ohci.c,v 1.161 2020/04/03 20:11:47 patrick Exp $ */ /* $NetBSD: ohci.c,v 1.139 2003/02/22 05:24:16 tsutsui Exp $ */ /* $FreeBSD: src/sys/dev/usb/ohci.c,v 1.22 1999/11/17 22:33:40 n_hibma Exp $ */ @@ -491,6 +491,10 @@ ohci_alloc_std_chain(struct ohci_softc *sc, u_int alen, struct usbd_xfer *xfer, DPRINTFN(alen < 4096,("ohci_alloc_std_chain: start len=%u\n", alen)); + usb_syncmem(&xfer->dmabuf, 0, xfer->length, + usbd_xfer_isread(xfer) ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + len = alen; cur = sp; end = NULL; @@ -1279,6 +1283,12 @@ ohci_softintr(void *v) ohci_free_std(sc, std); if (done) { + if (xfer->actlen) + usb_syncmem(&xfer->dmabuf, 0, + xfer->actlen, + usbd_xfer_isread(xfer) ? + BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); xfer->status = USBD_NORMAL_COMPLETION; s = splusb(); usb_transfer_complete(xfer); @@ -1309,9 +1319,15 @@ ohci_softintr(void *v) if (cc == OHCI_CC_STALL) xfer->status = USBD_STALLED; - else if (cc == OHCI_CC_DATA_UNDERRUN) + else if (cc == OHCI_CC_DATA_UNDERRUN) { + if (xfer->actlen) + usb_syncmem(&xfer->dmabuf, 0, + xfer->actlen, + usbd_xfer_isread(xfer) ? + BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); xfer->status = USBD_NORMAL_COMPLETION; - else + } else xfer->status = USBD_IOERROR; s = splusb(); usb_transfer_complete(xfer); @@ -1389,6 +1405,13 @@ ohci_softintr(void *v) xfer->actlen = actlen; xfer->hcpriv = NULL; + if (xfer->status == USBD_NORMAL_COMPLETION) { + usb_syncmem(&xfer->dmabuf, 0, xfer->length, + usbd_xfer_isread(xfer) ? + BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); + } + s = splusb(); usb_transfer_complete(xfer); splx(s); @@ -2846,6 +2869,10 @@ ohci_device_intr_start(struct usbd_xfer *xfer) panic("ohci_device_intr_transfer: a request"); #endif + usb_syncmem(&xfer->dmabuf, 0, xfer->length, + usbd_xfer_isread(xfer) ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + len = xfer->length; endpt = xfer->pipe->endpoint->edesc->bEndpointAddress; @@ -3057,6 +3084,10 @@ ohci_device_isoc_enter(struct usbd_xfer *xfer) if (sc->sc_bus.dying) return; + usb_syncmem(&xfer->dmabuf, 0, xfer->length, + usbd_xfer_isread(xfer) ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + if (iso->next == -1) { /* Not in use yet, schedule it a few frames ahead. */ iso->next = letoh32(sc->sc_hcca->hcca_frame_number) + 5; |