XHCI: Overhaul isochronous transfer handling.

* Each packet needs to be its own TD.

 * All transfers are scheduled in frames, not microframes.

Combined with a few fixes to the USB audio driver, this seems
to get things working much better than before.
This commit is contained in:
Augustin Cavalier 2024-01-30 00:14:23 -05:00
parent d49d21bba0
commit 1f6757e31f

View File

@ -981,19 +981,20 @@ XHCI::SubmitNormalRequest(Transfer *transfer)
td->trb_used++;
}
// Isochronous-specific
// Isochronous-specific.
if (isochronousData != NULL) {
// This is an isochronous transfer; we need to make the first TRB
// an isochronous TRB.
td->trbs[0].flags &= ~(TRB_3_TYPE(TRB_TYPE_NORMAL));
td->trbs[0].flags |= TRB_3_TYPE(TRB_TYPE_ISOCH);
// This is an isochronous transfer; it should have one TD per packet.
for (uint32 i = 0; i < isochronousData->packet_count; i++) {
td->trbs[i].flags &= ~(TRB_3_TYPE(TRB_TYPE_NORMAL));
td->trbs[i].flags |= TRB_3_TYPE(TRB_TYPE_ISOCH);
// Isochronous pipes are scheduled by microframes, one of which
// is 125us for USB 2 and above. But for USB 1 it was 1ms, so
// we need to use a different frame delta for that case.
uint8 frameDelta = 1;
if (transfer->TransferPipe()->Speed() == USB_SPEED_FULLSPEED)
frameDelta = 8;
if (i != (isochronousData->packet_count - 1)) {
// For all but the last TD, generate events (but not interrupts) on short packets.
// (The last TD uses the regular Event Data TRB.)
td->trbs[i].flags |= TRB_3_ISP_BIT | TRB_3_BEI_BIT;
td->trbs[i].flags &= ~TRB_3_CHAIN_BIT;
}
}
// TODO: We do not currently take Mult into account at all!
// How are we supposed to do that here?
@ -1006,13 +1007,12 @@ XHCI::SubmitNormalRequest(Transfer *transfer)
|| isochronousData->starting_frame_number == NULL) {
// All reads from the microframe index register must be
// incremented by 1. (XHCI 1.2 § 4.14.2.1.4 p265.)
frame = ReadRunReg32(XHCI_MFINDEX) + 1;
frame = (ReadRunReg32(XHCI_MFINDEX) + 1) >> 3;
td->trbs[0].flags |= TRB_3_ISO_SIA_BIT;
} else {
frame = *isochronousData->starting_frame_number;
td->trbs[0].flags |= TRB_3_FRID(frame);
}
frame = (frame + frameDelta) % 2048;
if (isochronousData->starting_frame_number != NULL)
*isochronousData->starting_frame_number = frame;
@ -2636,8 +2636,8 @@ XHCI::HandleTransferComplete(xhci_trb* trb)
transferred = remainder;
remainder = -1;
} else {
// This should only occur under error conditions.
TRACE("got an interrupt for a non-Event Data TRB!\n");
// This should only occur under error conditions, or for isochronous transfers.
TRACE("got transfer event for a non-Event Data TRB!\n");
if (completionCode == COMP_STOPPED_LENGTH_INVALID)
remainder = -1;
@ -2668,7 +2668,31 @@ XHCI::HandleTransferComplete(xhci_trb* trb)
TRACE("HandleTransferComplete td %p trb %" B_PRId64 " found\n",
td, offset);
if (completionCode == COMP_STOPPED_LENGTH_INVALID) {
if (td->transfer != NULL && td->transfer->IsochronousData() != NULL) {
usb_isochronous_data* isochronousData = td->transfer->IsochronousData();
if (transferred < 0)
transferred = (TRB_2_BYTES_GET(td->trbs[offset].status) - remainder);
isochronousData->packet_descriptors[offset].actual_length = transferred;
isochronousData->packet_descriptors[offset].status = (transferred > 0)
? B_OK : B_DEV_FIFO_UNDERRUN;
if (offset != (td->trb_used - 1)) {
// We'll be sent here again.
return;
}
// Compute the real transferred length.
transferred = 0;
for (int32 i = 0; i < offset; i++) {
usb_iso_packet_descriptor& descriptor = isochronousData->packet_descriptors[i];
if (descriptor.status == B_NO_INIT) {
// Assume success.
descriptor.actual_length = descriptor.request_length;
descriptor.status = B_OK;
}
transferred += descriptor.actual_length;
}
} else if (completionCode == COMP_STOPPED_LENGTH_INVALID) {
// To determine transferred length, sum up the lengths of all TRBs
// prior to the referenced one. (XHCI 1.2 § 4.6.9 p136.)
transferred = 0;
@ -3114,20 +3138,6 @@ XHCI::FinishTransfers()
actualLength);
}
usb_isochronous_data* isochronousData = transfer->IsochronousData();
if (isochronousData != NULL) {
size_t packetSize = transfer->DataLength()
/ isochronousData->packet_count,
left = actualLength;
for (uint32 i = 0; i < isochronousData->packet_count; i++) {
size_t size = min_c(packetSize, left);
isochronousData->packet_descriptors[i].actual_length = size;
isochronousData->packet_descriptors[i].status = (size > 0)
? B_OK : B_DEV_FIFO_UNDERRUN;
left -= size;
}
}
if (callbackStatus == B_OK && directionIn && actualLength > 0) {
TRACE("copying in iov count %ld\n", transfer->VectorCount());
status_t status = transfer->PrepareKernelAccess();