MdeModulePkg/XhciDxe: Event Ring traverse algorithm enhancement to avoid that those completed async transfer events don't get handled in time and are flushed by newer coming events.

Signed-off-by: erictian
Reviewed-by: li-elvin


git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13145 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
erictian
2012-03-27 12:07:38 +00:00
parent ecc722ad41
commit a50f7c4c09
7 changed files with 2346 additions and 2287 deletions

View File

@@ -47,10 +47,6 @@ XhcCreateCmdTrb (
Urb->TrbStart->CycleBit = Urb->Ring->RingPCS & BIT0;
Urb->TrbEnd = Urb->TrbStart;
Urb->EvtRing = &Xhc->EventRing;
XhcSyncEventRing (Xhc, Urb->EvtRing);
Urb->EvtTrbStart = Urb->EvtRing->EventRingEnqueue;
return Urb;
}
@@ -106,10 +102,8 @@ XhcCmdTransfer (
goto ON_EXIT;
}
ASSERT (Urb->EvtRing == &Xhc->EventRing);
Status = XhcExecTransfer (Xhc, TRUE, Urb, Timeout);
*EvtTrb = Urb->EvtTrbStart;
*EvtTrb = Urb->EvtTrb;
if (Urb->Result == EFI_USB_NOERROR) {
Status = EFI_SUCCESS;
@@ -216,6 +210,12 @@ XhcCreateTransferTrb (
return EFI_DEVICE_ERROR;
}
Urb->Finished = FALSE;
Urb->StartDone = FALSE;
Urb->EndDone = FALSE;
Urb->Completed = 0;
Urb->Result = EFI_USB_NOERROR;
Dci = XhcEndpointToDci (Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction));
ASSERT (Dci < 32);
EPRing = (TRANSFER_RING *)(UINTN) Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1];
@@ -234,9 +234,6 @@ XhcCreateTransferTrb (
Urb->TrbStart = EPRing->RingEnqueue;
switch (EPType) {
case ED_CONTROL_BIDIR:
Urb->EvtRing = &Xhc->EventRing;
XhcSyncEventRing (Xhc, Urb->EvtRing);
Urb->EvtTrbStart = Urb->EvtRing->EventRingEnqueue;
//
// For control transfer, create SETUP_STAGE_TRB first.
//
@@ -325,10 +322,6 @@ XhcCreateTransferTrb (
case ED_BULK_OUT:
case ED_BULK_IN:
Urb->EvtRing = &Xhc->EventRing;
XhcSyncEventRing (Xhc, Urb->EvtRing);
Urb->EvtTrbStart = Urb->EvtRing->EventRingEnqueue;
TotalLen = 0;
Len = 0;
TrbNum = 0;
@@ -364,10 +357,6 @@ XhcCreateTransferTrb (
case ED_INTERRUPT_OUT:
case ED_INTERRUPT_IN:
Urb->EvtRing = &Xhc->EventRing;
XhcSyncEventRing (Xhc, Urb->EvtRing);
Urb->EvtTrbStart = Urb->EvtRing->EventRingEnqueue;
TotalLen = 0;
Len = 0;
TrbNum = 0;
@@ -829,39 +818,78 @@ XhcFreeSched (
}
/**
Check if it is ring TRB.
Check if the Trb is a transaction of the URBs in XHCI's asynchronous transfer list.
@param Ring The transfer ring
@param Trb The TRB to check if it's in the transfer ring
@param Xhc The XHCI Instance.
@param Trb The TRB to be checked.
@param Urb The pointer to the matched Urb.
@retval TRUE It is in the ring
@retval FALSE It is not in the ring
@retval TRUE The Trb is matched with a transaction of the URBs in the async list.
@retval FALSE The Trb is not matched with any URBs in the async list.
**/
BOOLEAN
IsAsyncIntTrb (
IN USB_XHCI_INSTANCE *Xhc,
IN TRB_TEMPLATE *Trb,
OUT URB **Urb
)
{
LIST_ENTRY *Entry;
LIST_ENTRY *Next;
TRB_TEMPLATE *CheckedTrb;
URB *CheckedUrb;
UINTN Index;
EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Xhc->AsyncIntTransfers) {
CheckedUrb = EFI_LIST_CONTAINER (Entry, URB, UrbList);
CheckedTrb = CheckedUrb->TrbStart;
for (Index = 0; Index < CheckedUrb->TrbNum; Index++) {
if (Trb == CheckedTrb) {
*Urb = CheckedUrb;
return TRUE;
}
CheckedTrb++;
if ((UINTN)CheckedTrb >= ((UINTN) CheckedUrb->Ring->RingSeg0 + sizeof (TRB_TEMPLATE) * CheckedUrb->Ring->TrbNumber)) {
CheckedTrb = (TRB_TEMPLATE*) CheckedUrb->Ring->RingSeg0;
}
}
}
return FALSE;
}
/**
Check if the Trb is a transaction of the URB.
@param Trb The TRB to be checked
@param Urb The transfer ring to be checked.
@retval TRUE It is a transaction of the URB.
@retval FALSE It is not any transaction of the URB.
**/
BOOLEAN
IsTransferRingTrb (
IN TRANSFER_RING *Ring,
IN TRB_TEMPLATE *Trb
IN TRB_TEMPLATE *Trb,
IN URB *Urb
)
{
BOOLEAN Flag;
TRB_TEMPLATE *Trb1;
TRB_TEMPLATE *CheckedTrb;
UINTN Index;
Trb1 = Ring->RingSeg0;
Flag = FALSE;
CheckedTrb = Urb->Ring->RingSeg0;
ASSERT (Ring->TrbNumber == CMD_RING_TRB_NUMBER || Ring->TrbNumber == TR_RING_TRB_NUMBER);
ASSERT (Urb->Ring->TrbNumber == CMD_RING_TRB_NUMBER || Urb->Ring->TrbNumber == TR_RING_TRB_NUMBER);
for (Index = 0; Index < Ring->TrbNumber; Index++) {
if (Trb == Trb1) {
Flag = TRUE;
break;
for (Index = 0; Index < Urb->Ring->TrbNumber; Index++) {
if (Trb == CheckedTrb) {
return TRUE;
}
Trb1++;
CheckedTrb++;
}
return Flag;
return FALSE;
}
/**
@@ -880,20 +908,26 @@ XhcCheckUrbResult (
IN URB *Urb
)
{
BOOLEAN StartDone;
BOOLEAN EndDone;
EVT_TRB_TRANSFER *EvtTrb;
TRB_TEMPLATE *TRBPtr;
UINTN Index;
UINT8 TRBType;
EFI_STATUS Status;
URB *AsyncUrb;
URB *CheckedUrb;
UINT64 XhcDequeue;
UINT32 High;
UINT32 Low;
ASSERT ((Xhc != NULL) && (Urb != NULL));
Urb->Completed = 0;
Urb->Result = EFI_USB_NOERROR;
Status = EFI_SUCCESS;
EvtTrb = NULL;
Status = EFI_SUCCESS;
if (Urb->Finished) {
goto EXIT;
}
EvtTrb = NULL;
if (XhcIsHalt (Xhc) || XhcIsSysError (Xhc)) {
Urb->Result |= EFI_USB_ERR_SYSTEM;
@@ -902,16 +936,15 @@ XhcCheckUrbResult (
}
//
// Restore the EventRingDequeue and poll the transfer event ring from beginning
// Traverse the event ring to find out all new events from the previous check.
//
StartDone = FALSE;
EndDone = FALSE;
Urb->EvtRing->EventRingDequeue = Urb->EvtTrbStart;
for (Index = 0; Index < Urb->EvtRing->TrbNumber; Index++) {
XhcSyncEventRing (Xhc, Urb->EvtRing);
Status = XhcCheckNewEvent (Xhc, Urb->EvtRing, ((TRB_TEMPLATE **)&EvtTrb));
XhcSyncEventRing (Xhc, &Xhc->EventRing);
for (Index = 0; Index < Xhc->EventRing.TrbNumber; Index++) {
Status = XhcCheckNewEvent (Xhc, &Xhc->EventRing, ((TRB_TEMPLATE **)&EvtTrb));
if (Status == EFI_NOT_READY) {
Urb->Result |= EFI_USB_ERR_TIMEOUT;
//
// All new events are handled, return directly.
//
goto EXIT;
}
@@ -923,78 +956,106 @@ XhcCheckUrbResult (
}
TRBPtr = (TRB_TEMPLATE *)(UINTN)(EvtTrb->TRBPtrLo | LShiftU64 ((UINT64) EvtTrb->TRBPtrHi, 32));
if (IsTransferRingTrb (Urb->Ring, TRBPtr)) {
switch (EvtTrb->Completecode) {
case TRB_COMPLETION_STALL_ERROR:
Urb->Result |= EFI_USB_ERR_STALL;
Status = EFI_DEVICE_ERROR;
DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: STALL_ERROR! Completecode = %x\n",EvtTrb->Completecode));
goto EXIT;
break;
case TRB_COMPLETION_BABBLE_ERROR:
Urb->Result |= EFI_USB_ERR_BABBLE;
Status = EFI_DEVICE_ERROR;
DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: BABBLE_ERROR! Completecode = %x\n",EvtTrb->Completecode));
goto EXIT;
break;
case TRB_COMPLETION_DATA_BUFFER_ERROR:
Urb->Result |= EFI_USB_ERR_BUFFER;
Status = EFI_DEVICE_ERROR;
DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: ERR_BUFFER! Completecode = %x\n",EvtTrb->Completecode));
goto EXIT;
break;
case TRB_COMPLETION_USB_TRANSACTION_ERROR:
Urb->Result |= EFI_USB_ERR_TIMEOUT;
Status = EFI_DEVICE_ERROR;
DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n",EvtTrb->Completecode));
goto EXIT;
break;
case TRB_COMPLETION_SHORT_PACKET:
case TRB_COMPLETION_SUCCESS:
if (EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) {
DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: short packet happens!\n"));
}
TRBType = (UINT8) (TRBPtr->Type);
if ((TRBType == TRB_TYPE_DATA_STAGE) ||
(TRBType == TRB_TYPE_NORMAL) ||
(TRBType == TRB_TYPE_ISOCH)) {
Urb->Completed += (Urb->DataLen - EvtTrb->Lenth);
}
Status = EFI_SUCCESS;
break;
default:
DEBUG ((EFI_D_ERROR, "Transfer Default Error Occur! Completecode = 0x%x!\n",EvtTrb->Completecode));
Urb->Result |= EFI_USB_ERR_TIMEOUT;
Status = EFI_DEVICE_ERROR;
goto EXIT;
break;
}
//
// Only check first and end Trb event address
//
if (TRBPtr == Urb->TrbStart) {
StartDone = TRUE;
}
if (TRBPtr == Urb->TrbEnd) {
EndDone = TRUE;
}
if (StartDone && EndDone) {
//
// Update the status of Urb according to the finished event regardless of whether
// the urb is current checked one or in the XHCI's async transfer list.
// This way is used to avoid that those completed async transfer events don't get
// handled in time and are flushed by newer coming events.
//
if (IsTransferRingTrb (TRBPtr, Urb)) {
CheckedUrb = Urb;
} else if (IsAsyncIntTrb (Xhc, TRBPtr, &AsyncUrb)) {
CheckedUrb = AsyncUrb;
} else {
continue;
}
switch (EvtTrb->Completecode) {
case TRB_COMPLETION_STALL_ERROR:
CheckedUrb->Result |= EFI_USB_ERR_STALL;
CheckedUrb->Finished = TRUE;
DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: STALL_ERROR! Completecode = %x\n",EvtTrb->Completecode));
break;
}
case TRB_COMPLETION_BABBLE_ERROR:
CheckedUrb->Result |= EFI_USB_ERR_BABBLE;
CheckedUrb->Finished = TRUE;
DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: BABBLE_ERROR! Completecode = %x\n",EvtTrb->Completecode));
break;
case TRB_COMPLETION_DATA_BUFFER_ERROR:
CheckedUrb->Result |= EFI_USB_ERR_BUFFER;
CheckedUrb->Finished = TRUE;
DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: ERR_BUFFER! Completecode = %x\n",EvtTrb->Completecode));
break;
case TRB_COMPLETION_USB_TRANSACTION_ERROR:
CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT;
CheckedUrb->Finished = TRUE;
DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n",EvtTrb->Completecode));
break;
case TRB_COMPLETION_SHORT_PACKET:
case TRB_COMPLETION_SUCCESS:
if (EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) {
DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: short packet happens!\n"));
}
TRBType = (UINT8) (TRBPtr->Type);
if ((TRBType == TRB_TYPE_DATA_STAGE) ||
(TRBType == TRB_TYPE_NORMAL) ||
(TRBType == TRB_TYPE_ISOCH)) {
CheckedUrb->Completed += (CheckedUrb->DataLen - EvtTrb->Lenth);
}
break;
default:
DEBUG ((EFI_D_ERROR, "Transfer Default Error Occur! Completecode = 0x%x!\n",EvtTrb->Completecode));
CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT;
CheckedUrb->Finished = TRUE;
break;
}
//
// Only check first and end Trb event address
//
if (TRBPtr == CheckedUrb->TrbStart) {
CheckedUrb->StartDone = TRUE;
}
if (TRBPtr == CheckedUrb->TrbEnd) {
CheckedUrb->EndDone = TRUE;
}
if (CheckedUrb->StartDone && CheckedUrb->EndDone) {
CheckedUrb->Finished = TRUE;
CheckedUrb->EvtTrb = (TRB_TEMPLATE *)EvtTrb;
}
}
EXIT:
//
// Advance event ring to last available entry
//
// Some 3rd party XHCI external cards don't support single 64-bytes width register access,
// So divide it to two 32-bytes width register access.
//
Low = XhcReadRuntimeReg (Xhc, XHC_ERDP_OFFSET);
High = XhcReadRuntimeReg (Xhc, XHC_ERDP_OFFSET + 4);
XhcDequeue = (UINT64)(LShiftU64((UINT64)High, 32) | Low);
if ((XhcDequeue & (~0x0F)) != ((UINT64)(UINTN)Xhc->EventRing.EventRingDequeue & (~0x0F))) {
//
// Some 3rd party XHCI external cards don't support single 64-bytes width register access,
// So divide it to two 32-bytes width register access.
//
XhcWriteRuntimeReg (Xhc, XHC_ERDP_OFFSET, XHC_LOW_32BIT (Xhc->EventRing.EventRingDequeue) | BIT3);
XhcWriteRuntimeReg (Xhc, XHC_ERDP_OFFSET + 4, XHC_HIGH_32BIT (Xhc->EventRing.EventRingDequeue));
}
return Status;
}
@@ -1048,12 +1109,16 @@ XhcExecTransfer (
for (Index = 0; Index < Loop; Index++) {
Status = XhcCheckUrbResult (Xhc, Urb);
if ((Status != EFI_NOT_READY)) {
if (Urb->Finished) {
break;
}
gBS->Stall (XHC_POLL_DELAY);
}
if (Index == Loop) {
Urb->Result = EFI_USB_ERR_TIMEOUT;
}
return Status;
}
@@ -1196,7 +1261,7 @@ XhcMonitorAsyncRequests (
//
Status = XhcCheckUrbResult (Xhc, Urb);
if (Status == EFI_NOT_READY) {
if (!Urb->Finished) {
continue;
}
@@ -1219,8 +1284,6 @@ XhcMonitorAsyncRequests (
CopyMem (ProcBuf, Urb->Data, Urb->Completed);
}
XhcUpdateAsyncRequest (Xhc, Urb);
//
// Leave error recovery to its related device driver. A
// common case of the error recovery is to re-submit the
@@ -1244,6 +1307,8 @@ XhcMonitorAsyncRequests (
if (ProcBuf != NULL) {
gBS->FreePool (ProcBuf);
}
XhcUpdateAsyncRequest (Xhc, Urb);
}
gBS->RestoreTPL (OldTpl);
}
@@ -1445,10 +1510,6 @@ XhcSyncEventRing (
{
UINTN Index;
TRB_TEMPLATE *EvtTrb1;
TRB_TEMPLATE *EvtTrb2;
UINT64 XhcDequeue;
UINT32 High;
UINT32 Low;
ASSERT (EvtRing != NULL);
@@ -1456,41 +1517,25 @@ XhcSyncEventRing (
// Calculate the EventRingEnqueue and EventRingCCS.
// Note: only support single Segment
//
EvtTrb1 = EvtRing->EventRingSeg0;
EvtTrb2 = EvtRing->EventRingSeg0;
EvtTrb1 = EvtRing->EventRingDequeue;
for (Index = 0; Index < EvtRing->TrbNumber; Index++) {
if (EvtTrb1->CycleBit != EvtTrb2->CycleBit) {
if (EvtTrb1->CycleBit != EvtRing->EventRingCCS) {
break;
}
EvtTrb1++;
if ((UINTN)EvtTrb1 >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) {
EvtTrb1 = EvtRing->EventRingSeg0;
EvtRing->EventRingCCS = (EvtRing->EventRingCCS) ? 0 : 1;
}
}
if (Index < EvtRing->TrbNumber) {
EvtRing->EventRingEnqueue = EvtTrb1;
EvtRing->EventRingCCS = (EvtTrb2->CycleBit) ? 1 : 0;
} else {
EvtRing->EventRingEnqueue = EvtTrb2;
EvtRing->EventRingCCS = (EvtTrb2->CycleBit) ? 0 : 1;
}
//
// Apply the EventRingDequeue to Xhc
//
// Some 3rd party XHCI external cards don't support single 64-bytes width register access,
// So divide it to two 32-bytes width register access.
//
Low = XhcReadRuntimeReg (Xhc, XHC_ERDP_OFFSET);
High = XhcReadRuntimeReg (Xhc, XHC_ERDP_OFFSET + 4);
XhcDequeue = (UINT64)(LShiftU64((UINT64)High, 32) | Low);
if ((XhcDequeue & (~0x0F)) != ((UINT64)(UINTN)EvtRing->EventRingDequeue & (~0x0F))) {
//
// Some 3rd party XHCI external cards don't support single 64-bytes width register access,
// So divide it to two 32-bytes width register access.
//
XhcWriteRuntimeReg (Xhc, XHC_ERDP_OFFSET, XHC_LOW_32BIT (EvtRing->EventRingDequeue) | BIT3);
XhcWriteRuntimeReg (Xhc, XHC_ERDP_OFFSET + 4, XHC_HIGH_32BIT (EvtRing->EventRingDequeue));
ASSERT (FALSE);
}
return EFI_SUCCESS;
@@ -1579,8 +1624,8 @@ XhcCheckNewEvent (
OUT TRB_TEMPLATE **NewEvtTrb
)
{
EFI_STATUS Status;
TRB_TEMPLATE*EvtTrb;
EFI_STATUS Status;
TRB_TEMPLATE *EvtTrb;
ASSERT (EvtRing != NULL);
@@ -1593,15 +1638,11 @@ XhcCheckNewEvent (
Status = EFI_SUCCESS;
if (((EvtTrb->Status >> 24) & 0xFF) != TRB_COMPLETION_SUCCESS) {
Status = EFI_DEVICE_ERROR;
}
EvtRing->EventRingDequeue++;
//
// If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.
//
if ((UINTN)EvtRing->EventRingDequeue >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) {
if ((UINTN)EvtRing->EventRingDequeue >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) {
EvtRing->EventRingDequeue = EvtRing->EventRingSeg0;
}
@@ -1845,7 +1886,7 @@ XhcInitializeDeviceSlot (
ASSERT (!EFI_ERROR(Status));
DeviceAddress = (UINT8) ((DEVICE_CONTEXT *) OutputContext)->Slot.DeviceAddress;
DEBUG ((EFI_D_INFO, " Address %d assigned succeefully\n", DeviceAddress));
DEBUG ((EFI_D_INFO, " Address %d assigned successfully\n", DeviceAddress));
Xhc->UsbDevContext[SlotId].XhciDevAddr = DeviceAddress;
@@ -2038,7 +2079,7 @@ XhcInitializeDeviceSlot64 (
ASSERT (!EFI_ERROR(Status));
DeviceAddress = (UINT8) ((DEVICE_CONTEXT_64 *) OutputContext)->Slot.DeviceAddress;
DEBUG ((EFI_D_INFO, " Address %d assigned succeefully\n", DeviceAddress));
DEBUG ((EFI_D_INFO, " Address %d assigned successfully\n", DeviceAddress));
Xhc->UsbDevContext[SlotId].XhciDevAddr = DeviceAddress;