XHCI: Rework TRB size handling.

There is a section of the spec that dictates how TRBs need to be
sized within a TD, and we were not following that. This should
bring us into compliance.

See inline comments for more details.
This commit is contained in:
Augustin Cavalier 2019-12-31 17:42:21 -05:00
parent 84fc83275d
commit 0981cb8686
2 changed files with 62 additions and 60 deletions

View File

@ -795,44 +795,41 @@ XHCI::SubmitNormalRequest(Transfer *transfer)
if (status != B_OK)
return status;
// Compute the size to use for the TRBs, and then how many TRBs
// of this size we will need. We always need at least 1, of course.
size_t dataLength = transfer->DataLength(),
packetSize = pipe->MaxPacketSize(),
packetsPerTrb = 4;
// TRBs within a TD must be "grouped" into TD Fragments, which mostly means
// that a max_burst_payload boundary cannot be crossed within a TRB, but
// only between TRBs. More than one TRB can be in a TD Fragment, but we keep
// things simple by setting trbSize to the MBP. (XHCI 1.2 § 4.11.7.1 p235.)
size_t trbSize = endpoint->max_burst_payload;
if (isochronousData != NULL) {
if (isochronousData->packet_count == 0)
return B_BAD_VALUE;
// Isochronous transfers use more specifically sized packets.
packetSize = transfer->DataLength() / isochronousData->packet_count;
if (packetSize > pipe->MaxPacketSize() || packetSize
trbSize = transfer->DataLength() / isochronousData->packet_count;
if (trbSize > pipe->MaxPacketSize() || trbSize
!= (size_t)isochronousData->packet_descriptors[0].request_length)
return B_BAD_VALUE;
packetsPerTrb = 1;
}
// Now that we know packetSize & packetsPerTrb, compute TRB size and count.
const size_t trbSize = packetsPerTrb * packetSize;
const int32 trbCount = (dataLength + trbSize - 1) / trbSize;
// Now that we know trbSize, compute the count.
const int32 trbCount = (transfer->DataLength() + trbSize - 1) / trbSize;
xhci_td *td = CreateDescriptor(trbCount, trbCount, trbSize);
if (td == NULL)
return B_NO_MEMORY;
// Normal Stage
size_t remaining = dataLength;
int32 remainingPackets = (remaining - trbSize) / packetSize;
size_t remaining = transfer->DataLength();
for (int32 i = 0; i < trbCount; i++) {
// The "TD Size" field of a transfer TRB indicates the number of
// remaining maximum-size *packets* in this TD, *not* including the
// packets in the current TRB, and capped at 31 if there are more
// than 31 packets remaining in the TD. (XHCI 1.2 § 4.11.2.4 p218.)
int32 tdSize = remainingPackets > 31 ? 31 : remainingPackets;
if (tdSize < 0)
tdSize = 0;
int32 trbLength = remaining < trbSize ? remaining : trbSize;
int32 tdSize = remaining / pipe->MaxPacketSize();
if (tdSize > 31)
tdSize = 31;
int32 trbLength = (remaining < trbSize) ? remaining : trbSize;
td->trbs[i].address = td->buffer_addrs[i];
td->trbs[i].status = TRB_2_IRQ(0)
@ -844,7 +841,6 @@ XHCI::SubmitNormalRequest(Transfer *transfer)
td->trb_used++;
remaining -= trbLength;
remainingPackets -= packetsPerTrb;
}
// Isochronous-specific
@ -1482,9 +1478,19 @@ XHCI::AllocateDevice(Hub *parent, int8 hubAddress, uint8 hubPort,
break;
}
xhci_endpoint* endpoint0 = &device->endpoints[0];
mutex_init(&endpoint0->lock, "xhci endpoint lock");
endpoint0->device = device;
endpoint0->id = 0;
endpoint0->td_head = NULL;
endpoint0->used = 0;
endpoint0->current = 0;
endpoint0->trbs = device->trbs;
endpoint0->trb_addr = device->trb_addr;
// configure the Control endpoint 0
if (ConfigureEndpoint(slot, 0, USB_OBJECT_CONTROL_PIPE, false,
device->trb_addr, 0, maxPacketSize, speed, 0, 0) != B_OK) {
if (ConfigureEndpoint(endpoint0, slot, 0, USB_OBJECT_CONTROL_PIPE, false,
0, maxPacketSize, speed, 0, 0) != B_OK) {
TRACE_ERROR("unable to configure default control endpoint\n");
delete_area(device->input_ctx_area);
delete_area(device->device_ctx_area);
@ -1494,15 +1500,6 @@ XHCI::AllocateDevice(Hub *parent, int8 hubAddress, uint8 hubPort,
return NULL;
}
mutex_init(&device->endpoints[0].lock, "xhci endpoint lock");
device->endpoints[0].device = device;
device->endpoints[0].id = 0;
device->endpoints[0].td_head = NULL;
device->endpoints[0].used = 0;
device->endpoints[0].current = 0;
device->endpoints[0].trbs = device->trbs;
device->endpoints[0].trb_addr = device->trb_addr;
// device should get to addressed state (bsr = 0)
if (SetAddress(device->input_ctx_addr, false, slot) != B_OK) {
TRACE_ERROR("unable to set address\n");
@ -1527,7 +1524,7 @@ XHCI::AllocateDevice(Hub *parent, int8 hubAddress, uint8 hubPort,
// Create a temporary pipe with the new address
ControlPipe pipe(parent);
pipe.SetControllerCookie(&device->endpoints[0]);
pipe.SetControllerCookie(endpoint0);
pipe.InitCommon(device->address + 1, 0, speed, Pipe::Default, maxPacketSize, 0,
hubAddress, hubPort);
@ -1692,7 +1689,7 @@ XHCI::_InsertEndpointForPipe(Pipe *pipe)
return B_NO_INIT;
}
uint8 id = (2 * pipe->EndpointAddress()
const uint8 id = (2 * pipe->EndpointAddress()
+ (pipe->Direction() != Pipe::Out ? 1 : 0)) - 1;
if (id >= XHCI_MAX_ENDPOINTS - 1)
return B_BAD_VALUE;
@ -1707,46 +1704,45 @@ XHCI::_InsertEndpointForPipe(Pipe *pipe)
EvaluateContext(device->input_ctx_addr, device->slot);
}
mutex_init(&device->endpoints[id].lock, "xhci endpoint lock");
MutexLocker endpointLocker(device->endpoints[id].lock);
xhci_endpoint* endpoint = &device->endpoints[id];
mutex_init(&endpoint->lock, "xhci endpoint lock");
MutexLocker endpointLocker(endpoint->lock);
device->endpoints[id].device = device;
device->endpoints[id].id = id;
device->endpoints[id].td_head = NULL;
device->endpoints[id].used = 0;
device->endpoints[id].current = 0;
endpoint->device = device;
endpoint->id = id;
endpoint->td_head = NULL;
endpoint->used = 0;
endpoint->current = 0;
device->endpoints[id].trbs = device->trbs
+ id * XHCI_ENDPOINT_RING_SIZE;
device->endpoints[id].trb_addr = device->trb_addr
endpoint->trbs = device->trbs + id * XHCI_ENDPOINT_RING_SIZE;
endpoint->trb_addr = device->trb_addr
+ id * XHCI_ENDPOINT_RING_SIZE * sizeof(xhci_trb);
memset(device->endpoints[id].trbs, 0,
memset(endpoint->trbs, 0,
sizeof(xhci_trb) * XHCI_ENDPOINT_RING_SIZE);
TRACE("_InsertEndpointForPipe trbs device %p endpoint %p\n",
device->trbs, device->endpoints[id].trbs);
device->trbs, endpoint->trbs);
TRACE("_InsertEndpointForPipe trb_addr device 0x%" B_PRIxPHYSADDR
" endpoint 0x%" B_PRIxPHYSADDR "\n", device->trb_addr,
device->endpoints[id].trb_addr);
endpoint->trb_addr);
uint8 endpoint = id + 1;
const uint8 endpointNum = id + 1;
TRACE("trb_addr 0x%" B_PRIxPHYSADDR "\n", device->endpoints[id].trb_addr);
TRACE("trb_addr 0x%" B_PRIxPHYSADDR "\n", endpoint->trb_addr);
status_t status = ConfigureEndpoint(device->slot, id, pipe->Type(),
pipe->Direction() == Pipe::In, device->endpoints[id].trb_addr,
pipe->Interval(), pipe->MaxPacketSize(), usbDevice->Speed(),
pipe->MaxBurst(), pipe->BytesPerInterval());
status_t status = ConfigureEndpoint(endpoint, device->slot, id, pipe->Type(),
pipe->Direction() == Pipe::In, pipe->Interval(), pipe->MaxPacketSize(),
usbDevice->Speed(), pipe->MaxBurst(), pipe->BytesPerInterval());
if (status != B_OK) {
TRACE_ERROR("unable to configure endpoint\n");
TRACE_ERROR("unable to configure endpoint: %s\n", strerror(status));
return status;
}
_WriteContext(&device->input_ctx->input.dropFlags, 0);
_WriteContext(&device->input_ctx->input.addFlags,
(1 << endpoint) | (1 << 0));
(1 << endpointNum) | (1 << 0));
if (endpoint > 1)
if (endpointNum > 1)
ConfigureEndpoint(device->input_ctx_addr, false, device->slot);
else
EvaluateContext(device->input_ctx_addr, device->slot);
@ -1980,8 +1976,8 @@ XHCI::_UnlinkDescriptorForPipe(xhci_td *descriptor, xhci_endpoint *endpoint)
status_t
XHCI::ConfigureEndpoint(uint8 slot, uint8 number, uint8 type, bool directionIn,
uint64 ringAddr, uint16 interval, uint16 maxPacketSize, usb_speed speed,
XHCI::ConfigureEndpoint(xhci_endpoint* ep, uint8 slot, uint8 number, uint8 type,
bool directionIn, uint16 interval, uint16 maxPacketSize, usb_speed speed,
uint8 maxBurst, uint16 bytesPerInterval)
{
struct xhci_device* device = &fDevices[slot];
@ -2057,7 +2053,11 @@ XHCI::ConfigureEndpoint(uint8 slot, uint8 number, uint8 type, bool directionIn,
// Assign maximum packet size, set the ring address, and set the
// "Dequeue Cycle State" bit. (XHCI 1.2 § 6.2.3 Table 6-10 p453.)
dwendpoint1 |= ENDPOINT_1_MAXPACKETSIZE(maxPacketSize);
qwendpoint2 |= ENDPOINT_2_DCS_BIT | ringAddr;
qwendpoint2 |= ENDPOINT_2_DCS_BIT | ep->trb_addr;
// The Max Burst Payload is the number of bytes moved by a
// maximum sized burst. (XHCI 1.2 § 4.11.7.1 p236.)
ep->max_burst_payload = (maxBurst + 1) * maxPacketSize;
// Assign average TRB length.
if ((type & USB_OBJECT_CONTROL_PIPE) != 0) {
@ -2070,8 +2070,8 @@ XHCI::ConfigureEndpoint(uint8 slot, uint8 number, uint8 type, bool directionIn,
// but we don't know what it is here.)
dwendpoint4 |= ENDPOINT_4_AVGTRBLENGTH(maxPacketSize);
} else {
// Under all other circumstances, we put 4 packets in a TRB.
dwendpoint4 |= ENDPOINT_4_AVGTRBLENGTH(maxPacketSize * 4);
// Under all other circumstances, we put max_burst_payload in a TRB.
dwendpoint4 |= ENDPOINT_4_AVGTRBLENGTH(ep->max_burst_payload);
}
// Assign maximum ESIT payload. (XHCI 1.2 § 4.14.2 p259.)

View File

@ -62,6 +62,8 @@ typedef struct xhci_endpoint {
xhci_device* device;
uint8 id;
uint16 max_burst_payload;
xhci_td* td_head;
uint8 used;
uint8 current;
@ -137,8 +139,8 @@ private:
int32 Interrupt();
// Endpoint management
status_t ConfigureEndpoint(uint8 slot, uint8 number,
uint8 type, bool directionIn, uint64 ringAddr,
status_t ConfigureEndpoint(xhci_endpoint* ep, uint8 slot,
uint8 number, uint8 type, bool directionIn,
uint16 interval, uint16 maxPacketSize,
usb_speed speed, uint8 maxBurst,
uint16 bytesPerInterval);