summaryrefslogtreecommitdiff
path: root/sys/dev/usb/uhci.c
diff options
context:
space:
mode:
authorIan Dowse <iedowse@FreeBSD.org>2004-11-09 20:51:32 +0000
committerIan Dowse <iedowse@FreeBSD.org>2004-11-09 20:51:32 +0000
commit0ce606de7cbd028cd94279fb92793dc82d308a53 (patch)
treeeb804e85360c0ea4b8586a0dd555c7510e714c42 /sys/dev/usb/uhci.c
parent8e1cbbc61152afeb5eddb7d7b6f9f05101459687 (diff)
Notes
Diffstat (limited to 'sys/dev/usb/uhci.c')
-rw-r--r--sys/dev/usb/uhci.c56
1 files changed, 40 insertions, 16 deletions
diff --git a/sys/dev/usb/uhci.c b/sys/dev/usb/uhci.c
index 411f80931350..71152a6a2d13 100644
--- a/sys/dev/usb/uhci.c
+++ b/sys/dev/usb/uhci.c
@@ -1369,7 +1369,12 @@ uhci_check_intr(uhci_softc_t *sc, uhci_intr_info_t *ii)
}
done:
DPRINTFN(12, ("uhci_check_intr: ii=%p done\n", ii));
- usb_uncallout(ii->xfer->timeout_handle, uhci_timeout, ii);
+ /* The timeout may have fired already but not yet run. */
+ if (ii->xfer->timeout && !sc->sc_bus.use_polling &&
+ usb_uncallout(ii->xfer->timeout_handle, uhci_timeout, ii) == 0) {
+ /* Make uhci_idone() ignore this xfer. */
+ ii->xfer->status = USBD_TIMEOUT;
+ }
uhci_idone(ii);
}
@@ -1495,6 +1500,7 @@ uhci_idone(uhci_intr_info_t *ii)
}
end:
+ usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task);
usb_transfer_complete(xfer);
DPRINTFN(12, ("uhci_idone: ii=%p done\n", ii));
}
@@ -1512,12 +1518,20 @@ uhci_timeout(void *addr)
DPRINTF(("uhci_timeout: uxfer=%p\n", uxfer));
+ /*
+ * When a timeout happens concurrently with ehci_abort_xfer(), let
+ * the non-timeout process do the completion.
+ */
+ if (ii->xfer->status == USBD_CANCELLED)
+ return;
+
if (sc->sc_dying) {
uhci_abort_xfer(&uxfer->xfer, USBD_TIMEOUT);
return;
}
/* Execute the abort in a process context. */
+ uxfer->xfer.status = USBD_TIMEOUT;
usb_add_task(uxfer->xfer.pipe->device, &uxfer->abort_task);
}
@@ -1940,13 +1954,35 @@ uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
DPRINTFN(1,("uhci_abort_xfer: xfer=%p, status=%d\n", xfer, status));
+ /*
+ * Step 1: Make interrupt routine, timers and hardware ignore xfer.
+ */
+ s = splusb();
+ xfer->status = status; /* make software ignore it */
+ if (!sc->sc_dying) {
+ /* Disable the xfer in hardware to prevent interrupts. */
+ DPRINTFN(1,("uhci_abort_xfer: stop ii=%p\n", ii));
+ for (std = ii->stdstart; std != NULL; std = std->link.std)
+ std->td.td_status &= htole32(~(UHCI_TD_ACTIVE |
+ UHCI_TD_IOC));
+ }
+ if (status == USBD_CANCELLED) {
+ /*
+ * Stop the timeout timer, waiting if required. We can
+ * guarantee that interrupts or timeouts will not complete
+ * the the xfer while we wait, because both ignore transfers
+ * with a status of USBD_CANCELLED.
+ */
+ usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task);
+ usb_uncallout_drain(xfer->timeout_handle, uhci_timeout,
+ &UXFER(xfer)->iinfo);
+ }
+ splx(s);
+
if (sc->sc_dying) {
/* If we're dying, just do the software part. */
s = splusb();
- xfer->status = status; /* make software ignore it */
- usb_uncallout(xfer->timeout_handle, uhci_timeout, xfer);
usb_transfer_complete(xfer);
- usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task);
splx(s);
return;
}
@@ -1955,18 +1991,6 @@ uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
panic("uhci_abort_xfer: not in process context");
/*
- * Step 1: Make interrupt routine and hardware ignore xfer.
- */
- s = splusb();
- xfer->status = status; /* make software ignore it */
- usb_uncallout(xfer->timeout_handle, uhci_timeout, ii);
- usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task);
- DPRINTFN(1,("uhci_abort_xfer: stop ii=%p\n", ii));
- for (std = ii->stdstart; std != NULL; std = std->link.std)
- std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC));
- splx(s);
-
- /*
* Step 2: Wait until we know hardware has finished any possible
* use of the xfer. Also make sure the soft interrupt routine
* has run.