3 XHCI transfer scheduling routines.
5 Copyright (c) 2011 - 2020, Intel Corporation. All rights reserved.<BR>
6 Copyright (c) Microsoft Corporation.<BR>
7 Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
15 Create a command transfer TRB to support XHCI command interfaces.
17 @param Xhc The XHCI Instance.
18 @param CmdTrb The cmd TRB to be executed.
20 @return Created URB or NULL.
25 IN USB_XHCI_INSTANCE
*Xhc
,
26 IN TRB_TEMPLATE
*CmdTrb
31 Urb
= AllocateZeroPool (sizeof (URB
));
36 Urb
->Signature
= XHC_URB_SIG
;
38 Urb
->Ring
= &Xhc
->CmdRing
;
39 XhcSyncTrsRing (Xhc
, Urb
->Ring
);
41 Urb
->TrbStart
= Urb
->Ring
->RingEnqueue
;
42 CopyMem (Urb
->TrbStart
, CmdTrb
, sizeof (TRB_TEMPLATE
));
43 Urb
->TrbStart
->CycleBit
= Urb
->Ring
->RingPCS
& BIT0
;
44 Urb
->TrbEnd
= Urb
->TrbStart
;
50 Execute a XHCI cmd TRB pointed by CmdTrb.
52 @param Xhc The XHCI Instance.
53 @param CmdTrb The cmd TRB to be executed.
54 @param Timeout Indicates the maximum time, in millisecond, which the
55 transfer is allowed to complete.
56 @param EvtTrb The event TRB corresponding to the cmd TRB.
58 @retval EFI_SUCCESS The transfer was completed successfully.
59 @retval EFI_INVALID_PARAMETER Some parameters are invalid.
60 @retval EFI_TIMEOUT The transfer failed due to timeout.
61 @retval EFI_DEVICE_ERROR The transfer failed due to host controller error.
67 IN USB_XHCI_INSTANCE
*Xhc
,
68 IN TRB_TEMPLATE
*CmdTrb
,
70 OUT TRB_TEMPLATE
**EvtTrb
77 // Validate the parameters
79 if ((Xhc
== NULL
) || (CmdTrb
== NULL
)) {
80 return EFI_INVALID_PARAMETER
;
83 Status
= EFI_DEVICE_ERROR
;
85 if (XhcIsHalt (Xhc
) || XhcIsSysError (Xhc
)) {
86 DEBUG ((DEBUG_ERROR
, "XhcCmdTransfer: HC is halted\n"));
91 // Create a new URB, then poll the execution status.
93 Urb
= XhcCreateCmdTrb (Xhc
, CmdTrb
);
96 DEBUG ((DEBUG_ERROR
, "XhcCmdTransfer: failed to create URB\n"));
97 Status
= EFI_OUT_OF_RESOURCES
;
101 Status
= XhcExecTransfer (Xhc
, TRUE
, Urb
, Timeout
);
102 *EvtTrb
= Urb
->EvtTrb
;
104 if (Urb
->Result
== EFI_USB_NOERROR
) {
105 Status
= EFI_SUCCESS
;
108 XhcFreeUrb (Xhc
, Urb
);
115 Create a new URB for a new transaction.
117 @param Xhc The XHCI Instance
118 @param BusAddr The logical device address assigned by UsbBus driver
119 @param EpAddr Endpoint addrress
120 @param DevSpeed The device speed
121 @param MaxPacket The max packet length of the endpoint
122 @param Type The transaction type
123 @param Request The standard USB request for control transfer
124 @param Data The user data to transfer
125 @param DataLen The length of data buffer
126 @param Callback The function to call when data is transferred
127 @param Context The context to the callback
129 @return Created URB or NULL
134 IN USB_XHCI_INSTANCE
*Xhc
,
140 IN EFI_USB_DEVICE_REQUEST
*Request
,
143 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback
,
151 Urb
= AllocateZeroPool (sizeof (URB
));
156 Urb
->Signature
= XHC_URB_SIG
;
157 InitializeListHead (&Urb
->UrbList
);
160 Ep
->BusAddr
= BusAddr
;
161 Ep
->EpAddr
= (UINT8
)(EpAddr
& 0x0F);
162 Ep
->Direction
= ((EpAddr
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
;
163 Ep
->DevSpeed
= DevSpeed
;
164 Ep
->MaxPacket
= MaxPacket
;
167 Urb
->Request
= Request
;
169 Urb
->DataLen
= DataLen
;
170 Urb
->Callback
= Callback
;
171 Urb
->Context
= Context
;
173 Status
= XhcCreateTransferTrb (Xhc
, Urb
);
174 ASSERT_EFI_ERROR (Status
);
175 if (EFI_ERROR (Status
)) {
176 DEBUG ((DEBUG_ERROR
, "XhcCreateUrb: XhcCreateTransferTrb Failed, Status = %r\n", Status
));
185 Free an allocated URB.
187 @param Xhc The XHCI device.
188 @param Urb The URB to free.
193 IN USB_XHCI_INSTANCE
*Xhc
,
197 if ((Xhc
== NULL
) || (Urb
== NULL
)) {
201 if (Urb
->DataMap
!= NULL
) {
202 Xhc
->PciIo
->Unmap (Xhc
->PciIo
, Urb
->DataMap
);
209 Create a transfer TRB.
211 @param Xhc The XHCI Instance
212 @param Urb The urb used to construct the transfer TRB.
214 @return Created TRB or NULL
218 XhcCreateTransferTrb (
219 IN USB_XHCI_INSTANCE
*Xhc
,
224 TRANSFER_RING
*EPRing
;
232 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
233 EFI_PHYSICAL_ADDRESS PhyAddr
;
237 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
239 return EFI_DEVICE_ERROR
;
242 Urb
->Finished
= FALSE
;
243 Urb
->StartDone
= FALSE
;
244 Urb
->EndDone
= FALSE
;
246 Urb
->Result
= EFI_USB_NOERROR
;
248 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
250 EPRing
= (TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1];
252 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
253 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
254 EPType
= (UINT8
)((DEVICE_CONTEXT
*)OutputContext
)->EP
[Dci
-1].EPType
;
256 EPType
= (UINT8
)((DEVICE_CONTEXT_64
*)OutputContext
)->EP
[Dci
-1].EPType
;
262 if ((Urb
->Data
!= NULL
) && (Urb
->DataMap
== NULL
)) {
263 if (((UINT8
)(Urb
->Ep
.Direction
)) == EfiUsbDataIn
) {
264 MapOp
= EfiPciIoOperationBusMasterWrite
;
266 MapOp
= EfiPciIoOperationBusMasterRead
;
270 Status
= Xhc
->PciIo
->Map (Xhc
->PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
272 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
273 DEBUG ((DEBUG_ERROR
, "XhcCreateTransferTrb: Fail to map Urb->Data.\n"));
274 return EFI_OUT_OF_RESOURCES
;
277 Urb
->DataPhy
= (VOID
*)((UINTN
)PhyAddr
);
284 XhcSyncTrsRing (Xhc
, EPRing
);
285 Urb
->TrbStart
= EPRing
->RingEnqueue
;
287 case ED_CONTROL_BIDIR
:
289 // For control transfer, create SETUP_STAGE_TRB first.
291 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
292 TrbStart
->TrbCtrSetup
.bmRequestType
= Urb
->Request
->RequestType
;
293 TrbStart
->TrbCtrSetup
.bRequest
= Urb
->Request
->Request
;
294 TrbStart
->TrbCtrSetup
.wValue
= Urb
->Request
->Value
;
295 TrbStart
->TrbCtrSetup
.wIndex
= Urb
->Request
->Index
;
296 TrbStart
->TrbCtrSetup
.wLength
= Urb
->Request
->Length
;
297 TrbStart
->TrbCtrSetup
.Length
= 8;
298 TrbStart
->TrbCtrSetup
.IntTarget
= 0;
299 TrbStart
->TrbCtrSetup
.IOC
= 1;
300 TrbStart
->TrbCtrSetup
.IDT
= 1;
301 TrbStart
->TrbCtrSetup
.Type
= TRB_TYPE_SETUP_STAGE
;
302 if (Urb
->DataLen
> 0) {
303 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
304 TrbStart
->TrbCtrSetup
.TRT
= 3;
305 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
306 TrbStart
->TrbCtrSetup
.TRT
= 2;
308 DEBUG ((DEBUG_ERROR
, "XhcCreateTransferTrb: Direction sholud be IN or OUT when Data exists!\n"));
312 TrbStart
->TrbCtrSetup
.TRT
= 0;
316 // Update the cycle bit
318 TrbStart
->TrbCtrSetup
.CycleBit
= EPRing
->RingPCS
& BIT0
;
322 // For control transfer, create DATA_STAGE_TRB.
324 if (Urb
->DataLen
> 0) {
325 XhcSyncTrsRing (Xhc
, EPRing
);
326 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
327 TrbStart
->TrbCtrData
.TRBPtrLo
= XHC_LOW_32BIT (Urb
->DataPhy
);
328 TrbStart
->TrbCtrData
.TRBPtrHi
= XHC_HIGH_32BIT (Urb
->DataPhy
);
329 TrbStart
->TrbCtrData
.Length
= (UINT32
)Urb
->DataLen
;
330 TrbStart
->TrbCtrData
.TDSize
= 0;
331 TrbStart
->TrbCtrData
.IntTarget
= 0;
332 TrbStart
->TrbCtrData
.ISP
= 1;
333 TrbStart
->TrbCtrData
.IOC
= 1;
334 TrbStart
->TrbCtrData
.IDT
= 0;
335 TrbStart
->TrbCtrData
.CH
= 0;
336 TrbStart
->TrbCtrData
.Type
= TRB_TYPE_DATA_STAGE
;
337 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
338 TrbStart
->TrbCtrData
.DIR = 1;
339 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
340 TrbStart
->TrbCtrData
.DIR = 0;
342 TrbStart
->TrbCtrData
.DIR = 0;
346 // Update the cycle bit
348 TrbStart
->TrbCtrData
.CycleBit
= EPRing
->RingPCS
& BIT0
;
353 // For control transfer, create STATUS_STAGE_TRB.
354 // Get the pointer to next TRB for status stage use
356 XhcSyncTrsRing (Xhc
, EPRing
);
357 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
358 TrbStart
->TrbCtrStatus
.IntTarget
= 0;
359 TrbStart
->TrbCtrStatus
.IOC
= 1;
360 TrbStart
->TrbCtrStatus
.CH
= 0;
361 TrbStart
->TrbCtrStatus
.Type
= TRB_TYPE_STATUS_STAGE
;
362 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
363 TrbStart
->TrbCtrStatus
.DIR = 0;
364 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
365 TrbStart
->TrbCtrStatus
.DIR = 1;
367 TrbStart
->TrbCtrStatus
.DIR = 0;
371 // Update the cycle bit
373 TrbStart
->TrbCtrStatus
.CycleBit
= EPRing
->RingPCS
& BIT0
;
375 // Update the enqueue pointer
377 XhcSyncTrsRing (Xhc
, EPRing
);
379 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
388 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
389 while (TotalLen
< Urb
->DataLen
) {
390 if ((TotalLen
+ 0x10000) >= Urb
->DataLen
) {
391 Len
= Urb
->DataLen
- TotalLen
;
396 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
397 TrbStart
->TrbNormal
.TRBPtrLo
= XHC_LOW_32BIT ((UINT8
*)Urb
->DataPhy
+ TotalLen
);
398 TrbStart
->TrbNormal
.TRBPtrHi
= XHC_HIGH_32BIT ((UINT8
*)Urb
->DataPhy
+ TotalLen
);
399 TrbStart
->TrbNormal
.Length
= (UINT32
)Len
;
400 TrbStart
->TrbNormal
.TDSize
= 0;
401 TrbStart
->TrbNormal
.IntTarget
= 0;
402 TrbStart
->TrbNormal
.ISP
= 1;
403 TrbStart
->TrbNormal
.IOC
= 1;
404 TrbStart
->TrbNormal
.Type
= TRB_TYPE_NORMAL
;
406 // Update the cycle bit
408 TrbStart
->TrbNormal
.CycleBit
= EPRing
->RingPCS
& BIT0
;
410 XhcSyncTrsRing (Xhc
, EPRing
);
415 Urb
->TrbNum
= TrbNum
;
416 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
419 case ED_INTERRUPT_OUT
:
420 case ED_INTERRUPT_IN
:
424 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
425 while (TotalLen
< Urb
->DataLen
) {
426 if ((TotalLen
+ 0x10000) >= Urb
->DataLen
) {
427 Len
= Urb
->DataLen
- TotalLen
;
432 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
433 TrbStart
->TrbNormal
.TRBPtrLo
= XHC_LOW_32BIT ((UINT8
*)Urb
->DataPhy
+ TotalLen
);
434 TrbStart
->TrbNormal
.TRBPtrHi
= XHC_HIGH_32BIT ((UINT8
*)Urb
->DataPhy
+ TotalLen
);
435 TrbStart
->TrbNormal
.Length
= (UINT32
)Len
;
436 TrbStart
->TrbNormal
.TDSize
= 0;
437 TrbStart
->TrbNormal
.IntTarget
= 0;
438 TrbStart
->TrbNormal
.ISP
= 1;
439 TrbStart
->TrbNormal
.IOC
= 1;
440 TrbStart
->TrbNormal
.Type
= TRB_TYPE_NORMAL
;
442 // Update the cycle bit
444 TrbStart
->TrbNormal
.CycleBit
= EPRing
->RingPCS
& BIT0
;
446 XhcSyncTrsRing (Xhc
, EPRing
);
451 Urb
->TrbNum
= TrbNum
;
452 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
456 DEBUG ((DEBUG_INFO
, "Not supported EPType 0x%x!\n", EPType
));
465 Initialize the XHCI host controller for schedule.
467 @param Xhc The XHCI Instance to be initialized.
472 IN USB_XHCI_INSTANCE
*Xhc
476 EFI_PHYSICAL_ADDRESS DcbaaPhy
;
478 EFI_PHYSICAL_ADDRESS CmdRingPhy
;
480 UINT32 MaxScratchpadBufs
;
482 EFI_PHYSICAL_ADDRESS ScratchPhy
;
483 UINT64
*ScratchEntry
;
484 EFI_PHYSICAL_ADDRESS ScratchEntryPhy
;
486 UINTN
*ScratchEntryMap
;
490 // Initialize memory management.
492 Xhc
->MemPool
= UsbHcInitMemPool (Xhc
->PciIo
);
493 ASSERT (Xhc
->MemPool
!= NULL
);
496 // Program the Max Device Slots Enabled (MaxSlotsEn) field in the CONFIG register (5.4.7)
497 // to enable the device slots that system software is going to use.
499 Xhc
->MaxSlotsEn
= Xhc
->HcSParams1
.Data
.MaxSlots
;
500 ASSERT (Xhc
->MaxSlotsEn
>= 1 && Xhc
->MaxSlotsEn
<= 255);
501 XhcWriteOpReg (Xhc
, XHC_CONFIG_OFFSET
, Xhc
->MaxSlotsEn
);
504 // The Device Context Base Address Array entry associated with each allocated Device Slot
505 // shall contain a 64-bit pointer to the base of the associated Device Context.
506 // The Device Context Base Address Array shall contain MaxSlotsEn + 1 entries.
507 // Software shall set Device Context Base Address Array entries for unallocated Device Slots to '0'.
509 Entries
= (Xhc
->MaxSlotsEn
+ 1) * sizeof (UINT64
);
510 Dcbaa
= UsbHcAllocateMem (Xhc
->MemPool
, Entries
, FALSE
);
511 ASSERT (Dcbaa
!= NULL
);
512 ZeroMem (Dcbaa
, Entries
);
515 // A Scratchpad Buffer is a PAGESIZE block of system memory located on a PAGESIZE boundary.
516 // System software shall allocate the Scratchpad Buffer(s) before placing the xHC in to Run
517 // mode (Run/Stop(R/S) ='1').
519 MaxScratchpadBufs
= ((Xhc
->HcSParams2
.Data
.ScratchBufHi
) << 5) | (Xhc
->HcSParams2
.Data
.ScratchBufLo
);
520 Xhc
->MaxScratchpadBufs
= MaxScratchpadBufs
;
521 ASSERT (MaxScratchpadBufs
<= 1023);
522 if (MaxScratchpadBufs
!= 0) {
524 // Allocate the buffer to record the Mapping for each scratch buffer in order to Unmap them
526 ScratchEntryMap
= AllocateZeroPool (sizeof (UINTN
) * MaxScratchpadBufs
);
527 ASSERT (ScratchEntryMap
!= NULL
);
528 Xhc
->ScratchEntryMap
= ScratchEntryMap
;
531 // Allocate the buffer to record the host address for each entry
533 ScratchEntry
= AllocateZeroPool (sizeof (UINT64
) * MaxScratchpadBufs
);
534 ASSERT (ScratchEntry
!= NULL
);
535 Xhc
->ScratchEntry
= ScratchEntry
;
538 Status
= UsbHcAllocateAlignedPages (
540 EFI_SIZE_TO_PAGES (MaxScratchpadBufs
* sizeof (UINT64
)),
542 (VOID
**)&ScratchBuf
,
546 ASSERT_EFI_ERROR (Status
);
548 ZeroMem (ScratchBuf
, MaxScratchpadBufs
* sizeof (UINT64
));
549 Xhc
->ScratchBuf
= ScratchBuf
;
552 // Allocate each scratch buffer
554 for (Index
= 0; Index
< MaxScratchpadBufs
; Index
++) {
556 Status
= UsbHcAllocateAlignedPages (
558 EFI_SIZE_TO_PAGES (Xhc
->PageSize
),
560 (VOID
**)&ScratchEntry
[Index
],
562 (VOID
**)&ScratchEntryMap
[Index
]
564 ASSERT_EFI_ERROR (Status
);
565 ZeroMem ((VOID
*)(UINTN
)ScratchEntry
[Index
], Xhc
->PageSize
);
567 // Fill with the PCI device address
569 *ScratchBuf
++ = ScratchEntryPhy
;
573 // The Scratchpad Buffer Array contains pointers to the Scratchpad Buffers. Entry 0 of the
574 // Device Context Base Address Array points to the Scratchpad Buffer Array.
576 *(UINT64
*)Dcbaa
= (UINT64
)(UINTN
)ScratchPhy
;
580 // Program the Device Context Base Address Array Pointer (DCBAAP) register (5.4.6) with
581 // a 64-bit address pointing to where the Device Context Base Address Array is located.
583 Xhc
->DCBAA
= (UINT64
*)(UINTN
)Dcbaa
;
585 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
586 // So divide it to two 32-bytes width register access.
588 DcbaaPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Dcbaa
, Entries
);
589 XhcWriteOpReg (Xhc
, XHC_DCBAAP_OFFSET
, XHC_LOW_32BIT (DcbaaPhy
));
590 XhcWriteOpReg (Xhc
, XHC_DCBAAP_OFFSET
+ 4, XHC_HIGH_32BIT (DcbaaPhy
));
592 DEBUG ((DEBUG_INFO
, "XhcInitSched:DCBAA=0x%x\n", (UINT64
)(UINTN
)Xhc
->DCBAA
));
595 // Define the Command Ring Dequeue Pointer by programming the Command Ring Control Register
596 // (5.4.5) with a 64-bit address pointing to the starting address of the first TRB of the Command Ring.
597 // Note: The Command Ring is 64 byte aligned, so the low order 6 bits of the Command Ring Pointer shall
600 CreateTransferRing (Xhc
, CMD_RING_TRB_NUMBER
, &Xhc
->CmdRing
);
602 // The xHC uses the Enqueue Pointer to determine when a Transfer Ring is empty. As it fetches TRBs from a
603 // Transfer Ring it checks for a Cycle bit transition. If a transition detected, the ring is empty.
604 // So we set RCS as inverted PCS init value to let Command Ring empty
606 CmdRing
= (UINT64
)(UINTN
)Xhc
->CmdRing
.RingSeg0
;
607 CmdRingPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, (VOID
*)(UINTN
)CmdRing
, sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
);
608 ASSERT ((CmdRingPhy
& 0x3F) == 0);
609 CmdRingPhy
|= XHC_CRCR_RCS
;
611 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
612 // So divide it to two 32-bytes width register access.
614 XhcWriteOpReg (Xhc
, XHC_CRCR_OFFSET
, XHC_LOW_32BIT (CmdRingPhy
));
615 XhcWriteOpReg (Xhc
, XHC_CRCR_OFFSET
+ 4, XHC_HIGH_32BIT (CmdRingPhy
));
618 // Disable the 'interrupter enable' bit in USB_CMD
619 // and clear IE & IP bit in all Interrupter X Management Registers.
621 XhcClearOpRegBit (Xhc
, XHC_USBCMD_OFFSET
, XHC_USBCMD_INTE
);
622 for (Index
= 0; Index
< (UINT16
)(Xhc
->HcSParams1
.Data
.MaxIntrs
); Index
++) {
623 XhcClearRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
+ (Index
* 32), XHC_IMAN_IE
);
624 XhcSetRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
+ (Index
* 32), XHC_IMAN_IP
);
628 // Allocate EventRing for Cmd, Ctrl, Bulk, Interrupt, AsynInterrupt transfer
630 CreateEventRing (Xhc
, &Xhc
->EventRing
);
633 "XhcInitSched: Created CMD ring [%p~%p) EVENT ring [%p~%p)\n",
634 Xhc
->CmdRing
.RingSeg0
,
635 (UINTN
)Xhc
->CmdRing
.RingSeg0
+ sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
,
636 Xhc
->EventRing
.EventRingSeg0
,
637 (UINTN
)Xhc
->EventRing
.EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
642 System software shall use a Reset Endpoint Command (section 4.11.4.7) to remove the Halted
643 condition in the xHC. After the successful completion of the Reset Endpoint Command, the Endpoint
644 Context is transitioned from the Halted to the Stopped state and the Transfer Ring of the endpoint is
645 reenabled. The next write to the Doorbell of the Endpoint will transition the Endpoint Context from the
646 Stopped to the Running state.
648 @param Xhc The XHCI Instance.
649 @param Urb The urb which makes the endpoint halted.
651 @retval EFI_SUCCESS The recovery is successful.
652 @retval Others Failed to recovery halted endpoint.
657 XhcRecoverHaltedEndpoint (
658 IN USB_XHCI_INSTANCE
*Xhc
,
666 Status
= EFI_SUCCESS
;
667 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
669 return EFI_DEVICE_ERROR
;
672 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
675 DEBUG ((DEBUG_INFO
, "Recovery Halted Slot = %x,Dci = %x\n", SlotId
, Dci
));
678 // 1) Send Reset endpoint command to transit from halt to stop state
680 Status
= XhcResetEndpoint (Xhc
, SlotId
, Dci
);
681 if (EFI_ERROR (Status
)) {
682 DEBUG ((DEBUG_ERROR
, "XhcRecoverHaltedEndpoint: Reset Endpoint Failed, Status = %r\n", Status
));
687 // 2)Set dequeue pointer
689 Status
= XhcSetTrDequeuePointer (Xhc
, SlotId
, Dci
, Urb
);
690 if (EFI_ERROR (Status
)) {
691 DEBUG ((DEBUG_ERROR
, "XhcRecoverHaltedEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status
));
696 // 3)Ring the doorbell to transit from stop to active
698 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
705 System software shall use a Stop Endpoint Command (section 4.6.9) and the Set TR Dequeue Pointer
706 Command (section 4.6.10) to remove the timed-out TDs from the xHC transfer ring. The next write to
707 the Doorbell of the Endpoint will transition the Endpoint Context from the Stopped to the Running
710 @param Xhc The XHCI Instance.
711 @param Urb The urb which doesn't get completed in a specified timeout range.
713 @retval EFI_SUCCESS The dequeuing of the TDs is successful.
714 @retval EFI_ALREADY_STARTED The Urb is finished so no deque is needed.
715 @retval Others Failed to stop the endpoint and dequeue the TDs.
720 XhcDequeueTrbFromEndpoint (
721 IN USB_XHCI_INSTANCE
*Xhc
,
729 Status
= EFI_SUCCESS
;
730 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
732 return EFI_DEVICE_ERROR
;
735 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
738 DEBUG ((DEBUG_VERBOSE
, "Stop Slot = %x,Dci = %x\n", SlotId
, Dci
));
741 // 1) Send Stop endpoint command to stop xHC from executing of the TDs on the endpoint
743 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, Urb
);
744 if (EFI_ERROR (Status
)) {
745 DEBUG ((DEBUG_ERROR
, "XhcDequeueTrbFromEndpoint: Stop Endpoint Failed, Status = %r\n", Status
));
750 // 2)Set dequeue pointer
752 if (Urb
->Finished
&& (Urb
->Result
== EFI_USB_NOERROR
)) {
754 // Return Already Started to indicate the pending URB is finished.
755 // This fixes BULK data loss when transfer is detected as timeout
756 // but finished just before stopping endpoint.
758 Status
= EFI_ALREADY_STARTED
;
759 DEBUG ((DEBUG_INFO
, "XhcDequeueTrbFromEndpoint: Pending URB is finished: Length Actual/Expect = %d/%d!\n", Urb
->Completed
, Urb
->DataLen
));
761 Status
= XhcSetTrDequeuePointer (Xhc
, SlotId
, Dci
, Urb
);
762 if (EFI_ERROR (Status
)) {
763 DEBUG ((DEBUG_ERROR
, "XhcDequeueTrbFromEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status
));
769 // 3)Ring the doorbell to transit from stop to active
771 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
778 Create XHCI event ring.
780 @param Xhc The XHCI Instance.
781 @param EventRing The created event ring.
786 IN USB_XHCI_INSTANCE
*Xhc
,
787 OUT EVENT_RING
*EventRing
791 EVENT_RING_SEG_TABLE_ENTRY
*ERSTBase
;
793 EFI_PHYSICAL_ADDRESS ERSTPhy
;
794 EFI_PHYSICAL_ADDRESS DequeuePhy
;
796 ASSERT (EventRing
!= NULL
);
798 Size
= sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
;
799 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, Size
, TRUE
);
800 ASSERT (Buf
!= NULL
);
801 ASSERT (((UINTN
)Buf
& 0x3F) == 0);
804 EventRing
->EventRingSeg0
= Buf
;
805 EventRing
->TrbNumber
= EVENT_RING_TRB_NUMBER
;
806 EventRing
->EventRingDequeue
= (TRB_TEMPLATE
*)EventRing
->EventRingSeg0
;
807 EventRing
->EventRingEnqueue
= (TRB_TEMPLATE
*)EventRing
->EventRingSeg0
;
809 DequeuePhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Buf
, Size
);
812 // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1'
813 // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring.
815 EventRing
->EventRingCCS
= 1;
817 Size
= sizeof (EVENT_RING_SEG_TABLE_ENTRY
) * ERST_NUMBER
;
818 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, Size
, FALSE
);
819 ASSERT (Buf
!= NULL
);
820 ASSERT (((UINTN
)Buf
& 0x3F) == 0);
823 ERSTBase
= (EVENT_RING_SEG_TABLE_ENTRY
*)Buf
;
824 EventRing
->ERSTBase
= ERSTBase
;
825 ERSTBase
->PtrLo
= XHC_LOW_32BIT (DequeuePhy
);
826 ERSTBase
->PtrHi
= XHC_HIGH_32BIT (DequeuePhy
);
827 ERSTBase
->RingTrbSize
= EVENT_RING_TRB_NUMBER
;
829 ERSTPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, ERSTBase
, Size
);
832 // Program the Interrupter Event Ring Segment Table Size (ERSTSZ) register (5.5.2.3.1)
840 // Program the Interrupter Event Ring Dequeue Pointer (ERDP) register (5.5.2.3.3)
842 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
843 // So divide it to two 32-bytes width register access.
848 XHC_LOW_32BIT ((UINT64
)(UINTN
)DequeuePhy
)
853 XHC_HIGH_32BIT ((UINT64
)(UINTN
)DequeuePhy
)
856 // Program the Interrupter Event Ring Segment Table Base Address (ERSTBA) register(5.5.2.3.2)
858 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
859 // So divide it to two 32-bytes width register access.
864 XHC_LOW_32BIT ((UINT64
)(UINTN
)ERSTPhy
)
868 XHC_ERSTBA_OFFSET
+ 4,
869 XHC_HIGH_32BIT ((UINT64
)(UINTN
)ERSTPhy
)
872 // Need set IMAN IE bit to enble the ring interrupt
874 XhcSetRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
, XHC_IMAN_IE
);
878 Create XHCI transfer ring.
880 @param Xhc The XHCI Instance.
881 @param TrbNum The number of TRB in the ring.
882 @param TransferRing The created transfer ring.
887 IN USB_XHCI_INSTANCE
*Xhc
,
889 OUT TRANSFER_RING
*TransferRing
894 EFI_PHYSICAL_ADDRESS PhyAddr
;
896 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (TRB_TEMPLATE
) * TrbNum
, TRUE
);
897 ASSERT (Buf
!= NULL
);
898 ASSERT (((UINTN
)Buf
& 0x3F) == 0);
899 ZeroMem (Buf
, sizeof (TRB_TEMPLATE
) * TrbNum
);
901 TransferRing
->RingSeg0
= Buf
;
902 TransferRing
->TrbNumber
= TrbNum
;
903 TransferRing
->RingEnqueue
= (TRB_TEMPLATE
*)TransferRing
->RingSeg0
;
904 TransferRing
->RingDequeue
= (TRB_TEMPLATE
*)TransferRing
->RingSeg0
;
905 TransferRing
->RingPCS
= 1;
907 // 4.9.2 Transfer Ring Management
908 // To form a ring (or circular queue) a Link TRB may be inserted at the end of a ring to
909 // point to the first TRB in the ring.
911 EndTrb
= (LINK_TRB
*)((UINTN
)Buf
+ sizeof (TRB_TEMPLATE
) * (TrbNum
- 1));
912 EndTrb
->Type
= TRB_TYPE_LINK
;
913 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Buf
, sizeof (TRB_TEMPLATE
) * TrbNum
);
914 EndTrb
->PtrLo
= XHC_LOW_32BIT (PhyAddr
);
915 EndTrb
->PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
917 // Toggle Cycle (TC). When set to '1', the xHC shall toggle its interpretation of the Cycle bit.
921 // Set Cycle bit as other TRB PCS init value
923 EndTrb
->CycleBit
= 0;
927 Free XHCI event ring.
929 @param Xhc The XHCI Instance.
930 @param EventRing The event ring to be freed.
936 IN USB_XHCI_INSTANCE
*Xhc
,
937 IN EVENT_RING
*EventRing
940 if (EventRing
->EventRingSeg0
== NULL
) {
945 // Free EventRing Segment 0
947 UsbHcFreeMem (Xhc
->MemPool
, EventRing
->EventRingSeg0
, sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
);
952 UsbHcFreeMem (Xhc
->MemPool
, EventRing
->ERSTBase
, sizeof (EVENT_RING_SEG_TABLE_ENTRY
) * ERST_NUMBER
);
957 Free the resouce allocated at initializing schedule.
959 @param Xhc The XHCI Instance.
964 IN USB_XHCI_INSTANCE
*Xhc
968 UINT64
*ScratchEntry
;
970 if (Xhc
->ScratchBuf
!= NULL
) {
971 ScratchEntry
= Xhc
->ScratchEntry
;
972 for (Index
= 0; Index
< Xhc
->MaxScratchpadBufs
; Index
++) {
974 // Free Scratchpad Buffers
976 UsbHcFreeAlignedPages (Xhc
->PciIo
, (VOID
*)(UINTN
)ScratchEntry
[Index
], EFI_SIZE_TO_PAGES (Xhc
->PageSize
), (VOID
*)Xhc
->ScratchEntryMap
[Index
]);
980 // Free Scratchpad Buffer Array
982 UsbHcFreeAlignedPages (Xhc
->PciIo
, Xhc
->ScratchBuf
, EFI_SIZE_TO_PAGES (Xhc
->MaxScratchpadBufs
* sizeof (UINT64
)), Xhc
->ScratchMap
);
983 FreePool (Xhc
->ScratchEntryMap
);
984 FreePool (Xhc
->ScratchEntry
);
987 if (Xhc
->CmdRing
.RingSeg0
!= NULL
) {
988 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->CmdRing
.RingSeg0
, sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
);
989 Xhc
->CmdRing
.RingSeg0
= NULL
;
992 XhcFreeEventRing (Xhc
, &Xhc
->EventRing
);
994 if (Xhc
->DCBAA
!= NULL
) {
995 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->DCBAA
, (Xhc
->MaxSlotsEn
+ 1) * sizeof (UINT64
));
1000 // Free memory pool at last
1002 if (Xhc
->MemPool
!= NULL
) {
1003 UsbHcFreeMemPool (Xhc
->MemPool
);
1004 Xhc
->MemPool
= NULL
;
1009 Check if the Trb is a transaction of the URB.
1011 @param Xhc The XHCI Instance.
1012 @param Trb The TRB to be checked
1013 @param Urb The URB to be checked.
1015 @retval TRUE It is a transaction of the URB.
1016 @retval FALSE It is not any transaction of the URB.
1021 IN USB_XHCI_INSTANCE
*Xhc
,
1022 IN TRB_TEMPLATE
*Trb
,
1027 TRB_TEMPLATE
*CheckedTrb
;
1029 EFI_PHYSICAL_ADDRESS PhyAddr
;
1031 CheckedTrb
= Urb
->TrbStart
;
1032 for (Index
= 0; Index
< Urb
->TrbNum
; Index
++) {
1033 if (Trb
== CheckedTrb
) {
1039 // If the checked TRB is the link TRB at the end of the transfer ring,
1040 // recircle it to the head of the ring.
1042 if (CheckedTrb
->Type
== TRB_TYPE_LINK
) {
1043 LinkTrb
= (LINK_TRB
*)CheckedTrb
;
1044 PhyAddr
= (EFI_PHYSICAL_ADDRESS
)(LinkTrb
->PtrLo
| LShiftU64 ((UINT64
)LinkTrb
->PtrHi
, 32));
1045 CheckedTrb
= (TRB_TEMPLATE
*)(UINTN
)UsbHcGetHostAddrForPciAddr (Xhc
->MemPool
, (VOID
*)(UINTN
)PhyAddr
, sizeof (TRB_TEMPLATE
));
1046 ASSERT (CheckedTrb
== Urb
->Ring
->RingSeg0
);
1054 Check if the Trb is a transaction of the URBs in XHCI's asynchronous transfer list.
1056 @param Xhc The XHCI Instance.
1057 @param Trb The TRB to be checked.
1058 @param Urb The pointer to the matched Urb.
1060 @retval TRUE The Trb is matched with a transaction of the URBs in the async list.
1061 @retval FALSE The Trb is not matched with any URBs in the async list.
1066 IN USB_XHCI_INSTANCE
*Xhc
,
1067 IN TRB_TEMPLATE
*Trb
,
1075 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1076 CheckedUrb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1077 if (IsTransferRingTrb (Xhc
, Trb
, CheckedUrb
)) {
1087 Check the URB's execution result and update the URB's
1090 @param Xhc The XHCI Instance.
1091 @param Urb The URB to check result.
1093 @return Whether the result of URB transfer is finialized.
1098 IN USB_XHCI_INSTANCE
*Xhc
,
1102 EVT_TRB_TRANSFER
*EvtTrb
;
1103 TRB_TEMPLATE
*TRBPtr
;
1112 EFI_PHYSICAL_ADDRESS PhyAddr
;
1114 ASSERT ((Xhc
!= NULL
) && (Urb
!= NULL
));
1116 Status
= EFI_SUCCESS
;
1119 if (Urb
->Finished
) {
1125 if (XhcIsHalt (Xhc
) || XhcIsSysError (Xhc
)) {
1126 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
1131 // Traverse the event ring to find out all new events from the previous check.
1133 XhcSyncEventRing (Xhc
, &Xhc
->EventRing
);
1134 for (Index
= 0; Index
< Xhc
->EventRing
.TrbNumber
; Index
++) {
1135 Status
= XhcCheckNewEvent (Xhc
, &Xhc
->EventRing
, ((TRB_TEMPLATE
**)&EvtTrb
));
1136 if (Status
== EFI_NOT_READY
) {
1138 // All new events are handled, return directly.
1144 // Only handle COMMAND_COMPLETETION_EVENT and TRANSFER_EVENT.
1146 if ((EvtTrb
->Type
!= TRB_TYPE_COMMAND_COMPLT_EVENT
) && (EvtTrb
->Type
!= TRB_TYPE_TRANS_EVENT
)) {
1151 // Need convert pci device address to host address
1153 PhyAddr
= (EFI_PHYSICAL_ADDRESS
)(EvtTrb
->TRBPtrLo
| LShiftU64 ((UINT64
)EvtTrb
->TRBPtrHi
, 32));
1154 TRBPtr
= (TRB_TEMPLATE
*)(UINTN
)UsbHcGetHostAddrForPciAddr (Xhc
->MemPool
, (VOID
*)(UINTN
)PhyAddr
, sizeof (TRB_TEMPLATE
));
1157 // Update the status of URB including the pending URB, the URB that is currently checked,
1158 // and URBs in the XHCI's async interrupt transfer list.
1159 // This way is used to avoid that those completed async transfer events don't get
1160 // handled in time and are flushed by newer coming events.
1162 if ((Xhc
->PendingUrb
!= NULL
) && IsTransferRingTrb (Xhc
, TRBPtr
, Xhc
->PendingUrb
)) {
1163 CheckedUrb
= Xhc
->PendingUrb
;
1164 } else if (IsTransferRingTrb (Xhc
, TRBPtr
, Urb
)) {
1166 } else if (IsAsyncIntTrb (Xhc
, TRBPtr
, &AsyncUrb
)) {
1167 CheckedUrb
= AsyncUrb
;
1172 switch (EvtTrb
->Completecode
) {
1173 case TRB_COMPLETION_STALL_ERROR
:
1174 CheckedUrb
->Result
|= EFI_USB_ERR_STALL
;
1175 CheckedUrb
->Finished
= TRUE
;
1176 DEBUG ((DEBUG_ERROR
, "XhcCheckUrbResult: STALL_ERROR! Completecode = %x\n", EvtTrb
->Completecode
));
1179 case TRB_COMPLETION_BABBLE_ERROR
:
1180 CheckedUrb
->Result
|= EFI_USB_ERR_BABBLE
;
1181 CheckedUrb
->Finished
= TRUE
;
1182 DEBUG ((DEBUG_ERROR
, "XhcCheckUrbResult: BABBLE_ERROR! Completecode = %x\n", EvtTrb
->Completecode
));
1185 case TRB_COMPLETION_DATA_BUFFER_ERROR
:
1186 CheckedUrb
->Result
|= EFI_USB_ERR_BUFFER
;
1187 CheckedUrb
->Finished
= TRUE
;
1188 DEBUG ((DEBUG_ERROR
, "XhcCheckUrbResult: ERR_BUFFER! Completecode = %x\n", EvtTrb
->Completecode
));
1191 case TRB_COMPLETION_USB_TRANSACTION_ERROR
:
1192 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1193 CheckedUrb
->Finished
= TRUE
;
1194 DEBUG ((DEBUG_ERROR
, "XhcCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n", EvtTrb
->Completecode
));
1197 case TRB_COMPLETION_STOPPED
:
1198 case TRB_COMPLETION_STOPPED_LENGTH_INVALID
:
1199 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1200 CheckedUrb
->Finished
= TRUE
;
1202 // The pending URB is timeout and force stopped when stopping endpoint.
1203 // Continue the loop to receive the Command Complete Event for stopping endpoint.
1207 case TRB_COMPLETION_SHORT_PACKET
:
1208 case TRB_COMPLETION_SUCCESS
:
1209 if (EvtTrb
->Completecode
== TRB_COMPLETION_SHORT_PACKET
) {
1210 DEBUG ((DEBUG_VERBOSE
, "XhcCheckUrbResult: short packet happens!\n"));
1213 TRBType
= (UINT8
)(TRBPtr
->Type
);
1214 if ((TRBType
== TRB_TYPE_DATA_STAGE
) ||
1215 (TRBType
== TRB_TYPE_NORMAL
) ||
1216 (TRBType
== TRB_TYPE_ISOCH
))
1218 CheckedUrb
->Completed
+= (((TRANSFER_TRB_NORMAL
*)TRBPtr
)->Length
- EvtTrb
->Length
);
1224 DEBUG ((DEBUG_ERROR
, "Transfer Default Error Occur! Completecode = 0x%x!\n", EvtTrb
->Completecode
));
1225 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1226 CheckedUrb
->Finished
= TRUE
;
1231 // Only check first and end Trb event address
1233 if (TRBPtr
== CheckedUrb
->TrbStart
) {
1234 CheckedUrb
->StartDone
= TRUE
;
1237 if (TRBPtr
== CheckedUrb
->TrbEnd
) {
1238 CheckedUrb
->EndDone
= TRUE
;
1241 if (CheckedUrb
->StartDone
&& CheckedUrb
->EndDone
) {
1242 CheckedUrb
->Finished
= TRUE
;
1243 CheckedUrb
->EvtTrb
= (TRB_TEMPLATE
*)EvtTrb
;
1250 // Advance event ring to last available entry
1252 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
1253 // So divide it to two 32-bytes width register access.
1255 Low
= XhcReadRuntimeReg (Xhc
, XHC_ERDP_OFFSET
);
1256 High
= XhcReadRuntimeReg (Xhc
, XHC_ERDP_OFFSET
+ 4);
1257 XhcDequeue
= (UINT64
)(LShiftU64 ((UINT64
)High
, 32) | Low
);
1259 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->EventRing
.EventRingDequeue
, sizeof (TRB_TEMPLATE
));
1261 if ((XhcDequeue
& (~0x0F)) != (PhyAddr
& (~0x0F))) {
1263 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
1264 // So divide it to two 32-bytes width register access.
1266 XhcWriteRuntimeReg (Xhc
, XHC_ERDP_OFFSET
, XHC_LOW_32BIT (PhyAddr
) | BIT3
);
1267 XhcWriteRuntimeReg (Xhc
, XHC_ERDP_OFFSET
+ 4, XHC_HIGH_32BIT (PhyAddr
));
1270 return Urb
->Finished
;
1274 Execute the transfer by polling the URB. This is a synchronous operation.
1276 @param Xhc The XHCI Instance.
1277 @param CmdTransfer The executed URB is for cmd transfer or not.
1278 @param Urb The URB to execute.
1279 @param Timeout The time to wait before abort, in millisecond.
1281 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.
1282 @return EFI_TIMEOUT The transfer failed due to time out.
1283 @return EFI_SUCCESS The transfer finished OK.
1284 @retval EFI_OUT_OF_RESOURCES Memory for the timer event could not be allocated.
1289 IN USB_XHCI_INSTANCE
*Xhc
,
1290 IN BOOLEAN CmdTransfer
,
1299 EFI_EVENT TimeoutEvent
;
1300 BOOLEAN IndefiniteTimeout
;
1302 Status
= EFI_SUCCESS
;
1304 TimeoutEvent
= NULL
;
1305 IndefiniteTimeout
= FALSE
;
1311 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
1313 return EFI_DEVICE_ERROR
;
1316 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
1321 IndefiniteTimeout
= TRUE
;
1325 Status
= gBS
->CreateEvent (
1333 if (EFI_ERROR (Status
)) {
1337 Status
= gBS
->SetTimer (
1340 EFI_TIMER_PERIOD_MILLISECONDS (Timeout
)
1343 if (EFI_ERROR (Status
)) {
1348 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
1351 Finished
= XhcCheckUrbResult (Xhc
, Urb
);
1356 gBS
->Stall (XHC_1_MICROSECOND
);
1357 } while (IndefiniteTimeout
|| EFI_ERROR (gBS
->CheckEvent (TimeoutEvent
)));
1360 if (EFI_ERROR (Status
)) {
1361 Urb
->Result
= EFI_USB_ERR_NOTEXECUTE
;
1362 } else if (!Finished
) {
1363 Urb
->Result
= EFI_USB_ERR_TIMEOUT
;
1364 Status
= EFI_TIMEOUT
;
1365 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
1366 Status
= EFI_DEVICE_ERROR
;
1369 if (TimeoutEvent
!= NULL
) {
1370 gBS
->CloseEvent (TimeoutEvent
);
1377 Delete a single asynchronous interrupt transfer for
1378 the device and endpoint.
1380 @param Xhc The XHCI Instance.
1381 @param BusAddr The logical device address assigned by UsbBus driver.
1382 @param EpNum The endpoint of the target.
1384 @retval EFI_SUCCESS An asynchronous transfer is removed.
1385 @retval EFI_NOT_FOUND No transfer for the device is found.
1389 XhciDelAsyncIntTransfer (
1390 IN USB_XHCI_INSTANCE
*Xhc
,
1398 EFI_USB_DATA_DIRECTION Direction
;
1401 Direction
= ((EpNum
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
;
1406 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1407 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1408 if ((Urb
->Ep
.BusAddr
== BusAddr
) &&
1409 (Urb
->Ep
.EpAddr
== EpNum
) &&
1410 (Urb
->Ep
.Direction
== Direction
))
1413 // Device doesn't finish the IntTransfer until real data comes
1414 // So the TRB should be removed as well.
1416 Status
= XhcDequeueTrbFromEndpoint (Xhc
, Urb
);
1417 if (EFI_ERROR (Status
)) {
1418 DEBUG ((DEBUG_ERROR
, "XhciDelAsyncIntTransfer: XhcDequeueTrbFromEndpoint failed\n"));
1421 RemoveEntryList (&Urb
->UrbList
);
1422 FreePool (Urb
->Data
);
1423 XhcFreeUrb (Xhc
, Urb
);
1428 return EFI_NOT_FOUND
;
1432 Remove all the asynchronous interrutp transfers.
1434 @param Xhc The XHCI Instance.
1438 XhciDelAllAsyncIntTransfers (
1439 IN USB_XHCI_INSTANCE
*Xhc
1447 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1448 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1451 // Device doesn't finish the IntTransfer until real data comes
1452 // So the TRB should be removed as well.
1454 Status
= XhcDequeueTrbFromEndpoint (Xhc
, Urb
);
1455 if (EFI_ERROR (Status
)) {
1456 DEBUG ((DEBUG_ERROR
, "XhciDelAllAsyncIntTransfers: XhcDequeueTrbFromEndpoint failed\n"));
1459 RemoveEntryList (&Urb
->UrbList
);
1460 FreePool (Urb
->Data
);
1461 XhcFreeUrb (Xhc
, Urb
);
1466 Insert a single asynchronous interrupt transfer for
1467 the device and endpoint.
1469 @param Xhc The XHCI Instance
1470 @param BusAddr The logical device address assigned by UsbBus driver
1471 @param EpAddr Endpoint addrress
1472 @param DevSpeed The device speed
1473 @param MaxPacket The max packet length of the endpoint
1474 @param DataLen The length of data buffer
1475 @param Callback The function to call when data is transferred
1476 @param Context The context to the callback
1478 @return Created URB or NULL
1482 XhciInsertAsyncIntTransfer (
1483 IN USB_XHCI_INSTANCE
*Xhc
,
1489 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback
,
1496 Data
= AllocateZeroPool (DataLen
);
1498 DEBUG ((DEBUG_ERROR
, "%a: failed to allocate buffer\n", __FUNCTION__
));
1502 Urb
= XhcCreateUrb (
1508 XHC_INT_TRANSFER_ASYNC
,
1516 DEBUG ((DEBUG_ERROR
, "%a: failed to create URB\n", __FUNCTION__
));
1522 // New asynchronous transfer must inserted to the head.
1523 // Check the comments in XhcMoniteAsyncRequests
1525 InsertHeadList (&Xhc
->AsyncIntTransfers
, &Urb
->UrbList
);
1531 Update the queue head for next round of asynchronous transfer
1533 @param Xhc The XHCI Instance.
1534 @param Urb The URB to update
1538 XhcUpdateAsyncRequest (
1539 IN USB_XHCI_INSTANCE
*Xhc
,
1545 if (Urb
->Result
== EFI_USB_NOERROR
) {
1546 Status
= XhcCreateTransferTrb (Xhc
, Urb
);
1547 if (EFI_ERROR (Status
)) {
1551 Status
= RingIntTransferDoorBell (Xhc
, Urb
);
1552 if (EFI_ERROR (Status
)) {
1559 Flush data from PCI controller specific address to mapped system
1562 @param Xhc The XHCI device.
1563 @param Urb The URB to unmap.
1565 @retval EFI_SUCCESS Success to flush data to mapped system memory.
1566 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
1570 XhcFlushAsyncIntMap (
1571 IN USB_XHCI_INSTANCE
*Xhc
,
1576 EFI_PHYSICAL_ADDRESS PhyAddr
;
1577 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
1578 EFI_PCI_IO_PROTOCOL
*PciIo
;
1585 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
1586 MapOp
= EfiPciIoOperationBusMasterWrite
;
1588 MapOp
= EfiPciIoOperationBusMasterRead
;
1591 if (Urb
->DataMap
!= NULL
) {
1592 Status
= PciIo
->Unmap (PciIo
, Urb
->DataMap
);
1593 if (EFI_ERROR (Status
)) {
1598 Urb
->DataMap
= NULL
;
1600 Status
= PciIo
->Map (PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
1601 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
1605 Urb
->DataPhy
= (VOID
*)((UINTN
)PhyAddr
);
1610 return EFI_DEVICE_ERROR
;
1614 Interrupt transfer periodic check handler.
1616 @param Event Interrupt event.
1617 @param Context Pointer to USB_XHCI_INSTANCE.
1622 XhcMonitorAsyncRequests (
1627 USB_XHCI_INSTANCE
*Xhc
;
1636 OldTpl
= gBS
->RaiseTPL (XHC_TPL
);
1638 Xhc
= (USB_XHCI_INSTANCE
*)Context
;
1640 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1641 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1644 // Make sure that the device is available before every check.
1646 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
1652 // Check the result of URB execution. If it is still
1653 // active, check the next one.
1655 XhcCheckUrbResult (Xhc
, Urb
);
1657 if (!Urb
->Finished
) {
1662 // Flush any PCI posted write transactions from a PCI host
1663 // bridge to system memory.
1665 Status
= XhcFlushAsyncIntMap (Xhc
, Urb
);
1666 if (EFI_ERROR (Status
)) {
1667 DEBUG ((DEBUG_ERROR
, "XhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
1671 // Allocate a buffer then copy the transferred data for user.
1672 // If failed to allocate the buffer, update the URB for next
1673 // round of transfer. Ignore the data of this round.
1676 if (Urb
->Result
== EFI_USB_NOERROR
) {
1678 // Make sure the data received from HW is no more than expected.
1680 if (Urb
->Completed
<= Urb
->DataLen
) {
1681 ProcBuf
= AllocateZeroPool (Urb
->Completed
);
1684 if (ProcBuf
== NULL
) {
1685 XhcUpdateAsyncRequest (Xhc
, Urb
);
1689 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
1693 // Leave error recovery to its related device driver. A
1694 // common case of the error recovery is to re-submit the
1695 // interrupt transfer which is linked to the head of the
1696 // list. This function scans from head to tail. So the
1697 // re-submitted interrupt transfer's callback function
1698 // will not be called again in this round. Don't touch this
1699 // URB after the callback, it may have been removed by the
1702 if (Urb
->Callback
!= NULL
) {
1704 // Restore the old TPL, USB bus maybe connect device in
1705 // his callback. Some drivers may has a lower TPL restriction.
1707 gBS
->RestoreTPL (OldTpl
);
1708 (Urb
->Callback
)(ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
1709 OldTpl
= gBS
->RaiseTPL (XHC_TPL
);
1712 if (ProcBuf
!= NULL
) {
1713 gBS
->FreePool (ProcBuf
);
1716 XhcUpdateAsyncRequest (Xhc
, Urb
);
1718 gBS
->RestoreTPL (OldTpl
);
1722 Monitor the port status change. Enable/Disable device slot if there is a device attached/detached.
1724 @param Xhc The XHCI Instance.
1725 @param ParentRouteChart The route string pointed to the parent device if it exists.
1726 @param Port The port to be polled.
1727 @param PortState The port state.
1729 @retval EFI_SUCCESS Successfully enable/disable device slot according to port state.
1730 @retval Others Should not appear.
1735 XhcPollPortStatusChange (
1736 IN USB_XHCI_INSTANCE
*Xhc
,
1737 IN USB_DEV_ROUTE ParentRouteChart
,
1739 IN EFI_USB_PORT_STATUS
*PortState
1746 USB_DEV_ROUTE RouteChart
;
1748 Status
= EFI_SUCCESS
;
1749 Retries
= XHC_INIT_DEVICE_SLOT_RETRIES
;
1751 if ((PortState
->PortChangeStatus
& (USB_PORT_STAT_C_CONNECTION
| USB_PORT_STAT_C_ENABLE
| USB_PORT_STAT_C_OVERCURRENT
| USB_PORT_STAT_C_RESET
)) == 0) {
1755 if (ParentRouteChart
.Dword
== 0) {
1756 RouteChart
.Route
.RouteString
= 0;
1757 RouteChart
.Route
.RootPortNum
= Port
+ 1;
1758 RouteChart
.Route
.TierNum
= 1;
1761 RouteChart
.Route
.RouteString
= ParentRouteChart
.Route
.RouteString
| (Port
<< (4 * (ParentRouteChart
.Route
.TierNum
- 1)));
1763 RouteChart
.Route
.RouteString
= ParentRouteChart
.Route
.RouteString
| (15 << (4 * (ParentRouteChart
.Route
.TierNum
- 1)));
1766 RouteChart
.Route
.RootPortNum
= ParentRouteChart
.Route
.RootPortNum
;
1767 RouteChart
.Route
.TierNum
= ParentRouteChart
.Route
.TierNum
+ 1;
1770 SlotId
= XhcRouteStringToSlotId (Xhc
, RouteChart
);
1772 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
1773 Status
= XhcDisableSlotCmd (Xhc
, SlotId
);
1775 Status
= XhcDisableSlotCmd64 (Xhc
, SlotId
);
1779 if (((PortState
->PortStatus
& USB_PORT_STAT_ENABLE
) != 0) &&
1780 ((PortState
->PortStatus
& USB_PORT_STAT_CONNECTION
) != 0))
1783 // Has a device attached, Identify device speed after port is enabled.
1785 Speed
= EFI_USB_SPEED_FULL
;
1786 if ((PortState
->PortStatus
& USB_PORT_STAT_LOW_SPEED
) != 0) {
1787 Speed
= EFI_USB_SPEED_LOW
;
1788 } else if ((PortState
->PortStatus
& USB_PORT_STAT_HIGH_SPEED
) != 0) {
1789 Speed
= EFI_USB_SPEED_HIGH
;
1790 } else if ((PortState
->PortStatus
& USB_PORT_STAT_SUPER_SPEED
) != 0) {
1791 Speed
= EFI_USB_SPEED_SUPER
;
1796 // Execute Enable_Slot cmd for attached device, initialize device context and assign device address.
1798 SlotId
= XhcRouteStringToSlotId (Xhc
, RouteChart
);
1799 if ((SlotId
== 0) && ((PortState
->PortChangeStatus
& USB_PORT_STAT_C_RESET
) != 0)) {
1800 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
1801 Status
= XhcInitializeDeviceSlot (Xhc
, ParentRouteChart
, Port
, RouteChart
, Speed
);
1803 Status
= XhcInitializeDeviceSlot64 (Xhc
, ParentRouteChart
, Port
, RouteChart
, Speed
);
1808 // According to the xHCI specification (section 4.6.5), "a USB Transaction
1809 // Error Completion Code for an Address Device Command may be due to a Stall
1810 // response from a device. Software should issue a Disable Slot Command for
1811 // the Device Slot then an Enable Slot Command to recover from this error."
1812 // Therefore, retry the device slot initialization if it fails due to a
1815 } while ((Status
== EFI_DEVICE_ERROR
) && (Retries
-- != 0));
1822 Calculate the device context index by endpoint address and direction.
1824 @param EpAddr The target endpoint number.
1825 @param Direction The direction of the target endpoint.
1827 @return The device context index of endpoint.
1841 Index
= (UINT8
)(2 * EpAddr
);
1842 if (Direction
== EfiUsbDataIn
) {
1851 Find out the actual device address according to the requested device address from UsbBus.
1853 @param Xhc The XHCI Instance.
1854 @param BusDevAddr The requested device address by UsbBus upper driver.
1856 @return The actual device address assigned to the device.
1861 XhcBusDevAddrToSlotId (
1862 IN USB_XHCI_INSTANCE
*Xhc
,
1868 for (Index
= 0; Index
< 255; Index
++) {
1869 if (Xhc
->UsbDevContext
[Index
+ 1].Enabled
&&
1870 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
!= 0) &&
1871 (Xhc
->UsbDevContext
[Index
+ 1].BusDevAddr
== BusDevAddr
))
1881 return Xhc
->UsbDevContext
[Index
+ 1].SlotId
;
1885 Find out the slot id according to the device's route string.
1887 @param Xhc The XHCI Instance.
1888 @param RouteString The route string described the device location.
1890 @return The slot id used by the device.
1895 XhcRouteStringToSlotId (
1896 IN USB_XHCI_INSTANCE
*Xhc
,
1897 IN USB_DEV_ROUTE RouteString
1902 for (Index
= 0; Index
< 255; Index
++) {
1903 if (Xhc
->UsbDevContext
[Index
+ 1].Enabled
&&
1904 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
!= 0) &&
1905 (Xhc
->UsbDevContext
[Index
+ 1].RouteString
.Dword
== RouteString
.Dword
))
1915 return Xhc
->UsbDevContext
[Index
+ 1].SlotId
;
1919 Synchronize the specified event ring to update the enqueue and dequeue pointer.
1921 @param Xhc The XHCI Instance.
1922 @param EvtRing The event ring to sync.
1924 @retval EFI_SUCCESS The event ring is synchronized successfully.
1930 IN USB_XHCI_INSTANCE
*Xhc
,
1931 IN EVENT_RING
*EvtRing
1935 TRB_TEMPLATE
*EvtTrb1
;
1937 ASSERT (EvtRing
!= NULL
);
1940 // Calculate the EventRingEnqueue and EventRingCCS.
1941 // Note: only support single Segment
1943 EvtTrb1
= EvtRing
->EventRingDequeue
;
1945 for (Index
= 0; Index
< EvtRing
->TrbNumber
; Index
++) {
1946 if (EvtTrb1
->CycleBit
!= EvtRing
->EventRingCCS
) {
1952 if ((UINTN
)EvtTrb1
>= ((UINTN
)EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
1953 EvtTrb1
= EvtRing
->EventRingSeg0
;
1954 EvtRing
->EventRingCCS
= (EvtRing
->EventRingCCS
) ? 0 : 1;
1958 if (Index
< EvtRing
->TrbNumber
) {
1959 EvtRing
->EventRingEnqueue
= EvtTrb1
;
1968 Synchronize the specified transfer ring to update the enqueue and dequeue pointer.
1970 @param Xhc The XHCI Instance.
1971 @param TrsRing The transfer ring to sync.
1973 @retval EFI_SUCCESS The transfer ring is synchronized successfully.
1979 IN USB_XHCI_INSTANCE
*Xhc
,
1980 IN TRANSFER_RING
*TrsRing
1984 TRB_TEMPLATE
*TrsTrb
;
1986 ASSERT (TrsRing
!= NULL
);
1988 // Calculate the latest RingEnqueue and RingPCS
1990 TrsTrb
= TrsRing
->RingEnqueue
;
1991 ASSERT (TrsTrb
!= NULL
);
1993 for (Index
= 0; Index
< TrsRing
->TrbNumber
; Index
++) {
1994 if (TrsTrb
->CycleBit
!= (TrsRing
->RingPCS
& BIT0
)) {
1999 if ((UINT8
)TrsTrb
->Type
== TRB_TYPE_LINK
) {
2000 ASSERT (((LINK_TRB
*)TrsTrb
)->TC
!= 0);
2002 // set cycle bit in Link TRB as normal
2004 ((LINK_TRB
*)TrsTrb
)->CycleBit
= TrsRing
->RingPCS
& BIT0
;
2006 // Toggle PCS maintained by software
2008 TrsRing
->RingPCS
= (TrsRing
->RingPCS
& BIT0
) ? 0 : 1;
2009 TrsTrb
= (TRB_TEMPLATE
*)TrsRing
->RingSeg0
; // Use host address
2013 ASSERT (Index
!= TrsRing
->TrbNumber
);
2015 if (TrsTrb
!= TrsRing
->RingEnqueue
) {
2016 TrsRing
->RingEnqueue
= TrsTrb
;
2020 // Clear the Trb context for enqueue, but reserve the PCS bit
2022 TrsTrb
->Parameter1
= 0;
2023 TrsTrb
->Parameter2
= 0;
2027 TrsTrb
->Control
= 0;
2033 Check if there is a new generated event.
2035 @param Xhc The XHCI Instance.
2036 @param EvtRing The event ring to check.
2037 @param NewEvtTrb The new event TRB found.
2039 @retval EFI_SUCCESS Found a new event TRB at the event ring.
2040 @retval EFI_NOT_READY The event ring has no new event.
2046 IN USB_XHCI_INSTANCE
*Xhc
,
2047 IN EVENT_RING
*EvtRing
,
2048 OUT TRB_TEMPLATE
**NewEvtTrb
2051 ASSERT (EvtRing
!= NULL
);
2053 *NewEvtTrb
= EvtRing
->EventRingDequeue
;
2055 if (EvtRing
->EventRingDequeue
== EvtRing
->EventRingEnqueue
) {
2056 return EFI_NOT_READY
;
2059 EvtRing
->EventRingDequeue
++;
2061 // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.
2063 if ((UINTN
)EvtRing
->EventRingDequeue
>= ((UINTN
)EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
2064 EvtRing
->EventRingDequeue
= EvtRing
->EventRingSeg0
;
2071 Ring the door bell to notify XHCI there is a transaction to be executed.
2073 @param Xhc The XHCI Instance.
2074 @param SlotId The slot id of the target device.
2075 @param Dci The device context index of the target slot or endpoint.
2077 @retval EFI_SUCCESS Successfully ring the door bell.
2083 IN USB_XHCI_INSTANCE
*Xhc
,
2089 XhcWriteDoorBellReg (Xhc
, 0, 0);
2091 XhcWriteDoorBellReg (Xhc
, SlotId
* sizeof (UINT32
), Dci
);
2098 Ring the door bell to notify XHCI there is a transaction to be executed through URB.
2100 @param Xhc The XHCI Instance.
2101 @param Urb The URB to be rung.
2103 @retval EFI_SUCCESS Successfully ring the door bell.
2107 RingIntTransferDoorBell (
2108 IN USB_XHCI_INSTANCE
*Xhc
,
2115 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
2116 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
2117 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
2122 Assign and initialize the device slot for a new device.
2124 @param Xhc The XHCI Instance.
2125 @param ParentRouteChart The route string pointed to the parent device.
2126 @param ParentPort The port at which the device is located.
2127 @param RouteChart The route string pointed to the device.
2128 @param DeviceSpeed The device speed.
2130 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.
2135 XhcInitializeDeviceSlot (
2136 IN USB_XHCI_INSTANCE
*Xhc
,
2137 IN USB_DEV_ROUTE ParentRouteChart
,
2138 IN UINT16 ParentPort
,
2139 IN USB_DEV_ROUTE RouteChart
,
2140 IN UINT8 DeviceSpeed
2144 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
2145 INPUT_CONTEXT
*InputContext
;
2146 DEVICE_CONTEXT
*OutputContext
;
2147 TRANSFER_RING
*EndpointTransferRing
;
2148 CMD_TRB_ADDRESS_DEVICE CmdTrbAddr
;
2149 UINT8 DeviceAddress
;
2150 CMD_TRB_ENABLE_SLOT CmdTrb
;
2153 DEVICE_CONTEXT
*ParentDeviceContext
;
2154 EFI_PHYSICAL_ADDRESS PhyAddr
;
2156 ZeroMem (&CmdTrb
, sizeof (CMD_TRB_ENABLE_SLOT
));
2157 CmdTrb
.CycleBit
= 1;
2158 CmdTrb
.Type
= TRB_TYPE_EN_SLOT
;
2160 Status
= XhcCmdTransfer (
2162 (TRB_TEMPLATE
*)(UINTN
)&CmdTrb
,
2163 XHC_GENERIC_TIMEOUT
,
2164 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
2166 if (EFI_ERROR (Status
)) {
2167 DEBUG ((DEBUG_ERROR
, "XhcInitializeDeviceSlot: Enable Slot Failed, Status = %r\n", Status
));
2171 ASSERT (EvtTrb
->SlotId
<= Xhc
->MaxSlotsEn
);
2172 DEBUG ((DEBUG_INFO
, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb
->SlotId
));
2173 SlotId
= (UINT8
)EvtTrb
->SlotId
;
2174 ASSERT (SlotId
!= 0);
2176 ZeroMem (&Xhc
->UsbDevContext
[SlotId
], sizeof (USB_DEV_CONTEXT
));
2177 Xhc
->UsbDevContext
[SlotId
].Enabled
= TRUE
;
2178 Xhc
->UsbDevContext
[SlotId
].SlotId
= SlotId
;
2179 Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
= RouteChart
.Dword
;
2180 Xhc
->UsbDevContext
[SlotId
].ParentRouteString
.Dword
= ParentRouteChart
.Dword
;
2183 // 4.3.3 Device Slot Initialization
2184 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.
2186 InputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (INPUT_CONTEXT
), FALSE
);
2187 ASSERT (InputContext
!= NULL
);
2188 ASSERT (((UINTN
)InputContext
& 0x3F) == 0);
2189 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
2191 Xhc
->UsbDevContext
[SlotId
].InputContext
= (VOID
*)InputContext
;
2194 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1
2195 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input
2196 // Context are affected by the command.
2198 InputContext
->InputControlContext
.Dword2
|= (BIT0
| BIT1
);
2201 // 3) Initialize the Input Slot Context data structure
2203 InputContext
->Slot
.RouteString
= RouteChart
.Route
.RouteString
;
2204 InputContext
->Slot
.Speed
= DeviceSpeed
+ 1;
2205 InputContext
->Slot
.ContextEntries
= 1;
2206 InputContext
->Slot
.RootHubPortNum
= RouteChart
.Route
.RootPortNum
;
2208 if (RouteChart
.Route
.RouteString
) {
2210 // The device is behind of hub device.
2212 ParentSlotId
= XhcRouteStringToSlotId (Xhc
, ParentRouteChart
);
2213 ASSERT (ParentSlotId
!= 0);
2215 // if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context
2217 ParentDeviceContext
= (DEVICE_CONTEXT
*)Xhc
->UsbDevContext
[ParentSlotId
].OutputContext
;
2218 if ((ParentDeviceContext
->Slot
.TTPortNum
== 0) &&
2219 (ParentDeviceContext
->Slot
.TTHubSlotId
== 0))
2221 if ((ParentDeviceContext
->Slot
.Speed
== (EFI_USB_SPEED_HIGH
+ 1)) && (DeviceSpeed
< EFI_USB_SPEED_HIGH
)) {
2223 // Full/Low device attached to High speed hub port that isolates the high speed signaling
2224 // environment from Full/Low speed signaling environment for a device
2226 InputContext
->Slot
.TTPortNum
= ParentPort
;
2227 InputContext
->Slot
.TTHubSlotId
= ParentSlotId
;
2231 // Inherit the TT parameters from parent device.
2233 InputContext
->Slot
.TTPortNum
= ParentDeviceContext
->Slot
.TTPortNum
;
2234 InputContext
->Slot
.TTHubSlotId
= ParentDeviceContext
->Slot
.TTHubSlotId
;
2236 // If the device is a High speed device then down the speed to be the same as its parent Hub
2238 if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2239 InputContext
->Slot
.Speed
= ParentDeviceContext
->Slot
.Speed
;
2245 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.
2247 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2248 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0] = EndpointTransferRing
;
2249 CreateTransferRing (Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0]);
2251 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).
2253 InputContext
->EP
[0].EPType
= ED_CONTROL_BIDIR
;
2255 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2256 InputContext
->EP
[0].MaxPacketSize
= 512;
2257 } else if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2258 InputContext
->EP
[0].MaxPacketSize
= 64;
2260 InputContext
->EP
[0].MaxPacketSize
= 8;
2264 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints
2265 // 1KB, and Bulk and Isoch endpoints 3KB.
2267 InputContext
->EP
[0].AverageTRBLength
= 8;
2268 InputContext
->EP
[0].MaxBurstSize
= 0;
2269 InputContext
->EP
[0].Interval
= 0;
2270 InputContext
->EP
[0].MaxPStreams
= 0;
2271 InputContext
->EP
[0].Mult
= 0;
2272 InputContext
->EP
[0].CErr
= 3;
2275 // Init the DCS(dequeue cycle state) as the transfer ring's CCS
2277 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2279 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0])->RingSeg0
,
2280 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2282 InputContext
->EP
[0].PtrLo
= XHC_LOW_32BIT (PhyAddr
) | BIT0
;
2283 InputContext
->EP
[0].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2286 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
2288 OutputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (DEVICE_CONTEXT
), FALSE
);
2289 ASSERT (OutputContext
!= NULL
);
2290 ASSERT (((UINTN
)OutputContext
& 0x3F) == 0);
2291 ZeroMem (OutputContext
, sizeof (DEVICE_CONTEXT
));
2293 Xhc
->UsbDevContext
[SlotId
].OutputContext
= OutputContext
;
2295 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with
2296 // a pointer to the Output Device Context data structure (6.2.1).
2298 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, OutputContext
, sizeof (DEVICE_CONTEXT
));
2300 // Fill DCBAA with PCI device address
2302 Xhc
->DCBAA
[SlotId
] = (UINT64
)(UINTN
)PhyAddr
;
2305 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input
2306 // Context data structure described above.
2308 // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request
2311 gBS
->Stall (XHC_RESET_RECOVERY_DELAY
);
2312 ZeroMem (&CmdTrbAddr
, sizeof (CmdTrbAddr
));
2313 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT
));
2314 CmdTrbAddr
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2315 CmdTrbAddr
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2316 CmdTrbAddr
.CycleBit
= 1;
2317 CmdTrbAddr
.Type
= TRB_TYPE_ADDRESS_DEV
;
2318 CmdTrbAddr
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
2319 Status
= XhcCmdTransfer (
2321 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbAddr
,
2322 XHC_GENERIC_TIMEOUT
,
2323 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
2325 if (!EFI_ERROR (Status
)) {
2326 DeviceAddress
= (UINT8
)((DEVICE_CONTEXT
*)OutputContext
)->Slot
.DeviceAddress
;
2327 DEBUG ((DEBUG_INFO
, " Address %d assigned successfully\n", DeviceAddress
));
2328 Xhc
->UsbDevContext
[SlotId
].XhciDevAddr
= DeviceAddress
;
2330 DEBUG ((DEBUG_ERROR
, " Slot %d address not assigned successfully. Status = %r\n", SlotId
, Status
));
2331 XhcDisableSlotCmd (Xhc
, SlotId
);
2338 Assign and initialize the device slot for a new device.
2340 @param Xhc The XHCI Instance.
2341 @param ParentRouteChart The route string pointed to the parent device.
2342 @param ParentPort The port at which the device is located.
2343 @param RouteChart The route string pointed to the device.
2344 @param DeviceSpeed The device speed.
2346 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.
2351 XhcInitializeDeviceSlot64 (
2352 IN USB_XHCI_INSTANCE
*Xhc
,
2353 IN USB_DEV_ROUTE ParentRouteChart
,
2354 IN UINT16 ParentPort
,
2355 IN USB_DEV_ROUTE RouteChart
,
2356 IN UINT8 DeviceSpeed
2360 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
2361 INPUT_CONTEXT_64
*InputContext
;
2362 DEVICE_CONTEXT_64
*OutputContext
;
2363 TRANSFER_RING
*EndpointTransferRing
;
2364 CMD_TRB_ADDRESS_DEVICE CmdTrbAddr
;
2365 UINT8 DeviceAddress
;
2366 CMD_TRB_ENABLE_SLOT CmdTrb
;
2369 DEVICE_CONTEXT_64
*ParentDeviceContext
;
2370 EFI_PHYSICAL_ADDRESS PhyAddr
;
2372 ZeroMem (&CmdTrb
, sizeof (CMD_TRB_ENABLE_SLOT
));
2373 CmdTrb
.CycleBit
= 1;
2374 CmdTrb
.Type
= TRB_TYPE_EN_SLOT
;
2376 Status
= XhcCmdTransfer (
2378 (TRB_TEMPLATE
*)(UINTN
)&CmdTrb
,
2379 XHC_GENERIC_TIMEOUT
,
2380 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
2382 if (EFI_ERROR (Status
)) {
2383 DEBUG ((DEBUG_ERROR
, "XhcInitializeDeviceSlot64: Enable Slot Failed, Status = %r\n", Status
));
2387 ASSERT (EvtTrb
->SlotId
<= Xhc
->MaxSlotsEn
);
2388 DEBUG ((DEBUG_INFO
, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb
->SlotId
));
2389 SlotId
= (UINT8
)EvtTrb
->SlotId
;
2390 ASSERT (SlotId
!= 0);
2392 ZeroMem (&Xhc
->UsbDevContext
[SlotId
], sizeof (USB_DEV_CONTEXT
));
2393 Xhc
->UsbDevContext
[SlotId
].Enabled
= TRUE
;
2394 Xhc
->UsbDevContext
[SlotId
].SlotId
= SlotId
;
2395 Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
= RouteChart
.Dword
;
2396 Xhc
->UsbDevContext
[SlotId
].ParentRouteString
.Dword
= ParentRouteChart
.Dword
;
2399 // 4.3.3 Device Slot Initialization
2400 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.
2402 InputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (INPUT_CONTEXT_64
), FALSE
);
2403 ASSERT (InputContext
!= NULL
);
2404 ASSERT (((UINTN
)InputContext
& 0x3F) == 0);
2405 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
2407 Xhc
->UsbDevContext
[SlotId
].InputContext
= (VOID
*)InputContext
;
2410 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1
2411 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input
2412 // Context are affected by the command.
2414 InputContext
->InputControlContext
.Dword2
|= (BIT0
| BIT1
);
2417 // 3) Initialize the Input Slot Context data structure
2419 InputContext
->Slot
.RouteString
= RouteChart
.Route
.RouteString
;
2420 InputContext
->Slot
.Speed
= DeviceSpeed
+ 1;
2421 InputContext
->Slot
.ContextEntries
= 1;
2422 InputContext
->Slot
.RootHubPortNum
= RouteChart
.Route
.RootPortNum
;
2424 if (RouteChart
.Route
.RouteString
) {
2426 // The device is behind of hub device.
2428 ParentSlotId
= XhcRouteStringToSlotId (Xhc
, ParentRouteChart
);
2429 ASSERT (ParentSlotId
!= 0);
2431 // if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context
2433 ParentDeviceContext
= (DEVICE_CONTEXT_64
*)Xhc
->UsbDevContext
[ParentSlotId
].OutputContext
;
2434 if ((ParentDeviceContext
->Slot
.TTPortNum
== 0) &&
2435 (ParentDeviceContext
->Slot
.TTHubSlotId
== 0))
2437 if ((ParentDeviceContext
->Slot
.Speed
== (EFI_USB_SPEED_HIGH
+ 1)) && (DeviceSpeed
< EFI_USB_SPEED_HIGH
)) {
2439 // Full/Low device attached to High speed hub port that isolates the high speed signaling
2440 // environment from Full/Low speed signaling environment for a device
2442 InputContext
->Slot
.TTPortNum
= ParentPort
;
2443 InputContext
->Slot
.TTHubSlotId
= ParentSlotId
;
2447 // Inherit the TT parameters from parent device.
2449 InputContext
->Slot
.TTPortNum
= ParentDeviceContext
->Slot
.TTPortNum
;
2450 InputContext
->Slot
.TTHubSlotId
= ParentDeviceContext
->Slot
.TTHubSlotId
;
2452 // If the device is a High speed device then down the speed to be the same as its parent Hub
2454 if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2455 InputContext
->Slot
.Speed
= ParentDeviceContext
->Slot
.Speed
;
2461 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.
2463 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2464 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0] = EndpointTransferRing
;
2465 CreateTransferRing (Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0]);
2467 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).
2469 InputContext
->EP
[0].EPType
= ED_CONTROL_BIDIR
;
2471 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2472 InputContext
->EP
[0].MaxPacketSize
= 512;
2473 } else if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2474 InputContext
->EP
[0].MaxPacketSize
= 64;
2476 InputContext
->EP
[0].MaxPacketSize
= 8;
2480 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints
2481 // 1KB, and Bulk and Isoch endpoints 3KB.
2483 InputContext
->EP
[0].AverageTRBLength
= 8;
2484 InputContext
->EP
[0].MaxBurstSize
= 0;
2485 InputContext
->EP
[0].Interval
= 0;
2486 InputContext
->EP
[0].MaxPStreams
= 0;
2487 InputContext
->EP
[0].Mult
= 0;
2488 InputContext
->EP
[0].CErr
= 3;
2491 // Init the DCS(dequeue cycle state) as the transfer ring's CCS
2493 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2495 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0])->RingSeg0
,
2496 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2498 InputContext
->EP
[0].PtrLo
= XHC_LOW_32BIT (PhyAddr
) | BIT0
;
2499 InputContext
->EP
[0].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2502 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
2504 OutputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (DEVICE_CONTEXT_64
), FALSE
);
2505 ASSERT (OutputContext
!= NULL
);
2506 ASSERT (((UINTN
)OutputContext
& 0x3F) == 0);
2507 ZeroMem (OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2509 Xhc
->UsbDevContext
[SlotId
].OutputContext
= OutputContext
;
2511 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with
2512 // a pointer to the Output Device Context data structure (6.2.1).
2514 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2516 // Fill DCBAA with PCI device address
2518 Xhc
->DCBAA
[SlotId
] = (UINT64
)(UINTN
)PhyAddr
;
2521 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input
2522 // Context data structure described above.
2524 // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request
2527 gBS
->Stall (XHC_RESET_RECOVERY_DELAY
);
2528 ZeroMem (&CmdTrbAddr
, sizeof (CmdTrbAddr
));
2529 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT_64
));
2530 CmdTrbAddr
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2531 CmdTrbAddr
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2532 CmdTrbAddr
.CycleBit
= 1;
2533 CmdTrbAddr
.Type
= TRB_TYPE_ADDRESS_DEV
;
2534 CmdTrbAddr
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
2535 Status
= XhcCmdTransfer (
2537 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbAddr
,
2538 XHC_GENERIC_TIMEOUT
,
2539 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
2541 if (!EFI_ERROR (Status
)) {
2542 DeviceAddress
= (UINT8
)((DEVICE_CONTEXT_64
*)OutputContext
)->Slot
.DeviceAddress
;
2543 DEBUG ((DEBUG_INFO
, " Address %d assigned successfully\n", DeviceAddress
));
2544 Xhc
->UsbDevContext
[SlotId
].XhciDevAddr
= DeviceAddress
;
2546 DEBUG ((DEBUG_ERROR
, " Slot %d address not assigned successfully. Status = %r\n", SlotId
, Status
));
2547 XhcDisableSlotCmd64 (Xhc
, SlotId
);
2554 Disable the specified device slot.
2556 @param Xhc The XHCI Instance.
2557 @param SlotId The slot id to be disabled.
2559 @retval EFI_SUCCESS Successfully disable the device slot.
2565 IN USB_XHCI_INSTANCE
*Xhc
,
2570 TRB_TEMPLATE
*EvtTrb
;
2571 CMD_TRB_DISABLE_SLOT CmdTrbDisSlot
;
2576 // Disable the device slots occupied by these devices on its downstream ports.
2577 // Entry 0 is reserved.
2579 for (Index
= 0; Index
< 255; Index
++) {
2580 if (!Xhc
->UsbDevContext
[Index
+ 1].Enabled
||
2581 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
== 0) ||
2582 (Xhc
->UsbDevContext
[Index
+ 1].ParentRouteString
.Dword
!= Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
))
2587 Status
= XhcDisableSlotCmd (Xhc
, Xhc
->UsbDevContext
[Index
+ 1].SlotId
);
2589 if (EFI_ERROR (Status
)) {
2590 DEBUG ((DEBUG_ERROR
, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
2591 Xhc
->UsbDevContext
[Index
+ 1].SlotId
= 0;
2596 // Construct the disable slot command
2598 DEBUG ((DEBUG_INFO
, "Disable device slot %d!\n", SlotId
));
2600 ZeroMem (&CmdTrbDisSlot
, sizeof (CmdTrbDisSlot
));
2601 CmdTrbDisSlot
.CycleBit
= 1;
2602 CmdTrbDisSlot
.Type
= TRB_TYPE_DIS_SLOT
;
2603 CmdTrbDisSlot
.SlotId
= SlotId
;
2604 Status
= XhcCmdTransfer (
2606 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbDisSlot
,
2607 XHC_GENERIC_TIMEOUT
,
2608 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
2610 if (EFI_ERROR (Status
)) {
2611 DEBUG ((DEBUG_ERROR
, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status
));
2616 // Free the slot's device context entry
2618 Xhc
->DCBAA
[SlotId
] = 0;
2621 // Free the slot related data structure
2623 for (Index
= 0; Index
< 31; Index
++) {
2624 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] != NULL
) {
2625 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
])->RingSeg0
;
2626 if (RingSeg
!= NULL
) {
2627 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
2630 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
]);
2631 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] = NULL
;
2635 for (Index
= 0; Index
< Xhc
->UsbDevContext
[SlotId
].DevDesc
.NumConfigurations
; Index
++) {
2636 if (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
] != NULL
) {
2637 FreePool (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
]);
2641 if (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
!= NULL
) {
2642 FreePool (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
);
2645 if (Xhc
->UsbDevContext
[SlotId
].InputContext
!= NULL
) {
2646 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT
));
2649 if (Xhc
->UsbDevContext
[SlotId
].OutputContext
!= NULL
) {
2650 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].OutputContext
, sizeof (DEVICE_CONTEXT
));
2654 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established
2655 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to
2656 // remove urb from XHCI's asynchronous transfer list.
2658 Xhc
->UsbDevContext
[SlotId
].Enabled
= FALSE
;
2659 Xhc
->UsbDevContext
[SlotId
].SlotId
= 0;
2665 Disable the specified device slot.
2667 @param Xhc The XHCI Instance.
2668 @param SlotId The slot id to be disabled.
2670 @retval EFI_SUCCESS Successfully disable the device slot.
2675 XhcDisableSlotCmd64 (
2676 IN USB_XHCI_INSTANCE
*Xhc
,
2681 TRB_TEMPLATE
*EvtTrb
;
2682 CMD_TRB_DISABLE_SLOT CmdTrbDisSlot
;
2687 // Disable the device slots occupied by these devices on its downstream ports.
2688 // Entry 0 is reserved.
2690 for (Index
= 0; Index
< 255; Index
++) {
2691 if (!Xhc
->UsbDevContext
[Index
+ 1].Enabled
||
2692 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
== 0) ||
2693 (Xhc
->UsbDevContext
[Index
+ 1].ParentRouteString
.Dword
!= Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
))
2698 Status
= XhcDisableSlotCmd64 (Xhc
, Xhc
->UsbDevContext
[Index
+ 1].SlotId
);
2700 if (EFI_ERROR (Status
)) {
2701 DEBUG ((DEBUG_ERROR
, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
2702 Xhc
->UsbDevContext
[Index
+ 1].SlotId
= 0;
2707 // Construct the disable slot command
2709 DEBUG ((DEBUG_INFO
, "Disable device slot %d!\n", SlotId
));
2711 ZeroMem (&CmdTrbDisSlot
, sizeof (CmdTrbDisSlot
));
2712 CmdTrbDisSlot
.CycleBit
= 1;
2713 CmdTrbDisSlot
.Type
= TRB_TYPE_DIS_SLOT
;
2714 CmdTrbDisSlot
.SlotId
= SlotId
;
2715 Status
= XhcCmdTransfer (
2717 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbDisSlot
,
2718 XHC_GENERIC_TIMEOUT
,
2719 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
2721 if (EFI_ERROR (Status
)) {
2722 DEBUG ((DEBUG_ERROR
, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status
));
2727 // Free the slot's device context entry
2729 Xhc
->DCBAA
[SlotId
] = 0;
2732 // Free the slot related data structure
2734 for (Index
= 0; Index
< 31; Index
++) {
2735 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] != NULL
) {
2736 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
])->RingSeg0
;
2737 if (RingSeg
!= NULL
) {
2738 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
2741 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
]);
2742 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] = NULL
;
2746 for (Index
= 0; Index
< Xhc
->UsbDevContext
[SlotId
].DevDesc
.NumConfigurations
; Index
++) {
2747 if (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
] != NULL
) {
2748 FreePool (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
]);
2752 if (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
!= NULL
) {
2753 FreePool (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
);
2756 if (Xhc
->UsbDevContext
[SlotId
].InputContext
!= NULL
) {
2757 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT_64
));
2760 if (Xhc
->UsbDevContext
[SlotId
].OutputContext
!= NULL
) {
2761 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2765 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established
2766 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to
2767 // remove urb from XHCI's asynchronous transfer list.
2769 Xhc
->UsbDevContext
[SlotId
].Enabled
= FALSE
;
2770 Xhc
->UsbDevContext
[SlotId
].SlotId
= 0;
2776 Initialize endpoint context in input context.
2778 @param Xhc The XHCI Instance.
2779 @param SlotId The slot id to be configured.
2780 @param DeviceSpeed The device's speed.
2781 @param InputContext The pointer to the input context.
2782 @param IfDesc The pointer to the usb device interface descriptor.
2784 @return The maximum device context index of endpoint.
2789 XhcInitializeEndpointContext (
2790 IN USB_XHCI_INSTANCE
*Xhc
,
2792 IN UINT8 DeviceSpeed
,
2793 IN INPUT_CONTEXT
*InputContext
,
2794 IN USB_INTERFACE_DESCRIPTOR
*IfDesc
2797 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
2804 EFI_PHYSICAL_ADDRESS PhyAddr
;
2806 TRANSFER_RING
*EndpointTransferRing
;
2810 NumEp
= IfDesc
->NumEndpoints
;
2815 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDesc
+ 1);
2816 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
2817 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
2818 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2821 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
2822 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2826 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
2827 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
2829 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
2835 InputContext
->InputControlContext
.Dword2
|= (BIT0
<< Dci
);
2836 InputContext
->EP
[Dci
-1].MaxPacketSize
= EpDesc
->MaxPacketSize
;
2838 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2840 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.
2842 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2844 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2847 switch (EpDesc
->Attributes
& USB_ENDPOINT_TYPE_MASK
) {
2848 case USB_ENDPOINT_BULK
:
2849 if (Direction
== EfiUsbDataIn
) {
2850 InputContext
->EP
[Dci
-1].CErr
= 3;
2851 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_IN
;
2853 InputContext
->EP
[Dci
-1].CErr
= 3;
2854 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_OUT
;
2857 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2858 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2859 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2860 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*)EndpointTransferRing
;
2861 CreateTransferRing (Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2864 "Endpoint[%x]: Created BULK ring [%p~%p)\n",
2865 EpDesc
->EndpointAddress
,
2866 EndpointTransferRing
->RingSeg0
,
2867 (UINTN
)EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2872 case USB_ENDPOINT_ISO
:
2873 if (Direction
== EfiUsbDataIn
) {
2874 InputContext
->EP
[Dci
-1].CErr
= 0;
2875 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_IN
;
2877 InputContext
->EP
[Dci
-1].CErr
= 0;
2878 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_OUT
;
2882 // Get the bInterval from descriptor and init the the interval field of endpoint context.
2883 // Refer to XHCI 1.1 spec section 6.2.3.6.
2885 if (DeviceSpeed
== EFI_USB_SPEED_FULL
) {
2886 Interval
= EpDesc
->Interval
;
2887 ASSERT (Interval
>= 1 && Interval
<= 16);
2888 InputContext
->EP
[Dci
-1].Interval
= Interval
+ 2;
2889 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2890 Interval
= EpDesc
->Interval
;
2891 ASSERT (Interval
>= 1 && Interval
<= 16);
2892 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2896 // Do not support isochronous transfer now.
2898 DEBUG ((DEBUG_INFO
, "XhcInitializeEndpointContext: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
2899 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2901 case USB_ENDPOINT_INTERRUPT
:
2902 if (Direction
== EfiUsbDataIn
) {
2903 InputContext
->EP
[Dci
-1].CErr
= 3;
2904 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_IN
;
2906 InputContext
->EP
[Dci
-1].CErr
= 3;
2907 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_OUT
;
2910 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2911 InputContext
->EP
[Dci
-1].MaxESITPayload
= EpDesc
->MaxPacketSize
;
2913 // Get the bInterval from descriptor and init the the interval field of endpoint context
2915 if ((DeviceSpeed
== EFI_USB_SPEED_FULL
) || (DeviceSpeed
== EFI_USB_SPEED_LOW
)) {
2916 Interval
= EpDesc
->Interval
;
2918 // Calculate through the bInterval field of Endpoint descriptor.
2920 ASSERT (Interval
!= 0);
2921 InputContext
->EP
[Dci
-1].Interval
= (UINT32
)HighBitSet32 ((UINT32
)Interval
) + 3;
2922 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2923 Interval
= EpDesc
->Interval
;
2924 ASSERT (Interval
>= 1 && Interval
<= 16);
2926 // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
2928 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2929 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2930 InputContext
->EP
[Dci
-1].MaxESITPayload
= 0x0002;
2931 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2932 InputContext
->EP
[Dci
-1].CErr
= 3;
2935 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2936 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2937 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*)EndpointTransferRing
;
2938 CreateTransferRing (Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2941 "Endpoint[%x]: Created INT ring [%p~%p)\n",
2942 EpDesc
->EndpointAddress
,
2943 EndpointTransferRing
->RingSeg0
,
2944 (UINTN
)EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2950 case USB_ENDPOINT_CONTROL
:
2952 // Do not support control transfer now.
2954 DEBUG ((DEBUG_INFO
, "XhcInitializeEndpointContext: Unsupport Control EP found, Transfer ring is not allocated.\n"));
2956 DEBUG ((DEBUG_INFO
, "XhcInitializeEndpointContext: Unknown EP found, Transfer ring is not allocated.\n"));
2957 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2961 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2963 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingSeg0
,
2964 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2966 PhyAddr
&= ~((EFI_PHYSICAL_ADDRESS
)0x0F);
2967 PhyAddr
|= (EFI_PHYSICAL_ADDRESS
)((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingPCS
;
2968 InputContext
->EP
[Dci
-1].PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2969 InputContext
->EP
[Dci
-1].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2971 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2978 Initialize endpoint context in input context.
2980 @param Xhc The XHCI Instance.
2981 @param SlotId The slot id to be configured.
2982 @param DeviceSpeed The device's speed.
2983 @param InputContext The pointer to the input context.
2984 @param IfDesc The pointer to the usb device interface descriptor.
2986 @return The maximum device context index of endpoint.
2991 XhcInitializeEndpointContext64 (
2992 IN USB_XHCI_INSTANCE
*Xhc
,
2994 IN UINT8 DeviceSpeed
,
2995 IN INPUT_CONTEXT_64
*InputContext
,
2996 IN USB_INTERFACE_DESCRIPTOR
*IfDesc
2999 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3006 EFI_PHYSICAL_ADDRESS PhyAddr
;
3008 TRANSFER_RING
*EndpointTransferRing
;
3012 NumEp
= IfDesc
->NumEndpoints
;
3017 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDesc
+ 1);
3018 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3019 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3020 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3023 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3024 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3028 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
3029 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3031 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3037 InputContext
->InputControlContext
.Dword2
|= (BIT0
<< Dci
);
3038 InputContext
->EP
[Dci
-1].MaxPacketSize
= EpDesc
->MaxPacketSize
;
3040 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
3042 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.
3044 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
3046 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
3049 switch (EpDesc
->Attributes
& USB_ENDPOINT_TYPE_MASK
) {
3050 case USB_ENDPOINT_BULK
:
3051 if (Direction
== EfiUsbDataIn
) {
3052 InputContext
->EP
[Dci
-1].CErr
= 3;
3053 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_IN
;
3055 InputContext
->EP
[Dci
-1].CErr
= 3;
3056 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_OUT
;
3059 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3060 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
3061 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
3062 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*)EndpointTransferRing
;
3063 CreateTransferRing (Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
3066 "Endpoint64[%x]: Created BULK ring [%p~%p)\n",
3067 EpDesc
->EndpointAddress
,
3068 EndpointTransferRing
->RingSeg0
,
3069 (UINTN
)EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
3074 case USB_ENDPOINT_ISO
:
3075 if (Direction
== EfiUsbDataIn
) {
3076 InputContext
->EP
[Dci
-1].CErr
= 0;
3077 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_IN
;
3079 InputContext
->EP
[Dci
-1].CErr
= 0;
3080 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_OUT
;
3084 // Get the bInterval from descriptor and init the the interval field of endpoint context.
3085 // Refer to XHCI 1.1 spec section 6.2.3.6.
3087 if (DeviceSpeed
== EFI_USB_SPEED_FULL
) {
3088 Interval
= EpDesc
->Interval
;
3089 ASSERT (Interval
>= 1 && Interval
<= 16);
3090 InputContext
->EP
[Dci
-1].Interval
= Interval
+ 2;
3091 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
3092 Interval
= EpDesc
->Interval
;
3093 ASSERT (Interval
>= 1 && Interval
<= 16);
3094 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
3098 // Do not support isochronous transfer now.
3100 DEBUG ((DEBUG_INFO
, "XhcInitializeEndpointContext64: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
3101 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3103 case USB_ENDPOINT_INTERRUPT
:
3104 if (Direction
== EfiUsbDataIn
) {
3105 InputContext
->EP
[Dci
-1].CErr
= 3;
3106 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_IN
;
3108 InputContext
->EP
[Dci
-1].CErr
= 3;
3109 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_OUT
;
3112 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3113 InputContext
->EP
[Dci
-1].MaxESITPayload
= EpDesc
->MaxPacketSize
;
3115 // Get the bInterval from descriptor and init the the interval field of endpoint context
3117 if ((DeviceSpeed
== EFI_USB_SPEED_FULL
) || (DeviceSpeed
== EFI_USB_SPEED_LOW
)) {
3118 Interval
= EpDesc
->Interval
;
3120 // Calculate through the bInterval field of Endpoint descriptor.
3122 ASSERT (Interval
!= 0);
3123 InputContext
->EP
[Dci
-1].Interval
= (UINT32
)HighBitSet32 ((UINT32
)Interval
) + 3;
3124 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
3125 Interval
= EpDesc
->Interval
;
3126 ASSERT (Interval
>= 1 && Interval
<= 16);
3128 // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
3130 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
3131 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3132 InputContext
->EP
[Dci
-1].MaxESITPayload
= 0x0002;
3133 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
3134 InputContext
->EP
[Dci
-1].CErr
= 3;
3137 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
3138 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
3139 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*)EndpointTransferRing
;
3140 CreateTransferRing (Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
3143 "Endpoint64[%x]: Created INT ring [%p~%p)\n",
3144 EpDesc
->EndpointAddress
,
3145 EndpointTransferRing
->RingSeg0
,
3146 (UINTN
)EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
3152 case USB_ENDPOINT_CONTROL
:
3154 // Do not support control transfer now.
3156 DEBUG ((DEBUG_INFO
, "XhcInitializeEndpointContext64: Unsupport Control EP found, Transfer ring is not allocated.\n"));
3158 DEBUG ((DEBUG_INFO
, "XhcInitializeEndpointContext64: Unknown EP found, Transfer ring is not allocated.\n"));
3159 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3163 PhyAddr
= UsbHcGetPciAddrForHostAddr (
3165 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingSeg0
,
3166 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
3168 PhyAddr
&= ~((EFI_PHYSICAL_ADDRESS
)0x0F);
3169 PhyAddr
|= (EFI_PHYSICAL_ADDRESS
)((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingPCS
;
3170 InputContext
->EP
[Dci
-1].PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3171 InputContext
->EP
[Dci
-1].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3173 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3180 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
3182 @param Xhc The XHCI Instance.
3183 @param SlotId The slot id to be configured.
3184 @param DeviceSpeed The device's speed.
3185 @param ConfigDesc The pointer to the usb device configuration descriptor.
3187 @retval EFI_SUCCESS Successfully configure all the device endpoints.
3193 IN USB_XHCI_INSTANCE
*Xhc
,
3195 IN UINT8 DeviceSpeed
,
3196 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
3200 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3204 EFI_PHYSICAL_ADDRESS PhyAddr
;
3206 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3207 INPUT_CONTEXT
*InputContext
;
3208 DEVICE_CONTEXT
*OutputContext
;
3209 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3212 // 4.6.6 Configure Endpoint
3214 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3215 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3216 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3217 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT
));
3219 ASSERT (ConfigDesc
!= NULL
);
3223 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3224 for (Index
= 0; Index
< ConfigDesc
->NumInterfaces
; Index
++) {
3225 while ((IfDesc
->DescriptorType
!= USB_DESC_TYPE_INTERFACE
) || (IfDesc
->AlternateSetting
!= 0)) {
3226 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3229 if (IfDesc
->Length
< sizeof (USB_INTERFACE_DESCRIPTOR
)) {
3230 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3234 Dci
= XhcInitializeEndpointContext (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDesc
);
3239 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3242 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3243 InputContext
->Slot
.ContextEntries
= MaxDci
;
3245 // configure endpoint
3247 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3248 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3249 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3250 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3251 CmdTrbCfgEP
.CycleBit
= 1;
3252 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3253 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3254 DEBUG ((DEBUG_INFO
, "Configure Endpoint\n"));
3255 Status
= XhcCmdTransfer (
3257 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbCfgEP
,
3258 XHC_GENERIC_TIMEOUT
,
3259 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3261 if (EFI_ERROR (Status
)) {
3262 DEBUG ((DEBUG_ERROR
, "XhcSetConfigCmd: Config Endpoint Failed, Status = %r\n", Status
));
3264 Xhc
->UsbDevContext
[SlotId
].ActiveConfiguration
= ConfigDesc
->ConfigurationValue
;
3271 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
3273 @param Xhc The XHCI Instance.
3274 @param SlotId The slot id to be configured.
3275 @param DeviceSpeed The device's speed.
3276 @param ConfigDesc The pointer to the usb device configuration descriptor.
3278 @retval EFI_SUCCESS Successfully configure all the device endpoints.
3284 IN USB_XHCI_INSTANCE
*Xhc
,
3286 IN UINT8 DeviceSpeed
,
3287 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
3291 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3295 EFI_PHYSICAL_ADDRESS PhyAddr
;
3297 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3298 INPUT_CONTEXT_64
*InputContext
;
3299 DEVICE_CONTEXT_64
*OutputContext
;
3300 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3303 // 4.6.6 Configure Endpoint
3305 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3306 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3307 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3308 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT_64
));
3310 ASSERT (ConfigDesc
!= NULL
);
3314 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3315 for (Index
= 0; Index
< ConfigDesc
->NumInterfaces
; Index
++) {
3316 while ((IfDesc
->DescriptorType
!= USB_DESC_TYPE_INTERFACE
) || (IfDesc
->AlternateSetting
!= 0)) {
3317 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3320 if (IfDesc
->Length
< sizeof (USB_INTERFACE_DESCRIPTOR
)) {
3321 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3325 Dci
= XhcInitializeEndpointContext64 (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDesc
);
3330 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3333 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3334 InputContext
->Slot
.ContextEntries
= MaxDci
;
3336 // configure endpoint
3338 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3339 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3340 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3341 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3342 CmdTrbCfgEP
.CycleBit
= 1;
3343 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3344 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3345 DEBUG ((DEBUG_INFO
, "Configure Endpoint\n"));
3346 Status
= XhcCmdTransfer (
3348 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbCfgEP
,
3349 XHC_GENERIC_TIMEOUT
,
3350 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3352 if (EFI_ERROR (Status
)) {
3353 DEBUG ((DEBUG_ERROR
, "XhcSetConfigCmd64: Config Endpoint Failed, Status = %r\n", Status
));
3355 Xhc
->UsbDevContext
[SlotId
].ActiveConfiguration
= ConfigDesc
->ConfigurationValue
;
3362 Stop endpoint through XHCI's Stop_Endpoint cmd.
3364 @param Xhc The XHCI Instance.
3365 @param SlotId The slot id to be configured.
3366 @param Dci The device context index of endpoint.
3367 @param PendingUrb The pending URB to check completion status when stopping the end point.
3369 @retval EFI_SUCCESS Stop endpoint successfully.
3370 @retval Others Failed to stop endpoint.
3376 IN USB_XHCI_INSTANCE
*Xhc
,
3379 IN URB
*PendingUrb OPTIONAL
3383 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3384 CMD_TRB_STOP_ENDPOINT CmdTrbStopED
;
3386 DEBUG ((DEBUG_VERBOSE
, "XhcStopEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId
, Dci
));
3389 // When XhcCheckUrbResult waits for the Stop_Endpoint completion, it also checks
3390 // the PendingUrb completion status, because it's possible that the PendingUrb is
3391 // finished just before stopping the end point, but after the looping check.
3393 // The PendingUrb could be passed to XhcCmdTransfer to XhcExecTransfer to XhcCheckUrbResult
3394 // through function parameter, but That will cause every consumer of XhcCmdTransfer,
3395 // XhcExecTransfer and XhcCheckUrbResult pass a NULL PendingUrb.
3396 // But actually only XhcCheckUrbResult is aware of the PendingUrb.
3397 // So we choose to save the PendingUrb into the USB_XHCI_INSTANCE and use it in XhcCheckUrbResult.
3399 ASSERT (Xhc
->PendingUrb
== NULL
);
3400 Xhc
->PendingUrb
= PendingUrb
;
3402 // Reset the URB result from Timeout to NoError.
3403 // The USB result will be:
3404 // changed to Timeout when Stop/StopInvalidLength Transfer Event is received, or
3405 // remain NoError when Success/ShortPacket Transfer Event is received.
3407 if (PendingUrb
!= NULL
) {
3408 PendingUrb
->Result
= EFI_USB_NOERROR
;
3412 // Send stop endpoint command to transit Endpoint from running to stop state
3414 ZeroMem (&CmdTrbStopED
, sizeof (CmdTrbStopED
));
3415 CmdTrbStopED
.CycleBit
= 1;
3416 CmdTrbStopED
.Type
= TRB_TYPE_STOP_ENDPOINT
;
3417 CmdTrbStopED
.EDID
= Dci
;
3418 CmdTrbStopED
.SlotId
= SlotId
;
3419 Status
= XhcCmdTransfer (
3421 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbStopED
,
3422 XHC_GENERIC_TIMEOUT
,
3423 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3425 if (EFI_ERROR (Status
)) {
3426 DEBUG ((DEBUG_ERROR
, "XhcStopEndpoint: Stop Endpoint Failed, Status = %r\n", Status
));
3429 Xhc
->PendingUrb
= NULL
;
3435 Reset endpoint through XHCI's Reset_Endpoint cmd.
3437 @param Xhc The XHCI Instance.
3438 @param SlotId The slot id to be configured.
3439 @param Dci The device context index of endpoint.
3441 @retval EFI_SUCCESS Reset endpoint successfully.
3442 @retval Others Failed to reset endpoint.
3448 IN USB_XHCI_INSTANCE
*Xhc
,
3454 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3455 CMD_TRB_RESET_ENDPOINT CmdTrbResetED
;
3457 DEBUG ((DEBUG_INFO
, "XhcResetEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId
, Dci
));
3460 // Send stop endpoint command to transit Endpoint from running to stop state
3462 ZeroMem (&CmdTrbResetED
, sizeof (CmdTrbResetED
));
3463 CmdTrbResetED
.CycleBit
= 1;
3464 CmdTrbResetED
.Type
= TRB_TYPE_RESET_ENDPOINT
;
3465 CmdTrbResetED
.EDID
= Dci
;
3466 CmdTrbResetED
.SlotId
= SlotId
;
3467 Status
= XhcCmdTransfer (
3469 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbResetED
,
3470 XHC_GENERIC_TIMEOUT
,
3471 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3473 if (EFI_ERROR (Status
)) {
3474 DEBUG ((DEBUG_ERROR
, "XhcResetEndpoint: Reset Endpoint Failed, Status = %r\n", Status
));
3481 Set transfer ring dequeue pointer through XHCI's Set_Tr_Dequeue_Pointer cmd.
3483 @param Xhc The XHCI Instance.
3484 @param SlotId The slot id to be configured.
3485 @param Dci The device context index of endpoint.
3486 @param Urb The dequeue pointer of the transfer ring specified
3487 by the urb to be updated.
3489 @retval EFI_SUCCESS Set transfer ring dequeue pointer succeeds.
3490 @retval Others Failed to set transfer ring dequeue pointer.
3495 XhcSetTrDequeuePointer (
3496 IN USB_XHCI_INSTANCE
*Xhc
,
3503 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3504 CMD_SET_TR_DEQ_POINTER CmdSetTRDeq
;
3505 EFI_PHYSICAL_ADDRESS PhyAddr
;
3507 DEBUG ((DEBUG_VERBOSE
, "XhcSetTrDequeuePointer: Slot = 0x%x, Dci = 0x%x, Urb = 0x%x\n", SlotId
, Dci
, Urb
));
3510 // Send stop endpoint command to transit Endpoint from running to stop state
3512 ZeroMem (&CmdSetTRDeq
, sizeof (CmdSetTRDeq
));
3513 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Urb
->Ring
->RingEnqueue
, sizeof (CMD_SET_TR_DEQ_POINTER
));
3514 CmdSetTRDeq
.PtrLo
= XHC_LOW_32BIT (PhyAddr
) | Urb
->Ring
->RingPCS
;
3515 CmdSetTRDeq
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3516 CmdSetTRDeq
.CycleBit
= 1;
3517 CmdSetTRDeq
.Type
= TRB_TYPE_SET_TR_DEQUE
;
3518 CmdSetTRDeq
.Endpoint
= Dci
;
3519 CmdSetTRDeq
.SlotId
= SlotId
;
3520 Status
= XhcCmdTransfer (
3522 (TRB_TEMPLATE
*)(UINTN
)&CmdSetTRDeq
,
3523 XHC_GENERIC_TIMEOUT
,
3524 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3526 if (EFI_ERROR (Status
)) {
3527 DEBUG ((DEBUG_ERROR
, "XhcSetTrDequeuePointer: Set TR Dequeue Pointer Failed, Status = %r\n", Status
));
3534 Set interface through XHCI's Configure_Endpoint cmd.
3536 @param Xhc The XHCI Instance.
3537 @param SlotId The slot id to be configured.
3538 @param DeviceSpeed The device's speed.
3539 @param ConfigDesc The pointer to the usb device configuration descriptor.
3540 @param Request USB device request to send.
3542 @retval EFI_SUCCESS Successfully set interface.
3548 IN USB_XHCI_INSTANCE
*Xhc
,
3550 IN UINT8 DeviceSpeed
,
3551 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
,
3552 IN EFI_USB_DEVICE_REQUEST
*Request
3556 USB_INTERFACE_DESCRIPTOR
*IfDescActive
;
3557 USB_INTERFACE_DESCRIPTOR
*IfDescSet
;
3558 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3559 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3566 EFI_PHYSICAL_ADDRESS PhyAddr
;
3569 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3570 INPUT_CONTEXT
*InputContext
;
3571 DEVICE_CONTEXT
*OutputContext
;
3572 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3574 Status
= EFI_SUCCESS
;
3576 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3577 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3579 // XHCI 4.6.6 Configure Endpoint
3580 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3581 // Context and Add Context flags as follows:
3582 // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
3583 // Context and Add Context flags to '0'.
3585 // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
3586 // So the default Drop Context and Add Context flags can be '0' to cover 1).
3588 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3589 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT
));
3591 ASSERT (ConfigDesc
!= NULL
);
3595 IfDescActive
= NULL
;
3598 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3599 while ((UINTN
)IfDesc
< ((UINTN
)ConfigDesc
+ ConfigDesc
->TotalLength
)) {
3600 if ((IfDesc
->DescriptorType
== USB_DESC_TYPE_INTERFACE
) && (IfDesc
->Length
>= sizeof (USB_INTERFACE_DESCRIPTOR
))) {
3601 if (IfDesc
->InterfaceNumber
== (UINT8
)Request
->Index
) {
3602 if (IfDesc
->AlternateSetting
== Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[IfDesc
->InterfaceNumber
]) {
3604 // Find out the active interface descriptor.
3606 IfDescActive
= IfDesc
;
3607 } else if (IfDesc
->AlternateSetting
== (UINT8
)Request
->Value
) {
3609 // Find out the interface descriptor to set.
3616 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3620 // XHCI 4.6.6 Configure Endpoint
3621 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3622 // Context and Add Context flags as follows:
3623 // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
3624 // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
3625 // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
3626 // the Drop Context flag to '1' and Add Context flag to '0'.
3627 // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
3628 // and Add Context flags shall be set to '1'.
3630 // Below codes are to cover 2), 3) and 4).
3633 if ((IfDescActive
!= NULL
) && (IfDescSet
!= NULL
)) {
3634 NumEp
= IfDescActive
->NumEndpoints
;
3635 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDescActive
+ 1);
3636 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3637 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3638 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3641 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3642 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3646 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
3647 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3649 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3656 // XHCI 4.3.6 - Setting Alternate Interfaces
3657 // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
3659 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, NULL
);
3660 if (EFI_ERROR (Status
)) {
3665 // XHCI 4.3.6 - Setting Alternate Interfaces
3666 // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
3668 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] != NULL
) {
3669 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1])->RingSeg0
;
3670 if (RingSeg
!= NULL
) {
3671 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
3674 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1]);
3675 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] = NULL
;
3679 // Set the Drop Context flag to '1'.
3681 InputContext
->InputControlContext
.Dword1
|= (BIT0
<< Dci
);
3683 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3687 // XHCI 4.3.6 - Setting Alternate Interfaces
3688 // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
3689 // Interface setting, to '0'.
3691 // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
3695 // XHCI 4.3.6 - Setting Alternate Interfaces
3696 // 4) For each endpoint enabled by the Configure Endpoint Command:
3697 // a. Allocate a Transfer Ring.
3698 // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
3699 // c. Initialize the Endpoint Context data structure.
3701 Dci
= XhcInitializeEndpointContext (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDescSet
);
3706 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3707 InputContext
->Slot
.ContextEntries
= MaxDci
;
3709 // XHCI 4.3.6 - Setting Alternate Interfaces
3710 // 5) Issue and successfully complete a Configure Endpoint Command.
3712 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3713 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3714 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3715 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3716 CmdTrbCfgEP
.CycleBit
= 1;
3717 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3718 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3719 DEBUG ((DEBUG_INFO
, "SetInterface: Configure Endpoint\n"));
3720 Status
= XhcCmdTransfer (
3722 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbCfgEP
,
3723 XHC_GENERIC_TIMEOUT
,
3724 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3726 if (EFI_ERROR (Status
)) {
3727 DEBUG ((DEBUG_ERROR
, "SetInterface: Config Endpoint Failed, Status = %r\n", Status
));
3730 // Update the active AlternateSetting.
3732 Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[(UINT8
)Request
->Index
] = (UINT8
)Request
->Value
;
3740 Set interface through XHCI's Configure_Endpoint cmd.
3742 @param Xhc The XHCI Instance.
3743 @param SlotId The slot id to be configured.
3744 @param DeviceSpeed The device's speed.
3745 @param ConfigDesc The pointer to the usb device configuration descriptor.
3746 @param Request USB device request to send.
3748 @retval EFI_SUCCESS Successfully set interface.
3754 IN USB_XHCI_INSTANCE
*Xhc
,
3756 IN UINT8 DeviceSpeed
,
3757 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
,
3758 IN EFI_USB_DEVICE_REQUEST
*Request
3762 USB_INTERFACE_DESCRIPTOR
*IfDescActive
;
3763 USB_INTERFACE_DESCRIPTOR
*IfDescSet
;
3764 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3765 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3772 EFI_PHYSICAL_ADDRESS PhyAddr
;
3775 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3776 INPUT_CONTEXT_64
*InputContext
;
3777 DEVICE_CONTEXT_64
*OutputContext
;
3778 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3780 Status
= EFI_SUCCESS
;
3782 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3783 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3785 // XHCI 4.6.6 Configure Endpoint
3786 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3787 // Context and Add Context flags as follows:
3788 // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
3789 // Context and Add Context flags to '0'.
3791 // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
3792 // So the default Drop Context and Add Context flags can be '0' to cover 1).
3794 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3795 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT_64
));
3797 ASSERT (ConfigDesc
!= NULL
);
3801 IfDescActive
= NULL
;
3804 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3805 while ((UINTN
)IfDesc
< ((UINTN
)ConfigDesc
+ ConfigDesc
->TotalLength
)) {
3806 if ((IfDesc
->DescriptorType
== USB_DESC_TYPE_INTERFACE
) && (IfDesc
->Length
>= sizeof (USB_INTERFACE_DESCRIPTOR
))) {
3807 if (IfDesc
->InterfaceNumber
== (UINT8
)Request
->Index
) {
3808 if (IfDesc
->AlternateSetting
== Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[IfDesc
->InterfaceNumber
]) {
3810 // Find out the active interface descriptor.
3812 IfDescActive
= IfDesc
;
3813 } else if (IfDesc
->AlternateSetting
== (UINT8
)Request
->Value
) {
3815 // Find out the interface descriptor to set.
3822 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3826 // XHCI 4.6.6 Configure Endpoint
3827 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3828 // Context and Add Context flags as follows:
3829 // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
3830 // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
3831 // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
3832 // the Drop Context flag to '1' and Add Context flag to '0'.
3833 // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
3834 // and Add Context flags shall be set to '1'.
3836 // Below codes are to cover 2), 3) and 4).
3839 if ((IfDescActive
!= NULL
) && (IfDescSet
!= NULL
)) {
3840 NumEp
= IfDescActive
->NumEndpoints
;
3841 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDescActive
+ 1);
3842 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3843 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3844 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3847 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3848 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3852 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
3853 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3855 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3862 // XHCI 4.3.6 - Setting Alternate Interfaces
3863 // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
3865 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, NULL
);
3866 if (EFI_ERROR (Status
)) {
3871 // XHCI 4.3.6 - Setting Alternate Interfaces
3872 // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
3874 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] != NULL
) {
3875 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1])->RingSeg0
;
3876 if (RingSeg
!= NULL
) {
3877 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
3880 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1]);
3881 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] = NULL
;
3885 // Set the Drop Context flag to '1'.
3887 InputContext
->InputControlContext
.Dword1
|= (BIT0
<< Dci
);
3889 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3893 // XHCI 4.3.6 - Setting Alternate Interfaces
3894 // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
3895 // Interface setting, to '0'.
3897 // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
3901 // XHCI 4.3.6 - Setting Alternate Interfaces
3902 // 4) For each endpoint enabled by the Configure Endpoint Command:
3903 // a. Allocate a Transfer Ring.
3904 // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
3905 // c. Initialize the Endpoint Context data structure.
3907 Dci
= XhcInitializeEndpointContext64 (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDescSet
);
3912 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3913 InputContext
->Slot
.ContextEntries
= MaxDci
;
3915 // XHCI 4.3.6 - Setting Alternate Interfaces
3916 // 5) Issue and successfully complete a Configure Endpoint Command.
3918 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3919 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3920 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3921 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3922 CmdTrbCfgEP
.CycleBit
= 1;
3923 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3924 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3925 DEBUG ((DEBUG_INFO
, "SetInterface64: Configure Endpoint\n"));
3926 Status
= XhcCmdTransfer (
3928 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbCfgEP
,
3929 XHC_GENERIC_TIMEOUT
,
3930 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3932 if (EFI_ERROR (Status
)) {
3933 DEBUG ((DEBUG_ERROR
, "SetInterface64: Config Endpoint Failed, Status = %r\n", Status
));
3936 // Update the active AlternateSetting.
3938 Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[(UINT8
)Request
->Index
] = (UINT8
)Request
->Value
;
3946 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
3948 @param Xhc The XHCI Instance.
3949 @param SlotId The slot id to be evaluated.
3950 @param MaxPacketSize The max packet size supported by the device control transfer.
3952 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.
3957 XhcEvaluateContext (
3958 IN USB_XHCI_INSTANCE
*Xhc
,
3960 IN UINT32 MaxPacketSize
3964 CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu
;
3965 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3966 INPUT_CONTEXT
*InputContext
;
3967 DEVICE_CONTEXT
*OutputContext
;
3968 EFI_PHYSICAL_ADDRESS PhyAddr
;
3970 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3973 // 4.6.7 Evaluate Context
3975 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3976 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3977 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3979 CopyMem (&InputContext
->EP
[0], &OutputContext
->EP
[0], sizeof (ENDPOINT_CONTEXT
));
3981 InputContext
->InputControlContext
.Dword2
|= BIT1
;
3982 InputContext
->EP
[0].MaxPacketSize
= MaxPacketSize
;
3983 InputContext
->EP
[0].EPState
= 0;
3985 ZeroMem (&CmdTrbEvalu
, sizeof (CmdTrbEvalu
));
3986 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3987 CmdTrbEvalu
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3988 CmdTrbEvalu
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3989 CmdTrbEvalu
.CycleBit
= 1;
3990 CmdTrbEvalu
.Type
= TRB_TYPE_EVALU_CONTXT
;
3991 CmdTrbEvalu
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3992 DEBUG ((DEBUG_INFO
, "Evaluate context\n"));
3993 Status
= XhcCmdTransfer (
3995 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbEvalu
,
3996 XHC_GENERIC_TIMEOUT
,
3997 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3999 if (EFI_ERROR (Status
)) {
4000 DEBUG ((DEBUG_ERROR
, "XhcEvaluateContext: Evaluate Context Failed, Status = %r\n", Status
));
4007 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
4009 @param Xhc The XHCI Instance.
4010 @param SlotId The slot id to be evaluated.
4011 @param MaxPacketSize The max packet size supported by the device control transfer.
4013 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.
4018 XhcEvaluateContext64 (
4019 IN USB_XHCI_INSTANCE
*Xhc
,
4021 IN UINT32 MaxPacketSize
4025 CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu
;
4026 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
4027 INPUT_CONTEXT_64
*InputContext
;
4028 DEVICE_CONTEXT_64
*OutputContext
;
4029 EFI_PHYSICAL_ADDRESS PhyAddr
;
4031 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
4034 // 4.6.7 Evaluate Context
4036 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
4037 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
4038 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
4040 CopyMem (&InputContext
->EP
[0], &OutputContext
->EP
[0], sizeof (ENDPOINT_CONTEXT_64
));
4042 InputContext
->InputControlContext
.Dword2
|= BIT1
;
4043 InputContext
->EP
[0].MaxPacketSize
= MaxPacketSize
;
4044 InputContext
->EP
[0].EPState
= 0;
4046 ZeroMem (&CmdTrbEvalu
, sizeof (CmdTrbEvalu
));
4047 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
4048 CmdTrbEvalu
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
4049 CmdTrbEvalu
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
4050 CmdTrbEvalu
.CycleBit
= 1;
4051 CmdTrbEvalu
.Type
= TRB_TYPE_EVALU_CONTXT
;
4052 CmdTrbEvalu
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
4053 DEBUG ((DEBUG_INFO
, "Evaluate context\n"));
4054 Status
= XhcCmdTransfer (
4056 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbEvalu
,
4057 XHC_GENERIC_TIMEOUT
,
4058 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
4060 if (EFI_ERROR (Status
)) {
4061 DEBUG ((DEBUG_ERROR
, "XhcEvaluateContext64: Evaluate Context Failed, Status = %r\n", Status
));
4068 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
4070 @param Xhc The XHCI Instance.
4071 @param SlotId The slot id to be configured.
4072 @param PortNum The total number of downstream port supported by the hub.
4073 @param TTT The TT think time of the hub device.
4074 @param MTT The multi-TT of the hub device.
4076 @retval EFI_SUCCESS Successfully configure the hub device's slot context.
4080 XhcConfigHubContext (
4081 IN USB_XHCI_INSTANCE
*Xhc
,
4089 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
4090 INPUT_CONTEXT
*InputContext
;
4091 DEVICE_CONTEXT
*OutputContext
;
4092 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
4093 EFI_PHYSICAL_ADDRESS PhyAddr
;
4095 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
4096 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
4097 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
4100 // 4.6.7 Evaluate Context
4102 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
4104 InputContext
->InputControlContext
.Dword2
|= BIT0
;
4107 // Copy the slot context from OutputContext to Input context
4109 CopyMem (&(InputContext
->Slot
), &(OutputContext
->Slot
), sizeof (SLOT_CONTEXT
));
4110 InputContext
->Slot
.Hub
= 1;
4111 InputContext
->Slot
.PortNum
= PortNum
;
4112 InputContext
->Slot
.TTT
= TTT
;
4113 InputContext
->Slot
.MTT
= MTT
;
4115 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
4116 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
4117 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
4118 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
4119 CmdTrbCfgEP
.CycleBit
= 1;
4120 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
4121 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
4122 DEBUG ((DEBUG_INFO
, "Configure Hub Slot Context\n"));
4123 Status
= XhcCmdTransfer (
4125 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbCfgEP
,
4126 XHC_GENERIC_TIMEOUT
,
4127 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
4129 if (EFI_ERROR (Status
)) {
4130 DEBUG ((DEBUG_ERROR
, "XhcConfigHubContext: Config Endpoint Failed, Status = %r\n", Status
));
4137 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
4139 @param Xhc The XHCI Instance.
4140 @param SlotId The slot id to be configured.
4141 @param PortNum The total number of downstream port supported by the hub.
4142 @param TTT The TT think time of the hub device.
4143 @param MTT The multi-TT of the hub device.
4145 @retval EFI_SUCCESS Successfully configure the hub device's slot context.
4149 XhcConfigHubContext64 (
4150 IN USB_XHCI_INSTANCE
*Xhc
,
4158 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
4159 INPUT_CONTEXT_64
*InputContext
;
4160 DEVICE_CONTEXT_64
*OutputContext
;
4161 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
4162 EFI_PHYSICAL_ADDRESS PhyAddr
;
4164 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
4165 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
4166 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
4169 // 4.6.7 Evaluate Context
4171 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
4173 InputContext
->InputControlContext
.Dword2
|= BIT0
;
4176 // Copy the slot context from OutputContext to Input context
4178 CopyMem (&(InputContext
->Slot
), &(OutputContext
->Slot
), sizeof (SLOT_CONTEXT_64
));
4179 InputContext
->Slot
.Hub
= 1;
4180 InputContext
->Slot
.PortNum
= PortNum
;
4181 InputContext
->Slot
.TTT
= TTT
;
4182 InputContext
->Slot
.MTT
= MTT
;
4184 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
4185 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
4186 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
4187 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
4188 CmdTrbCfgEP
.CycleBit
= 1;
4189 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
4190 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
4191 DEBUG ((DEBUG_INFO
, "Configure Hub Slot Context\n"));
4192 Status
= XhcCmdTransfer (
4194 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbCfgEP
,
4195 XHC_GENERIC_TIMEOUT
,
4196 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
4198 if (EFI_ERROR (Status
)) {
4199 DEBUG ((DEBUG_ERROR
, "XhcConfigHubContext64: Config Endpoint Failed, Status = %r\n", Status
));