2 Debug Port Library implementation based on usb3 debug port.
4 Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php.
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 #include "DebugCommunicationLibUsb3Internal.h"
17 Synchronize the specified transfer ring to update the enqueue and dequeue pointer.
19 @param Handle Debug port handle.
20 @param TrsRing The transfer ring to sync.
22 @retval EFI_SUCCESS The transfer ring is synchronized successfully.
28 IN USB3_DEBUG_PORT_HANDLE
*Handle
,
29 IN TRANSFER_RING
*TrsRing
36 ASSERT (TrsRing
!= NULL
);
39 // Calculate the latest RingEnqueue and RingPCS
41 TrsTrb
= (TRB_TEMPLATE
*)(UINTN
) TrsRing
->RingEnqueue
;
43 ASSERT (TrsTrb
!= NULL
);
45 for (Index
= 0; Index
< TrsRing
->TrbNumber
; Index
++) {
46 if (TrsTrb
->CycleBit
!= (TrsRing
->RingPCS
& BIT0
)) {
50 if ((UINT8
) TrsTrb
->Type
== TRB_TYPE_LINK
) {
51 ASSERT (((LINK_TRB
*)TrsTrb
)->TC
!= 0);
53 // set cycle bit in Link TRB as normal
55 ((LINK_TRB
*)TrsTrb
)->CycleBit
= TrsRing
->RingPCS
& BIT0
;
57 // Toggle PCS maintained by software
59 TrsRing
->RingPCS
= (TrsRing
->RingPCS
& BIT0
) ? 0 : 1;
60 TrsTrb
= (TRB_TEMPLATE
*)(UINTN
)((TrsTrb
->Parameter1
| LShiftU64 ((UINT64
)TrsTrb
->Parameter2
, 32)) & ~0x0F);
63 ASSERT (Index
!= TrsRing
->TrbNumber
);
65 if ((EFI_PHYSICAL_ADDRESS
)(UINTN
) TrsTrb
!= TrsRing
->RingEnqueue
) {
66 TrsRing
->RingEnqueue
= (EFI_PHYSICAL_ADDRESS
)(UINTN
) TrsTrb
;
70 // Clear the Trb context for enqueue, but reserve the PCS bit which indicates free Trb.
72 CycleBit
= TrsTrb
->CycleBit
;
73 ZeroMem (TrsTrb
, sizeof (TRB_TEMPLATE
));
74 TrsTrb
->CycleBit
= CycleBit
;
80 Synchronize the specified event ring to update the enqueue and dequeue pointer.
82 @param Handle Debug port handle.
83 @param EvtRing The event ring to sync.
85 @retval EFI_SUCCESS The event ring is synchronized successfully.
91 IN USB3_DEBUG_PORT_HANDLE
*Handle
,
92 IN EVENT_RING
*EvtRing
96 TRB_TEMPLATE
*EvtTrb1
;
98 ASSERT (EvtRing
!= NULL
);
101 // Calculate the EventRingEnqueue and EventRingCCS.
102 // Note: only support single Segment
104 EvtTrb1
= (TRB_TEMPLATE
*)(UINTN
) EvtRing
->EventRingDequeue
;
106 for (Index
= 0; Index
< EvtRing
->TrbNumber
; Index
++) {
107 if (EvtTrb1
->CycleBit
!= EvtRing
->EventRingCCS
) {
113 if ((UINTN
)EvtTrb1
>= ((UINTN
) EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
114 EvtTrb1
= (TRB_TEMPLATE
*)(UINTN
) EvtRing
->EventRingSeg0
;
115 EvtRing
->EventRingCCS
= (EvtRing
->EventRingCCS
) ? 0 : 1;
119 if (Index
< EvtRing
->TrbNumber
) {
120 EvtRing
->EventRingEnqueue
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)EvtTrb1
;
129 Check if there is a new generated event.
131 @param Handle Debug port handle.
132 @param EvtRing The event ring to check.
133 @param NewEvtTrb The new event TRB found.
135 @retval EFI_SUCCESS Found a new event TRB at the event ring.
136 @retval EFI_NOT_READY The event ring has no new event.
142 IN USB3_DEBUG_PORT_HANDLE
*Handle
,
143 IN EVENT_RING
*EvtRing
,
144 OUT TRB_TEMPLATE
**NewEvtTrb
148 TRB_TEMPLATE
*EvtTrb
;
150 ASSERT (EvtRing
!= NULL
);
152 EvtTrb
= (TRB_TEMPLATE
*)(UINTN
) EvtRing
->EventRingDequeue
;
153 *NewEvtTrb
= (TRB_TEMPLATE
*)(UINTN
) EvtRing
->EventRingDequeue
;
155 if (EvtRing
->EventRingDequeue
== EvtRing
->EventRingEnqueue
) {
156 return EFI_NOT_READY
;
159 Status
= EFI_SUCCESS
;
161 EvtRing
->EventRingDequeue
+= sizeof (TRB_TEMPLATE
);
163 // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.
165 if ((UINTN
)EvtRing
->EventRingDequeue
>= ((UINTN
) EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
166 EvtRing
->EventRingDequeue
= EvtRing
->EventRingSeg0
;
173 Check if the Trb is a transaction of the URB.
175 @param Ring The transfer ring to be checked.
176 @param Trb The TRB to be checked.
178 @retval TRUE It is a transaction of the URB.
179 @retval FALSE It is not any transaction of the URB.
184 IN TRANSFER_RING
*Ring
,
188 TRB_TEMPLATE
*CheckedTrb
;
191 CheckedTrb
= (TRB_TEMPLATE
*)(UINTN
) Ring
->RingSeg0
;
193 ASSERT (Ring
->TrbNumber
== TR_RING_TRB_NUMBER
);
195 for (Index
= 0; Index
< Ring
->TrbNumber
; Index
++) {
196 if (Trb
== CheckedTrb
) {
206 Check the URB's execution result and update the URB's
209 @param Handle Debug port handle.
210 @param Urb The URB to check result.
215 IN USB3_DEBUG_PORT_HANDLE
*Handle
,
219 EVT_TRB_TRANSFER
*EvtTrb
;
220 TRB_TEMPLATE
*TRBPtr
;
228 ASSERT ((Handle
!= NULL
) && (Urb
!= NULL
));
237 // Traverse the event ring to find out all new events from the previous check.
239 XhcSyncEventRing (Handle
, &Handle
->EventRing
);
241 for (Index
= 0; Index
< Handle
->EventRing
.TrbNumber
; Index
++) {
243 Status
= XhcCheckNewEvent (Handle
, &Handle
->EventRing
, ((TRB_TEMPLATE
**)&EvtTrb
));
244 if (Status
== EFI_NOT_READY
) {
246 // All new events are handled, return directly.
251 if ((EvtTrb
->Type
!= TRB_TYPE_COMMAND_COMPLT_EVENT
) && (EvtTrb
->Type
!= TRB_TYPE_TRANS_EVENT
)) {
255 TRBPtr
= (TRB_TEMPLATE
*)(UINTN
)(EvtTrb
->TRBPtrLo
| LShiftU64 ((UINT64
) EvtTrb
->TRBPtrHi
, 32));
257 if (IsTrbInTrsRing ((TRANSFER_RING
*)(UINTN
)(Urb
->Ring
), TRBPtr
)) {
259 } else if (IsTrbInTrsRing ((TRANSFER_RING
*)(UINTN
)(Handle
->UrbIn
.Ring
), TRBPtr
)) {
261 // If it is read event and it should be generated by poll, and current operation is write, we need save data into internal buffer.
262 // Internal buffer is used by next read.
264 Handle
->DataCount
= (UINT8
) (Handle
->UrbIn
.DataLen
- EvtTrb
->Length
);
265 CopyMem ((VOID
*)(UINTN
)Handle
->Data
, (VOID
*)(UINTN
)Handle
->UrbIn
.Data
, Handle
->DataCount
);
267 // Fill this TRB complete with CycleBit, otherwise next read will fail with old TRB.
269 TRBPtr
->CycleBit
= (TRBPtr
->CycleBit
& BIT0
) ? 0 : 1;
275 if ((EvtTrb
->Completecode
== TRB_COMPLETION_SHORT_PACKET
) ||
276 (EvtTrb
->Completecode
== TRB_COMPLETION_SUCCESS
)) {
278 // The length of data which were transferred.
280 CheckedUrb
->Completed
+= (((TRANSFER_TRB_NORMAL
*)TRBPtr
)->Length
- EvtTrb
->Length
);
282 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
285 // This Urb has been processed
287 CheckedUrb
->Finished
= TRUE
;
292 // Advance event ring to last available entry
294 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
295 // So divide it to two 32-bytes width register access.
297 Low
= XhcReadDebugReg (Handle
, XHC_DC_DCERDP
);
298 High
= XhcReadDebugReg (Handle
, XHC_DC_DCERDP
+ 4);
299 XhcDequeue
= (UINT64
)(LShiftU64((UINT64
)High
, 32) | Low
);
301 if ((XhcDequeue
& (~0x0F)) != ((UINT64
)(UINTN
)Handle
->EventRing
.EventRingDequeue
& (~0x0F))) {
303 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
304 // So divide it to two 32-bytes width register access.
306 XhcWriteDebugReg (Handle
, XHC_DC_DCERDP
, XHC_LOW_32BIT (Handle
->EventRing
.EventRingDequeue
));
307 XhcWriteDebugReg (Handle
, XHC_DC_DCERDP
+ 4, XHC_HIGH_32BIT (Handle
->EventRing
.EventRingDequeue
));
312 Ring the door bell to notify XHCI there is a transaction to be executed.
314 @param Handle Debug port handle.
315 @param Urb The pointer to URB.
317 @retval EFI_SUCCESS Successfully ring the door bell.
323 IN USB3_DEBUG_PORT_HANDLE
*Handle
,
330 // 7.6.8.2 DCDB Register
332 Dcdb
= (Urb
->Direction
== EfiUsbDataIn
) ? 0x100 : 0x0;
344 Execute the transfer by polling the URB. This is a synchronous operation.
346 @param Handle Debug port handle.
347 @param Urb The URB to execute.
348 @param Timeout The time to wait before abort, in microsecond.
353 IN USB3_DEBUG_PORT_HANDLE
*Handle
,
363 Loop
= Timeout
/ XHC_DEBUG_PORT_1_MILLISECOND
;
367 XhcRingDoorBell (Handle
, Urb
);
369 // Event Ring Not Empty bit can only be set to 1 by XHC after ringing door bell with some delay.
371 for (Index
= 0; Index
< Loop
; Index
++) {
372 XhcCheckUrbResult (Handle
, Urb
);
376 MicroSecondDelay (XHC_DEBUG_PORT_1_MILLISECOND
);
380 // If time out occurs.
382 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
385 // If URB transfer is error, restore transfer ring to original value before URB transfer
386 // This will make the current transfer TRB is always at the latest unused one in transfer ring.
388 Ring
= (TRANSFER_RING
*)(UINTN
) Urb
->Ring
;
389 if ((Urb
->Result
!= EFI_USB_NOERROR
) && (Urb
->Direction
== EfiUsbDataIn
)) {
391 // Adjust Enqueue pointer
393 Ring
->RingEnqueue
= Urb
->Trb
;
395 // Clear CCS flag for next use
397 Trb
= (TRB_TEMPLATE
*)(UINTN
) Urb
->Trb
;
398 Trb
->CycleBit
= ((~Ring
->RingPCS
) & BIT0
);
401 // Update transfer ring for next transfer.
403 XhcSyncTrsRing (Handle
, Ring
);
408 Create a transfer TRB.
410 @param Handle Debug port handle.
411 @param Urb The urb used to construct the transfer TRB.
413 @return Created TRB or NULL
417 XhcCreateTransferTrb (
418 IN USB3_DEBUG_PORT_HANDLE
*Handle
,
422 TRANSFER_RING
*EPRing
;
425 if (Urb
->Direction
== EfiUsbDataIn
) {
426 EPRing
= &Handle
->TransferRingIn
;
428 EPRing
= &Handle
->TransferRingOut
;
431 Urb
->Ring
= (EFI_PHYSICAL_ADDRESS
)(UINTN
) EPRing
;
432 XhcSyncTrsRing (Handle
, EPRing
);
434 Urb
->Trb
= EPRing
->RingEnqueue
;
435 Trb
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
436 Trb
->TrbNormal
.TRBPtrLo
= XHC_LOW_32BIT (Urb
->Data
);
437 Trb
->TrbNormal
.TRBPtrHi
= XHC_HIGH_32BIT (Urb
->Data
);
438 Trb
->TrbNormal
.Length
= Urb
->DataLen
;
439 Trb
->TrbNormal
.TDSize
= 0;
440 Trb
->TrbNormal
.IntTarget
= 0;
441 Trb
->TrbNormal
.ISP
= 1;
442 Trb
->TrbNormal
.IOC
= 1;
443 Trb
->TrbNormal
.Type
= TRB_TYPE_NORMAL
;
446 // Update the cycle bit to indicate this TRB has been consumed.
448 Trb
->TrbNormal
.CycleBit
= EPRing
->RingPCS
& BIT0
;
454 Create a new URB for a new transaction.
456 @param Handle Debug port handle.
457 @param Direction The direction of data flow.
458 @param Data The user data to transfer
459 @param DataLen The length of data buffer
461 @return Created URB or NULL
466 IN USB3_DEBUG_PORT_HANDLE
*Handle
,
467 IN EFI_USB_DATA_DIRECTION Direction
,
474 EFI_PHYSICAL_ADDRESS UrbData
;
476 if (Direction
== EfiUsbDataIn
) {
477 Urb
= &Handle
->UrbIn
;
479 Urb
= &Handle
->UrbOut
;
484 ZeroMem (Urb
, sizeof (URB
));
485 Urb
->Direction
= Direction
;
488 // Allocate memory to move data from CAR or SMRAM to normal memory
489 // to make XHCI DMA successfully
490 // re-use the pre-allocate buffer in PEI to avoid DXE memory service or gBS are not ready
494 if (Direction
== EfiUsbDataIn
) {
496 // Do not break URB data in buffer as it may contain the data which were just put in via DMA by XHC
498 Urb
->DataLen
= (UINT32
) DataLen
;
501 // Put data into URB data out buffer which will create TRBs
503 ZeroMem ((VOID
*)(UINTN
) Urb
->Data
, DataLen
);
504 CopyMem ((VOID
*)(UINTN
) Urb
->Data
, Data
, DataLen
);
505 Urb
->DataLen
= (UINT32
) DataLen
;
508 Status
= XhcCreateTransferTrb (Handle
, Urb
);
509 ASSERT_EFI_ERROR (Status
);
515 Submits bulk transfer to a bulk endpoint of a USB device.
517 @param Handle Debug port handle.
518 @param Direction The direction of data transfer.
519 @param Data Array of pointers to the buffers of data to transmit
520 from or receive into.
521 @param DataLength The lenght of the data buffer.
522 @param Timeout Indicates the maximum time, in microsecond, which
523 the transfer is allowed to complete.
525 @retval EFI_SUCCESS The transfer was completed successfully.
526 @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource.
527 @retval EFI_INVALID_PARAMETER Some parameters are invalid.
528 @retval EFI_TIMEOUT The transfer failed due to timeout.
529 @retval EFI_DEVICE_ERROR The transfer failed due to host controller error.
535 IN USB3_DEBUG_PORT_HANDLE
*Handle
,
536 IN EFI_USB_DATA_DIRECTION Direction
,
538 IN OUT UINTN
*DataLength
,
546 // Validate the parameters
548 if ((DataLength
== NULL
) || (*DataLength
== 0) || (Data
== NULL
)) {
549 return EFI_INVALID_PARAMETER
;
553 // Create a new URB, insert it into the asynchronous
554 // schedule list, then poll the execution status.
556 Urb
= XhcCreateUrb (Handle
, Direction
, Data
, *DataLength
);
557 ASSERT (Urb
!= NULL
);
559 XhcExecTransfer (Handle
, Urb
, Timeout
);
561 *DataLength
= Urb
->Completed
;
563 Status
= EFI_TIMEOUT
;
564 if (Urb
->Result
== EFI_USB_NOERROR
) {
565 Status
= EFI_SUCCESS
;
568 if (Direction
== EfiUsbDataIn
) {
570 // Move data from internal buffer to outside buffer (outside buffer may be in SMRAM...)
571 // SMRAM does not allow to do DMA, so we create an internal buffer.
573 CopyMem (Data
, (VOID
*)(UINTN
)Urb
->Data
, *DataLength
);