summaryrefslogtreecommitdiffstats
path: root/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c
diff options
context:
space:
mode:
Diffstat (limited to 'SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c')
-rw-r--r--SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c606
1 files changed, 606 insertions, 0 deletions
diff --git a/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c
new file mode 100644
index 0000000000..73963e1199
--- /dev/null
+++ b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c
@@ -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;
+}
+