/** @file
Usb3 Debug Port library instance
Copyright (c) 2013 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include "Usb3DebugPortLibInternal.h"
EFI_SMRAM_DESCRIPTOR mSmramCheckRanges[MAX_SMRAM_RANGE];
UINTN mSmramCheckRangeCount = 0;
BOOLEAN mUsb3InSmm = FALSE;
UINT64 mUsb3MmioSize = 0;
/**
Synchronize the specified transfer ring to update the enqueue and dequeue pointer.
@param Xhc The XHCI Instance.
@param TrsRing The transfer ring to sync.
@retval EFI_SUCCESS The transfer ring is synchronized successfully.
**/
EFI_STATUS
EFIAPI
XhcSyncTrsRing (
IN USB3_DEBUG_PORT_INSTANCE *Xhc,
IN TRANSFER_RING *TrsRing
)
{
UINTN Index;
TRB_TEMPLATE *TrsTrb;
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
//
TrsTrb->Parameter1 = 0;
TrsTrb->Parameter2 = 0;
TrsTrb->Status = 0;
TrsTrb->RsvdZ1 = 0;
TrsTrb->Type = 0;
TrsTrb->Control = 0;
return EFI_SUCCESS;
}
/**
Synchronize the specified event ring to update the enqueue and dequeue pointer.
@param Xhc The XHCI Instance.
@param EvtRing The event ring to sync.
@retval EFI_SUCCESS The event ring is synchronized successfully.
**/
EFI_STATUS
EFIAPI
XhcSyncEventRing (
IN USB3_DEBUG_PORT_INSTANCE *Xhc,
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 Xhc The XHCI Instance.
@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_INSTANCE *Xhc,
IN EVENT_RING *EvtRing,
OUT TRB_TEMPLATE **NewEvtTrb
)
{
EFI_STATUS Status;
ASSERT (EvtRing != NULL);
*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 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 TRB_TEMPLATE *Trb,
IN URB *Urb
)
{
TRB_TEMPLATE *CheckedTrb;
TRANSFER_RING *Ring;
UINTN Index;
Ring = (TRANSFER_RING *)(UINTN) Urb->Ring;
CheckedTrb = (TRB_TEMPLATE *)(UINTN) Ring->RingSeg0;
ASSERT (Ring->TrbNumber == CMD_RING_TRB_NUMBER || 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 Xhc The XHCI Instance.
@param Urb The URB to check result.
@return Whether the result of URB transfer is finialized.
**/
EFI_STATUS
XhcCheckUrbResult (
IN USB3_DEBUG_PORT_INSTANCE *Xhc,
IN URB *Urb
)
{
EVT_TRB_TRANSFER *EvtTrb;
TRB_TEMPLATE *TRBPtr;
UINTN Index;
UINT8 TRBType;
EFI_STATUS Status;
URB *CheckedUrb;
UINT64 XhcDequeue;
UINT32 High;
UINT32 Low;
ASSERT ((Xhc != NULL) && (Urb != NULL));
Status = EFI_SUCCESS;
if (Urb->Finished) {
goto EXIT;
}
EvtTrb = NULL;
//
// Traverse the event ring to find out all new events from the previous check.
//
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) {
//
// All new events are handled, return directly.
//
goto EXIT;
}
//
// Only handle COMMAND_COMPLETETION_EVENT and TRANSFER_EVENT.
//
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));
//
// 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 {
continue;
}
switch (EvtTrb->Completecode) {
case TRB_COMPLETION_STALL_ERROR:
CheckedUrb->Result |= EFI_USB_ERR_STALL;
CheckedUrb->Finished = TRUE;
break;
case TRB_COMPLETION_BABBLE_ERROR:
CheckedUrb->Result |= EFI_USB_ERR_BABBLE;
CheckedUrb->Finished = TRUE;
break;
case TRB_COMPLETION_DATA_BUFFER_ERROR:
CheckedUrb->Result |= EFI_USB_ERR_BUFFER;
CheckedUrb->Finished = TRUE;
break;
case TRB_COMPLETION_USB_TRANSACTION_ERROR:
CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT;
CheckedUrb->Finished = TRUE;
break;
case TRB_COMPLETION_SHORT_PACKET:
case TRB_COMPLETION_SUCCESS:
if (EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) {
}
TRBType = (UINT8) (TRBPtr->Type);
if ((TRBType == TRB_TYPE_DATA_STAGE) ||
(TRBType == TRB_TYPE_NORMAL) ||
(TRBType == TRB_TYPE_ISOCH)) {
CheckedUrb->Completed += (CheckedUrb->DataLen - EvtTrb->Length);
}
break;
default:
CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT;
CheckedUrb->Finished = TRUE;
break;
}
//
// Only check first and end Trb event address
//
if ((EFI_PHYSICAL_ADDRESS)(UINTN) TRBPtr == CheckedUrb->TrbStart) {
CheckedUrb->StartDone = TRUE;
}
if ((EFI_PHYSICAL_ADDRESS)(UINTN) TRBPtr == CheckedUrb->TrbEnd) {
CheckedUrb->EndDone = TRUE;
}
if (CheckedUrb->StartDone && CheckedUrb->EndDone) {
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 (Xhc, XHC_DC_DCERDP);
High = XhcReadDebugReg (Xhc, XHC_DC_DCERDP + 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.
//
XhcWriteDebugReg (Xhc, XHC_DC_DCERDP, XHC_LOW_32BIT (Xhc->EventRing.EventRingDequeue));
XhcWriteDebugReg (Xhc, XHC_DC_DCERDP + 4, XHC_HIGH_32BIT (Xhc->EventRing.EventRingDequeue));
}
return Status;
}
/**
Ring the door bell to notify XHCI there is a transaction to be executed.
@param Xhc The XHCI Instance.
@param Urb The pointer to URB.
@retval EFI_SUCCESS Successfully ring the door bell.
**/
EFI_STATUS
EFIAPI
XhcRingDoorBell (
IN USB3_DEBUG_PORT_INSTANCE *Xhc,
IN URB *Urb
)
{
UINT32 Dcdb;
//
// 7.6.8.2 DCDB Register
//
if (Urb->Direction == EfiUsbDataIn) {
Dcdb = 0x100;
} else {
Dcdb = 0x0;
}
XhcWriteDebugReg (
Xhc,
XHC_DC_DCDB,
Dcdb
);
return EFI_SUCCESS;
}
/**
Execute the transfer by polling the URB. This is a synchronous operation.
@param Xhc The XHCI Instance.
@param Urb The URB to execute.
@param Timeout The time to wait before abort, in millisecond.
@return EFI_DEVICE_ERROR The transfer failed due to transfer error.
@return EFI_TIMEOUT The transfer failed due to time out.
@return EFI_SUCCESS The transfer finished OK.
**/
EFI_STATUS
XhcExecTransfer (
IN USB3_DEBUG_PORT_INSTANCE *Xhc,
IN URB *Urb,
IN UINTN Timeout
)
{
EFI_STATUS Status;
UINTN Index;
UINTN Loop;
TRB_TEMPLATE *Trb;
TRANSFER_RING *Ring;
TRB_TEMPLATE *TrbStart;
TRB_TEMPLATE *TrbEnd;
Status = EFI_SUCCESS;
Loop = (Timeout * XHC_1_MILLISECOND / XHC_POLL_DELAY) + 1;
if (Timeout == 0) {
Loop = 0xFFFFFFFF;
}
XhcRingDoorBell (Xhc, Urb);
//
// DSCT BIT0: Event Ring Not Empty bit can only be set to 1 by XHC after ringing door bell with some delay.
//
for (Index = 0; Index < Loop; Index++) {
Status = XhcCheckUrbResult (Xhc, Urb);
if (Urb->Finished) {
break;
}
MicroSecondDelay (XHC_POLL_DELAY);
}
if (Index == Loop) {
Urb->Result = EFI_USB_ERR_TIMEOUT;
}
//
// 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.
// Without this code, when there is read TRB from target, but host does not write anything, this TRB (A)
// will be still here, next read TRB (B) will be put next to TRB (A), when host write then, the TRB (A)
// will be used to contain data, not TRB(B), this will cause Finished flag will not be set and return error in this function.
//
Ring = (TRANSFER_RING *)(UINTN) Urb->Ring;
if (Urb->Result != EFI_USB_NOERROR) {
Ring->RingEnqueue = Urb->TrbStart;
//
// Clear CCS flag for next use
//
TrbStart = (TRB_TEMPLATE *)(UINTN) Urb->TrbStart;
TrbEnd = (TRB_TEMPLATE *)(UINTN) Urb->TrbEnd;
for (Trb = TrbStart; Trb <= TrbEnd; Trb++) {
Trb->CycleBit = ((~Ring->RingPCS) & BIT0);
}
}
return Status;
}
/**
Create a transfer TRB.
@param Xhc The XHCI Instance
@param Urb The urb used to construct the transfer TRB.
@return Created TRB or NULL
**/
EFI_STATUS
XhcCreateTransferTrb (
IN USB3_DEBUG_PORT_INSTANCE *Xhc,
IN URB *Urb
)
{
TRANSFER_RING *EPRing;
TRB *TrbStart;
UINT32 TotalLen;
UINT32 Len;
UINT32 TrbNum;
Urb->Finished = FALSE;
Urb->StartDone = FALSE;
Urb->EndDone = FALSE;
Urb->Completed = 0;
Urb->Result = EFI_USB_NOERROR;
if (Urb->Direction == EfiUsbDataIn) {
EPRing = &Xhc->TransferRingIn;
} else {
EPRing = &Xhc->TransferRingOut;
}
Urb->Ring = (EFI_PHYSICAL_ADDRESS)(UINTN) EPRing;
//
// Construct the TRB for ED_BULK_OUT and ED_BULK_IN
//
XhcSyncTrsRing (Xhc, EPRing);
Urb->TrbStart = EPRing->RingEnqueue;
TotalLen = 0;
Len = 0;
TrbNum = 0;
TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue;
while (TotalLen < Urb->DataLen) {
if ((TotalLen + 0x10000) >= Urb->DataLen) {
Len = Urb->DataLen - TotalLen;
} else {
Len = 0x10000;
}
TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue;
TrbStart->TrbNormal.TRBPtrLo = XHC_LOW_32BIT(Urb->Data + TotalLen);
TrbStart->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT(Urb->Data + TotalLen);
TrbStart->TrbNormal.Length = Len;
TrbStart->TrbNormal.TDSize = 0;
TrbStart->TrbNormal.IntTarget = 0;
TrbStart->TrbNormal.ISP = 1;
TrbStart->TrbNormal.IOC = 1;
TrbStart->TrbNormal.Type = TRB_TYPE_NORMAL;
//
// Update the cycle bit
//
TrbStart->TrbNormal.CycleBit = EPRing->RingPCS & BIT0;
XhcSyncTrsRing (Xhc, EPRing);
TrbNum++;
TotalLen += Len;
}
Urb->TrbNum = TrbNum;
//
// Update to the last TRB
//
Urb->TrbEnd = (EFI_PHYSICAL_ADDRESS)(UINTN) TrbStart;
return EFI_SUCCESS;
}
/**
Create a new URB for a new transaction.
@param Xhc The XHCI Instance
@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_INSTANCE *Xhc,
IN EFI_USB_DATA_DIRECTION Direction,
IN VOID *Data,
IN UINTN DataLen
)
{
EFI_STATUS Status;
URB *Urb;
EFI_PHYSICAL_ADDRESS DataAddress;
Urb = &Xhc->Urb;
ASSERT (Urb->Data != 0);
DataAddress = Urb->Data;
ZeroMem (Urb, sizeof (URB));
Urb->Signature = USB3_DEBUG_PORT_INSTANCE_SIGNATURE;
Urb->Direction = Direction;
Urb->Data = DataAddress;
ZeroMem ((VOID*)(UINTN) Urb->Data, DataLen);
CopyMem ((VOID*)(UINTN) Urb->Data, Data, DataLen);
Urb->DataLen = (UINT32) DataLen;
Status = XhcCreateTransferTrb (Xhc, Urb);
ASSERT_EFI_ERROR (Status);
return Urb;
}
/**
Submits bulk transfer to a bulk endpoint of a USB device.
@param Xhc The instance of debug device.
@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 millisecond, which
the transfer is allowed to complete.
@param TransferResult Transfer result.
@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_INSTANCE *Xhc,
IN EFI_USB_DATA_DIRECTION Direction,
IN OUT VOID *Data,
IN OUT UINTN *DataLength,
IN UINTN Timeout,
OUT UINT32 *TransferResult
)
{
URB *Urb;
EFI_STATUS Status;
//
// Validate the parameters
//
if ((DataLength == NULL) || (*DataLength == 0) ||
(Data == NULL) || (TransferResult == NULL)) {
return EFI_INVALID_PARAMETER;
}
*TransferResult = EFI_USB_ERR_SYSTEM;
Status = EFI_DEVICE_ERROR;
//
// Create a new URB, insert it into the asynchronous
// schedule list, then poll the execution status.
//
Urb = XhcCreateUrb (Xhc, Direction, Data, *DataLength);
if (Urb == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
Status = XhcExecTransfer (Xhc, Urb, Timeout);
*TransferResult = Urb->Result;
*DataLength = Urb->Completed;
if (*TransferResult == EFI_USB_NOERROR) {
Status = EFI_SUCCESS;
}
ON_EXIT:
return Status;
}
/**
Check whether the MMIO Bar is within any of the SMRAM ranges.
@param[in] XhcMmioBase The address of the MMIO to be checked.
@retval TURE The XHCI MMIO is in SMRAM ranges.
@retval FALSE The XHCI MMIO is out of SMRAM ranges.
**/
BOOLEAN
EFIAPI
Usb3DebugIsAddressInSmram (
IN EFI_PHYSICAL_ADDRESS XhcMmioBase
)
{
UINTN Index;
if (mSmramCheckRangeCount == 0) {
//
// When we invoke this function, we are already in SMM mode,
// but SmmAccess->GetCapabilities failed which kept mSmramCheckRanges as 0.
//
return TRUE;
}
for (Index = 0; Index < mSmramCheckRangeCount; Index ++) {
if (((XhcMmioBase >= mSmramCheckRanges[Index].CpuStart) && (XhcMmioBase < mSmramCheckRanges[Index].CpuStart + mSmramCheckRanges[Index].PhysicalSize)) ||
((mSmramCheckRanges[Index].CpuStart >= XhcMmioBase) && (mSmramCheckRanges[Index].CpuStart < XhcMmioBase + mUsb3MmioSize))) {
return TRUE;
}
}
return FALSE;
}
/**
Transfer data via XHC controller.
@param Data Data buffer.
@param Length Data length.
@param Direction Transfer direction.
**/
VOID
Usb3DebugPortDataTransfer (
UINT8 *Data,
UINTN *Length,
EFI_USB_DATA_DIRECTION Direction
)
{
USB3_DEBUG_PORT_INSTANCE *Instance;
EFI_PHYSICAL_ADDRESS XhcMmioBase;
UINT16 Command;
UINT8 Bus;
UINT8 Device;
UINT8 Function;
UINT32 TransferResult;
UINT32 Dcctrl;
EFI_PHYSICAL_ADDRESS UsbBase;
UINTN BytesToSend;
USB3_DEBUG_PORT_CONTROLLER UsbDebugPort;
EFI_STATUS Status;
USB3_DEBUG_PORT_INSTANCE UsbDbgInstance;
UsbDebugPort.Controller = GetUsb3DebugPortController();
Bus = UsbDebugPort.PciAddress.Bus;
Device = UsbDebugPort.PciAddress.Device;
Function = UsbDebugPort.PciAddress.Function;
//
// MMIO base address is possible to clear, set it if it is cleared. (XhciMemorySpaceClose in PchUsbCommon.c)
//
XhcMmioBase = GetXhciBaseAddress ();
Command = GetXhciPciCommand ();
if ((XhcMmioBase == 0) || (XhcMmioBase == XHCI_BASE_ADDRESS_64_BIT_MASK)) {
//
// XHCI device MMIO base is cleared by someone, set it again
//
UsbBase = PcdGet32 (PcdXhciDefaultBaseAddress);
PciWrite32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET), (UINT32)UsbBase);
PciWrite32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4), 0x0);
UsbBase = PciRead32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET)) & XHCI_BASE_ADDRESS_32_BIT_MASK;
if (UsbBase == 0 || UsbBase == XHCI_BASE_ADDRESS_32_BIT_MASK) {
return;
}
}
//
// Check if XHC debug MMIO range is in SMRAM
//
if ((mUsb3InSmm) && (Usb3DebugIsAddressInSmram (XhcMmioBase))) {
return;
}
//
// Save and set Command Register
//
if (((Command & EFI_PCI_COMMAND_MEMORY_SPACE) == 0) || ((Command & EFI_PCI_COMMAND_BUS_MASTER) == 0)) {
PciWrite16(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET), Command | EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_BUS_MASTER);
PciRead16(PCI_LIB_ADDRESS(Bus, Device, Function, PCI_COMMAND_OFFSET));
}
Instance = GetUsb3DebugPortInstance ();
if (Instance != NULL) {
if (!Instance->DebugSupport) {
//
// Debug device is not supported by XHCI, return
//
goto Done;
}
}
if ((Instance != NULL) && (Instance->Ready)) {
//
// Debug device is broken suddently (e.g. Windows OS), return
//
Dcctrl = XhcReadDebugReg (Instance, XHC_DC_DCCTRL);
if ((Dcctrl & BIT0) == 0) {
goto Done;
}
}
if ((Instance != NULL) && (!Instance->Ready)) {
//
// Debug host does not connect with target
//
goto Done;
}
if (Instance == NULL) {
ZeroMem (&UsbDbgInstance, sizeof (USB3_DEBUG_PORT_INSTANCE));
DiscoverUsb3DebugPort (&UsbDbgInstance);
if (UsbDbgInstance.DebugSupport) {
if (!IsAllocatePagesReady ()) {
//
// AllocatePages can not work, return
//
goto Done;
}
}
Status = USB3InitializeReal ();
if (EFI_ERROR (Status)) {
//
// Debug device is failed to initialize
//
goto Done;
}
//
// Update instance
//
Instance = GetUsb3DebugPortInstance ();
if (Instance == NULL) {
//
// Debug device instance is failed to create
//
goto Done;
}
if ((!Instance->Ready) || (!Instance->DebugSupport)) {
//
// Debug host does not connect at first or is not supported
//
goto Done;
}
}
BytesToSend = 0;
while (*Length > 0) {
BytesToSend = ((*Length) > XHC_DEBUG_PORT_DATA_LENGTH) ? XHC_DEBUG_PORT_DATA_LENGTH : *Length;
XhcDataTransfer (
Instance,
Direction,
Data,
&BytesToSend,
DATA_TRANSFER_TIME_OUT,
&TransferResult
);
if (TransferResult != EFI_USB_NOERROR) {
break;
}
*Length -= BytesToSend;
Data += BytesToSend;
}
Done:
//
// Restore Command Register
//
PciWrite16(PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), Command);
}
/**
Receive data over the USB3 debug cable.
@param[out] Data Pointer to data
@param[in, out] Length Pointer to data length
**/
RETURN_STATUS
Usb3DbgIn (
OUT UINT8 *Data,
IN OUT UINTN *Length
)
{
Usb3DebugPortDataTransfer (Data, Length, EfiUsbDataIn);
return EFI_SUCCESS;
}
/**
Send data over the USB3 debug cable.
@param[out] Data Pointer to data
@param[in, out] Length Pointer to data length
**/
VOID
Usb3DbgOut (
OUT UINT8 *Data,
IN OUT UINTN *Length
)
{
Usb3DebugPortDataTransfer (Data, Length, EfiUsbDataOut);
}