XHCI: Properly handle "Length invalid" and erroring TRBs on the endpoint ring.
Should improve #18432 and other tickets. Change-Id: Iaafe2d9d61bc0514e4dd6283b9e75496d5e2d44a Reviewed-on: https://review.haiku-os.org/c/haiku/+/7341 Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org> Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
parent
0b733c9c80
commit
aeb3a97aba
@ -248,7 +248,7 @@ xhci_error_string(uint32 error)
|
||||
case COMP_COMMAND_RING_STOPPED: return "Command ring stopped";
|
||||
case COMP_COMMAND_ABORTED: return "Command aborted";
|
||||
case COMP_STOPPED: return "Stopped";
|
||||
case COMP_LENGTH_INVALID: return "Length invalid";
|
||||
case COMP_STOPPED_LENGTH_INVALID: return "Stopped (length invalid)";
|
||||
case COMP_MAX_EXIT_LATENCY: return "Max exit latency too large";
|
||||
case COMP_ISOC_OVERRUN: return "Isoch buffer overrun";
|
||||
case COMP_EVENT_LOST: return "Event lost";
|
||||
@ -2637,29 +2637,42 @@ XHCI::HandleTransferComplete(xhci_trb* trb)
|
||||
return;
|
||||
}
|
||||
|
||||
// In the case of an Event Data TRB, the "transferred" field refers
|
||||
// to the actual number of bytes transferred across the whole TD.
|
||||
// (XHCI 1.2 § 6.4.2.1 Table 6-38 p478.)
|
||||
TRACE("HandleTransferComplete: ed %" B_PRIu32 ", status %" B_PRId32 "\n",
|
||||
(flags & TRB_3_EVENT_DATA_BIT), trb->status);
|
||||
|
||||
const uint8 completionCode = TRB_2_COMP_CODE_GET(trb->status);
|
||||
int32 transferred = TRB_2_REM_GET(trb->status), remainder = -1;
|
||||
|
||||
TRACE("HandleTransferComplete: ed %" B_PRIu32 ", code %" B_PRIu8 ", transferred %" B_PRId32 "\n",
|
||||
(flags & TRB_3_EVENT_DATA_BIT), completionCode, transferred);
|
||||
|
||||
if ((flags & TRB_3_EVENT_DATA_BIT) == 0) {
|
||||
int32 remainder = TRB_2_REM_GET(trb->status), transferred = -1;
|
||||
if ((flags & TRB_3_EVENT_DATA_BIT) != 0) {
|
||||
// In the case of an Event Data TRB, value in the status field refers
|
||||
// to the actual number of bytes transferred across the whole TD.
|
||||
// (XHCI 1.2 § 6.4.2.1 Table 6-38 p478.)
|
||||
transferred = remainder;
|
||||
remainder = -1;
|
||||
} else {
|
||||
// This should only occur under error conditions.
|
||||
TRACE("got an interrupt for a non-Event Data TRB!\n");
|
||||
remainder = transferred;
|
||||
transferred = -1;
|
||||
|
||||
if (completionCode == COMP_STOPPED_LENGTH_INVALID)
|
||||
remainder = -1;
|
||||
}
|
||||
|
||||
if (completionCode != COMP_SUCCESS && completionCode != COMP_SHORT_PACKET
|
||||
&& completionCode != COMP_STOPPED) {
|
||||
&& completionCode != COMP_STOPPED && completionCode != COMP_STOPPED_LENGTH_INVALID) {
|
||||
TRACE_ALWAYS("transfer error on slot %" B_PRId8 " endpoint %" B_PRId8
|
||||
": %s\n", slot, endpointNumber, xhci_error_string(completionCode));
|
||||
}
|
||||
|
||||
const phys_addr_t source = B_LENDIAN_TO_HOST_INT64(trb->address);
|
||||
phys_addr_t source = B_LENDIAN_TO_HOST_INT64(trb->address);
|
||||
if (source >= endpoint->trb_addr
|
||||
&& (source - endpoint->trb_addr) < (XHCI_ENDPOINT_RING_SIZE * sizeof(xhci_trb))) {
|
||||
// The "source" address points to a TRB on the ring.
|
||||
// See if we can figure out what it really corresponds to.
|
||||
const int64 offset = (source - endpoint->trb_addr) / sizeof(xhci_trb);
|
||||
const int32 type = TRB_3_TYPE_GET(endpoint->trbs[offset].flags);
|
||||
if (type == TRB_TYPE_EVENT_DATA || type == TRB_TYPE_LINK)
|
||||
source = B_LENDIAN_TO_HOST_INT64(endpoint->trbs[offset].address);
|
||||
}
|
||||
|
||||
for (xhci_td *td = endpoint->td_head; td != NULL; td = td->next) {
|
||||
int64 offset = (source - td->trb_addr) / sizeof(xhci_trb);
|
||||
if (offset < 0 || offset >= td->trb_count)
|
||||
@ -2668,6 +2681,14 @@ XHCI::HandleTransferComplete(xhci_trb* trb)
|
||||
TRACE("HandleTransferComplete td %p trb %" B_PRId64 " found\n",
|
||||
td, offset);
|
||||
|
||||
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;
|
||||
for (int32 i = 0; i < offset; i++)
|
||||
transferred += TRB_2_BYTES_GET(td->trbs[i].status);
|
||||
}
|
||||
|
||||
// The TRB at offset trb_used will be the link TRB, which we do not
|
||||
// care about (and should not generate an interrupt at all.) We really
|
||||
// care about the properly last TRB, at index "count - 1", which the
|
||||
|
@ -220,7 +220,7 @@
|
||||
#define COMP_COMMAND_RING_STOPPED 24
|
||||
#define COMP_COMMAND_ABORTED 25
|
||||
#define COMP_STOPPED 26
|
||||
#define COMP_LENGTH_INVALID 27
|
||||
#define COMP_STOPPED_LENGTH_INVALID 27
|
||||
#define COMP_MAX_EXIT_LATENCY 29
|
||||
#define COMP_ISOC_OVERRUN 31
|
||||
#define COMP_EVENT_LOST 32
|
||||
|
Loading…
x
Reference in New Issue
Block a user