/** @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); }