From 8a12a74e81e7969b2b963f95bb27779b80dc9ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Wed, 27 Jul 2016 21:26:18 +0200 Subject: [PATCH] XHCI: fix KDL for transfers using more than 1TD. * TD chains were broken, any transfer above ~1MB would trigger a KDL. * _LinkDescriptorForPipe() links the last TD of the chain instead of the first. * Still buggy: the transfer ring can still block and transfers then fail. * Enable TRB_3_ISP_BIT where appropriate (Interrupt on Short Packet). * Also fix WriteDescriptorChain() and ReadDescriptorChain() for multiple-TD chains. --- src/add-ons/kernel/busses/usb/xhci.cpp | 105 ++++++++++++++++--------- src/add-ons/kernel/busses/usb/xhci.h | 3 +- 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/src/add-ons/kernel/busses/usb/xhci.cpp b/src/add-ons/kernel/busses/usb/xhci.cpp index 764a6c73a9..7564ca1978 100644 --- a/src/add-ons/kernel/busses/usb/xhci.cpp +++ b/src/add-ons/kernel/busses/usb/xhci.cpp @@ -614,7 +614,8 @@ XHCI::SubmitControlRequest(Transfer *transfer) | TRB_2_BYTES(requestData->Length) | TRB_2_TD_SIZE(transfer->VectorCount()); setupDescriptor->trbs[index].dwtrb3 = TRB_3_TYPE(TRB_TYPE_DATA_STAGE) - | (directionIn ? TRB_3_DIR_IN : 0) | TRB_3_CYCLE_BIT; + | (directionIn ? (TRB_3_DIR_IN | TRB_3_ISP_BIT) : 0) + | TRB_3_CYCLE_BIT; // TODO copy data for out transfers index++; @@ -663,23 +664,44 @@ XHCI::SubmitNormalRequest(Transfer *transfer) return B_BAD_VALUE; bool directionIn = (pipe->Direction() == Pipe::In); - xhci_td *descriptor = CreateDescriptorChain(transfer->DataLength()); + int32 trbCount = 0; + xhci_td *descriptor = CreateDescriptorChain(transfer->DataLength(), trbCount); if (descriptor == NULL) return B_NO_MEMORY; - descriptor->trb_count = descriptor->buffer_count; + + xhci_td *td_chain = descriptor; + xhci_td *last = descriptor; + int32 rest = trbCount - 1; // set NormalStage - uint8 index; - for (index = 0; index < descriptor->buffer_count; index++) { - descriptor->trbs[index].qwtrb0 = descriptor->buffer_phy[index]; - descriptor->trbs[index].dwtrb2 = TRB_2_IRQ(0) - | TRB_2_BYTES(descriptor->buffer_size[index]) - | TRB_2_TD_SIZE(descriptor->trb_count); - descriptor->trbs[index].dwtrb3 = TRB_3_TYPE(TRB_TYPE_NORMAL) - | TRB_3_CYCLE_BIT; + while (td_chain != NULL) { + td_chain->trb_count = td_chain->buffer_count; + uint8 index; + for (index = 0; index < td_chain->buffer_count; index++) { + td_chain->trbs[index].qwtrb0 = descriptor->buffer_phy[index]; + td_chain->trbs[index].dwtrb2 = TRB_2_IRQ(0) + | TRB_2_BYTES(descriptor->buffer_size[index]) + | TRB_2_TD_SIZE(rest); + td_chain->trbs[index].dwtrb3 = TRB_3_TYPE(TRB_TYPE_NORMAL) + | TRB_3_CYCLE_BIT | TRB_3_CHAIN_BIT | (directionIn ? TRB_3_ISP_BIT : 0); + rest--; + } + // link next td, if any + if (td_chain->next_chain != NULL) { + td_chain->trbs[td_chain->trb_count].qwtrb0 = td_chain->next_chain->this_phy; + td_chain->trbs[td_chain->trb_count].dwtrb2 = TRB_2_IRQ(0); + td_chain->trbs[td_chain->trb_count].dwtrb3 = TRB_3_TYPE(TRB_TYPE_LINK) + | TRB_3_CYCLE_BIT | TRB_3_CHAIN_BIT; + } + + last = td_chain; + td_chain = td_chain->next_chain; + } + + if (last->trb_count > 0) { + last->trbs[last->trb_count - 1].dwtrb3 |= TRB_3_IOC_BIT; + last->trbs[last->trb_count - 1].dwtrb3 &= ~TRB_3_CHAIN_BIT; } - if (descriptor->trb_count > 0) - descriptor->trbs[index - 1].dwtrb3 |= TRB_3_IOC_BIT; if (!directionIn) { TRACE("copying out iov count %ld\n", transfer->VectorCount()); @@ -826,10 +848,10 @@ XHCI::AddTo(Stack *stack) xhci_td * -XHCI::CreateDescriptorChain(size_t bufferSize) +XHCI::CreateDescriptorChain(size_t bufferSize, int32 &trbCount) { size_t packetSize = B_PAGE_SIZE * 16; - int32 trbCount = (bufferSize + packetSize - 1) / packetSize; + trbCount = (bufferSize + packetSize - 1) / packetSize; // keep one trb for linking int32 tdCount = (trbCount + XHCI_MAX_TRBS_PER_TD - 2) / (XHCI_MAX_TRBS_PER_TD - 1); @@ -839,12 +861,13 @@ XHCI::CreateDescriptorChain(size_t bufferSize) for (int32 i = 0; i < tdCount; i++) { xhci_td *descriptor = CreateDescriptor(0); if (!descriptor) { - //FreeDescriptorChain(firstDescriptor); + if (first != NULL) + FreeDescriptor(first); return NULL; } else if (first == NULL) first = descriptor; - uint8 trbs = min_c(trbCount, XHCI_MAX_TRBS_PER_TD); + uint8 trbs = min_c(trbCount, XHCI_MAX_TRBS_PER_TD - 1); TRACE("CreateDescriptorChain trbs %d for td %" B_PRId32 "\n", trbs, i); for (int j = 0; j < trbs; j++) { if (fStack->AllocateChunk(&descriptor->buffer_log[j], @@ -888,6 +911,8 @@ XHCI::CreateDescriptor(size_t bufferSize) result->buffer_size[0] = bufferSize; result->trb_count = 0; result->buffer_count = 1; + result->next = NULL; + result->next_chain = NULL; if (bufferSize <= 0) { result->buffer_log[0] = NULL; result->buffer_phy[0] = 0; @@ -902,6 +927,9 @@ XHCI::CreateDescriptor(size_t bufferSize) return NULL; } + TRACE("CreateDescriptor allocated buffer_size %ld %p\n", + result->buffer_size[0], result->buffer_log[0]); + return result; } @@ -909,20 +937,22 @@ XHCI::CreateDescriptor(size_t bufferSize) void XHCI::FreeDescriptor(xhci_td *descriptor) { - if (!descriptor) - return; + while (descriptor != NULL) { - for (int i = 0; i < descriptor->buffer_count; i++) { - if (descriptor->buffer_size[i] == 0) - continue; - TRACE("FreeDescriptor buffer %d buffer_size %ld\n", i, - descriptor->buffer_size[i]); - fStack->FreeChunk(descriptor->buffer_log[i], - descriptor->buffer_phy[i], descriptor->buffer_size[i]); + for (int i = 0; i < descriptor->buffer_count; i++) { + if (descriptor->buffer_size[i] == 0) + continue; + TRACE("FreeDescriptor buffer %d buffer_size %ld %p\n", i, + descriptor->buffer_size[i], descriptor->buffer_log[i]); + fStack->FreeChunk(descriptor->buffer_log[i], + descriptor->buffer_phy[i], descriptor->buffer_size[i]); + } + + xhci_td *next = descriptor->next_chain; + fStack->FreeChunk(descriptor, descriptor->this_phy, + sizeof(xhci_td)); + descriptor = next; } - - fStack->FreeChunk(descriptor, descriptor->this_phy, - sizeof(xhci_td)); } @@ -966,9 +996,9 @@ XHCI::WriteDescriptorChain(xhci_td *descriptor, iovec *vector, } if (bufferOffset >= current->buffer_size[trbIndex]) { + bufferOffset = 0; if (++trbIndex >= current->buffer_count) break; - bufferOffset = 0; } } @@ -1016,14 +1046,14 @@ XHCI::ReadDescriptorChain(xhci_td *descriptor, iovec *vector, actualLength); return actualLength; } - vectorOffset = 0; + } if (bufferOffset >= current->buffer_size[trbIndex]) { + bufferOffset = 0; if (++trbIndex >= current->buffer_count) break; - bufferOffset = 0; } } @@ -1470,17 +1500,22 @@ XHCI::_LinkDescriptorForPipe(xhci_td *descriptor, xhci_endpoint *endpoint) TRACE("_LinkDescriptorForPipe current %d, next %d\n", current, next); + xhci_td *last = descriptor; + while (last->next_chain != NULL) + last = last->next_chain; + // compute next link addr_t addr = endpoint->trb_addr + next * sizeof(struct xhci_trb); - descriptor->trbs[descriptor->trb_count].qwtrb0 = addr; - descriptor->trbs[descriptor->trb_count].dwtrb2 = TRB_2_IRQ(0); - descriptor->trbs[descriptor->trb_count].dwtrb3 = TRB_3_TYPE(TRB_TYPE_LINK) + last->trbs[last->trb_count].qwtrb0 = addr; + last->trbs[last->trb_count].dwtrb2 = TRB_2_IRQ(0); + last->trbs[last->trb_count].dwtrb3 = TRB_3_TYPE(TRB_TYPE_LINK) | TRB_3_IOC_BIT | TRB_3_CYCLE_BIT; endpoint->trbs[next].qwtrb0 = 0; endpoint->trbs[next].dwtrb2 = 0; endpoint->trbs[next].dwtrb3 = 0; + // link the descriptor endpoint->trbs[current].qwtrb0 = descriptor->this_phy; endpoint->trbs[current].dwtrb2 = TRB_2_IRQ(0); endpoint->trbs[current].dwtrb3 = TRB_3_TYPE(TRB_TYPE_LINK) diff --git a/src/add-ons/kernel/busses/usb/xhci.h b/src/add-ons/kernel/busses/usb/xhci.h index de84a827d9..1ad1e012ab 100644 --- a/src/add-ons/kernel/busses/usb/xhci.h +++ b/src/add-ons/kernel/busses/usb/xhci.h @@ -140,7 +140,8 @@ private: // Descriptor xhci_td * CreateDescriptor(size_t bufferSize); - xhci_td * CreateDescriptorChain(size_t bufferSize); + xhci_td * CreateDescriptorChain(size_t bufferSize, + int32 &trbCount); void FreeDescriptor(xhci_td *descriptor); size_t WriteDescriptorChain(xhci_td *descriptor,