mirror of
				https://git.proxmox.com/git/mirror_edk2
				synced 2025-10-30 23:50:23 +00:00 
			
		
		
		
	 1825c24fd8
			
		
	
	
		1825c24fd8
		
	
	
	
	
		
			
			Fix various typos in comments and documentation. Cc: Hao A Wu <hao.a.wu@intel.com> Signed-off-by: Antoine Coeur <coeur@gmx.fr> Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com> Reviewed-by: Hao A Wu <hao.a.wu@intel.com> Signed-off-by: Philippe Mathieu-Daude <philmd@redhat.com> Message-Id: <20200207010831.9046-73-philmd@redhat.com>
		
			
				
	
	
		
			578 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			578 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Debug Port Library implementation based on usb3 debug port.
 | |
| 
 | |
|   Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| #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;
 | |
| 
 | |
|   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 beginning 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 ((VOID *)(UINTN)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 += (((TRANSFER_TRB_NORMAL*)TRBPtr)->Length - 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;
 | |
|   TRB_TEMPLATE            *Trb;
 | |
|   UINTN                   Loop;
 | |
|   UINTN                   Index;
 | |
| 
 | |
|   Loop = Timeout / XHC_DEBUG_PORT_1_MILLISECOND;
 | |
|   if (Timeout == 0) {
 | |
|     Loop = 0xFFFFFFFF;
 | |
|   }
 | |
|   XhcRingDoorBell (Handle, Urb);
 | |
|   //
 | |
|   // 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++) {
 | |
|     XhcCheckUrbResult (Handle, Urb);
 | |
|     if (Urb->Finished) {
 | |
|       break;
 | |
|     }
 | |
|     MicroSecondDelay (XHC_DEBUG_PORT_1_MILLISECOND);
 | |
|   }
 | |
|   if (Index == Loop) {
 | |
|     //
 | |
|     // If time out occurs.
 | |
|     //
 | |
|     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.
 | |
|   //
 | |
|   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->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 length 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);
 | |
| 
 | |
|   //
 | |
|   // Make sure the data received from HW can fit in the received buffer.
 | |
|   //
 | |
|   if (Urb->Completed > *DataLength) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   *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;
 | |
| }
 | |
| 
 |