xhci(4): Fix handling of endpoint reset/stop.
Use the same asynchronous task resetting a stalled/halted endpoint and stopping a running endpoint -- either way we need to put the endpoint back into a known state and, if there are transfers waiting to run, start them up again. - xhci_abortx must not drop the pipe (bus) lock -- usbdi(9) requires this. So arrange to stop the endpoint and empty the queue asynchronously. - If the xhci softint claims an xfer for completion with usbd_xfer_trycomplete, it must call usb_transfer_complete without ever releasing the pipe (bus) lock -- it can't claim the xfer and then defer the usb_transfer_complete to a task. So arrange to reset the endpoint asynchronously, hold up new transfers until the endpoint has been reset, and then do usb_transfer_complete immediately.
This commit is contained in:
parent
c6b66c0365
commit
6e5399e670
|
@ -1,4 +1,4 @@
|
||||||
/* $NetBSD: xhci.c,v 1.154 2022/01/25 11:17:39 msaitoh Exp $ */
|
/* $NetBSD: xhci.c,v 1.155 2022/01/29 21:36:12 riastradh Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Jonathan A. Kollasch
|
* Copyright (c) 2013 Jonathan A. Kollasch
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.154 2022/01/25 11:17:39 msaitoh Exp $");
|
__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.155 2022/01/29 21:36:12 riastradh Exp $");
|
||||||
|
|
||||||
#ifdef _KERNEL_OPT
|
#ifdef _KERNEL_OPT
|
||||||
#include "opt_usb.h"
|
#include "opt_usb.h"
|
||||||
|
@ -154,15 +154,18 @@ static usbd_status xhci_new_device(device_t, struct usbd_bus *, int, int, int,
|
||||||
static int xhci_roothub_ctrl(struct usbd_bus *, usb_device_request_t *,
|
static int xhci_roothub_ctrl(struct usbd_bus *, usb_device_request_t *,
|
||||||
void *, int);
|
void *, int);
|
||||||
|
|
||||||
|
static void xhci_pipe_async_task(void *);
|
||||||
|
static void xhci_pipe_restart(struct usbd_pipe *);
|
||||||
|
|
||||||
static usbd_status xhci_configure_endpoint(struct usbd_pipe *);
|
static usbd_status xhci_configure_endpoint(struct usbd_pipe *);
|
||||||
//static usbd_status xhci_unconfigure_endpoint(struct usbd_pipe *);
|
//static usbd_status xhci_unconfigure_endpoint(struct usbd_pipe *);
|
||||||
static usbd_status xhci_reset_endpoint(struct usbd_pipe *);
|
static void xhci_reset_endpoint(struct usbd_pipe *);
|
||||||
static usbd_status xhci_stop_endpoint_cmd(struct xhci_softc *,
|
static usbd_status xhci_stop_endpoint_cmd(struct xhci_softc *,
|
||||||
struct xhci_slot *, u_int, uint32_t);
|
struct xhci_slot *, u_int, uint32_t);
|
||||||
static usbd_status xhci_stop_endpoint(struct usbd_pipe *);
|
static usbd_status xhci_stop_endpoint(struct usbd_pipe *);
|
||||||
|
|
||||||
static void xhci_host_dequeue(struct xhci_ring * const);
|
static void xhci_host_dequeue(struct xhci_ring * const);
|
||||||
static usbd_status xhci_set_dequeue(struct usbd_pipe *);
|
static void xhci_set_dequeue(struct usbd_pipe *);
|
||||||
|
|
||||||
static usbd_status xhci_do_command(struct xhci_softc * const,
|
static usbd_status xhci_do_command(struct xhci_softc * const,
|
||||||
struct xhci_soft_trb * const, int);
|
struct xhci_soft_trb * const, int);
|
||||||
|
@ -1858,14 +1861,13 @@ xhci_unconfigure_endpoint(struct usbd_pipe *pipe)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* 4.6.8, 6.4.3.7 */
|
/* 4.6.8, 6.4.3.7 */
|
||||||
static usbd_status
|
static void
|
||||||
xhci_reset_endpoint_locked(struct usbd_pipe *pipe)
|
xhci_reset_endpoint(struct usbd_pipe *pipe)
|
||||||
{
|
{
|
||||||
struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
|
struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
|
||||||
struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
|
struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
|
||||||
const u_int dci = xhci_ep_get_dci(pipe->up_endpoint->ue_edesc);
|
const u_int dci = xhci_ep_get_dci(pipe->up_endpoint->ue_edesc);
|
||||||
struct xhci_soft_trb trb;
|
struct xhci_soft_trb trb;
|
||||||
usbd_status err;
|
|
||||||
|
|
||||||
XHCIHIST_FUNC();
|
XHCIHIST_FUNC();
|
||||||
XHCIHIST_CALLARGS("slot %ju dci %ju", xs->xs_idx, dci, 0, 0);
|
XHCIHIST_CALLARGS("slot %ju dci %ju", xs->xs_idx, dci, 0, 0);
|
||||||
|
@ -1878,21 +1880,10 @@ xhci_reset_endpoint_locked(struct usbd_pipe *pipe)
|
||||||
XHCI_TRB_3_EP_SET(dci) |
|
XHCI_TRB_3_EP_SET(dci) |
|
||||||
XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_EP);
|
XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_EP);
|
||||||
|
|
||||||
err = xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT);
|
if (xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT)) {
|
||||||
|
device_printf(sc->sc_dev, "%s: endpoint 0x%x: timed out\n",
|
||||||
return err;
|
__func__, pipe->up_endpoint->ue_edesc->bEndpointAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
static usbd_status
|
|
||||||
xhci_reset_endpoint(struct usbd_pipe *pipe)
|
|
||||||
{
|
|
||||||
struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
|
|
||||||
|
|
||||||
mutex_enter(&sc->sc_lock);
|
|
||||||
usbd_status ret = xhci_reset_endpoint_locked(pipe);
|
|
||||||
mutex_exit(&sc->sc_lock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1947,15 +1938,14 @@ xhci_stop_endpoint(struct usbd_pipe *pipe)
|
||||||
* EPSTATE of endpoint must be ERROR or STOPPED, otherwise CONTEXT_STATE
|
* EPSTATE of endpoint must be ERROR or STOPPED, otherwise CONTEXT_STATE
|
||||||
* error will be generated.
|
* error will be generated.
|
||||||
*/
|
*/
|
||||||
static usbd_status
|
static void
|
||||||
xhci_set_dequeue_locked(struct usbd_pipe *pipe)
|
xhci_set_dequeue(struct usbd_pipe *pipe)
|
||||||
{
|
{
|
||||||
struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
|
struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
|
||||||
struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
|
struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
|
||||||
const u_int dci = xhci_ep_get_dci(pipe->up_endpoint->ue_edesc);
|
const u_int dci = xhci_ep_get_dci(pipe->up_endpoint->ue_edesc);
|
||||||
struct xhci_ring * const xr = xs->xs_xr[dci];
|
struct xhci_ring * const xr = xs->xs_xr[dci];
|
||||||
struct xhci_soft_trb trb;
|
struct xhci_soft_trb trb;
|
||||||
usbd_status err;
|
|
||||||
|
|
||||||
XHCIHIST_FUNC();
|
XHCIHIST_FUNC();
|
||||||
XHCIHIST_CALLARGS("slot %ju dci %ju", xs->xs_idx, dci, 0, 0);
|
XHCIHIST_CALLARGS("slot %ju dci %ju", xs->xs_idx, dci, 0, 0);
|
||||||
|
@ -1972,21 +1962,10 @@ xhci_set_dequeue_locked(struct usbd_pipe *pipe)
|
||||||
XHCI_TRB_3_EP_SET(dci) |
|
XHCI_TRB_3_EP_SET(dci) |
|
||||||
XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SET_TR_DEQUEUE);
|
XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SET_TR_DEQUEUE);
|
||||||
|
|
||||||
err = xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT);
|
if (xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT)) {
|
||||||
|
device_printf(sc->sc_dev, "%s: endpoint 0x%x: timed out\n",
|
||||||
return err;
|
__func__, pipe->up_endpoint->ue_edesc->bEndpointAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
static usbd_status
|
|
||||||
xhci_set_dequeue(struct usbd_pipe *pipe)
|
|
||||||
{
|
|
||||||
struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
|
|
||||||
|
|
||||||
mutex_enter(&sc->sc_lock);
|
|
||||||
usbd_status ret = xhci_set_dequeue_locked(pipe);
|
|
||||||
mutex_exit(&sc->sc_lock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2036,6 +2015,9 @@ xhci_open(struct usbd_pipe *pipe)
|
||||||
return USBD_NORMAL_COMPLETION;
|
return USBD_NORMAL_COMPLETION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usb_init_task(&xpipe->xp_async_task, xhci_pipe_async_task, xpipe,
|
||||||
|
USB_TASKQ_MPSAFE);
|
||||||
|
|
||||||
switch (xfertype) {
|
switch (xfertype) {
|
||||||
case UE_CONTROL:
|
case UE_CONTROL:
|
||||||
pipe->up_methods = &xhci_device_ctrl_methods;
|
pipe->up_methods = &xhci_device_ctrl_methods;
|
||||||
|
@ -2081,6 +2063,8 @@ xhci_open(struct usbd_pipe *pipe)
|
||||||
static void
|
static void
|
||||||
xhci_close_pipe(struct usbd_pipe *pipe)
|
xhci_close_pipe(struct usbd_pipe *pipe)
|
||||||
{
|
{
|
||||||
|
struct xhci_pipe * const xp =
|
||||||
|
container_of(pipe, struct xhci_pipe, xp_pipe);
|
||||||
struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
|
struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
|
||||||
struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
|
struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
|
||||||
usb_endpoint_descriptor_t * const ed = pipe->up_endpoint->ue_edesc;
|
usb_endpoint_descriptor_t * const ed = pipe->up_endpoint->ue_edesc;
|
||||||
|
@ -2090,6 +2074,9 @@ xhci_close_pipe(struct usbd_pipe *pipe)
|
||||||
|
|
||||||
XHCIHIST_FUNC();
|
XHCIHIST_FUNC();
|
||||||
|
|
||||||
|
usb_rem_task_wait(pipe->up_dev, &xp->xp_async_task, USB_TASKQ_HC,
|
||||||
|
&sc->sc_lock);
|
||||||
|
|
||||||
if (sc->sc_dying)
|
if (sc->sc_dying)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -2149,68 +2136,27 @@ xhci_close_pipe(struct usbd_pipe *pipe)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Abort transfer.
|
* Abort transfer.
|
||||||
* Should be called with sc_lock held.
|
* Should be called with sc_lock held. Must not drop sc_lock.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
xhci_abortx(struct usbd_xfer *xfer)
|
xhci_abortx(struct usbd_xfer *xfer)
|
||||||
{
|
{
|
||||||
XHCIHIST_FUNC();
|
XHCIHIST_FUNC();
|
||||||
struct xhci_softc * const sc = XHCI_XFER2SC(xfer);
|
struct xhci_softc * const sc = XHCI_XFER2SC(xfer);
|
||||||
struct xhci_slot * const xs = xfer->ux_pipe->up_dev->ud_hcpriv;
|
|
||||||
const u_int dci = xhci_ep_get_dci(xfer->ux_pipe->up_endpoint->ue_edesc);
|
|
||||||
|
|
||||||
XHCIHIST_CALLARGS("xfer %#jx pipe %#jx",
|
XHCIHIST_CALLARGS("xfer %#jx pipe %#jx",
|
||||||
(uintptr_t)xfer, (uintptr_t)xfer->ux_pipe, 0, 0);
|
(uintptr_t)xfer, (uintptr_t)xfer->ux_pipe, 0, 0);
|
||||||
|
|
||||||
KASSERT(mutex_owned(&sc->sc_lock));
|
KASSERT(mutex_owned(&sc->sc_lock));
|
||||||
ASSERT_SLEEPABLE();
|
|
||||||
|
|
||||||
KASSERTMSG((xfer->ux_status == USBD_CANCELLED ||
|
KASSERTMSG((xfer->ux_status == USBD_CANCELLED ||
|
||||||
xfer->ux_status == USBD_TIMEOUT),
|
xfer->ux_status == USBD_TIMEOUT),
|
||||||
"bad abort status: %d", xfer->ux_status);
|
"bad abort status: %d", xfer->ux_status);
|
||||||
|
|
||||||
/*
|
xhci_pipe_restart(xfer->ux_pipe);
|
||||||
* If we're dying, skip the hardware action and just notify the
|
|
||||||
* software that we're done.
|
|
||||||
*/
|
|
||||||
if (sc->sc_dying) {
|
|
||||||
DPRINTFN(4, "xfer %#jx dying %ju", (uintptr_t)xfer,
|
|
||||||
xfer->ux_status, 0, 0);
|
|
||||||
goto dying;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* HC Step 1: Stop execution of TD on the ring.
|
|
||||||
*/
|
|
||||||
switch (xhci_get_epstate(sc, xs, dci)) {
|
|
||||||
case XHCI_EPSTATE_HALTED:
|
|
||||||
(void)xhci_reset_endpoint_locked(xfer->ux_pipe);
|
|
||||||
break;
|
|
||||||
case XHCI_EPSTATE_STOPPED:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
(void)xhci_stop_endpoint(xfer->ux_pipe);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#ifdef DIAGNOSTIC
|
|
||||||
uint32_t epst = xhci_get_epstate(sc, xs, dci);
|
|
||||||
if (epst != XHCI_EPSTATE_STOPPED)
|
|
||||||
DPRINTFN(4, "dci %ju not stopped %ju", dci, epst, 0, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* HC Step 2: Remove any vestiges of the xfer from the ring.
|
|
||||||
*/
|
|
||||||
xhci_set_dequeue_locked(xfer->ux_pipe);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Final Step: Notify completion to waiting xfers.
|
|
||||||
*/
|
|
||||||
dying:
|
|
||||||
usb_transfer_complete(xfer);
|
usb_transfer_complete(xfer);
|
||||||
DPRINTFN(14, "end", 0, 0, 0, 0);
|
|
||||||
|
|
||||||
KASSERT(mutex_owned(&sc->sc_lock));
|
DPRINTFN(14, "end", 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -2227,69 +2173,102 @@ xhci_host_dequeue(struct xhci_ring * const xr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recover STALLed endpoint.
|
* Recover STALLed endpoint, or stop endpoint to abort a pipe.
|
||||||
* xHCI 1.1 sect 4.10.2.1
|
* xHCI 1.1 sect 4.10.2.1
|
||||||
* Issue RESET_EP to recover halt condition and SET_TR_DEQUEUE to remove
|
* Issue RESET_EP to recover halt condition and SET_TR_DEQUEUE to remove
|
||||||
* all transfers on transfer ring.
|
* all transfers on transfer ring.
|
||||||
* These are done in thread context asynchronously.
|
* These are done in thread context asynchronously.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
xhci_clear_endpoint_stall_async_task(void *cookie)
|
xhci_pipe_async_task(void *cookie)
|
||||||
{
|
{
|
||||||
struct usbd_xfer * const xfer = cookie;
|
struct xhci_pipe * const xp = cookie;
|
||||||
struct xhci_softc * const sc = XHCI_XFER2SC(xfer);
|
struct usbd_pipe * const pipe = &xp->xp_pipe;
|
||||||
struct xhci_slot * const xs = xfer->ux_pipe->up_dev->ud_hcpriv;
|
struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
|
||||||
const u_int dci = xhci_ep_get_dci(xfer->ux_pipe->up_endpoint->ue_edesc);
|
struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
|
||||||
|
const u_int dci = xhci_ep_get_dci(pipe->up_endpoint->ue_edesc);
|
||||||
struct xhci_ring * const tr = xs->xs_xr[dci];
|
struct xhci_ring * const tr = xs->xs_xr[dci];
|
||||||
|
bool restart = false;
|
||||||
|
|
||||||
XHCIHIST_FUNC();
|
XHCIHIST_FUNC();
|
||||||
XHCIHIST_CALLARGS("xfer %#jx slot %ju dci %ju", (uintptr_t)xfer, xs->xs_idx,
|
XHCIHIST_CALLARGS("pipe %#jx slot %ju dci %ju",
|
||||||
dci, 0);
|
(uintptr_t)pipe, xs->xs_idx, dci, 0);
|
||||||
|
|
||||||
/*
|
|
||||||
* XXXMRG: Stall task can run after slot is disabled when yanked.
|
|
||||||
* This hack notices that the xs has been memset() in
|
|
||||||
* xhci_disable_slot() and returns. Both xhci_reset_endpoint()
|
|
||||||
* and xhci_set_dequeue() rely upon a valid ring setup for correct
|
|
||||||
* operation, and the latter will fault, as would
|
|
||||||
* usb_transfer_complete() if it got that far.
|
|
||||||
*/
|
|
||||||
if (xs->xs_idx == 0) {
|
|
||||||
DPRINTFN(4, "ends xs_idx is 0", 0, 0, 0, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KASSERT(tr != NULL);
|
|
||||||
|
|
||||||
xhci_reset_endpoint(xfer->ux_pipe);
|
|
||||||
xhci_set_dequeue(xfer->ux_pipe);
|
|
||||||
|
|
||||||
mutex_enter(&sc->sc_lock);
|
mutex_enter(&sc->sc_lock);
|
||||||
tr->is_halted = false;
|
|
||||||
usb_transfer_complete(xfer);
|
/*
|
||||||
|
* - If the endpoint is halted, indicating a stall, reset it.
|
||||||
|
* - If the endpoint is stopped, we're already good.
|
||||||
|
* - Otherwise, someone wanted to abort the pipe, so stop the
|
||||||
|
* endpoint.
|
||||||
|
*
|
||||||
|
* In any case, clear the ring.
|
||||||
|
*/
|
||||||
|
switch (xhci_get_epstate(sc, xs, dci)) {
|
||||||
|
case XHCI_EPSTATE_HALTED:
|
||||||
|
xhci_reset_endpoint(pipe);
|
||||||
|
break;
|
||||||
|
case XHCI_EPSTATE_STOPPED:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
xhci_stop_endpoint(pipe);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (xhci_get_epstate(sc, xs, dci)) {
|
||||||
|
case XHCI_EPSTATE_STOPPED:
|
||||||
|
break;
|
||||||
|
case XHCI_EPSTATE_ERROR:
|
||||||
|
device_printf(sc->sc_dev, "endpoint 0x%x error\n",
|
||||||
|
pipe->up_endpoint->ue_edesc->bEndpointAddress);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
device_printf(sc->sc_dev, "endpoint 0x%x failed to stop\n",
|
||||||
|
pipe->up_endpoint->ue_edesc->bEndpointAddress);
|
||||||
|
}
|
||||||
|
xhci_set_dequeue(pipe);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we halted our own queue because it stalled, mark it no
|
||||||
|
* longer halted and arrange to start it up again.
|
||||||
|
*/
|
||||||
|
if (tr->is_halted) {
|
||||||
|
tr->is_halted = false;
|
||||||
|
if (!SIMPLEQ_EMPTY(&pipe->up_queue))
|
||||||
|
restart = true;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_exit(&sc->sc_lock);
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the endpoint was stalled, start issuing queued transfers
|
||||||
|
* again.
|
||||||
|
*/
|
||||||
|
if (restart) {
|
||||||
|
/*
|
||||||
|
* XXX Shouldn't touch the queue unlocked -- upm_start
|
||||||
|
* should be called with the lock held instead. The
|
||||||
|
* pipe could be aborted at this point, and the xfer
|
||||||
|
* freed.
|
||||||
|
*/
|
||||||
|
struct usbd_xfer *xfer = SIMPLEQ_FIRST(&pipe->up_queue);
|
||||||
|
(*pipe->up_methods->upm_start)(xfer);
|
||||||
|
}
|
||||||
|
|
||||||
DPRINTFN(4, "ends", 0, 0, 0, 0);
|
DPRINTFN(4, "ends", 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static usbd_status
|
static void
|
||||||
xhci_clear_endpoint_stall_async(struct usbd_xfer *xfer)
|
xhci_pipe_restart(struct usbd_pipe *pipe)
|
||||||
{
|
{
|
||||||
struct xhci_softc * const sc = XHCI_XFER2SC(xfer);
|
struct xhci_pipe * const xp =
|
||||||
struct xhci_pipe * const xp = (struct xhci_pipe *)xfer->ux_pipe;
|
container_of(pipe, struct xhci_pipe, xp_pipe);
|
||||||
|
|
||||||
XHCIHIST_FUNC();
|
XHCIHIST_FUNC();
|
||||||
XHCIHIST_CALLARGS("xfer %#jx", (uintptr_t)xfer, 0, 0, 0);
|
XHCIHIST_CALLARGS("pipe %#jx", (uintptr_t)pipe, 0, 0, 0);
|
||||||
|
|
||||||
if (sc->sc_dying) {
|
usb_add_task(pipe->up_dev, &xp->xp_async_task, USB_TASKQ_HC);
|
||||||
return USBD_IOERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
usb_init_task(&xp->xp_async_task,
|
|
||||||
xhci_clear_endpoint_stall_async_task, xfer, USB_TASKQ_MPSAFE);
|
|
||||||
usb_add_task(xfer->ux_pipe->up_dev, &xp->xp_async_task, USB_TASKQ_HC);
|
|
||||||
DPRINTFN(4, "ends", 0, 0, 0, 0);
|
DPRINTFN(4, "ends", 0, 0, 0, 0);
|
||||||
|
|
||||||
return USBD_NORMAL_COMPLETION;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process roothub port status/change events and notify to uhub_intr. */
|
/* Process roothub port status/change events and notify to uhub_intr. */
|
||||||
|
@ -2467,32 +2446,9 @@ xhci_event_transfer(struct xhci_softc * const sc,
|
||||||
case XHCI_TRB_ERROR_BABBLE:
|
case XHCI_TRB_ERROR_BABBLE:
|
||||||
DPRINTFN(1, "ERR %ju slot %ju dci %ju", trbcode, slot, dci, 0);
|
DPRINTFN(1, "ERR %ju slot %ju dci %ju", trbcode, slot, dci, 0);
|
||||||
xr->is_halted = true;
|
xr->is_halted = true;
|
||||||
/*
|
xhci_pipe_restart(xfer->ux_pipe);
|
||||||
* Try to claim this xfer for completion. If it has already
|
err = USBD_STALLED;
|
||||||
* completed or aborted, drop it on the floor.
|
break;
|
||||||
*/
|
|
||||||
if (!usbd_xfer_trycomplete(xfer))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Stalled endpoints can be recoverd by issuing
|
|
||||||
* command TRB TYPE_RESET_EP on xHCI instead of
|
|
||||||
* issuing request CLEAR_FEATURE UF_ENDPOINT_HALT
|
|
||||||
* on the endpoint. However, this function may be
|
|
||||||
* called from softint context (e.g. from umass),
|
|
||||||
* in that case driver gets KASSERT in cv_timedwait
|
|
||||||
* in xhci_do_command.
|
|
||||||
* To avoid this, this runs reset_endpoint and
|
|
||||||
* usb_transfer_complete in usb task thread
|
|
||||||
* asynchronously (and then umass issues clear
|
|
||||||
* UF_ENDPOINT_HALT).
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Override the status. */
|
|
||||||
xfer->ux_status = USBD_STALLED;
|
|
||||||
|
|
||||||
xhci_clear_endpoint_stall_async(xfer);
|
|
||||||
return;
|
|
||||||
default:
|
default:
|
||||||
DPRINTFN(1, "ERR %ju slot %ju dci %ju", trbcode, slot, dci, 0);
|
DPRINTFN(1, "ERR %ju slot %ju dci %ju", trbcode, slot, dci, 0);
|
||||||
err = USBD_IOERROR;
|
err = USBD_IOERROR;
|
||||||
|
@ -4322,6 +4278,11 @@ xhci_device_ctrl_start(struct usbd_xfer *xfer)
|
||||||
|
|
||||||
KASSERT((xfer->ux_rqflags & URQ_REQUEST) != 0);
|
KASSERT((xfer->ux_rqflags & URQ_REQUEST) != 0);
|
||||||
|
|
||||||
|
if (!polling)
|
||||||
|
mutex_enter(&sc->sc_lock);
|
||||||
|
if (tr->is_halted)
|
||||||
|
goto out;
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
|
|
||||||
/* setup phase */
|
/* setup phase */
|
||||||
|
@ -4364,11 +4325,18 @@ xhci_device_ctrl_start(struct usbd_xfer *xfer)
|
||||||
if (!polling)
|
if (!polling)
|
||||||
mutex_exit(&tr->xr_lock);
|
mutex_exit(&tr->xr_lock);
|
||||||
|
|
||||||
if (!polling)
|
|
||||||
mutex_enter(&sc->sc_lock);
|
|
||||||
xfer->ux_status = USBD_IN_PROGRESS;
|
|
||||||
xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
|
xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
|
||||||
usbd_xfer_schedule_timeout(xfer);
|
|
||||||
|
out: if (xfer->ux_status == USBD_NOT_STARTED) {
|
||||||
|
usbd_xfer_schedule_timeout(xfer);
|
||||||
|
xfer->ux_status = USBD_IN_PROGRESS;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* We must be coming from xhci_pipe_restart -- timeout
|
||||||
|
* already set up, nothing to do.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
KASSERT(xfer->ux_status == USBD_IN_PROGRESS);
|
||||||
if (!polling)
|
if (!polling)
|
||||||
mutex_exit(&sc->sc_lock);
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
@ -4618,6 +4586,11 @@ xhci_device_bulk_start(struct usbd_xfer *xfer)
|
||||||
|
|
||||||
KASSERT((xfer->ux_rqflags & URQ_REQUEST) == 0);
|
KASSERT((xfer->ux_rqflags & URQ_REQUEST) == 0);
|
||||||
|
|
||||||
|
if (!polling)
|
||||||
|
mutex_enter(&sc->sc_lock);
|
||||||
|
if (tr->is_halted)
|
||||||
|
goto out;
|
||||||
|
|
||||||
parameter = DMAADDR(dma, 0);
|
parameter = DMAADDR(dma, 0);
|
||||||
const bool isread = usbd_xfer_isread(xfer);
|
const bool isread = usbd_xfer_isread(xfer);
|
||||||
if (len)
|
if (len)
|
||||||
|
@ -4650,11 +4623,18 @@ xhci_device_bulk_start(struct usbd_xfer *xfer)
|
||||||
if (!polling)
|
if (!polling)
|
||||||
mutex_exit(&tr->xr_lock);
|
mutex_exit(&tr->xr_lock);
|
||||||
|
|
||||||
if (!polling)
|
|
||||||
mutex_enter(&sc->sc_lock);
|
|
||||||
xfer->ux_status = USBD_IN_PROGRESS;
|
|
||||||
xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
|
xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
|
||||||
usbd_xfer_schedule_timeout(xfer);
|
|
||||||
|
out: if (xfer->ux_status == USBD_NOT_STARTED) {
|
||||||
|
xfer->ux_status = USBD_IN_PROGRESS;
|
||||||
|
usbd_xfer_schedule_timeout(xfer);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* We must be coming from xhci_pipe_restart -- timeout
|
||||||
|
* already set up, nothing to do.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
KASSERT(xfer->ux_status == USBD_IN_PROGRESS);
|
||||||
if (!polling)
|
if (!polling)
|
||||||
mutex_exit(&sc->sc_lock);
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
@ -4742,6 +4722,11 @@ xhci_device_intr_start(struct usbd_xfer *xfer)
|
||||||
if (sc->sc_dying)
|
if (sc->sc_dying)
|
||||||
return USBD_IOERROR;
|
return USBD_IOERROR;
|
||||||
|
|
||||||
|
if (!polling)
|
||||||
|
mutex_enter(&sc->sc_lock);
|
||||||
|
if (tr->is_halted)
|
||||||
|
goto out;
|
||||||
|
|
||||||
KASSERT((xfer->ux_rqflags & URQ_REQUEST) == 0);
|
KASSERT((xfer->ux_rqflags & URQ_REQUEST) == 0);
|
||||||
|
|
||||||
const bool isread = usbd_xfer_isread(xfer);
|
const bool isread = usbd_xfer_isread(xfer);
|
||||||
|
@ -4764,11 +4749,18 @@ xhci_device_intr_start(struct usbd_xfer *xfer)
|
||||||
if (!polling)
|
if (!polling)
|
||||||
mutex_exit(&tr->xr_lock);
|
mutex_exit(&tr->xr_lock);
|
||||||
|
|
||||||
if (!polling)
|
|
||||||
mutex_enter(&sc->sc_lock);
|
|
||||||
xfer->ux_status = USBD_IN_PROGRESS;
|
|
||||||
xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
|
xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
|
||||||
usbd_xfer_schedule_timeout(xfer);
|
|
||||||
|
out: if (xfer->ux_status == USBD_NOT_STARTED) {
|
||||||
|
xfer->ux_status = USBD_IN_PROGRESS;
|
||||||
|
usbd_xfer_schedule_timeout(xfer);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* We must be coming from xhci_pipe_restart -- timeout
|
||||||
|
* already set up, nothing to do.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
KASSERT(xfer->ux_status == USBD_IN_PROGRESS);
|
||||||
if (!polling)
|
if (!polling)
|
||||||
mutex_exit(&sc->sc_lock);
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue