XHCI: Rework _LinkDescriptorForPipe to avoid double-links.
The previous logic would link from the end of the ring to the start, and the TRB there would always itself be a link TRB. Now, we avoid this by linking back to the start from within the TD segments, and putting the "Event Data" TRB there. May help with some problems. Change-Id: I92c4e135ee28c8c89646594a99fb40ee0d6c4484
This commit is contained in:
@@ -1132,7 +1132,7 @@ XHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
|
||||
// Clear the endpoint's TRBs.
|
||||
memset(endpoint->trbs, 0, sizeof(xhci_trb) * XHCI_ENDPOINT_RING_SIZE);
|
||||
endpoint->used = 0;
|
||||
endpoint->current = 0;
|
||||
endpoint->next = 0;
|
||||
|
||||
// Set dequeue pointer location to the beginning of the ring.
|
||||
SetTRDequeue(endpoint->trb_addr, 0, endpoint->id + 1,
|
||||
@@ -1647,7 +1647,7 @@ XHCI::AllocateDevice(Hub *parent, int8 hubAddress, uint8 hubPort,
|
||||
endpoint0->id = 0;
|
||||
endpoint0->td_head = NULL;
|
||||
endpoint0->used = 0;
|
||||
endpoint0->current = 0;
|
||||
endpoint0->next = 0;
|
||||
endpoint0->trbs = device->trbs;
|
||||
endpoint0->trb_addr = device->trb_addr;
|
||||
|
||||
@@ -1881,7 +1881,7 @@ XHCI::_InsertEndpointForPipe(Pipe *pipe)
|
||||
endpoint->id = id;
|
||||
endpoint->td_head = NULL;
|
||||
endpoint->used = 0;
|
||||
endpoint->current = 0;
|
||||
endpoint->next = 0;
|
||||
|
||||
endpoint->trbs = device->trbs + id * XHCI_ENDPOINT_RING_SIZE;
|
||||
endpoint->trb_addr = device->trb_addr
|
||||
@@ -1984,6 +1984,7 @@ XHCI::_LinkDescriptorForPipe(xhci_td *descriptor, xhci_endpoint *endpoint)
|
||||
|
||||
// "used" refers to the number of currently linked TDs, not the number of
|
||||
// used TRBs on the ring (we use 2 TRBs on the ring per transfer.)
|
||||
// Furthermore, we have to leave an empty item between the head and tail.
|
||||
if (endpoint->used >= (XHCI_MAX_TRANSFERS - 1)) {
|
||||
TRACE_ERROR("link descriptor for pipe: max transfers count exceeded\n");
|
||||
return B_BAD_VALUE;
|
||||
@@ -2000,15 +2001,18 @@ XHCI::_LinkDescriptorForPipe(xhci_td *descriptor, xhci_endpoint *endpoint)
|
||||
descriptor->next = endpoint->td_head;
|
||||
endpoint->td_head = descriptor;
|
||||
|
||||
const uint32 current = endpoint->current,
|
||||
eventdata = current + 1,
|
||||
last = XHCI_ENDPOINT_RING_SIZE - 1;
|
||||
uint32 next = eventdata + 1;
|
||||
uint32 link = endpoint->next, eventdata = link + 1, next = eventdata + 1;
|
||||
if (eventdata == XHCI_ENDPOINT_RING_SIZE || next == XHCI_ENDPOINT_RING_SIZE) {
|
||||
// If it's "next" not "eventdata" that got us here, we will be leaving
|
||||
// one TRB at the end of the ring unused.
|
||||
eventdata = 0;
|
||||
next = 1;
|
||||
}
|
||||
|
||||
TRACE("link descriptor for pipe: current %d, next %d\n", current, next);
|
||||
TRACE("link descriptor for pipe: link %d, next %d\n", link, next);
|
||||
|
||||
// Add a Link TRB to the end of the descriptor.
|
||||
phys_addr_t addr = endpoint->trb_addr + eventdata * sizeof(xhci_trb);
|
||||
phys_addr_t addr = endpoint->trb_addr + (eventdata * sizeof(xhci_trb));
|
||||
descriptor->trbs[descriptor->trb_used].address = addr;
|
||||
descriptor->trbs[descriptor->trb_used].status = TRB_2_IRQ(0);
|
||||
descriptor->trbs[descriptor->trb_used].flags = TRB_3_TYPE(TRB_TYPE_LINK)
|
||||
@@ -2032,11 +2036,11 @@ XHCI::_LinkDescriptorForPipe(xhci_td *descriptor, xhci_endpoint *endpoint)
|
||||
#endif
|
||||
|
||||
// Link the descriptor.
|
||||
endpoint->trbs[current].address =
|
||||
endpoint->trbs[link].address =
|
||||
B_HOST_TO_LENDIAN_INT64(descriptor->trb_addr);
|
||||
endpoint->trbs[current].status =
|
||||
endpoint->trbs[link].status =
|
||||
B_HOST_TO_LENDIAN_INT32(TRB_2_IRQ(0));
|
||||
endpoint->trbs[current].flags =
|
||||
endpoint->trbs[link].flags =
|
||||
B_HOST_TO_LENDIAN_INT32(TRB_3_TYPE(TRB_TYPE_LINK));
|
||||
|
||||
// Set up the Event Data TRB (XHCI 1.2 § 4.11.5.2 p230.)
|
||||
@@ -2060,39 +2064,22 @@ XHCI::_LinkDescriptorForPipe(xhci_td *descriptor, xhci_endpoint *endpoint)
|
||||
B_HOST_TO_LENDIAN_INT32(TRB_3_TYPE(TRB_TYPE_EVENT_DATA)
|
||||
| TRB_3_IOC_BIT | TRB_3_CYCLE_BIT);
|
||||
|
||||
if (next == last) {
|
||||
// We always use 2 TRBs per _Link..() call, so if "next" is the last
|
||||
// TRB in the ring, we need to generate a link TRB at "next", and
|
||||
// then wrap it to 0. (We write the cycle bit later, after wrapping,
|
||||
// for the reason noted in the previous comment.)
|
||||
endpoint->trbs[next].address =
|
||||
B_HOST_TO_LENDIAN_INT64(endpoint->trb_addr);
|
||||
endpoint->trbs[next].status =
|
||||
B_HOST_TO_LENDIAN_INT32(TRB_2_IRQ(0));
|
||||
endpoint->trbs[next].flags =
|
||||
B_HOST_TO_LENDIAN_INT32(TRB_3_TYPE(TRB_TYPE_LINK));
|
||||
|
||||
next = 0;
|
||||
}
|
||||
|
||||
endpoint->trbs[next].address = 0;
|
||||
endpoint->trbs[next].status = 0;
|
||||
endpoint->trbs[next].flags = 0;
|
||||
|
||||
memory_write_barrier();
|
||||
|
||||
// Everything is ready, so write the cycle bit(s).
|
||||
endpoint->trbs[current].flags |= B_HOST_TO_LENDIAN_INT32(TRB_3_CYCLE_BIT);
|
||||
if (current == 0 && endpoint->trbs[last].address != 0)
|
||||
endpoint->trbs[last].flags |= B_HOST_TO_LENDIAN_INT32(TRB_3_CYCLE_BIT);
|
||||
// Everything is ready, so write the cycle bit.
|
||||
endpoint->trbs[link].flags |= B_HOST_TO_LENDIAN_INT32(TRB_3_CYCLE_BIT);
|
||||
|
||||
TRACE("_LinkDescriptorForPipe pCurrent %p phys 0x%" B_PRIxPHYSADDR
|
||||
" 0x%" B_PRIxPHYSADDR " 0x%08" B_PRIx32 "\n", &endpoint->trbs[current],
|
||||
endpoint->trb_addr + current * sizeof(struct xhci_trb),
|
||||
endpoint->trbs[current].address,
|
||||
B_LENDIAN_TO_HOST_INT32(endpoint->trbs[current].flags));
|
||||
TRACE("_LinkDescriptorForPipe pLink %p phys 0x%" B_PRIxPHYSADDR
|
||||
" 0x%" B_PRIxPHYSADDR " 0x%08" B_PRIx32 "\n", &endpoint->trbs[link],
|
||||
endpoint->trb_addr + link * sizeof(struct xhci_trb),
|
||||
endpoint->trbs[link].address,
|
||||
B_LENDIAN_TO_HOST_INT32(endpoint->trbs[link].flags));
|
||||
|
||||
endpoint->current = next;
|
||||
endpoint->next = next;
|
||||
endpointLocker.Unlock();
|
||||
|
||||
TRACE("Endpoint status 0x%08" B_PRIx32 " 0x%08" B_PRIx32 " 0x%016" B_PRIx64 "\n",
|
||||
|
||||
@@ -25,10 +25,9 @@ struct xhci_endpoint;
|
||||
class XHCIRootHub;
|
||||
|
||||
|
||||
/* Each transfer requires 2 TRBs on the endpoint "ring" (one for the link TRB,
|
||||
* and one for the Event Data TRB), plus one more at the end for the link TRB
|
||||
* to the start. */
|
||||
#define XHCI_ENDPOINT_RING_SIZE (XHCI_MAX_TRANSFERS * 2 + 1)
|
||||
/* The endpoint ring needs space for 2 TRBs per transfer
|
||||
* (one for the link TRB, and one for the Event Data TRB). */
|
||||
#define XHCI_ENDPOINT_RING_SIZE (XHCI_MAX_TRANSFERS * 2)
|
||||
|
||||
|
||||
typedef struct xhci_td {
|
||||
@@ -61,7 +60,7 @@ typedef struct xhci_endpoint {
|
||||
|
||||
xhci_td* td_head;
|
||||
uint8 used;
|
||||
uint8 current;
|
||||
uint8 next;
|
||||
|
||||
xhci_trb* trbs; // [XHCI_ENDPOINT_RING_SIZE]
|
||||
phys_addr_t trb_addr;
|
||||
|
||||
Reference in New Issue
Block a user