summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c
diff options
context:
space:
mode:
Diffstat (limited to 'MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c')
-rw-r--r--MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c341
1 files changed, 191 insertions, 150 deletions
diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c
index 372702d3f0..5d1f2b96dd 100644
--- a/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c
+++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c
@@ -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;
+ //
+ // 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_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_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_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_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_SHORT_PACKET:
- case TRB_COMPLETION_SUCCESS:
- if (EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) {
- DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: short packet happens!\n"));
- }
+ 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;
- TRBType = (UINT8) (TRBPtr->Type);
- if ((TRBType == TRB_TYPE_DATA_STAGE) ||
- (TRBType == TRB_TYPE_NORMAL) ||
- (TRBType == TRB_TYPE_ISOCH)) {
- Urb->Completed += (Urb->DataLen - EvtTrb->Lenth);
- }
+ 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"));
+ }
- Status = EFI_SUCCESS;
- break;
+ TRBType = (UINT8) (TRBPtr->Type);
+ if ((TRBType == TRB_TYPE_DATA_STAGE) ||
+ (TRBType == TRB_TYPE_NORMAL) ||
+ (TRBType == TRB_TYPE_ISOCH)) {
+ CheckedUrb->Completed += (CheckedUrb->DataLen - EvtTrb->Lenth);
+ }
- 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;
- }
+ break;
- //
- // Only check first and end Trb event address
- //
- if (TRBPtr == Urb->TrbStart) {
- StartDone = TRUE;
- }
+ 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;
+ }
- if (TRBPtr == Urb->TrbEnd) {
- EndDone = TRUE;
- }
+ //
+ // Only check first and end Trb event address
+ //
+ if (TRBPtr == CheckedUrb->TrbStart) {
+ CheckedUrb->StartDone = TRUE;
+ }
- if (StartDone && EndDone) {
- break;
- }
+ 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;