diff options
| author | Ian Dowse <iedowse@FreeBSD.org> | 2004-11-09 20:51:32 +0000 |
|---|---|---|
| committer | Ian Dowse <iedowse@FreeBSD.org> | 2004-11-09 20:51:32 +0000 |
| commit | 0ce606de7cbd028cd94279fb92793dc82d308a53 (patch) | |
| tree | eb804e85360c0ea4b8586a0dd555c7510e714c42 /sys/dev/usb/uhci.c | |
| parent | 8e1cbbc61152afeb5eddb7d7b6f9f05101459687 (diff) | |
Notes
Diffstat (limited to 'sys/dev/usb/uhci.c')
| -rw-r--r-- | sys/dev/usb/uhci.c | 56 |
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. |
