Add DebugCommunicationLibUsb3 for USB3.0 source level debug support.
Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Elvin Li <elvin.li@intel.com> Reviewed-by: Ruiyu Ni <ruiyu.ni@intel.com> Reviewed-by: Feng Tian <feng.tian@intel.com> Reviewed-by: Jeff Fan <jeff.fan@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16224 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
@ -0,0 +1,606 @@
|
||||
/** @file
|
||||
Debug Port Library implementation based on usb3 debug port.
|
||||
|
||||
Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>
|
||||
This program and the accompanying materials
|
||||
are licensed and made available under the terms and conditions of the BSD License
|
||||
which accompanies this distribution. The full text of the license may be found at
|
||||
http://opensource.org/licenses/bsd-license.php.
|
||||
|
||||
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||||
|
||||
**/
|
||||
#include "DebugCommunicationLibUsb3Internal.h"
|
||||
|
||||
/**
|
||||
Synchronize the specified transfer ring to update the enqueue and dequeue pointer.
|
||||
|
||||
@param Handle Debug port handle.
|
||||
@param TrsRing The transfer ring to sync.
|
||||
|
||||
@retval EFI_SUCCESS The transfer ring is synchronized successfully.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
XhcSyncTrsRing (
|
||||
IN USB3_DEBUG_PORT_HANDLE *Handle,
|
||||
IN TRANSFER_RING *TrsRing
|
||||
)
|
||||
{
|
||||
UINTN Index;
|
||||
TRB_TEMPLATE *TrsTrb;
|
||||
UINT32 CycleBit;
|
||||
|
||||
ASSERT (TrsRing != NULL);
|
||||
|
||||
//
|
||||
// Calculate the latest RingEnqueue and RingPCS
|
||||
//
|
||||
TrsTrb = (TRB_TEMPLATE *)(UINTN) TrsRing->RingEnqueue;
|
||||
|
||||
ASSERT (TrsTrb != NULL);
|
||||
|
||||
for (Index = 0; Index < TrsRing->TrbNumber; Index++) {
|
||||
if (TrsTrb->CycleBit != (TrsRing->RingPCS & BIT0)) {
|
||||
break;
|
||||
}
|
||||
TrsTrb++;
|
||||
if ((UINT8) TrsTrb->Type == TRB_TYPE_LINK) {
|
||||
ASSERT (((LINK_TRB*)TrsTrb)->TC != 0);
|
||||
//
|
||||
// set cycle bit in Link TRB as normal
|
||||
//
|
||||
((LINK_TRB*)TrsTrb)->CycleBit = TrsRing->RingPCS & BIT0;
|
||||
//
|
||||
// Toggle PCS maintained by software
|
||||
//
|
||||
TrsRing->RingPCS = (TrsRing->RingPCS & BIT0) ? 0 : 1;
|
||||
TrsTrb = (TRB_TEMPLATE *)(UINTN)((TrsTrb->Parameter1 | LShiftU64 ((UINT64)TrsTrb->Parameter2, 32)) & ~0x0F);
|
||||
}
|
||||
}
|
||||
ASSERT (Index != TrsRing->TrbNumber);
|
||||
|
||||
if ((EFI_PHYSICAL_ADDRESS)(UINTN) TrsTrb != TrsRing->RingEnqueue) {
|
||||
TrsRing->RingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN) TrsTrb;
|
||||
}
|
||||
|
||||
//
|
||||
// Clear the Trb context for enqueue, but reserve the PCS bit which indicates free Trb.
|
||||
//
|
||||
CycleBit = TrsTrb->CycleBit;
|
||||
ZeroMem (TrsTrb, sizeof (TRB_TEMPLATE));
|
||||
TrsTrb->CycleBit = CycleBit;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Synchronize the specified event ring to update the enqueue and dequeue pointer.
|
||||
|
||||
@param Handle Debug port handle.
|
||||
@param EvtRing The event ring to sync.
|
||||
|
||||
@retval EFI_SUCCESS The event ring is synchronized successfully.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
XhcSyncEventRing (
|
||||
IN USB3_DEBUG_PORT_HANDLE *Handle,
|
||||
IN EVENT_RING *EvtRing
|
||||
)
|
||||
{
|
||||
UINTN Index;
|
||||
TRB_TEMPLATE *EvtTrb1;
|
||||
|
||||
ASSERT (EvtRing != NULL);
|
||||
|
||||
//
|
||||
// Calculate the EventRingEnqueue and EventRingCCS.
|
||||
// Note: only support single Segment
|
||||
//
|
||||
EvtTrb1 = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;
|
||||
|
||||
for (Index = 0; Index < EvtRing->TrbNumber; Index++) {
|
||||
if (EvtTrb1->CycleBit != EvtRing->EventRingCCS) {
|
||||
break;
|
||||
}
|
||||
|
||||
EvtTrb1++;
|
||||
|
||||
if ((UINTN)EvtTrb1 >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) {
|
||||
EvtTrb1 = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingSeg0;
|
||||
EvtRing->EventRingCCS = (EvtRing->EventRingCCS) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (Index < EvtRing->TrbNumber) {
|
||||
EvtRing->EventRingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN)EvtTrb1;
|
||||
} else {
|
||||
ASSERT (FALSE);
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Check if there is a new generated event.
|
||||
|
||||
@param Handle Debug port handle.
|
||||
@param EvtRing The event ring to check.
|
||||
@param NewEvtTrb The new event TRB found.
|
||||
|
||||
@retval EFI_SUCCESS Found a new event TRB at the event ring.
|
||||
@retval EFI_NOT_READY The event ring has no new event.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
XhcCheckNewEvent (
|
||||
IN USB3_DEBUG_PORT_HANDLE *Handle,
|
||||
IN EVENT_RING *EvtRing,
|
||||
OUT TRB_TEMPLATE **NewEvtTrb
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
TRB_TEMPLATE *EvtTrb;
|
||||
|
||||
ASSERT (EvtRing != NULL);
|
||||
|
||||
EvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;
|
||||
*NewEvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;
|
||||
|
||||
if (EvtRing->EventRingDequeue == EvtRing->EventRingEnqueue) {
|
||||
return EFI_NOT_READY;
|
||||
}
|
||||
|
||||
Status = EFI_SUCCESS;
|
||||
|
||||
EvtRing->EventRingDequeue += sizeof (TRB_TEMPLATE);
|
||||
//
|
||||
// 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)) {
|
||||
EvtRing->EventRingDequeue = EvtRing->EventRingSeg0;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
Check if the Trb is a transaction of the URB.
|
||||
|
||||
@param Ring The transfer ring to be checked.
|
||||
@param Trb The TRB to be checked.
|
||||
|
||||
@retval TRUE It is a transaction of the URB.
|
||||
@retval FALSE It is not any transaction of the URB.
|
||||
|
||||
**/
|
||||
BOOLEAN
|
||||
IsTrbInTrsRing (
|
||||
IN TRANSFER_RING *Ring,
|
||||
IN TRB_TEMPLATE *Trb
|
||||
)
|
||||
{
|
||||
TRB_TEMPLATE *CheckedTrb;
|
||||
UINTN Index;
|
||||
|
||||
CheckedTrb = (TRB_TEMPLATE *)(UINTN) Ring->RingSeg0;
|
||||
|
||||
ASSERT (Ring->TrbNumber == TR_RING_TRB_NUMBER);
|
||||
|
||||
for (Index = 0; Index < Ring->TrbNumber; Index++) {
|
||||
if (Trb == CheckedTrb) {
|
||||
return TRUE;
|
||||
}
|
||||
CheckedTrb++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
Check the URB's execution result and update the URB's
|
||||
result accordingly.
|
||||
|
||||
@param Handle Debug port handle.
|
||||
@param Urb The URB to check result.
|
||||
|
||||
**/
|
||||
VOID
|
||||
XhcCheckUrbResult (
|
||||
IN USB3_DEBUG_PORT_HANDLE *Handle,
|
||||
IN URB *Urb
|
||||
)
|
||||
{
|
||||
EVT_TRB_TRANSFER *EvtTrb;
|
||||
TRB_TEMPLATE *TRBPtr;
|
||||
UINTN Index;
|
||||
EFI_STATUS Status;
|
||||
URB *CheckedUrb;
|
||||
UINT64 XhcDequeue;
|
||||
UINT32 High;
|
||||
UINT32 Low;
|
||||
|
||||
ASSERT ((Handle != NULL) && (Urb != NULL));
|
||||
|
||||
if (Urb->Finished) {
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
EvtTrb = NULL;
|
||||
|
||||
//
|
||||
// Traverse the event ring to find out all new events from the previous check.
|
||||
//
|
||||
XhcSyncEventRing (Handle, &Handle->EventRing);
|
||||
|
||||
for (Index = 0; Index < Handle->EventRing.TrbNumber; Index++) {
|
||||
|
||||
Status = XhcCheckNewEvent (Handle, &Handle->EventRing, ((TRB_TEMPLATE **)&EvtTrb));
|
||||
if (Status == EFI_NOT_READY) {
|
||||
//
|
||||
// All new events are handled, return directly.
|
||||
//
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if ((EvtTrb->Type != TRB_TYPE_COMMAND_COMPLT_EVENT) && (EvtTrb->Type != TRB_TYPE_TRANS_EVENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TRBPtr = (TRB_TEMPLATE *)(UINTN)(EvtTrb->TRBPtrLo | LShiftU64 ((UINT64) EvtTrb->TRBPtrHi, 32));
|
||||
|
||||
if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Urb->Ring), TRBPtr)) {
|
||||
CheckedUrb = Urb;
|
||||
} else if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Handle->UrbIn.Ring), TRBPtr)) {
|
||||
//
|
||||
// If it is read event and it should be generated by poll, and current operation is write, we need save data into internal buffer.
|
||||
// Internal buffer is used by next read.
|
||||
//
|
||||
Handle->DataCount = (UINT8) (Handle->UrbIn.DataLen - EvtTrb->Length);
|
||||
CopyMem (Handle->Data, (VOID *)(UINTN)Handle->UrbIn.Data, Handle->DataCount);
|
||||
//
|
||||
// Fill this TRB complete with CycleBit, otherwise next read will fail with old TRB.
|
||||
//
|
||||
TRBPtr->CycleBit = (TRBPtr->CycleBit & BIT0) ? 0 : 1;
|
||||
continue;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) ||
|
||||
(EvtTrb->Completecode == TRB_COMPLETION_SUCCESS)) {
|
||||
//
|
||||
// The length of data which were transferred.
|
||||
//
|
||||
CheckedUrb->Completed += (CheckedUrb->DataLen - EvtTrb->Length);
|
||||
} else {
|
||||
CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT;
|
||||
}
|
||||
//
|
||||
// This Urb has been processed
|
||||
//
|
||||
CheckedUrb->Finished = TRUE;
|
||||
}
|
||||
|
||||
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 = XhcReadDebugReg (Handle, XHC_DC_DCERDP);
|
||||
High = XhcReadDebugReg (Handle, XHC_DC_DCERDP + 4);
|
||||
XhcDequeue = (UINT64)(LShiftU64((UINT64)High, 32) | Low);
|
||||
|
||||
if ((XhcDequeue & (~0x0F)) != ((UINT64)(UINTN)Handle->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.
|
||||
//
|
||||
XhcWriteDebugReg (Handle, XHC_DC_DCERDP, XHC_LOW_32BIT (Handle->EventRing.EventRingDequeue));
|
||||
XhcWriteDebugReg (Handle, XHC_DC_DCERDP + 4, XHC_HIGH_32BIT (Handle->EventRing.EventRingDequeue));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Ring the door bell to notify XHCI there is a transaction to be executed.
|
||||
|
||||
@param Handle Debug port handle.
|
||||
@param Urb The pointer to URB.
|
||||
|
||||
@retval EFI_SUCCESS Successfully ring the door bell.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
XhcRingDoorBell (
|
||||
IN USB3_DEBUG_PORT_HANDLE *Handle,
|
||||
IN URB *Urb
|
||||
)
|
||||
{
|
||||
UINT32 Dcdb;
|
||||
|
||||
//
|
||||
// 7.6.8.2 DCDB Register
|
||||
//
|
||||
Dcdb = (Urb->Direction == EfiUsbDataIn) ? 0x100 : 0x0;
|
||||
|
||||
XhcWriteDebugReg (
|
||||
Handle,
|
||||
XHC_DC_DCDB,
|
||||
Dcdb
|
||||
);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Execute the transfer by polling the URB. This is a synchronous operation.
|
||||
|
||||
@param Handle Debug port handle.
|
||||
@param Urb The URB to execute.
|
||||
@param Timeout The time to wait before abort, in microsecond.
|
||||
|
||||
**/
|
||||
VOID
|
||||
XhcExecTransfer (
|
||||
IN USB3_DEBUG_PORT_HANDLE *Handle,
|
||||
IN URB *Urb,
|
||||
IN UINTN Timeout
|
||||
)
|
||||
{
|
||||
TRANSFER_RING *Ring;
|
||||
UINT64 Begin;
|
||||
UINT64 TimeoutTicker;
|
||||
UINT64 TimerRound;
|
||||
TRB_TEMPLATE *Trb;
|
||||
|
||||
Begin = 0;
|
||||
TimeoutTicker = 0;
|
||||
TimerRound = 0;
|
||||
|
||||
XhcRingDoorBell (Handle, Urb);
|
||||
|
||||
if (Timeout != 0) {
|
||||
Begin = GetPerformanceCounter ();
|
||||
TimeoutTicker = DivU64x32 (
|
||||
MultU64x64 (
|
||||
Handle->TimerFrequency,
|
||||
Timeout
|
||||
),
|
||||
1000000u
|
||||
);
|
||||
TimerRound = DivU64x64Remainder (
|
||||
TimeoutTicker,
|
||||
DivU64x32 (Handle->TimerCycle, 2),
|
||||
&TimeoutTicker
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Event Ring Not Empty bit can only be set to 1 by XHC after ringing door bell with some delay.
|
||||
//
|
||||
while (TRUE) {
|
||||
if (Timeout != 0) {
|
||||
if (TimerRound == 0) {
|
||||
if (IsTimerTimeout (Handle, Begin, TimeoutTicker)) {
|
||||
//
|
||||
// If time out occurs.
|
||||
//
|
||||
Urb->Result |= EFI_USB_ERR_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (IsTimerTimeout (Handle, Begin, DivU64x32 (Handle->TimerCycle, 2))) {
|
||||
TimerRound --;
|
||||
}
|
||||
}
|
||||
}
|
||||
XhcCheckUrbResult (Handle, Urb);
|
||||
if (Urb->Finished) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// If URB transfer is error, restore transfer ring to original value before URB transfer
|
||||
// This will make the current transfer TRB is always at the latest unused one in transfer ring.
|
||||
//
|
||||
Ring = (TRANSFER_RING *)(UINTN) Urb->Ring;
|
||||
if ((Urb->Result != EFI_USB_NOERROR) && (Urb->Direction == EfiUsbDataIn)) {
|
||||
//
|
||||
// Adjust Enqueue pointer
|
||||
//
|
||||
Ring->RingEnqueue = Urb->Trb;
|
||||
//
|
||||
// Clear CCS flag for next use
|
||||
//
|
||||
Trb = (TRB_TEMPLATE *)(UINTN) Urb->Trb;
|
||||
Trb->CycleBit = ((~Ring->RingPCS) & BIT0);
|
||||
} else {
|
||||
//
|
||||
// Update transfer ring for next transfer.
|
||||
//
|
||||
XhcSyncTrsRing (Handle, Ring);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Create a transfer TRB.
|
||||
|
||||
@param Handle Debug port handle.
|
||||
@param Urb The urb used to construct the transfer TRB.
|
||||
|
||||
@return Created TRB or NULL
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
XhcCreateTransferTrb (
|
||||
IN USB3_DEBUG_PORT_HANDLE *Handle,
|
||||
IN URB *Urb
|
||||
)
|
||||
{
|
||||
TRANSFER_RING *EPRing;
|
||||
TRB *Trb;
|
||||
|
||||
if (Urb->Direction == EfiUsbDataIn) {
|
||||
EPRing = &Handle->TransferRingIn;
|
||||
} else {
|
||||
EPRing = &Handle->TransferRingOut;
|
||||
}
|
||||
|
||||
Urb->Ring = (EFI_PHYSICAL_ADDRESS)(UINTN) EPRing;
|
||||
XhcSyncTrsRing (Handle, EPRing);
|
||||
|
||||
Urb->Trb = EPRing->RingEnqueue;
|
||||
Trb = (TRB *)(UINTN)EPRing->RingEnqueue;
|
||||
Trb = (TRB *)(UINTN)EPRing->RingEnqueue;
|
||||
Trb->TrbNormal.TRBPtrLo = XHC_LOW_32BIT (Urb->Data);
|
||||
Trb->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT (Urb->Data);
|
||||
Trb->TrbNormal.Length = Urb->DataLen;
|
||||
Trb->TrbNormal.TDSize = 0;
|
||||
Trb->TrbNormal.IntTarget = 0;
|
||||
Trb->TrbNormal.ISP = 1;
|
||||
Trb->TrbNormal.IOC = 1;
|
||||
Trb->TrbNormal.Type = TRB_TYPE_NORMAL;
|
||||
|
||||
//
|
||||
// Update the cycle bit to indicate this TRB has been consumed.
|
||||
//
|
||||
Trb->TrbNormal.CycleBit = EPRing->RingPCS & BIT0;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Create a new URB for a new transaction.
|
||||
|
||||
@param Handle Debug port handle.
|
||||
@param Direction The direction of data flow.
|
||||
@param Data The user data to transfer
|
||||
@param DataLen The length of data buffer
|
||||
|
||||
@return Created URB or NULL
|
||||
|
||||
**/
|
||||
URB*
|
||||
XhcCreateUrb (
|
||||
IN USB3_DEBUG_PORT_HANDLE *Handle,
|
||||
IN EFI_USB_DATA_DIRECTION Direction,
|
||||
IN VOID *Data,
|
||||
IN UINTN DataLen
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
URB *Urb;
|
||||
EFI_PHYSICAL_ADDRESS UrbData;
|
||||
|
||||
if (Direction == EfiUsbDataIn) {
|
||||
Urb = &Handle->UrbIn;
|
||||
} else {
|
||||
Urb = &Handle->UrbOut;
|
||||
}
|
||||
|
||||
UrbData = Urb->Data;
|
||||
|
||||
ZeroMem (Urb, sizeof (URB));
|
||||
Urb->Direction = Direction;
|
||||
|
||||
//
|
||||
// Allocate memory to move data from CAR or SMRAM to normal memory
|
||||
// to make XHCI DMA successfully
|
||||
// re-use the pre-allocate buffer in PEI to avoid DXE memory service or gBS are not ready
|
||||
//
|
||||
Urb->Data = UrbData;
|
||||
|
||||
if (Direction == EfiUsbDataIn) {
|
||||
//
|
||||
// Do not break URB data in buffer as it may contain the data which were just put in via DMA by XHC
|
||||
//
|
||||
Urb->DataLen = (UINT32) DataLen;
|
||||
} else {
|
||||
//
|
||||
// Put data into URB data out buffer which will create TRBs
|
||||
//
|
||||
ZeroMem ((VOID*)(UINTN) Urb->Data, DataLen);
|
||||
CopyMem ((VOID*)(UINTN) Urb->Data, Data, DataLen);
|
||||
Urb->DataLen = (UINT32) DataLen;
|
||||
}
|
||||
|
||||
Status = XhcCreateTransferTrb (Handle, Urb);
|
||||
ASSERT_EFI_ERROR (Status);
|
||||
|
||||
return Urb;
|
||||
}
|
||||
|
||||
/**
|
||||
Submits bulk transfer to a bulk endpoint of a USB device.
|
||||
|
||||
@param Handle Debug port handle.
|
||||
@param Direction The direction of data transfer.
|
||||
@param Data Array of pointers to the buffers of data to transmit
|
||||
from or receive into.
|
||||
@param DataLength The lenght of the data buffer.
|
||||
@param Timeout Indicates the maximum time, in microsecond, which
|
||||
the transfer is allowed to complete.
|
||||
|
||||
@retval EFI_SUCCESS The transfer was completed successfully.
|
||||
@retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource.
|
||||
@retval EFI_INVALID_PARAMETER Some parameters are invalid.
|
||||
@retval EFI_TIMEOUT The transfer failed due to timeout.
|
||||
@retval EFI_DEVICE_ERROR The transfer failed due to host controller error.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
XhcDataTransfer (
|
||||
IN USB3_DEBUG_PORT_HANDLE *Handle,
|
||||
IN EFI_USB_DATA_DIRECTION Direction,
|
||||
IN OUT VOID *Data,
|
||||
IN OUT UINTN *DataLength,
|
||||
IN UINTN Timeout
|
||||
)
|
||||
{
|
||||
URB *Urb;
|
||||
EFI_STATUS Status;
|
||||
|
||||
//
|
||||
// Validate the parameters
|
||||
//
|
||||
if ((DataLength == NULL) || (*DataLength == 0) || (Data == NULL)) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
//
|
||||
// Create a new URB, insert it into the asynchronous
|
||||
// schedule list, then poll the execution status.
|
||||
//
|
||||
Urb = XhcCreateUrb (Handle, Direction, Data, *DataLength);
|
||||
ASSERT (Urb != NULL);
|
||||
|
||||
XhcExecTransfer (Handle, Urb, Timeout);
|
||||
|
||||
*DataLength = Urb->Completed;
|
||||
|
||||
Status = EFI_TIMEOUT;
|
||||
if (Urb->Result == EFI_USB_NOERROR) {
|
||||
Status = EFI_SUCCESS;
|
||||
}
|
||||
|
||||
if (Direction == EfiUsbDataIn) {
|
||||
//
|
||||
// Move data from internal buffer to outside buffer (outside buffer may be in SMRAM...)
|
||||
// SMRAM does not allow to do DMA, so we create an internal buffer.
|
||||
//
|
||||
CopyMem (Data, (VOID *)(UINTN)Urb->Data, *DataLength);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
Reference in New Issue
Block a user