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:
parent
d49d21bba0
commit
1f6757e31f
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user