From 1d79fe616f62046c964bad789ea0324431272dd4 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Wed, 13 Mar 2019 00:09:39 -0400 Subject: [PATCH] XHCI: Implement CancelQueuedTransfers and RemoveEndpointForPipe. Seems to work OK on my hardware now. Possibly helps with device unplug/replug issues; though that worked on my hardware before this change, too. This is now more correct at least. --- src/add-ons/kernel/busses/usb/xhci.cpp | 96 +++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/src/add-ons/kernel/busses/usb/xhci.cpp b/src/add-ons/kernel/busses/usb/xhci.cpp index 6d1489a283..f60ca296f4 100644 --- a/src/add-ons/kernel/busses/usb/xhci.cpp +++ b/src/add-ons/kernel/busses/usb/xhci.cpp @@ -862,6 +862,61 @@ XHCI::CancelQueuedTransfers(Pipe *pipe, bool force) { TRACE_ALWAYS("cancel queued transfers for pipe %p (%d)\n", pipe, pipe->EndpointAddress()); + + xhci_endpoint *endpoint = (xhci_endpoint *)pipe->ControllerCookie(); + if (endpoint == NULL || endpoint->trbs == NULL) { + // Someone's de-allocated this pipe or endpoint in the meantime. + // (Possibly AllocateDevice failed, and we were the temporary pipe.) + return B_NO_INIT; + } + + MutexLocker endpointLocker(endpoint->lock); + + if (endpoint->td_head == NULL) { + // There aren't any currently pending transfers to cancel. + return B_OK; + } + + // Get the head TD from the endpoint. We don't want to free the TDs while + // holding the endpoint lock, as the callbacks could potentially cause + // deadlocks. + xhci_td* td_head = endpoint->td_head; + endpoint->td_head = NULL; + + if (StopEndpoint(false, endpoint->id + 1, endpoint->device->slot) == B_OK) { + // Clear the endpoint's TRBs. + memset(endpoint->trbs, 0, sizeof(xhci_trb) * XHCI_MAX_TRANSFERS); + endpoint->used = 0; + endpoint->current = 0; + + // Set dequeue pointer location to the beginning of the ring. + SetTRDequeue(endpoint->trb_addr, 0, endpoint->id + 1, + endpoint->device->slot); + + // We don't need to do anything else to restart the ring, as it will resume + // operation as normal upon the next doorbell. (XHCI 1.1 ยง 4.6.9 p132.) + } else { + // We couldn't stop the endpoint. Most likely the device has been + // removed and the endpoint was stopped by the hardware. + TRACE("CancelQueuedTransfers: could not stop endpoint\n"); + } + + endpointLocker.Unlock(); + + xhci_td* td; + while ((td = td_head) != NULL) { + td_head = td_head->next; + + // We can't cancel or delete transfers under "force", as they probably + // are not safe to use anymore. + if (!force && td->transfer != NULL) { + td->transfer->Finished(B_CANCELED, 0); + delete td->transfer; + td->transfer = NULL; + } + FreeDescriptor(td); + } + return B_OK; } @@ -1486,6 +1541,11 @@ XHCI::AllocateDevice(Hub *parent, int8 hubAddress, uint8 hubPort, device->state = XHCI_STATE_DISABLED; return NULL; } + + // We don't want to disable the default endpoint, naturally, which would + // otherwise happen when this Pipe object is destroyed. + pipe.SetControllerCookie(NULL); + fPortSlots[hubPort] = slot; TRACE("AllocateDevice() port %d slot %d\n", hubPort, slot); return deviceObject; @@ -1517,8 +1577,7 @@ XHCI::FreeDevice(Device *device) status_t XHCI::_InsertEndpointForPipe(Pipe *pipe) { - TRACE("_InsertEndpointForPipe endpoint address %" B_PRId8 "\n", - pipe->EndpointAddress()); + TRACE("insert endpoint for pipe %p (%d)\n", pipe, pipe->EndpointAddress()); if (pipe->ControllerCookie() != NULL || pipe->Parent()->Type() != USB_OBJECT_DEVICE) { @@ -1618,9 +1677,40 @@ XHCI::_InsertEndpointForPipe(Pipe *pipe) status_t XHCI::_RemoveEndpointForPipe(Pipe *pipe) { + TRACE("remove endpoint for pipe %p (%d)\n", pipe, pipe->EndpointAddress()); + if (pipe->Parent()->Type() != USB_OBJECT_DEVICE) return B_OK; - //Device* device = (Device *)pipe->Parent(); + Device* usbDevice = (Device *)pipe->Parent(); + if (usbDevice->Parent() == RootObject()) + return B_BAD_VALUE; + + xhci_endpoint *endpoint = (xhci_endpoint *)pipe->ControllerCookie(); + if (endpoint == NULL || endpoint->trbs == NULL) + return B_NO_INIT; + + xhci_device *device = endpoint->device; + + if (endpoint->id > 0) { + mutex_lock(&endpoint->lock); + + uint8 epNumber = endpoint->id + 1; + StopEndpoint(true, epNumber, device->slot); + + mutex_destroy(&endpoint->lock); + memset(endpoint, 0, sizeof(xhci_endpoint)); + + _WriteContext(&device->input_ctx->input.dropFlags, (1 << epNumber)); + _WriteContext(&device->input_ctx->input.addFlags, 0); + + if (epNumber > 1) + ConfigureEndpoint(device->input_ctx_addr, true, device->slot); + else + EvaluateContext(device->input_ctx_addr, device->slot); + + device->state = XHCI_STATE_ADDRESSED; + } + pipe->SetControllerCookie(NULL); return B_OK; }