void usbd_abort_xfer(struct usbd_xfer *xfer, usbd_status status) { struct usbd_pipe *pipe = xfer->ux_pipe; struct usbd_device *dev = pipe->up_dev; struct usbd_bus *bus = dev->ud_bus; KASSERTMSG((status == USBD_CANCELLED || status == USBD_TIMEOUT), "invalid status for abort: %d", (int)status); KASSERT(mutex_owned(bus->ub_lock)); ASSERT_SLEEPABLE(); if (status == USBD_CANCELLED) { /* * If we are synchronously aborting, wait for the * callout and the task to complete. While we are * wating, they may beat us to aborting the xfer. */ callout_halt(&xfer->ux_callout, bus->ub_lock); usb_rem_task_wait(dev, &xfer->ux_aborttask, USB_TASKQ_HC, bus->ub_lock); /* * The xfer cannot have been cancelled already. It is * the caller's responsibility of usbd_abort_pipe not * to try to abort a pipe multiple times concurrently. */ KASSERT(xfer->ux_status != USBD_CANCELLED); /* If the timeout beat us, we're done. */ if (xfer->ux_status == USBD_TIMEOUT) return; } else { /* Otherwise, we are timing out. */ KASSERT(status == USBD_TIMEOUT); /* Nobody else can time it out. */ KASSERT(xfer->ux_status != USBD_TIMEOUT); /* * If it was synchronously aborted before we timed out, * we're done. */ if (xfer->ux_status == USBD_CANCELLED) return; } /* * We are the first to abort, whether due to a timeout or * otherwise. Set the status. */ KASSERT(xfer->ux_status != USBD_CANCELLED); KASSERT(xfer->ux_status != USBD_TIMEOUT); xfer->ux_status = status; /* * If we're dying, skip the hardware action and just notify the * software that we're done. */ if (sc->sc_dying) goto out; /* * Take whatever HCI-specific actions are necessary to notify * the hardware that we're not interested any more. */ (*pipe->up_methods->upm_abort)(xfer); out: /* * We are done -- the transfer is complete, cancelled or timed * out, so notify any waiters, call any callbacks, and move the * pipe on to the next transfer. */ usb_transfer_complete(xfer); KASSERT(mutex_owned(bus->ub_lock)); }