3 XHCI transfer scheduling routines.
5 Copyright (c) 2011 - 2020, Intel Corporation. All rights reserved.<BR>
6 Copyright (c) Microsoft Corporation.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
14 Create a command transfer TRB to support XHCI command interfaces.
16 @param Xhc The XHCI Instance.
17 @param CmdTrb The cmd TRB to be executed.
19 @return Created URB or NULL.
24 IN USB_XHCI_INSTANCE
*Xhc
,
25 IN TRB_TEMPLATE
*CmdTrb
30 Urb
= AllocateZeroPool (sizeof (URB
));
35 Urb
->Signature
= XHC_URB_SIG
;
37 Urb
->Ring
= &Xhc
->CmdRing
;
38 XhcSyncTrsRing (Xhc
, Urb
->Ring
);
40 Urb
->TrbStart
= Urb
->Ring
->RingEnqueue
;
41 CopyMem (Urb
->TrbStart
, CmdTrb
, sizeof (TRB_TEMPLATE
));
42 Urb
->TrbStart
->CycleBit
= Urb
->Ring
->RingPCS
& BIT0
;
43 Urb
->TrbEnd
= Urb
->TrbStart
;
49 Execute a XHCI cmd TRB pointed by CmdTrb.
51 @param Xhc The XHCI Instance.
52 @param CmdTrb The cmd TRB to be executed.
53 @param Timeout Indicates the maximum time, in millisecond, which the
54 transfer is allowed to complete.
55 @param EvtTrb The event TRB corresponding to the cmd TRB.
57 @retval EFI_SUCCESS The transfer was completed successfully.
58 @retval EFI_INVALID_PARAMETER Some parameters are invalid.
59 @retval EFI_TIMEOUT The transfer failed due to timeout.
60 @retval EFI_DEVICE_ERROR The transfer failed due to host controller error.
66 IN USB_XHCI_INSTANCE
*Xhc
,
67 IN TRB_TEMPLATE
*CmdTrb
,
69 OUT TRB_TEMPLATE
**EvtTrb
76 // Validate the parameters
78 if ((Xhc
== NULL
) || (CmdTrb
== NULL
)) {
79 return EFI_INVALID_PARAMETER
;
82 Status
= EFI_DEVICE_ERROR
;
84 if (XhcIsHalt (Xhc
) || XhcIsSysError (Xhc
)) {
85 DEBUG ((DEBUG_ERROR
, "XhcCmdTransfer: HC is halted\n"));
90 // Create a new URB, then poll the execution status.
92 Urb
= XhcCreateCmdTrb (Xhc
, CmdTrb
);
95 DEBUG ((DEBUG_ERROR
, "XhcCmdTransfer: failed to create URB\n"));
96 Status
= EFI_OUT_OF_RESOURCES
;
100 Status
= XhcExecTransfer (Xhc
, TRUE
, Urb
, Timeout
);
101 *EvtTrb
= Urb
->EvtTrb
;
103 if (Urb
->Result
== EFI_USB_NOERROR
) {
104 Status
= EFI_SUCCESS
;
107 XhcFreeUrb (Xhc
, Urb
);
114 Create a new URB for a new transaction.
116 @param Xhc The XHCI Instance
117 @param BusAddr The logical device address assigned by UsbBus driver
118 @param EpAddr Endpoint addrress
119 @param DevSpeed The device speed
120 @param MaxPacket The max packet length of the endpoint
121 @param Type The transaction type
122 @param Request The standard USB request for control transfer
123 @param Data The user data to transfer
124 @param DataLen The length of data buffer
125 @param Callback The function to call when data is transferred
126 @param Context The context to the callback
128 @return Created URB or NULL
133 IN USB_XHCI_INSTANCE
*Xhc
,
139 IN EFI_USB_DEVICE_REQUEST
*Request
,
142 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback
,
150 Urb
= AllocateZeroPool (sizeof (URB
));
155 Urb
->Signature
= XHC_URB_SIG
;
156 InitializeListHead (&Urb
->UrbList
);
159 Ep
->BusAddr
= BusAddr
;
160 Ep
->EpAddr
= (UINT8
)(EpAddr
& 0x0F);
161 Ep
->Direction
= ((EpAddr
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
;
162 Ep
->DevSpeed
= DevSpeed
;
163 Ep
->MaxPacket
= MaxPacket
;
166 Urb
->Request
= Request
;
168 Urb
->DataLen
= DataLen
;
169 Urb
->Callback
= Callback
;
170 Urb
->Context
= Context
;
172 Status
= XhcCreateTransferTrb (Xhc
, Urb
);
173 ASSERT_EFI_ERROR (Status
);
174 if (EFI_ERROR (Status
)) {
175 DEBUG ((DEBUG_ERROR
, "XhcCreateUrb: XhcCreateTransferTrb Failed, Status = %r\n", Status
));
184 Free an allocated URB.
186 @param Xhc The XHCI device.
187 @param Urb The URB to free.
192 IN USB_XHCI_INSTANCE
*Xhc
,
196 if ((Xhc
== NULL
) || (Urb
== NULL
)) {
200 if (Urb
->DataMap
!= NULL
) {
201 Xhc
->PciIo
->Unmap (Xhc
->PciIo
, Urb
->DataMap
);
208 Create a transfer TRB.
210 @param Xhc The XHCI Instance
211 @param Urb The urb used to construct the transfer TRB.
213 @return Created TRB or NULL
217 XhcCreateTransferTrb (
218 IN USB_XHCI_INSTANCE
*Xhc
,
223 TRANSFER_RING
*EPRing
;
231 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
232 EFI_PHYSICAL_ADDRESS PhyAddr
;
236 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
238 return EFI_DEVICE_ERROR
;
241 Urb
->Finished
= FALSE
;
242 Urb
->StartDone
= FALSE
;
243 Urb
->EndDone
= FALSE
;
245 Urb
->Result
= EFI_USB_NOERROR
;
247 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
249 EPRing
= (TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1];
251 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
252 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
253 EPType
= (UINT8
)((DEVICE_CONTEXT
*)OutputContext
)->EP
[Dci
-1].EPType
;
255 EPType
= (UINT8
)((DEVICE_CONTEXT_64
*)OutputContext
)->EP
[Dci
-1].EPType
;
261 if ((Urb
->Data
!= NULL
) && (Urb
->DataMap
== NULL
)) {
262 if (((UINT8
)(Urb
->Ep
.Direction
)) == EfiUsbDataIn
) {
263 MapOp
= EfiPciIoOperationBusMasterWrite
;
265 MapOp
= EfiPciIoOperationBusMasterRead
;
269 Status
= Xhc
->PciIo
->Map (Xhc
->PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
271 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
272 DEBUG ((DEBUG_ERROR
, "XhcCreateTransferTrb: Fail to map Urb->Data.\n"));
273 return EFI_OUT_OF_RESOURCES
;
276 Urb
->DataPhy
= (VOID
*)((UINTN
)PhyAddr
);
283 XhcSyncTrsRing (Xhc
, EPRing
);
284 Urb
->TrbStart
= EPRing
->RingEnqueue
;
286 case ED_CONTROL_BIDIR
:
288 // For control transfer, create SETUP_STAGE_TRB first.
290 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
291 TrbStart
->TrbCtrSetup
.bmRequestType
= Urb
->Request
->RequestType
;
292 TrbStart
->TrbCtrSetup
.bRequest
= Urb
->Request
->Request
;
293 TrbStart
->TrbCtrSetup
.wValue
= Urb
->Request
->Value
;
294 TrbStart
->TrbCtrSetup
.wIndex
= Urb
->Request
->Index
;
295 TrbStart
->TrbCtrSetup
.wLength
= Urb
->Request
->Length
;
296 TrbStart
->TrbCtrSetup
.Length
= 8;
297 TrbStart
->TrbCtrSetup
.IntTarget
= 0;
298 TrbStart
->TrbCtrSetup
.IOC
= 1;
299 TrbStart
->TrbCtrSetup
.IDT
= 1;
300 TrbStart
->TrbCtrSetup
.Type
= TRB_TYPE_SETUP_STAGE
;
301 if (Urb
->DataLen
> 0) {
302 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
303 TrbStart
->TrbCtrSetup
.TRT
= 3;
304 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
305 TrbStart
->TrbCtrSetup
.TRT
= 2;
307 DEBUG ((DEBUG_ERROR
, "XhcCreateTransferTrb: Direction sholud be IN or OUT when Data exists!\n"));
311 TrbStart
->TrbCtrSetup
.TRT
= 0;
315 // Update the cycle bit
317 TrbStart
->TrbCtrSetup
.CycleBit
= EPRing
->RingPCS
& BIT0
;
321 // For control transfer, create DATA_STAGE_TRB.
323 if (Urb
->DataLen
> 0) {
324 XhcSyncTrsRing (Xhc
, EPRing
);
325 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
326 TrbStart
->TrbCtrData
.TRBPtrLo
= XHC_LOW_32BIT (Urb
->DataPhy
);
327 TrbStart
->TrbCtrData
.TRBPtrHi
= XHC_HIGH_32BIT (Urb
->DataPhy
);
328 TrbStart
->TrbCtrData
.Length
= (UINT32
)Urb
->DataLen
;
329 TrbStart
->TrbCtrData
.TDSize
= 0;
330 TrbStart
->TrbCtrData
.IntTarget
= 0;
331 TrbStart
->TrbCtrData
.ISP
= 1;
332 TrbStart
->TrbCtrData
.IOC
= 1;
333 TrbStart
->TrbCtrData
.IDT
= 0;
334 TrbStart
->TrbCtrData
.CH
= 0;
335 TrbStart
->TrbCtrData
.Type
= TRB_TYPE_DATA_STAGE
;
336 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
337 TrbStart
->TrbCtrData
.DIR = 1;
338 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
339 TrbStart
->TrbCtrData
.DIR = 0;
341 TrbStart
->TrbCtrData
.DIR = 0;
345 // Update the cycle bit
347 TrbStart
->TrbCtrData
.CycleBit
= EPRing
->RingPCS
& BIT0
;
352 // For control transfer, create STATUS_STAGE_TRB.
353 // Get the pointer to next TRB for status stage use
355 XhcSyncTrsRing (Xhc
, EPRing
);
356 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
357 TrbStart
->TrbCtrStatus
.IntTarget
= 0;
358 TrbStart
->TrbCtrStatus
.IOC
= 1;
359 TrbStart
->TrbCtrStatus
.CH
= 0;
360 TrbStart
->TrbCtrStatus
.Type
= TRB_TYPE_STATUS_STAGE
;
361 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
362 TrbStart
->TrbCtrStatus
.DIR = 0;
363 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
364 TrbStart
->TrbCtrStatus
.DIR = 1;
366 TrbStart
->TrbCtrStatus
.DIR = 0;
370 // Update the cycle bit
372 TrbStart
->TrbCtrStatus
.CycleBit
= EPRing
->RingPCS
& BIT0
;
374 // Update the enqueue pointer
376 XhcSyncTrsRing (Xhc
, EPRing
);
378 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
387 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
388 while (TotalLen
< Urb
->DataLen
) {
389 if ((TotalLen
+ 0x10000) >= Urb
->DataLen
) {
390 Len
= Urb
->DataLen
- TotalLen
;
395 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
396 TrbStart
->TrbNormal
.TRBPtrLo
= XHC_LOW_32BIT ((UINT8
*)Urb
->DataPhy
+ TotalLen
);
397 TrbStart
->TrbNormal
.TRBPtrHi
= XHC_HIGH_32BIT ((UINT8
*)Urb
->DataPhy
+ TotalLen
);
398 TrbStart
->TrbNormal
.Length
= (UINT32
)Len
;
399 TrbStart
->TrbNormal
.TDSize
= 0;
400 TrbStart
->TrbNormal
.IntTarget
= 0;
401 TrbStart
->TrbNormal
.ISP
= 1;
402 TrbStart
->TrbNormal
.IOC
= 1;
403 TrbStart
->TrbNormal
.Type
= TRB_TYPE_NORMAL
;
405 // Update the cycle bit
407 TrbStart
->TrbNormal
.CycleBit
= EPRing
->RingPCS
& BIT0
;
409 XhcSyncTrsRing (Xhc
, EPRing
);
414 Urb
->TrbNum
= TrbNum
;
415 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
418 case ED_INTERRUPT_OUT
:
419 case ED_INTERRUPT_IN
:
423 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
424 while (TotalLen
< Urb
->DataLen
) {
425 if ((TotalLen
+ 0x10000) >= Urb
->DataLen
) {
426 Len
= Urb
->DataLen
- TotalLen
;
431 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
432 TrbStart
->TrbNormal
.TRBPtrLo
= XHC_LOW_32BIT ((UINT8
*)Urb
->DataPhy
+ TotalLen
);
433 TrbStart
->TrbNormal
.TRBPtrHi
= XHC_HIGH_32BIT ((UINT8
*)Urb
->DataPhy
+ TotalLen
);
434 TrbStart
->TrbNormal
.Length
= (UINT32
)Len
;
435 TrbStart
->TrbNormal
.TDSize
= 0;
436 TrbStart
->TrbNormal
.IntTarget
= 0;
437 TrbStart
->TrbNormal
.ISP
= 1;
438 TrbStart
->TrbNormal
.IOC
= 1;
439 TrbStart
->TrbNormal
.Type
= TRB_TYPE_NORMAL
;
441 // Update the cycle bit
443 TrbStart
->TrbNormal
.CycleBit
= EPRing
->RingPCS
& BIT0
;
445 XhcSyncTrsRing (Xhc
, EPRing
);
450 Urb
->TrbNum
= TrbNum
;
451 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
455 DEBUG ((DEBUG_INFO
, "Not supported EPType 0x%x!\n", EPType
));
464 Initialize the XHCI host controller for schedule.
466 @param Xhc The XHCI Instance to be initialized.
471 IN USB_XHCI_INSTANCE
*Xhc
475 EFI_PHYSICAL_ADDRESS DcbaaPhy
;
477 EFI_PHYSICAL_ADDRESS CmdRingPhy
;
479 UINT32 MaxScratchpadBufs
;
481 EFI_PHYSICAL_ADDRESS ScratchPhy
;
482 UINT64
*ScratchEntry
;
483 EFI_PHYSICAL_ADDRESS ScratchEntryPhy
;
485 UINTN
*ScratchEntryMap
;
489 // Initialize memory management.
491 Xhc
->MemPool
= UsbHcInitMemPool (Xhc
->PciIo
);
492 ASSERT (Xhc
->MemPool
!= NULL
);
495 // Program the Max Device Slots Enabled (MaxSlotsEn) field in the CONFIG register (5.4.7)
496 // to enable the device slots that system software is going to use.
498 Xhc
->MaxSlotsEn
= Xhc
->HcSParams1
.Data
.MaxSlots
;
499 ASSERT (Xhc
->MaxSlotsEn
>= 1 && Xhc
->MaxSlotsEn
<= 255);
500 XhcWriteOpReg (Xhc
, XHC_CONFIG_OFFSET
, Xhc
->MaxSlotsEn
);
503 // The Device Context Base Address Array entry associated with each allocated Device Slot
504 // shall contain a 64-bit pointer to the base of the associated Device Context.
505 // The Device Context Base Address Array shall contain MaxSlotsEn + 1 entries.
506 // Software shall set Device Context Base Address Array entries for unallocated Device Slots to '0'.
508 Entries
= (Xhc
->MaxSlotsEn
+ 1) * sizeof (UINT64
);
509 Dcbaa
= UsbHcAllocateMem (Xhc
->MemPool
, Entries
);
510 ASSERT (Dcbaa
!= NULL
);
511 ZeroMem (Dcbaa
, Entries
);
514 // A Scratchpad Buffer is a PAGESIZE block of system memory located on a PAGESIZE boundary.
515 // System software shall allocate the Scratchpad Buffer(s) before placing the xHC in to Run
516 // mode (Run/Stop(R/S) ='1').
518 MaxScratchpadBufs
= ((Xhc
->HcSParams2
.Data
.ScratchBufHi
) << 5) | (Xhc
->HcSParams2
.Data
.ScratchBufLo
);
519 Xhc
->MaxScratchpadBufs
= MaxScratchpadBufs
;
520 ASSERT (MaxScratchpadBufs
<= 1023);
521 if (MaxScratchpadBufs
!= 0) {
523 // Allocate the buffer to record the Mapping for each scratch buffer in order to Unmap them
525 ScratchEntryMap
= AllocateZeroPool (sizeof (UINTN
) * MaxScratchpadBufs
);
526 ASSERT (ScratchEntryMap
!= NULL
);
527 Xhc
->ScratchEntryMap
= ScratchEntryMap
;
530 // Allocate the buffer to record the host address for each entry
532 ScratchEntry
= AllocateZeroPool (sizeof (UINT64
) * MaxScratchpadBufs
);
533 ASSERT (ScratchEntry
!= NULL
);
534 Xhc
->ScratchEntry
= ScratchEntry
;
537 Status
= UsbHcAllocateAlignedPages (
539 EFI_SIZE_TO_PAGES (MaxScratchpadBufs
* sizeof (UINT64
)),
541 (VOID
**)&ScratchBuf
,
545 ASSERT_EFI_ERROR (Status
);
547 ZeroMem (ScratchBuf
, MaxScratchpadBufs
* sizeof (UINT64
));
548 Xhc
->ScratchBuf
= ScratchBuf
;
551 // Allocate each scratch buffer
553 for (Index
= 0; Index
< MaxScratchpadBufs
; Index
++) {
555 Status
= UsbHcAllocateAlignedPages (
557 EFI_SIZE_TO_PAGES (Xhc
->PageSize
),
559 (VOID
**)&ScratchEntry
[Index
],
561 (VOID
**)&ScratchEntryMap
[Index
]
563 ASSERT_EFI_ERROR (Status
);
564 ZeroMem ((VOID
*)(UINTN
)ScratchEntry
[Index
], Xhc
->PageSize
);
566 // Fill with the PCI device address
568 *ScratchBuf
++ = ScratchEntryPhy
;
572 // The Scratchpad Buffer Array contains pointers to the Scratchpad Buffers. Entry 0 of the
573 // Device Context Base Address Array points to the Scratchpad Buffer Array.
575 *(UINT64
*)Dcbaa
= (UINT64
)(UINTN
)ScratchPhy
;
579 // Program the Device Context Base Address Array Pointer (DCBAAP) register (5.4.6) with
580 // a 64-bit address pointing to where the Device Context Base Address Array is located.
582 Xhc
->DCBAA
= (UINT64
*)(UINTN
)Dcbaa
;
584 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
585 // So divide it to two 32-bytes width register access.
587 DcbaaPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Dcbaa
, Entries
);
588 XhcWriteOpReg (Xhc
, XHC_DCBAAP_OFFSET
, XHC_LOW_32BIT (DcbaaPhy
));
589 XhcWriteOpReg (Xhc
, XHC_DCBAAP_OFFSET
+ 4, XHC_HIGH_32BIT (DcbaaPhy
));
591 DEBUG ((DEBUG_INFO
, "XhcInitSched:DCBAA=0x%x\n", (UINT64
)(UINTN
)Xhc
->DCBAA
));
594 // Define the Command Ring Dequeue Pointer by programming the Command Ring Control Register
595 // (5.4.5) with a 64-bit address pointing to the starting address of the first TRB of the Command Ring.
596 // Note: The Command Ring is 64 byte aligned, so the low order 6 bits of the Command Ring Pointer shall
599 CreateTransferRing (Xhc
, CMD_RING_TRB_NUMBER
, &Xhc
->CmdRing
);
601 // The xHC uses the Enqueue Pointer to determine when a Transfer Ring is empty. As it fetches TRBs from a
602 // Transfer Ring it checks for a Cycle bit transition. If a transition detected, the ring is empty.
603 // So we set RCS as inverted PCS init value to let Command Ring empty
605 CmdRing
= (UINT64
)(UINTN
)Xhc
->CmdRing
.RingSeg0
;
606 CmdRingPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, (VOID
*)(UINTN
)CmdRing
, sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
);
607 ASSERT ((CmdRingPhy
& 0x3F) == 0);
608 CmdRingPhy
|= XHC_CRCR_RCS
;
610 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
611 // So divide it to two 32-bytes width register access.
613 XhcWriteOpReg (Xhc
, XHC_CRCR_OFFSET
, XHC_LOW_32BIT (CmdRingPhy
));
614 XhcWriteOpReg (Xhc
, XHC_CRCR_OFFSET
+ 4, XHC_HIGH_32BIT (CmdRingPhy
));
617 // Disable the 'interrupter enable' bit in USB_CMD
618 // and clear IE & IP bit in all Interrupter X Management Registers.
620 XhcClearOpRegBit (Xhc
, XHC_USBCMD_OFFSET
, XHC_USBCMD_INTE
);
621 for (Index
= 0; Index
< (UINT16
)(Xhc
->HcSParams1
.Data
.MaxIntrs
); Index
++) {
622 XhcClearRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
+ (Index
* 32), XHC_IMAN_IE
);
623 XhcSetRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
+ (Index
* 32), XHC_IMAN_IP
);
627 // Allocate EventRing for Cmd, Ctrl, Bulk, Interrupt, AsynInterrupt transfer
629 CreateEventRing (Xhc
, &Xhc
->EventRing
);
632 "XhcInitSched: Created CMD ring [%p~%p) EVENT ring [%p~%p)\n",
633 Xhc
->CmdRing
.RingSeg0
,
634 (UINTN
)Xhc
->CmdRing
.RingSeg0
+ sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
,
635 Xhc
->EventRing
.EventRingSeg0
,
636 (UINTN
)Xhc
->EventRing
.EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
641 System software shall use a Reset Endpoint Command (section 4.11.4.7) to remove the Halted
642 condition in the xHC. After the successful completion of the Reset Endpoint Command, the Endpoint
643 Context is transitioned from the Halted to the Stopped state and the Transfer Ring of the endpoint is
644 reenabled. The next write to the Doorbell of the Endpoint will transition the Endpoint Context from the
645 Stopped to the Running state.
647 @param Xhc The XHCI Instance.
648 @param Urb The urb which makes the endpoint halted.
650 @retval EFI_SUCCESS The recovery is successful.
651 @retval Others Failed to recovery halted endpoint.
656 XhcRecoverHaltedEndpoint (
657 IN USB_XHCI_INSTANCE
*Xhc
,
665 Status
= EFI_SUCCESS
;
666 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
668 return EFI_DEVICE_ERROR
;
671 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
674 DEBUG ((DEBUG_INFO
, "Recovery Halted Slot = %x,Dci = %x\n", SlotId
, Dci
));
677 // 1) Send Reset endpoint command to transit from halt to stop state
679 Status
= XhcResetEndpoint (Xhc
, SlotId
, Dci
);
680 if (EFI_ERROR (Status
)) {
681 DEBUG ((DEBUG_ERROR
, "XhcRecoverHaltedEndpoint: Reset Endpoint Failed, Status = %r\n", Status
));
686 // 2)Set dequeue pointer
688 Status
= XhcSetTrDequeuePointer (Xhc
, SlotId
, Dci
, Urb
);
689 if (EFI_ERROR (Status
)) {
690 DEBUG ((DEBUG_ERROR
, "XhcRecoverHaltedEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status
));
695 // 3)Ring the doorbell to transit from stop to active
697 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
704 System software shall use a Stop Endpoint Command (section 4.6.9) and the Set TR Dequeue Pointer
705 Command (section 4.6.10) to remove the timed-out TDs from the xHC transfer ring. The next write to
706 the Doorbell of the Endpoint will transition the Endpoint Context from the Stopped to the Running
709 @param Xhc The XHCI Instance.
710 @param Urb The urb which doesn't get completed in a specified timeout range.
712 @retval EFI_SUCCESS The dequeuing of the TDs is successful.
713 @retval EFI_ALREADY_STARTED The Urb is finished so no deque is needed.
714 @retval Others Failed to stop the endpoint and dequeue the TDs.
719 XhcDequeueTrbFromEndpoint (
720 IN USB_XHCI_INSTANCE
*Xhc
,
728 Status
= EFI_SUCCESS
;
729 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
731 return EFI_DEVICE_ERROR
;
734 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
737 DEBUG ((DEBUG_INFO
, "Stop Slot = %x,Dci = %x\n", SlotId
, Dci
));
740 // 1) Send Stop endpoint command to stop xHC from executing of the TDs on the endpoint
742 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, Urb
);
743 if (EFI_ERROR (Status
)) {
744 DEBUG ((DEBUG_ERROR
, "XhcDequeueTrbFromEndpoint: Stop Endpoint Failed, Status = %r\n", Status
));
749 // 2)Set dequeue pointer
751 if (Urb
->Finished
&& (Urb
->Result
== EFI_USB_NOERROR
)) {
753 // Return Already Started to indicate the pending URB is finished.
754 // This fixes BULK data loss when transfer is detected as timeout
755 // but finished just before stopping endpoint.
757 Status
= EFI_ALREADY_STARTED
;
758 DEBUG ((DEBUG_INFO
, "XhcDequeueTrbFromEndpoint: Pending URB is finished: Length Actual/Expect = %d/%d!\n", Urb
->Completed
, Urb
->DataLen
));
760 Status
= XhcSetTrDequeuePointer (Xhc
, SlotId
, Dci
, Urb
);
761 if (EFI_ERROR (Status
)) {
762 DEBUG ((DEBUG_ERROR
, "XhcDequeueTrbFromEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status
));
768 // 3)Ring the doorbell to transit from stop to active
770 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
777 Create XHCI event ring.
779 @param Xhc The XHCI Instance.
780 @param EventRing The created event ring.
785 IN USB_XHCI_INSTANCE
*Xhc
,
786 OUT EVENT_RING
*EventRing
790 EVENT_RING_SEG_TABLE_ENTRY
*ERSTBase
;
792 EFI_PHYSICAL_ADDRESS ERSTPhy
;
793 EFI_PHYSICAL_ADDRESS DequeuePhy
;
795 ASSERT (EventRing
!= NULL
);
797 Size
= sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
;
798 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, Size
);
799 ASSERT (Buf
!= NULL
);
800 ASSERT (((UINTN
)Buf
& 0x3F) == 0);
803 EventRing
->EventRingSeg0
= Buf
;
804 EventRing
->TrbNumber
= EVENT_RING_TRB_NUMBER
;
805 EventRing
->EventRingDequeue
= (TRB_TEMPLATE
*)EventRing
->EventRingSeg0
;
806 EventRing
->EventRingEnqueue
= (TRB_TEMPLATE
*)EventRing
->EventRingSeg0
;
808 DequeuePhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Buf
, Size
);
811 // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1'
812 // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring.
814 EventRing
->EventRingCCS
= 1;
816 Size
= sizeof (EVENT_RING_SEG_TABLE_ENTRY
) * ERST_NUMBER
;
817 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, Size
);
818 ASSERT (Buf
!= NULL
);
819 ASSERT (((UINTN
)Buf
& 0x3F) == 0);
822 ERSTBase
= (EVENT_RING_SEG_TABLE_ENTRY
*)Buf
;
823 EventRing
->ERSTBase
= ERSTBase
;
824 ERSTBase
->PtrLo
= XHC_LOW_32BIT (DequeuePhy
);
825 ERSTBase
->PtrHi
= XHC_HIGH_32BIT (DequeuePhy
);
826 ERSTBase
->RingTrbSize
= EVENT_RING_TRB_NUMBER
;
828 ERSTPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, ERSTBase
, Size
);
831 // Program the Interrupter Event Ring Segment Table Size (ERSTSZ) register (5.5.2.3.1)
839 // Program the Interrupter Event Ring Dequeue Pointer (ERDP) register (5.5.2.3.3)
841 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
842 // So divide it to two 32-bytes width register access.
847 XHC_LOW_32BIT ((UINT64
)(UINTN
)DequeuePhy
)
852 XHC_HIGH_32BIT ((UINT64
)(UINTN
)DequeuePhy
)
855 // Program the Interrupter Event Ring Segment Table Base Address (ERSTBA) register(5.5.2.3.2)
857 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
858 // So divide it to two 32-bytes width register access.
863 XHC_LOW_32BIT ((UINT64
)(UINTN
)ERSTPhy
)
867 XHC_ERSTBA_OFFSET
+ 4,
868 XHC_HIGH_32BIT ((UINT64
)(UINTN
)ERSTPhy
)
871 // Need set IMAN IE bit to enble the ring interrupt
873 XhcSetRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
, XHC_IMAN_IE
);
877 Create XHCI transfer ring.
879 @param Xhc The XHCI Instance.
880 @param TrbNum The number of TRB in the ring.
881 @param TransferRing The created transfer ring.
886 IN USB_XHCI_INSTANCE
*Xhc
,
888 OUT TRANSFER_RING
*TransferRing
893 EFI_PHYSICAL_ADDRESS PhyAddr
;
895 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (TRB_TEMPLATE
) * TrbNum
);
896 ASSERT (Buf
!= NULL
);
897 ASSERT (((UINTN
)Buf
& 0x3F) == 0);
898 ZeroMem (Buf
, sizeof (TRB_TEMPLATE
) * TrbNum
);
900 TransferRing
->RingSeg0
= Buf
;
901 TransferRing
->TrbNumber
= TrbNum
;
902 TransferRing
->RingEnqueue
= (TRB_TEMPLATE
*)TransferRing
->RingSeg0
;
903 TransferRing
->RingDequeue
= (TRB_TEMPLATE
*)TransferRing
->RingSeg0
;
904 TransferRing
->RingPCS
= 1;
906 // 4.9.2 Transfer Ring Management
907 // To form a ring (or circular queue) a Link TRB may be inserted at the end of a ring to
908 // point to the first TRB in the ring.
910 EndTrb
= (LINK_TRB
*)((UINTN
)Buf
+ sizeof (TRB_TEMPLATE
) * (TrbNum
- 1));
911 EndTrb
->Type
= TRB_TYPE_LINK
;
912 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Buf
, sizeof (TRB_TEMPLATE
) * TrbNum
);
913 EndTrb
->PtrLo
= XHC_LOW_32BIT (PhyAddr
);
914 EndTrb
->PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
916 // Toggle Cycle (TC). When set to '1', the xHC shall toggle its interpretation of the Cycle bit.
920 // Set Cycle bit as other TRB PCS init value
922 EndTrb
->CycleBit
= 0;
926 Free XHCI event ring.
928 @param Xhc The XHCI Instance.
929 @param EventRing The event ring to be freed.
935 IN USB_XHCI_INSTANCE
*Xhc
,
936 IN EVENT_RING
*EventRing
939 if (EventRing
->EventRingSeg0
== NULL
) {
944 // Free EventRing Segment 0
946 UsbHcFreeMem (Xhc
->MemPool
, EventRing
->EventRingSeg0
, sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
);
951 UsbHcFreeMem (Xhc
->MemPool
, EventRing
->ERSTBase
, sizeof (EVENT_RING_SEG_TABLE_ENTRY
) * ERST_NUMBER
);
956 Free the resouce allocated at initializing schedule.
958 @param Xhc The XHCI Instance.
963 IN USB_XHCI_INSTANCE
*Xhc
967 UINT64
*ScratchEntry
;
969 if (Xhc
->ScratchBuf
!= NULL
) {
970 ScratchEntry
= Xhc
->ScratchEntry
;
971 for (Index
= 0; Index
< Xhc
->MaxScratchpadBufs
; Index
++) {
973 // Free Scratchpad Buffers
975 UsbHcFreeAlignedPages (Xhc
->PciIo
, (VOID
*)(UINTN
)ScratchEntry
[Index
], EFI_SIZE_TO_PAGES (Xhc
->PageSize
), (VOID
*)Xhc
->ScratchEntryMap
[Index
]);
979 // Free Scratchpad Buffer Array
981 UsbHcFreeAlignedPages (Xhc
->PciIo
, Xhc
->ScratchBuf
, EFI_SIZE_TO_PAGES (Xhc
->MaxScratchpadBufs
* sizeof (UINT64
)), Xhc
->ScratchMap
);
982 FreePool (Xhc
->ScratchEntryMap
);
983 FreePool (Xhc
->ScratchEntry
);
986 if (Xhc
->CmdRing
.RingSeg0
!= NULL
) {
987 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->CmdRing
.RingSeg0
, sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
);
988 Xhc
->CmdRing
.RingSeg0
= NULL
;
991 XhcFreeEventRing (Xhc
, &Xhc
->EventRing
);
993 if (Xhc
->DCBAA
!= NULL
) {
994 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->DCBAA
, (Xhc
->MaxSlotsEn
+ 1) * sizeof (UINT64
));
999 // Free memory pool at last
1001 if (Xhc
->MemPool
!= NULL
) {
1002 UsbHcFreeMemPool (Xhc
->MemPool
);
1003 Xhc
->MemPool
= NULL
;
1008 Check if the Trb is a transaction of the URB.
1010 @param Xhc The XHCI Instance.
1011 @param Trb The TRB to be checked
1012 @param Urb The URB to be checked.
1014 @retval TRUE It is a transaction of the URB.
1015 @retval FALSE It is not any transaction of the URB.
1020 IN USB_XHCI_INSTANCE
*Xhc
,
1021 IN TRB_TEMPLATE
*Trb
,
1026 TRB_TEMPLATE
*CheckedTrb
;
1028 EFI_PHYSICAL_ADDRESS PhyAddr
;
1030 CheckedTrb
= Urb
->TrbStart
;
1031 for (Index
= 0; Index
< Urb
->TrbNum
; Index
++) {
1032 if (Trb
== CheckedTrb
) {
1038 // If the checked TRB is the link TRB at the end of the transfer ring,
1039 // recircle it to the head of the ring.
1041 if (CheckedTrb
->Type
== TRB_TYPE_LINK
) {
1042 LinkTrb
= (LINK_TRB
*)CheckedTrb
;
1043 PhyAddr
= (EFI_PHYSICAL_ADDRESS
)(LinkTrb
->PtrLo
| LShiftU64 ((UINT64
)LinkTrb
->PtrHi
, 32));
1044 CheckedTrb
= (TRB_TEMPLATE
*)(UINTN
)UsbHcGetHostAddrForPciAddr (Xhc
->MemPool
, (VOID
*)(UINTN
)PhyAddr
, sizeof (TRB_TEMPLATE
));
1045 ASSERT (CheckedTrb
== Urb
->Ring
->RingSeg0
);
1053 Check if the Trb is a transaction of the URBs in XHCI's asynchronous transfer list.
1055 @param Xhc The XHCI Instance.
1056 @param Trb The TRB to be checked.
1057 @param Urb The pointer to the matched Urb.
1059 @retval TRUE The Trb is matched with a transaction of the URBs in the async list.
1060 @retval FALSE The Trb is not matched with any URBs in the async list.
1065 IN USB_XHCI_INSTANCE
*Xhc
,
1066 IN TRB_TEMPLATE
*Trb
,
1074 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1075 CheckedUrb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1076 if (IsTransferRingTrb (Xhc
, Trb
, CheckedUrb
)) {
1086 Check the URB's execution result and update the URB's
1089 @param Xhc The XHCI Instance.
1090 @param Urb The URB to check result.
1092 @return Whether the result of URB transfer is finialized.
1097 IN USB_XHCI_INSTANCE
*Xhc
,
1101 EVT_TRB_TRANSFER
*EvtTrb
;
1102 TRB_TEMPLATE
*TRBPtr
;
1111 EFI_PHYSICAL_ADDRESS PhyAddr
;
1113 ASSERT ((Xhc
!= NULL
) && (Urb
!= NULL
));
1115 Status
= EFI_SUCCESS
;
1118 if (Urb
->Finished
) {
1124 if (XhcIsHalt (Xhc
) || XhcIsSysError (Xhc
)) {
1125 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
1130 // Traverse the event ring to find out all new events from the previous check.
1132 XhcSyncEventRing (Xhc
, &Xhc
->EventRing
);
1133 for (Index
= 0; Index
< Xhc
->EventRing
.TrbNumber
; Index
++) {
1134 Status
= XhcCheckNewEvent (Xhc
, &Xhc
->EventRing
, ((TRB_TEMPLATE
**)&EvtTrb
));
1135 if (Status
== EFI_NOT_READY
) {
1137 // All new events are handled, return directly.
1143 // Only handle COMMAND_COMPLETETION_EVENT and TRANSFER_EVENT.
1145 if ((EvtTrb
->Type
!= TRB_TYPE_COMMAND_COMPLT_EVENT
) && (EvtTrb
->Type
!= TRB_TYPE_TRANS_EVENT
)) {
1150 // Need convert pci device address to host address
1152 PhyAddr
= (EFI_PHYSICAL_ADDRESS
)(EvtTrb
->TRBPtrLo
| LShiftU64 ((UINT64
)EvtTrb
->TRBPtrHi
, 32));
1153 TRBPtr
= (TRB_TEMPLATE
*)(UINTN
)UsbHcGetHostAddrForPciAddr (Xhc
->MemPool
, (VOID
*)(UINTN
)PhyAddr
, sizeof (TRB_TEMPLATE
));
1156 // Update the status of URB including the pending URB, the URB that is currently checked,
1157 // and URBs in the XHCI's async interrupt transfer list.
1158 // This way is used to avoid that those completed async transfer events don't get
1159 // handled in time and are flushed by newer coming events.
1161 if ((Xhc
->PendingUrb
!= NULL
) && IsTransferRingTrb (Xhc
, TRBPtr
, Xhc
->PendingUrb
)) {
1162 CheckedUrb
= Xhc
->PendingUrb
;
1163 } else if (IsTransferRingTrb (Xhc
, TRBPtr
, Urb
)) {
1165 } else if (IsAsyncIntTrb (Xhc
, TRBPtr
, &AsyncUrb
)) {
1166 CheckedUrb
= AsyncUrb
;
1171 switch (EvtTrb
->Completecode
) {
1172 case TRB_COMPLETION_STALL_ERROR
:
1173 CheckedUrb
->Result
|= EFI_USB_ERR_STALL
;
1174 CheckedUrb
->Finished
= TRUE
;
1175 DEBUG ((DEBUG_ERROR
, "XhcCheckUrbResult: STALL_ERROR! Completecode = %x\n", EvtTrb
->Completecode
));
1178 case TRB_COMPLETION_BABBLE_ERROR
:
1179 CheckedUrb
->Result
|= EFI_USB_ERR_BABBLE
;
1180 CheckedUrb
->Finished
= TRUE
;
1181 DEBUG ((DEBUG_ERROR
, "XhcCheckUrbResult: BABBLE_ERROR! Completecode = %x\n", EvtTrb
->Completecode
));
1184 case TRB_COMPLETION_DATA_BUFFER_ERROR
:
1185 CheckedUrb
->Result
|= EFI_USB_ERR_BUFFER
;
1186 CheckedUrb
->Finished
= TRUE
;
1187 DEBUG ((DEBUG_ERROR
, "XhcCheckUrbResult: ERR_BUFFER! Completecode = %x\n", EvtTrb
->Completecode
));
1190 case TRB_COMPLETION_USB_TRANSACTION_ERROR
:
1191 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1192 CheckedUrb
->Finished
= TRUE
;
1193 DEBUG ((DEBUG_ERROR
, "XhcCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n", EvtTrb
->Completecode
));
1196 case TRB_COMPLETION_STOPPED
:
1197 case TRB_COMPLETION_STOPPED_LENGTH_INVALID
:
1198 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1199 CheckedUrb
->Finished
= TRUE
;
1201 // The pending URB is timeout and force stopped when stopping endpoint.
1202 // Continue the loop to receive the Command Complete Event for stopping endpoint.
1206 case TRB_COMPLETION_SHORT_PACKET
:
1207 case TRB_COMPLETION_SUCCESS
:
1208 if (EvtTrb
->Completecode
== TRB_COMPLETION_SHORT_PACKET
) {
1209 DEBUG ((DEBUG_VERBOSE
, "XhcCheckUrbResult: short packet happens!\n"));
1212 TRBType
= (UINT8
)(TRBPtr
->Type
);
1213 if ((TRBType
== TRB_TYPE_DATA_STAGE
) ||
1214 (TRBType
== TRB_TYPE_NORMAL
) ||
1215 (TRBType
== TRB_TYPE_ISOCH
))
1217 CheckedUrb
->Completed
+= (((TRANSFER_TRB_NORMAL
*)TRBPtr
)->Length
- EvtTrb
->Length
);
1223 DEBUG ((DEBUG_ERROR
, "Transfer Default Error Occur! Completecode = 0x%x!\n", EvtTrb
->Completecode
));
1224 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1225 CheckedUrb
->Finished
= TRUE
;
1230 // Only check first and end Trb event address
1232 if (TRBPtr
== CheckedUrb
->TrbStart
) {
1233 CheckedUrb
->StartDone
= TRUE
;
1236 if (TRBPtr
== CheckedUrb
->TrbEnd
) {
1237 CheckedUrb
->EndDone
= TRUE
;
1240 if (CheckedUrb
->StartDone
&& CheckedUrb
->EndDone
) {
1241 CheckedUrb
->Finished
= TRUE
;
1242 CheckedUrb
->EvtTrb
= (TRB_TEMPLATE
*)EvtTrb
;
1249 // Advance event ring to last available entry
1251 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
1252 // So divide it to two 32-bytes width register access.
1254 Low
= XhcReadRuntimeReg (Xhc
, XHC_ERDP_OFFSET
);
1255 High
= XhcReadRuntimeReg (Xhc
, XHC_ERDP_OFFSET
+ 4);
1256 XhcDequeue
= (UINT64
)(LShiftU64 ((UINT64
)High
, 32) | Low
);
1258 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->EventRing
.EventRingDequeue
, sizeof (TRB_TEMPLATE
));
1260 if ((XhcDequeue
& (~0x0F)) != (PhyAddr
& (~0x0F))) {
1262 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
1263 // So divide it to two 32-bytes width register access.
1265 XhcWriteRuntimeReg (Xhc
, XHC_ERDP_OFFSET
, XHC_LOW_32BIT (PhyAddr
) | BIT3
);
1266 XhcWriteRuntimeReg (Xhc
, XHC_ERDP_OFFSET
+ 4, XHC_HIGH_32BIT (PhyAddr
));
1269 return Urb
->Finished
;
1273 Execute the transfer by polling the URB. This is a synchronous operation.
1275 @param Xhc The XHCI Instance.
1276 @param CmdTransfer The executed URB is for cmd transfer or not.
1277 @param Urb The URB to execute.
1278 @param Timeout The time to wait before abort, in millisecond.
1280 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.
1281 @return EFI_TIMEOUT The transfer failed due to time out.
1282 @return EFI_SUCCESS The transfer finished OK.
1283 @retval EFI_OUT_OF_RESOURCES Memory for the timer event could not be allocated.
1288 IN USB_XHCI_INSTANCE
*Xhc
,
1289 IN BOOLEAN CmdTransfer
,
1298 EFI_EVENT TimeoutEvent
;
1299 BOOLEAN IndefiniteTimeout
;
1301 Status
= EFI_SUCCESS
;
1303 TimeoutEvent
= NULL
;
1304 IndefiniteTimeout
= FALSE
;
1310 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
1312 return EFI_DEVICE_ERROR
;
1315 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
1320 IndefiniteTimeout
= TRUE
;
1324 Status
= gBS
->CreateEvent (
1332 if (EFI_ERROR (Status
)) {
1336 Status
= gBS
->SetTimer (
1339 EFI_TIMER_PERIOD_MILLISECONDS (Timeout
)
1342 if (EFI_ERROR (Status
)) {
1347 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
1350 Finished
= XhcCheckUrbResult (Xhc
, Urb
);
1355 gBS
->Stall (XHC_1_MICROSECOND
);
1356 } while (IndefiniteTimeout
|| EFI_ERROR (gBS
->CheckEvent (TimeoutEvent
)));
1359 if (EFI_ERROR (Status
)) {
1360 Urb
->Result
= EFI_USB_ERR_NOTEXECUTE
;
1361 } else if (!Finished
) {
1362 Urb
->Result
= EFI_USB_ERR_TIMEOUT
;
1363 Status
= EFI_TIMEOUT
;
1364 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
1365 Status
= EFI_DEVICE_ERROR
;
1368 if (TimeoutEvent
!= NULL
) {
1369 gBS
->CloseEvent (TimeoutEvent
);
1376 Delete a single asynchronous interrupt transfer for
1377 the device and endpoint.
1379 @param Xhc The XHCI Instance.
1380 @param BusAddr The logical device address assigned by UsbBus driver.
1381 @param EpNum The endpoint of the target.
1383 @retval EFI_SUCCESS An asynchronous transfer is removed.
1384 @retval EFI_NOT_FOUND No transfer for the device is found.
1388 XhciDelAsyncIntTransfer (
1389 IN USB_XHCI_INSTANCE
*Xhc
,
1397 EFI_USB_DATA_DIRECTION Direction
;
1400 Direction
= ((EpNum
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
;
1405 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1406 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1407 if ((Urb
->Ep
.BusAddr
== BusAddr
) &&
1408 (Urb
->Ep
.EpAddr
== EpNum
) &&
1409 (Urb
->Ep
.Direction
== Direction
))
1412 // Device doesn't finish the IntTransfer until real data comes
1413 // So the TRB should be removed as well.
1415 Status
= XhcDequeueTrbFromEndpoint (Xhc
, Urb
);
1416 if (EFI_ERROR (Status
)) {
1417 DEBUG ((DEBUG_ERROR
, "XhciDelAsyncIntTransfer: XhcDequeueTrbFromEndpoint failed\n"));
1420 RemoveEntryList (&Urb
->UrbList
);
1421 FreePool (Urb
->Data
);
1422 XhcFreeUrb (Xhc
, Urb
);
1427 return EFI_NOT_FOUND
;
1431 Remove all the asynchronous interrutp transfers.
1433 @param Xhc The XHCI Instance.
1437 XhciDelAllAsyncIntTransfers (
1438 IN USB_XHCI_INSTANCE
*Xhc
1446 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1447 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1450 // Device doesn't finish the IntTransfer until real data comes
1451 // So the TRB should be removed as well.
1453 Status
= XhcDequeueTrbFromEndpoint (Xhc
, Urb
);
1454 if (EFI_ERROR (Status
)) {
1455 DEBUG ((DEBUG_ERROR
, "XhciDelAllAsyncIntTransfers: XhcDequeueTrbFromEndpoint failed\n"));
1458 RemoveEntryList (&Urb
->UrbList
);
1459 FreePool (Urb
->Data
);
1460 XhcFreeUrb (Xhc
, Urb
);
1465 Insert a single asynchronous interrupt transfer for
1466 the device and endpoint.
1468 @param Xhc The XHCI Instance
1469 @param BusAddr The logical device address assigned by UsbBus driver
1470 @param EpAddr Endpoint addrress
1471 @param DevSpeed The device speed
1472 @param MaxPacket The max packet length of the endpoint
1473 @param DataLen The length of data buffer
1474 @param Callback The function to call when data is transferred
1475 @param Context The context to the callback
1477 @return Created URB or NULL
1481 XhciInsertAsyncIntTransfer (
1482 IN USB_XHCI_INSTANCE
*Xhc
,
1488 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback
,
1495 Data
= AllocateZeroPool (DataLen
);
1497 DEBUG ((DEBUG_ERROR
, "%a: failed to allocate buffer\n", __FUNCTION__
));
1501 Urb
= XhcCreateUrb (
1507 XHC_INT_TRANSFER_ASYNC
,
1515 DEBUG ((DEBUG_ERROR
, "%a: failed to create URB\n", __FUNCTION__
));
1521 // New asynchronous transfer must inserted to the head.
1522 // Check the comments in XhcMoniteAsyncRequests
1524 InsertHeadList (&Xhc
->AsyncIntTransfers
, &Urb
->UrbList
);
1530 Update the queue head for next round of asynchronous transfer
1532 @param Xhc The XHCI Instance.
1533 @param Urb The URB to update
1537 XhcUpdateAsyncRequest (
1538 IN USB_XHCI_INSTANCE
*Xhc
,
1544 if (Urb
->Result
== EFI_USB_NOERROR
) {
1545 Status
= XhcCreateTransferTrb (Xhc
, Urb
);
1546 if (EFI_ERROR (Status
)) {
1550 Status
= RingIntTransferDoorBell (Xhc
, Urb
);
1551 if (EFI_ERROR (Status
)) {
1558 Flush data from PCI controller specific address to mapped system
1561 @param Xhc The XHCI device.
1562 @param Urb The URB to unmap.
1564 @retval EFI_SUCCESS Success to flush data to mapped system memory.
1565 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
1569 XhcFlushAsyncIntMap (
1570 IN USB_XHCI_INSTANCE
*Xhc
,
1575 EFI_PHYSICAL_ADDRESS PhyAddr
;
1576 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
1577 EFI_PCI_IO_PROTOCOL
*PciIo
;
1584 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
1585 MapOp
= EfiPciIoOperationBusMasterWrite
;
1587 MapOp
= EfiPciIoOperationBusMasterRead
;
1590 if (Urb
->DataMap
!= NULL
) {
1591 Status
= PciIo
->Unmap (PciIo
, Urb
->DataMap
);
1592 if (EFI_ERROR (Status
)) {
1597 Urb
->DataMap
= NULL
;
1599 Status
= PciIo
->Map (PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
1600 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
1604 Urb
->DataPhy
= (VOID
*)((UINTN
)PhyAddr
);
1609 return EFI_DEVICE_ERROR
;
1613 Interrupt transfer periodic check handler.
1615 @param Event Interrupt event.
1616 @param Context Pointer to USB_XHCI_INSTANCE.
1621 XhcMonitorAsyncRequests (
1626 USB_XHCI_INSTANCE
*Xhc
;
1635 OldTpl
= gBS
->RaiseTPL (XHC_TPL
);
1637 Xhc
= (USB_XHCI_INSTANCE
*)Context
;
1639 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1640 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1643 // Make sure that the device is available before every check.
1645 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
1651 // Check the result of URB execution. If it is still
1652 // active, check the next one.
1654 XhcCheckUrbResult (Xhc
, Urb
);
1656 if (!Urb
->Finished
) {
1661 // Flush any PCI posted write transactions from a PCI host
1662 // bridge to system memory.
1664 Status
= XhcFlushAsyncIntMap (Xhc
, Urb
);
1665 if (EFI_ERROR (Status
)) {
1666 DEBUG ((DEBUG_ERROR
, "XhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
1670 // Allocate a buffer then copy the transferred data for user.
1671 // If failed to allocate the buffer, update the URB for next
1672 // round of transfer. Ignore the data of this round.
1675 if (Urb
->Result
== EFI_USB_NOERROR
) {
1677 // Make sure the data received from HW is no more than expected.
1679 if (Urb
->Completed
<= Urb
->DataLen
) {
1680 ProcBuf
= AllocateZeroPool (Urb
->Completed
);
1683 if (ProcBuf
== NULL
) {
1684 XhcUpdateAsyncRequest (Xhc
, Urb
);
1688 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
1692 // Leave error recovery to its related device driver. A
1693 // common case of the error recovery is to re-submit the
1694 // interrupt transfer which is linked to the head of the
1695 // list. This function scans from head to tail. So the
1696 // re-submitted interrupt transfer's callback function
1697 // will not be called again in this round. Don't touch this
1698 // URB after the callback, it may have been removed by the
1701 if (Urb
->Callback
!= NULL
) {
1703 // Restore the old TPL, USB bus maybe connect device in
1704 // his callback. Some drivers may has a lower TPL restriction.
1706 gBS
->RestoreTPL (OldTpl
);
1707 (Urb
->Callback
)(ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
1708 OldTpl
= gBS
->RaiseTPL (XHC_TPL
);
1711 if (ProcBuf
!= NULL
) {
1712 gBS
->FreePool (ProcBuf
);
1715 XhcUpdateAsyncRequest (Xhc
, Urb
);
1717 gBS
->RestoreTPL (OldTpl
);
1721 Monitor the port status change. Enable/Disable device slot if there is a device attached/detached.
1723 @param Xhc The XHCI Instance.
1724 @param ParentRouteChart The route string pointed to the parent device if it exists.
1725 @param Port The port to be polled.
1726 @param PortState The port state.
1728 @retval EFI_SUCCESS Successfully enable/disable device slot according to port state.
1729 @retval Others Should not appear.
1734 XhcPollPortStatusChange (
1735 IN USB_XHCI_INSTANCE
*Xhc
,
1736 IN USB_DEV_ROUTE ParentRouteChart
,
1738 IN EFI_USB_PORT_STATUS
*PortState
1745 USB_DEV_ROUTE RouteChart
;
1747 Status
= EFI_SUCCESS
;
1748 Retries
= XHC_INIT_DEVICE_SLOT_RETRIES
;
1750 if ((PortState
->PortChangeStatus
& (USB_PORT_STAT_C_CONNECTION
| USB_PORT_STAT_C_ENABLE
| USB_PORT_STAT_C_OVERCURRENT
| USB_PORT_STAT_C_RESET
)) == 0) {
1754 if (ParentRouteChart
.Dword
== 0) {
1755 RouteChart
.Route
.RouteString
= 0;
1756 RouteChart
.Route
.RootPortNum
= Port
+ 1;
1757 RouteChart
.Route
.TierNum
= 1;
1760 RouteChart
.Route
.RouteString
= ParentRouteChart
.Route
.RouteString
| (Port
<< (4 * (ParentRouteChart
.Route
.TierNum
- 1)));
1762 RouteChart
.Route
.RouteString
= ParentRouteChart
.Route
.RouteString
| (15 << (4 * (ParentRouteChart
.Route
.TierNum
- 1)));
1765 RouteChart
.Route
.RootPortNum
= ParentRouteChart
.Route
.RootPortNum
;
1766 RouteChart
.Route
.TierNum
= ParentRouteChart
.Route
.TierNum
+ 1;
1769 SlotId
= XhcRouteStringToSlotId (Xhc
, RouteChart
);
1771 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
1772 Status
= XhcDisableSlotCmd (Xhc
, SlotId
);
1774 Status
= XhcDisableSlotCmd64 (Xhc
, SlotId
);
1778 if (((PortState
->PortStatus
& USB_PORT_STAT_ENABLE
) != 0) &&
1779 ((PortState
->PortStatus
& USB_PORT_STAT_CONNECTION
) != 0))
1782 // Has a device attached, Identify device speed after port is enabled.
1784 Speed
= EFI_USB_SPEED_FULL
;
1785 if ((PortState
->PortStatus
& USB_PORT_STAT_LOW_SPEED
) != 0) {
1786 Speed
= EFI_USB_SPEED_LOW
;
1787 } else if ((PortState
->PortStatus
& USB_PORT_STAT_HIGH_SPEED
) != 0) {
1788 Speed
= EFI_USB_SPEED_HIGH
;
1789 } else if ((PortState
->PortStatus
& USB_PORT_STAT_SUPER_SPEED
) != 0) {
1790 Speed
= EFI_USB_SPEED_SUPER
;
1795 // Execute Enable_Slot cmd for attached device, initialize device context and assign device address.
1797 SlotId
= XhcRouteStringToSlotId (Xhc
, RouteChart
);
1798 if ((SlotId
== 0) && ((PortState
->PortChangeStatus
& USB_PORT_STAT_C_RESET
) != 0)) {
1799 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
1800 Status
= XhcInitializeDeviceSlot (Xhc
, ParentRouteChart
, Port
, RouteChart
, Speed
);
1802 Status
= XhcInitializeDeviceSlot64 (Xhc
, ParentRouteChart
, Port
, RouteChart
, Speed
);
1807 // According to the xHCI specification (section 4.6.5), "a USB Transaction
1808 // Error Completion Code for an Address Device Command may be due to a Stall
1809 // response from a device. Software should issue a Disable Slot Command for
1810 // the Device Slot then an Enable Slot Command to recover from this error."
1811 // Therefore, retry the device slot initialization if it fails due to a
1814 } while ((Status
== EFI_DEVICE_ERROR
) && (Retries
-- != 0));
1821 Calculate the device context index by endpoint address and direction.
1823 @param EpAddr The target endpoint number.
1824 @param Direction The direction of the target endpoint.
1826 @return The device context index of endpoint.
1840 Index
= (UINT8
)(2 * EpAddr
);
1841 if (Direction
== EfiUsbDataIn
) {
1850 Find out the actual device address according to the requested device address from UsbBus.
1852 @param Xhc The XHCI Instance.
1853 @param BusDevAddr The requested device address by UsbBus upper driver.
1855 @return The actual device address assigned to the device.
1860 XhcBusDevAddrToSlotId (
1861 IN USB_XHCI_INSTANCE
*Xhc
,
1867 for (Index
= 0; Index
< 255; Index
++) {
1868 if (Xhc
->UsbDevContext
[Index
+ 1].Enabled
&&
1869 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
!= 0) &&
1870 (Xhc
->UsbDevContext
[Index
+ 1].BusDevAddr
== BusDevAddr
))
1880 return Xhc
->UsbDevContext
[Index
+ 1].SlotId
;
1884 Find out the slot id according to the device's route string.
1886 @param Xhc The XHCI Instance.
1887 @param RouteString The route string described the device location.
1889 @return The slot id used by the device.
1894 XhcRouteStringToSlotId (
1895 IN USB_XHCI_INSTANCE
*Xhc
,
1896 IN USB_DEV_ROUTE RouteString
1901 for (Index
= 0; Index
< 255; Index
++) {
1902 if (Xhc
->UsbDevContext
[Index
+ 1].Enabled
&&
1903 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
!= 0) &&
1904 (Xhc
->UsbDevContext
[Index
+ 1].RouteString
.Dword
== RouteString
.Dword
))
1914 return Xhc
->UsbDevContext
[Index
+ 1].SlotId
;
1918 Synchronize the specified event ring to update the enqueue and dequeue pointer.
1920 @param Xhc The XHCI Instance.
1921 @param EvtRing The event ring to sync.
1923 @retval EFI_SUCCESS The event ring is synchronized successfully.
1929 IN USB_XHCI_INSTANCE
*Xhc
,
1930 IN EVENT_RING
*EvtRing
1934 TRB_TEMPLATE
*EvtTrb1
;
1936 ASSERT (EvtRing
!= NULL
);
1939 // Calculate the EventRingEnqueue and EventRingCCS.
1940 // Note: only support single Segment
1942 EvtTrb1
= EvtRing
->EventRingDequeue
;
1944 for (Index
= 0; Index
< EvtRing
->TrbNumber
; Index
++) {
1945 if (EvtTrb1
->CycleBit
!= EvtRing
->EventRingCCS
) {
1951 if ((UINTN
)EvtTrb1
>= ((UINTN
)EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
1952 EvtTrb1
= EvtRing
->EventRingSeg0
;
1953 EvtRing
->EventRingCCS
= (EvtRing
->EventRingCCS
) ? 0 : 1;
1957 if (Index
< EvtRing
->TrbNumber
) {
1958 EvtRing
->EventRingEnqueue
= EvtTrb1
;
1967 Synchronize the specified transfer ring to update the enqueue and dequeue pointer.
1969 @param Xhc The XHCI Instance.
1970 @param TrsRing The transfer ring to sync.
1972 @retval EFI_SUCCESS The transfer ring is synchronized successfully.
1978 IN USB_XHCI_INSTANCE
*Xhc
,
1979 IN TRANSFER_RING
*TrsRing
1983 TRB_TEMPLATE
*TrsTrb
;
1985 ASSERT (TrsRing
!= NULL
);
1987 // Calculate the latest RingEnqueue and RingPCS
1989 TrsTrb
= TrsRing
->RingEnqueue
;
1990 ASSERT (TrsTrb
!= NULL
);
1992 for (Index
= 0; Index
< TrsRing
->TrbNumber
; Index
++) {
1993 if (TrsTrb
->CycleBit
!= (TrsRing
->RingPCS
& BIT0
)) {
1998 if ((UINT8
)TrsTrb
->Type
== TRB_TYPE_LINK
) {
1999 ASSERT (((LINK_TRB
*)TrsTrb
)->TC
!= 0);
2001 // set cycle bit in Link TRB as normal
2003 ((LINK_TRB
*)TrsTrb
)->CycleBit
= TrsRing
->RingPCS
& BIT0
;
2005 // Toggle PCS maintained by software
2007 TrsRing
->RingPCS
= (TrsRing
->RingPCS
& BIT0
) ? 0 : 1;
2008 TrsTrb
= (TRB_TEMPLATE
*)TrsRing
->RingSeg0
; // Use host address
2012 ASSERT (Index
!= TrsRing
->TrbNumber
);
2014 if (TrsTrb
!= TrsRing
->RingEnqueue
) {
2015 TrsRing
->RingEnqueue
= TrsTrb
;
2019 // Clear the Trb context for enqueue, but reserve the PCS bit
2021 TrsTrb
->Parameter1
= 0;
2022 TrsTrb
->Parameter2
= 0;
2026 TrsTrb
->Control
= 0;
2032 Check if there is a new generated event.
2034 @param Xhc The XHCI Instance.
2035 @param EvtRing The event ring to check.
2036 @param NewEvtTrb The new event TRB found.
2038 @retval EFI_SUCCESS Found a new event TRB at the event ring.
2039 @retval EFI_NOT_READY The event ring has no new event.
2045 IN USB_XHCI_INSTANCE
*Xhc
,
2046 IN EVENT_RING
*EvtRing
,
2047 OUT TRB_TEMPLATE
**NewEvtTrb
2050 ASSERT (EvtRing
!= NULL
);
2052 *NewEvtTrb
= EvtRing
->EventRingDequeue
;
2054 if (EvtRing
->EventRingDequeue
== EvtRing
->EventRingEnqueue
) {
2055 return EFI_NOT_READY
;
2058 EvtRing
->EventRingDequeue
++;
2060 // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.
2062 if ((UINTN
)EvtRing
->EventRingDequeue
>= ((UINTN
)EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
2063 EvtRing
->EventRingDequeue
= EvtRing
->EventRingSeg0
;
2070 Ring the door bell to notify XHCI there is a transaction to be executed.
2072 @param Xhc The XHCI Instance.
2073 @param SlotId The slot id of the target device.
2074 @param Dci The device context index of the target slot or endpoint.
2076 @retval EFI_SUCCESS Successfully ring the door bell.
2082 IN USB_XHCI_INSTANCE
*Xhc
,
2088 XhcWriteDoorBellReg (Xhc
, 0, 0);
2090 XhcWriteDoorBellReg (Xhc
, SlotId
* sizeof (UINT32
), Dci
);
2097 Ring the door bell to notify XHCI there is a transaction to be executed through URB.
2099 @param Xhc The XHCI Instance.
2100 @param Urb The URB to be rung.
2102 @retval EFI_SUCCESS Successfully ring the door bell.
2106 RingIntTransferDoorBell (
2107 IN USB_XHCI_INSTANCE
*Xhc
,
2114 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
2115 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
2116 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
2121 Assign and initialize the device slot for a new device.
2123 @param Xhc The XHCI Instance.
2124 @param ParentRouteChart The route string pointed to the parent device.
2125 @param ParentPort The port at which the device is located.
2126 @param RouteChart The route string pointed to the device.
2127 @param DeviceSpeed The device speed.
2129 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.
2134 XhcInitializeDeviceSlot (
2135 IN USB_XHCI_INSTANCE
*Xhc
,
2136 IN USB_DEV_ROUTE ParentRouteChart
,
2137 IN UINT16 ParentPort
,
2138 IN USB_DEV_ROUTE RouteChart
,
2139 IN UINT8 DeviceSpeed
2143 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
2144 INPUT_CONTEXT
*InputContext
;
2145 DEVICE_CONTEXT
*OutputContext
;
2146 TRANSFER_RING
*EndpointTransferRing
;
2147 CMD_TRB_ADDRESS_DEVICE CmdTrbAddr
;
2148 UINT8 DeviceAddress
;
2149 CMD_TRB_ENABLE_SLOT CmdTrb
;
2152 DEVICE_CONTEXT
*ParentDeviceContext
;
2153 EFI_PHYSICAL_ADDRESS PhyAddr
;
2155 ZeroMem (&CmdTrb
, sizeof (CMD_TRB_ENABLE_SLOT
));
2156 CmdTrb
.CycleBit
= 1;
2157 CmdTrb
.Type
= TRB_TYPE_EN_SLOT
;
2159 Status
= XhcCmdTransfer (
2161 (TRB_TEMPLATE
*)(UINTN
)&CmdTrb
,
2162 XHC_GENERIC_TIMEOUT
,
2163 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
2165 if (EFI_ERROR (Status
)) {
2166 DEBUG ((DEBUG_ERROR
, "XhcInitializeDeviceSlot: Enable Slot Failed, Status = %r\n", Status
));
2170 ASSERT (EvtTrb
->SlotId
<= Xhc
->MaxSlotsEn
);
2171 DEBUG ((DEBUG_INFO
, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb
->SlotId
));
2172 SlotId
= (UINT8
)EvtTrb
->SlotId
;
2173 ASSERT (SlotId
!= 0);
2175 ZeroMem (&Xhc
->UsbDevContext
[SlotId
], sizeof (USB_DEV_CONTEXT
));
2176 Xhc
->UsbDevContext
[SlotId
].Enabled
= TRUE
;
2177 Xhc
->UsbDevContext
[SlotId
].SlotId
= SlotId
;
2178 Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
= RouteChart
.Dword
;
2179 Xhc
->UsbDevContext
[SlotId
].ParentRouteString
.Dword
= ParentRouteChart
.Dword
;
2182 // 4.3.3 Device Slot Initialization
2183 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.
2185 InputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (INPUT_CONTEXT
));
2186 ASSERT (InputContext
!= NULL
);
2187 ASSERT (((UINTN
)InputContext
& 0x3F) == 0);
2188 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
2190 Xhc
->UsbDevContext
[SlotId
].InputContext
= (VOID
*)InputContext
;
2193 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1
2194 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input
2195 // Context are affected by the command.
2197 InputContext
->InputControlContext
.Dword2
|= (BIT0
| BIT1
);
2200 // 3) Initialize the Input Slot Context data structure
2202 InputContext
->Slot
.RouteString
= RouteChart
.Route
.RouteString
;
2203 InputContext
->Slot
.Speed
= DeviceSpeed
+ 1;
2204 InputContext
->Slot
.ContextEntries
= 1;
2205 InputContext
->Slot
.RootHubPortNum
= RouteChart
.Route
.RootPortNum
;
2207 if (RouteChart
.Route
.RouteString
) {
2209 // The device is behind of hub device.
2211 ParentSlotId
= XhcRouteStringToSlotId (Xhc
, ParentRouteChart
);
2212 ASSERT (ParentSlotId
!= 0);
2214 // if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context
2216 ParentDeviceContext
= (DEVICE_CONTEXT
*)Xhc
->UsbDevContext
[ParentSlotId
].OutputContext
;
2217 if ((ParentDeviceContext
->Slot
.TTPortNum
== 0) &&
2218 (ParentDeviceContext
->Slot
.TTHubSlotId
== 0))
2220 if ((ParentDeviceContext
->Slot
.Speed
== (EFI_USB_SPEED_HIGH
+ 1)) && (DeviceSpeed
< EFI_USB_SPEED_HIGH
)) {
2222 // Full/Low device attached to High speed hub port that isolates the high speed signaling
2223 // environment from Full/Low speed signaling environment for a device
2225 InputContext
->Slot
.TTPortNum
= ParentPort
;
2226 InputContext
->Slot
.TTHubSlotId
= ParentSlotId
;
2230 // Inherit the TT parameters from parent device.
2232 InputContext
->Slot
.TTPortNum
= ParentDeviceContext
->Slot
.TTPortNum
;
2233 InputContext
->Slot
.TTHubSlotId
= ParentDeviceContext
->Slot
.TTHubSlotId
;
2235 // If the device is a High speed device then down the speed to be the same as its parent Hub
2237 if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2238 InputContext
->Slot
.Speed
= ParentDeviceContext
->Slot
.Speed
;
2244 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.
2246 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2247 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0] = EndpointTransferRing
;
2248 CreateTransferRing (Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0]);
2250 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).
2252 InputContext
->EP
[0].EPType
= ED_CONTROL_BIDIR
;
2254 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2255 InputContext
->EP
[0].MaxPacketSize
= 512;
2256 } else if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2257 InputContext
->EP
[0].MaxPacketSize
= 64;
2259 InputContext
->EP
[0].MaxPacketSize
= 8;
2263 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints
2264 // 1KB, and Bulk and Isoch endpoints 3KB.
2266 InputContext
->EP
[0].AverageTRBLength
= 8;
2267 InputContext
->EP
[0].MaxBurstSize
= 0;
2268 InputContext
->EP
[0].Interval
= 0;
2269 InputContext
->EP
[0].MaxPStreams
= 0;
2270 InputContext
->EP
[0].Mult
= 0;
2271 InputContext
->EP
[0].CErr
= 3;
2274 // Init the DCS(dequeue cycle state) as the transfer ring's CCS
2276 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2278 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0])->RingSeg0
,
2279 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2281 InputContext
->EP
[0].PtrLo
= XHC_LOW_32BIT (PhyAddr
) | BIT0
;
2282 InputContext
->EP
[0].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2285 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
2287 OutputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (DEVICE_CONTEXT
));
2288 ASSERT (OutputContext
!= NULL
);
2289 ASSERT (((UINTN
)OutputContext
& 0x3F) == 0);
2290 ZeroMem (OutputContext
, sizeof (DEVICE_CONTEXT
));
2292 Xhc
->UsbDevContext
[SlotId
].OutputContext
= OutputContext
;
2294 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with
2295 // a pointer to the Output Device Context data structure (6.2.1).
2297 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, OutputContext
, sizeof (DEVICE_CONTEXT
));
2299 // Fill DCBAA with PCI device address
2301 Xhc
->DCBAA
[SlotId
] = (UINT64
)(UINTN
)PhyAddr
;
2304 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input
2305 // Context data structure described above.
2307 // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request
2310 gBS
->Stall (XHC_RESET_RECOVERY_DELAY
);
2311 ZeroMem (&CmdTrbAddr
, sizeof (CmdTrbAddr
));
2312 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT
));
2313 CmdTrbAddr
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2314 CmdTrbAddr
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2315 CmdTrbAddr
.CycleBit
= 1;
2316 CmdTrbAddr
.Type
= TRB_TYPE_ADDRESS_DEV
;
2317 CmdTrbAddr
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
2318 Status
= XhcCmdTransfer (
2320 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbAddr
,
2321 XHC_GENERIC_TIMEOUT
,
2322 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
2324 if (!EFI_ERROR (Status
)) {
2325 DeviceAddress
= (UINT8
)((DEVICE_CONTEXT
*)OutputContext
)->Slot
.DeviceAddress
;
2326 DEBUG ((DEBUG_INFO
, " Address %d assigned successfully\n", DeviceAddress
));
2327 Xhc
->UsbDevContext
[SlotId
].XhciDevAddr
= DeviceAddress
;
2329 DEBUG ((DEBUG_ERROR
, " Slot %d address not assigned successfully. Status = %r\n", SlotId
, Status
));
2330 XhcDisableSlotCmd (Xhc
, SlotId
);
2337 Assign and initialize the device slot for a new device.
2339 @param Xhc The XHCI Instance.
2340 @param ParentRouteChart The route string pointed to the parent device.
2341 @param ParentPort The port at which the device is located.
2342 @param RouteChart The route string pointed to the device.
2343 @param DeviceSpeed The device speed.
2345 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.
2350 XhcInitializeDeviceSlot64 (
2351 IN USB_XHCI_INSTANCE
*Xhc
,
2352 IN USB_DEV_ROUTE ParentRouteChart
,
2353 IN UINT16 ParentPort
,
2354 IN USB_DEV_ROUTE RouteChart
,
2355 IN UINT8 DeviceSpeed
2359 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
2360 INPUT_CONTEXT_64
*InputContext
;
2361 DEVICE_CONTEXT_64
*OutputContext
;
2362 TRANSFER_RING
*EndpointTransferRing
;
2363 CMD_TRB_ADDRESS_DEVICE CmdTrbAddr
;
2364 UINT8 DeviceAddress
;
2365 CMD_TRB_ENABLE_SLOT CmdTrb
;
2368 DEVICE_CONTEXT_64
*ParentDeviceContext
;
2369 EFI_PHYSICAL_ADDRESS PhyAddr
;
2371 ZeroMem (&CmdTrb
, sizeof (CMD_TRB_ENABLE_SLOT
));
2372 CmdTrb
.CycleBit
= 1;
2373 CmdTrb
.Type
= TRB_TYPE_EN_SLOT
;
2375 Status
= XhcCmdTransfer (
2377 (TRB_TEMPLATE
*)(UINTN
)&CmdTrb
,
2378 XHC_GENERIC_TIMEOUT
,
2379 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
2381 if (EFI_ERROR (Status
)) {
2382 DEBUG ((DEBUG_ERROR
, "XhcInitializeDeviceSlot64: Enable Slot Failed, Status = %r\n", Status
));
2386 ASSERT (EvtTrb
->SlotId
<= Xhc
->MaxSlotsEn
);
2387 DEBUG ((DEBUG_INFO
, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb
->SlotId
));
2388 SlotId
= (UINT8
)EvtTrb
->SlotId
;
2389 ASSERT (SlotId
!= 0);
2391 ZeroMem (&Xhc
->UsbDevContext
[SlotId
], sizeof (USB_DEV_CONTEXT
));
2392 Xhc
->UsbDevContext
[SlotId
].Enabled
= TRUE
;
2393 Xhc
->UsbDevContext
[SlotId
].SlotId
= SlotId
;
2394 Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
= RouteChart
.Dword
;
2395 Xhc
->UsbDevContext
[SlotId
].ParentRouteString
.Dword
= ParentRouteChart
.Dword
;
2398 // 4.3.3 Device Slot Initialization
2399 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.
2401 InputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (INPUT_CONTEXT_64
));
2402 ASSERT (InputContext
!= NULL
);
2403 ASSERT (((UINTN
)InputContext
& 0x3F) == 0);
2404 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
2406 Xhc
->UsbDevContext
[SlotId
].InputContext
= (VOID
*)InputContext
;
2409 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1
2410 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input
2411 // Context are affected by the command.
2413 InputContext
->InputControlContext
.Dword2
|= (BIT0
| BIT1
);
2416 // 3) Initialize the Input Slot Context data structure
2418 InputContext
->Slot
.RouteString
= RouteChart
.Route
.RouteString
;
2419 InputContext
->Slot
.Speed
= DeviceSpeed
+ 1;
2420 InputContext
->Slot
.ContextEntries
= 1;
2421 InputContext
->Slot
.RootHubPortNum
= RouteChart
.Route
.RootPortNum
;
2423 if (RouteChart
.Route
.RouteString
) {
2425 // The device is behind of hub device.
2427 ParentSlotId
= XhcRouteStringToSlotId (Xhc
, ParentRouteChart
);
2428 ASSERT (ParentSlotId
!= 0);
2430 // if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context
2432 ParentDeviceContext
= (DEVICE_CONTEXT_64
*)Xhc
->UsbDevContext
[ParentSlotId
].OutputContext
;
2433 if ((ParentDeviceContext
->Slot
.TTPortNum
== 0) &&
2434 (ParentDeviceContext
->Slot
.TTHubSlotId
== 0))
2436 if ((ParentDeviceContext
->Slot
.Speed
== (EFI_USB_SPEED_HIGH
+ 1)) && (DeviceSpeed
< EFI_USB_SPEED_HIGH
)) {
2438 // Full/Low device attached to High speed hub port that isolates the high speed signaling
2439 // environment from Full/Low speed signaling environment for a device
2441 InputContext
->Slot
.TTPortNum
= ParentPort
;
2442 InputContext
->Slot
.TTHubSlotId
= ParentSlotId
;
2446 // Inherit the TT parameters from parent device.
2448 InputContext
->Slot
.TTPortNum
= ParentDeviceContext
->Slot
.TTPortNum
;
2449 InputContext
->Slot
.TTHubSlotId
= ParentDeviceContext
->Slot
.TTHubSlotId
;
2451 // If the device is a High speed device then down the speed to be the same as its parent Hub
2453 if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2454 InputContext
->Slot
.Speed
= ParentDeviceContext
->Slot
.Speed
;
2460 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.
2462 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2463 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0] = EndpointTransferRing
;
2464 CreateTransferRing (Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0]);
2466 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).
2468 InputContext
->EP
[0].EPType
= ED_CONTROL_BIDIR
;
2470 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2471 InputContext
->EP
[0].MaxPacketSize
= 512;
2472 } else if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2473 InputContext
->EP
[0].MaxPacketSize
= 64;
2475 InputContext
->EP
[0].MaxPacketSize
= 8;
2479 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints
2480 // 1KB, and Bulk and Isoch endpoints 3KB.
2482 InputContext
->EP
[0].AverageTRBLength
= 8;
2483 InputContext
->EP
[0].MaxBurstSize
= 0;
2484 InputContext
->EP
[0].Interval
= 0;
2485 InputContext
->EP
[0].MaxPStreams
= 0;
2486 InputContext
->EP
[0].Mult
= 0;
2487 InputContext
->EP
[0].CErr
= 3;
2490 // Init the DCS(dequeue cycle state) as the transfer ring's CCS
2492 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2494 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0])->RingSeg0
,
2495 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2497 InputContext
->EP
[0].PtrLo
= XHC_LOW_32BIT (PhyAddr
) | BIT0
;
2498 InputContext
->EP
[0].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2501 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
2503 OutputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (DEVICE_CONTEXT_64
));
2504 ASSERT (OutputContext
!= NULL
);
2505 ASSERT (((UINTN
)OutputContext
& 0x3F) == 0);
2506 ZeroMem (OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2508 Xhc
->UsbDevContext
[SlotId
].OutputContext
= OutputContext
;
2510 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with
2511 // a pointer to the Output Device Context data structure (6.2.1).
2513 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2515 // Fill DCBAA with PCI device address
2517 Xhc
->DCBAA
[SlotId
] = (UINT64
)(UINTN
)PhyAddr
;
2520 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input
2521 // Context data structure described above.
2523 // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request
2526 gBS
->Stall (XHC_RESET_RECOVERY_DELAY
);
2527 ZeroMem (&CmdTrbAddr
, sizeof (CmdTrbAddr
));
2528 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT_64
));
2529 CmdTrbAddr
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2530 CmdTrbAddr
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2531 CmdTrbAddr
.CycleBit
= 1;
2532 CmdTrbAddr
.Type
= TRB_TYPE_ADDRESS_DEV
;
2533 CmdTrbAddr
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
2534 Status
= XhcCmdTransfer (
2536 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbAddr
,
2537 XHC_GENERIC_TIMEOUT
,
2538 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
2540 if (!EFI_ERROR (Status
)) {
2541 DeviceAddress
= (UINT8
)((DEVICE_CONTEXT_64
*)OutputContext
)->Slot
.DeviceAddress
;
2542 DEBUG ((DEBUG_INFO
, " Address %d assigned successfully\n", DeviceAddress
));
2543 Xhc
->UsbDevContext
[SlotId
].XhciDevAddr
= DeviceAddress
;
2545 DEBUG ((DEBUG_ERROR
, " Slot %d address not assigned successfully. Status = %r\n", SlotId
, Status
));
2546 XhcDisableSlotCmd64 (Xhc
, SlotId
);
2553 Disable the specified device slot.
2555 @param Xhc The XHCI Instance.
2556 @param SlotId The slot id to be disabled.
2558 @retval EFI_SUCCESS Successfully disable the device slot.
2564 IN USB_XHCI_INSTANCE
*Xhc
,
2569 TRB_TEMPLATE
*EvtTrb
;
2570 CMD_TRB_DISABLE_SLOT CmdTrbDisSlot
;
2575 // Disable the device slots occupied by these devices on its downstream ports.
2576 // Entry 0 is reserved.
2578 for (Index
= 0; Index
< 255; Index
++) {
2579 if (!Xhc
->UsbDevContext
[Index
+ 1].Enabled
||
2580 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
== 0) ||
2581 (Xhc
->UsbDevContext
[Index
+ 1].ParentRouteString
.Dword
!= Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
))
2586 Status
= XhcDisableSlotCmd (Xhc
, Xhc
->UsbDevContext
[Index
+ 1].SlotId
);
2588 if (EFI_ERROR (Status
)) {
2589 DEBUG ((DEBUG_ERROR
, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
2590 Xhc
->UsbDevContext
[Index
+ 1].SlotId
= 0;
2595 // Construct the disable slot command
2597 DEBUG ((DEBUG_INFO
, "Disable device slot %d!\n", SlotId
));
2599 ZeroMem (&CmdTrbDisSlot
, sizeof (CmdTrbDisSlot
));
2600 CmdTrbDisSlot
.CycleBit
= 1;
2601 CmdTrbDisSlot
.Type
= TRB_TYPE_DIS_SLOT
;
2602 CmdTrbDisSlot
.SlotId
= SlotId
;
2603 Status
= XhcCmdTransfer (
2605 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbDisSlot
,
2606 XHC_GENERIC_TIMEOUT
,
2607 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
2609 if (EFI_ERROR (Status
)) {
2610 DEBUG ((DEBUG_ERROR
, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status
));
2615 // Free the slot's device context entry
2617 Xhc
->DCBAA
[SlotId
] = 0;
2620 // Free the slot related data structure
2622 for (Index
= 0; Index
< 31; Index
++) {
2623 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] != NULL
) {
2624 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
])->RingSeg0
;
2625 if (RingSeg
!= NULL
) {
2626 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
2629 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
]);
2630 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] = NULL
;
2634 for (Index
= 0; Index
< Xhc
->UsbDevContext
[SlotId
].DevDesc
.NumConfigurations
; Index
++) {
2635 if (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
] != NULL
) {
2636 FreePool (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
]);
2640 if (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
!= NULL
) {
2641 FreePool (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
);
2644 if (Xhc
->UsbDevContext
[SlotId
].InputContext
!= NULL
) {
2645 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT
));
2648 if (Xhc
->UsbDevContext
[SlotId
].OutputContext
!= NULL
) {
2649 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].OutputContext
, sizeof (DEVICE_CONTEXT
));
2653 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established
2654 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to
2655 // remove urb from XHCI's asynchronous transfer list.
2657 Xhc
->UsbDevContext
[SlotId
].Enabled
= FALSE
;
2658 Xhc
->UsbDevContext
[SlotId
].SlotId
= 0;
2664 Disable the specified device slot.
2666 @param Xhc The XHCI Instance.
2667 @param SlotId The slot id to be disabled.
2669 @retval EFI_SUCCESS Successfully disable the device slot.
2674 XhcDisableSlotCmd64 (
2675 IN USB_XHCI_INSTANCE
*Xhc
,
2680 TRB_TEMPLATE
*EvtTrb
;
2681 CMD_TRB_DISABLE_SLOT CmdTrbDisSlot
;
2686 // Disable the device slots occupied by these devices on its downstream ports.
2687 // Entry 0 is reserved.
2689 for (Index
= 0; Index
< 255; Index
++) {
2690 if (!Xhc
->UsbDevContext
[Index
+ 1].Enabled
||
2691 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
== 0) ||
2692 (Xhc
->UsbDevContext
[Index
+ 1].ParentRouteString
.Dword
!= Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
))
2697 Status
= XhcDisableSlotCmd64 (Xhc
, Xhc
->UsbDevContext
[Index
+ 1].SlotId
);
2699 if (EFI_ERROR (Status
)) {
2700 DEBUG ((DEBUG_ERROR
, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
2701 Xhc
->UsbDevContext
[Index
+ 1].SlotId
= 0;
2706 // Construct the disable slot command
2708 DEBUG ((DEBUG_INFO
, "Disable device slot %d!\n", SlotId
));
2710 ZeroMem (&CmdTrbDisSlot
, sizeof (CmdTrbDisSlot
));
2711 CmdTrbDisSlot
.CycleBit
= 1;
2712 CmdTrbDisSlot
.Type
= TRB_TYPE_DIS_SLOT
;
2713 CmdTrbDisSlot
.SlotId
= SlotId
;
2714 Status
= XhcCmdTransfer (
2716 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbDisSlot
,
2717 XHC_GENERIC_TIMEOUT
,
2718 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
2720 if (EFI_ERROR (Status
)) {
2721 DEBUG ((DEBUG_ERROR
, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status
));
2726 // Free the slot's device context entry
2728 Xhc
->DCBAA
[SlotId
] = 0;
2731 // Free the slot related data structure
2733 for (Index
= 0; Index
< 31; Index
++) {
2734 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] != NULL
) {
2735 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
])->RingSeg0
;
2736 if (RingSeg
!= NULL
) {
2737 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
2740 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
]);
2741 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] = NULL
;
2745 for (Index
= 0; Index
< Xhc
->UsbDevContext
[SlotId
].DevDesc
.NumConfigurations
; Index
++) {
2746 if (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
] != NULL
) {
2747 FreePool (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
]);
2751 if (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
!= NULL
) {
2752 FreePool (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
);
2755 if (Xhc
->UsbDevContext
[SlotId
].InputContext
!= NULL
) {
2756 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT_64
));
2759 if (Xhc
->UsbDevContext
[SlotId
].OutputContext
!= NULL
) {
2760 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2764 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established
2765 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to
2766 // remove urb from XHCI's asynchronous transfer list.
2768 Xhc
->UsbDevContext
[SlotId
].Enabled
= FALSE
;
2769 Xhc
->UsbDevContext
[SlotId
].SlotId
= 0;
2775 Initialize endpoint context in input context.
2777 @param Xhc The XHCI Instance.
2778 @param SlotId The slot id to be configured.
2779 @param DeviceSpeed The device's speed.
2780 @param InputContext The pointer to the input context.
2781 @param IfDesc The pointer to the usb device interface descriptor.
2783 @return The maximum device context index of endpoint.
2788 XhcInitializeEndpointContext (
2789 IN USB_XHCI_INSTANCE
*Xhc
,
2791 IN UINT8 DeviceSpeed
,
2792 IN INPUT_CONTEXT
*InputContext
,
2793 IN USB_INTERFACE_DESCRIPTOR
*IfDesc
2796 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
2803 EFI_PHYSICAL_ADDRESS PhyAddr
;
2805 TRANSFER_RING
*EndpointTransferRing
;
2809 NumEp
= IfDesc
->NumEndpoints
;
2811 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDesc
+ 1);
2812 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
2813 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
2814 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2817 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
2818 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2822 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
2823 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
2825 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
2831 InputContext
->InputControlContext
.Dword2
|= (BIT0
<< Dci
);
2832 InputContext
->EP
[Dci
-1].MaxPacketSize
= EpDesc
->MaxPacketSize
;
2834 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2836 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.
2838 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2840 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2843 switch (EpDesc
->Attributes
& USB_ENDPOINT_TYPE_MASK
) {
2844 case USB_ENDPOINT_BULK
:
2845 if (Direction
== EfiUsbDataIn
) {
2846 InputContext
->EP
[Dci
-1].CErr
= 3;
2847 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_IN
;
2849 InputContext
->EP
[Dci
-1].CErr
= 3;
2850 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_OUT
;
2853 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2854 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2855 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2856 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*)EndpointTransferRing
;
2857 CreateTransferRing (Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2860 "Endpoint[%x]: Created BULK ring [%p~%p)\n",
2861 EpDesc
->EndpointAddress
,
2862 EndpointTransferRing
->RingSeg0
,
2863 (UINTN
)EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2868 case USB_ENDPOINT_ISO
:
2869 if (Direction
== EfiUsbDataIn
) {
2870 InputContext
->EP
[Dci
-1].CErr
= 0;
2871 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_IN
;
2873 InputContext
->EP
[Dci
-1].CErr
= 0;
2874 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_OUT
;
2878 // Get the bInterval from descriptor and init the the interval field of endpoint context.
2879 // Refer to XHCI 1.1 spec section 6.2.3.6.
2881 if (DeviceSpeed
== EFI_USB_SPEED_FULL
) {
2882 Interval
= EpDesc
->Interval
;
2883 ASSERT (Interval
>= 1 && Interval
<= 16);
2884 InputContext
->EP
[Dci
-1].Interval
= Interval
+ 2;
2885 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2886 Interval
= EpDesc
->Interval
;
2887 ASSERT (Interval
>= 1 && Interval
<= 16);
2888 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2892 // Do not support isochronous transfer now.
2894 DEBUG ((DEBUG_INFO
, "XhcInitializeEndpointContext: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
2895 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2897 case USB_ENDPOINT_INTERRUPT
:
2898 if (Direction
== EfiUsbDataIn
) {
2899 InputContext
->EP
[Dci
-1].CErr
= 3;
2900 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_IN
;
2902 InputContext
->EP
[Dci
-1].CErr
= 3;
2903 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_OUT
;
2906 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2907 InputContext
->EP
[Dci
-1].MaxESITPayload
= EpDesc
->MaxPacketSize
;
2909 // Get the bInterval from descriptor and init the the interval field of endpoint context
2911 if ((DeviceSpeed
== EFI_USB_SPEED_FULL
) || (DeviceSpeed
== EFI_USB_SPEED_LOW
)) {
2912 Interval
= EpDesc
->Interval
;
2914 // Calculate through the bInterval field of Endpoint descriptor.
2916 ASSERT (Interval
!= 0);
2917 InputContext
->EP
[Dci
-1].Interval
= (UINT32
)HighBitSet32 ((UINT32
)Interval
) + 3;
2918 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2919 Interval
= EpDesc
->Interval
;
2920 ASSERT (Interval
>= 1 && Interval
<= 16);
2922 // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
2924 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2925 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2926 InputContext
->EP
[Dci
-1].MaxESITPayload
= 0x0002;
2927 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2928 InputContext
->EP
[Dci
-1].CErr
= 3;
2931 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2932 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2933 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*)EndpointTransferRing
;
2934 CreateTransferRing (Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2937 "Endpoint[%x]: Created INT ring [%p~%p)\n",
2938 EpDesc
->EndpointAddress
,
2939 EndpointTransferRing
->RingSeg0
,
2940 (UINTN
)EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2946 case USB_ENDPOINT_CONTROL
:
2948 // Do not support control transfer now.
2950 DEBUG ((DEBUG_INFO
, "XhcInitializeEndpointContext: Unsupport Control EP found, Transfer ring is not allocated.\n"));
2952 DEBUG ((DEBUG_INFO
, "XhcInitializeEndpointContext: Unknown EP found, Transfer ring is not allocated.\n"));
2953 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2957 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2959 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingSeg0
,
2960 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2962 PhyAddr
&= ~((EFI_PHYSICAL_ADDRESS
)0x0F);
2963 PhyAddr
|= (EFI_PHYSICAL_ADDRESS
)((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingPCS
;
2964 InputContext
->EP
[Dci
-1].PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2965 InputContext
->EP
[Dci
-1].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2967 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2974 Initialize endpoint context in input context.
2976 @param Xhc The XHCI Instance.
2977 @param SlotId The slot id to be configured.
2978 @param DeviceSpeed The device's speed.
2979 @param InputContext The pointer to the input context.
2980 @param IfDesc The pointer to the usb device interface descriptor.
2982 @return The maximum device context index of endpoint.
2987 XhcInitializeEndpointContext64 (
2988 IN USB_XHCI_INSTANCE
*Xhc
,
2990 IN UINT8 DeviceSpeed
,
2991 IN INPUT_CONTEXT_64
*InputContext
,
2992 IN USB_INTERFACE_DESCRIPTOR
*IfDesc
2995 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3002 EFI_PHYSICAL_ADDRESS PhyAddr
;
3004 TRANSFER_RING
*EndpointTransferRing
;
3008 NumEp
= IfDesc
->NumEndpoints
;
3010 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDesc
+ 1);
3011 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3012 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3013 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3016 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3017 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3021 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
3022 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3024 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3030 InputContext
->InputControlContext
.Dword2
|= (BIT0
<< Dci
);
3031 InputContext
->EP
[Dci
-1].MaxPacketSize
= EpDesc
->MaxPacketSize
;
3033 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
3035 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.
3037 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
3039 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
3042 switch (EpDesc
->Attributes
& USB_ENDPOINT_TYPE_MASK
) {
3043 case USB_ENDPOINT_BULK
:
3044 if (Direction
== EfiUsbDataIn
) {
3045 InputContext
->EP
[Dci
-1].CErr
= 3;
3046 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_IN
;
3048 InputContext
->EP
[Dci
-1].CErr
= 3;
3049 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_OUT
;
3052 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3053 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
3054 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
3055 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*)EndpointTransferRing
;
3056 CreateTransferRing (Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
3059 "Endpoint64[%x]: Created BULK ring [%p~%p)\n",
3060 EpDesc
->EndpointAddress
,
3061 EndpointTransferRing
->RingSeg0
,
3062 (UINTN
)EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
3067 case USB_ENDPOINT_ISO
:
3068 if (Direction
== EfiUsbDataIn
) {
3069 InputContext
->EP
[Dci
-1].CErr
= 0;
3070 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_IN
;
3072 InputContext
->EP
[Dci
-1].CErr
= 0;
3073 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_OUT
;
3077 // Get the bInterval from descriptor and init the the interval field of endpoint context.
3078 // Refer to XHCI 1.1 spec section 6.2.3.6.
3080 if (DeviceSpeed
== EFI_USB_SPEED_FULL
) {
3081 Interval
= EpDesc
->Interval
;
3082 ASSERT (Interval
>= 1 && Interval
<= 16);
3083 InputContext
->EP
[Dci
-1].Interval
= Interval
+ 2;
3084 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
3085 Interval
= EpDesc
->Interval
;
3086 ASSERT (Interval
>= 1 && Interval
<= 16);
3087 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
3091 // Do not support isochronous transfer now.
3093 DEBUG ((DEBUG_INFO
, "XhcInitializeEndpointContext64: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
3094 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3096 case USB_ENDPOINT_INTERRUPT
:
3097 if (Direction
== EfiUsbDataIn
) {
3098 InputContext
->EP
[Dci
-1].CErr
= 3;
3099 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_IN
;
3101 InputContext
->EP
[Dci
-1].CErr
= 3;
3102 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_OUT
;
3105 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3106 InputContext
->EP
[Dci
-1].MaxESITPayload
= EpDesc
->MaxPacketSize
;
3108 // Get the bInterval from descriptor and init the the interval field of endpoint context
3110 if ((DeviceSpeed
== EFI_USB_SPEED_FULL
) || (DeviceSpeed
== EFI_USB_SPEED_LOW
)) {
3111 Interval
= EpDesc
->Interval
;
3113 // Calculate through the bInterval field of Endpoint descriptor.
3115 ASSERT (Interval
!= 0);
3116 InputContext
->EP
[Dci
-1].Interval
= (UINT32
)HighBitSet32 ((UINT32
)Interval
) + 3;
3117 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
3118 Interval
= EpDesc
->Interval
;
3119 ASSERT (Interval
>= 1 && Interval
<= 16);
3121 // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
3123 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
3124 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3125 InputContext
->EP
[Dci
-1].MaxESITPayload
= 0x0002;
3126 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
3127 InputContext
->EP
[Dci
-1].CErr
= 3;
3130 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
3131 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
3132 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*)EndpointTransferRing
;
3133 CreateTransferRing (Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
3136 "Endpoint64[%x]: Created INT ring [%p~%p)\n",
3137 EpDesc
->EndpointAddress
,
3138 EndpointTransferRing
->RingSeg0
,
3139 (UINTN
)EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
3145 case USB_ENDPOINT_CONTROL
:
3147 // Do not support control transfer now.
3149 DEBUG ((DEBUG_INFO
, "XhcInitializeEndpointContext64: Unsupport Control EP found, Transfer ring is not allocated.\n"));
3151 DEBUG ((DEBUG_INFO
, "XhcInitializeEndpointContext64: Unknown EP found, Transfer ring is not allocated.\n"));
3152 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3156 PhyAddr
= UsbHcGetPciAddrForHostAddr (
3158 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingSeg0
,
3159 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
3161 PhyAddr
&= ~((EFI_PHYSICAL_ADDRESS
)0x0F);
3162 PhyAddr
|= (EFI_PHYSICAL_ADDRESS
)((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingPCS
;
3163 InputContext
->EP
[Dci
-1].PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3164 InputContext
->EP
[Dci
-1].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3166 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3173 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
3175 @param Xhc The XHCI Instance.
3176 @param SlotId The slot id to be configured.
3177 @param DeviceSpeed The device's speed.
3178 @param ConfigDesc The pointer to the usb device configuration descriptor.
3180 @retval EFI_SUCCESS Successfully configure all the device endpoints.
3186 IN USB_XHCI_INSTANCE
*Xhc
,
3188 IN UINT8 DeviceSpeed
,
3189 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
3193 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3197 EFI_PHYSICAL_ADDRESS PhyAddr
;
3199 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3200 INPUT_CONTEXT
*InputContext
;
3201 DEVICE_CONTEXT
*OutputContext
;
3202 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3205 // 4.6.6 Configure Endpoint
3207 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3208 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3209 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3210 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT
));
3212 ASSERT (ConfigDesc
!= NULL
);
3216 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3217 for (Index
= 0; Index
< ConfigDesc
->NumInterfaces
; Index
++) {
3218 while ((IfDesc
->DescriptorType
!= USB_DESC_TYPE_INTERFACE
) || (IfDesc
->AlternateSetting
!= 0)) {
3219 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3222 if (IfDesc
->Length
< sizeof (USB_INTERFACE_DESCRIPTOR
)) {
3223 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3227 Dci
= XhcInitializeEndpointContext (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDesc
);
3232 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3235 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3236 InputContext
->Slot
.ContextEntries
= MaxDci
;
3238 // configure endpoint
3240 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3241 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3242 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3243 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3244 CmdTrbCfgEP
.CycleBit
= 1;
3245 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3246 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3247 DEBUG ((DEBUG_INFO
, "Configure Endpoint\n"));
3248 Status
= XhcCmdTransfer (
3250 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbCfgEP
,
3251 XHC_GENERIC_TIMEOUT
,
3252 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3254 if (EFI_ERROR (Status
)) {
3255 DEBUG ((DEBUG_ERROR
, "XhcSetConfigCmd: Config Endpoint Failed, Status = %r\n", Status
));
3257 Xhc
->UsbDevContext
[SlotId
].ActiveConfiguration
= ConfigDesc
->ConfigurationValue
;
3264 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
3266 @param Xhc The XHCI Instance.
3267 @param SlotId The slot id to be configured.
3268 @param DeviceSpeed The device's speed.
3269 @param ConfigDesc The pointer to the usb device configuration descriptor.
3271 @retval EFI_SUCCESS Successfully configure all the device endpoints.
3277 IN USB_XHCI_INSTANCE
*Xhc
,
3279 IN UINT8 DeviceSpeed
,
3280 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
3284 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3288 EFI_PHYSICAL_ADDRESS PhyAddr
;
3290 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3291 INPUT_CONTEXT_64
*InputContext
;
3292 DEVICE_CONTEXT_64
*OutputContext
;
3293 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3296 // 4.6.6 Configure Endpoint
3298 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3299 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3300 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3301 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT_64
));
3303 ASSERT (ConfigDesc
!= NULL
);
3307 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3308 for (Index
= 0; Index
< ConfigDesc
->NumInterfaces
; Index
++) {
3309 while ((IfDesc
->DescriptorType
!= USB_DESC_TYPE_INTERFACE
) || (IfDesc
->AlternateSetting
!= 0)) {
3310 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3313 if (IfDesc
->Length
< sizeof (USB_INTERFACE_DESCRIPTOR
)) {
3314 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3318 Dci
= XhcInitializeEndpointContext64 (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDesc
);
3323 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3326 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3327 InputContext
->Slot
.ContextEntries
= MaxDci
;
3329 // configure endpoint
3331 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3332 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3333 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3334 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3335 CmdTrbCfgEP
.CycleBit
= 1;
3336 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3337 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3338 DEBUG ((DEBUG_INFO
, "Configure Endpoint\n"));
3339 Status
= XhcCmdTransfer (
3341 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbCfgEP
,
3342 XHC_GENERIC_TIMEOUT
,
3343 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3345 if (EFI_ERROR (Status
)) {
3346 DEBUG ((DEBUG_ERROR
, "XhcSetConfigCmd64: Config Endpoint Failed, Status = %r\n", Status
));
3348 Xhc
->UsbDevContext
[SlotId
].ActiveConfiguration
= ConfigDesc
->ConfigurationValue
;
3355 Stop endpoint through XHCI's Stop_Endpoint cmd.
3357 @param Xhc The XHCI Instance.
3358 @param SlotId The slot id to be configured.
3359 @param Dci The device context index of endpoint.
3360 @param PendingUrb The pending URB to check completion status when stopping the end point.
3362 @retval EFI_SUCCESS Stop endpoint successfully.
3363 @retval Others Failed to stop endpoint.
3369 IN USB_XHCI_INSTANCE
*Xhc
,
3372 IN URB
*PendingUrb OPTIONAL
3376 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3377 CMD_TRB_STOP_ENDPOINT CmdTrbStopED
;
3379 DEBUG ((DEBUG_INFO
, "XhcStopEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId
, Dci
));
3382 // When XhcCheckUrbResult waits for the Stop_Endpoint completion, it also checks
3383 // the PendingUrb completion status, because it's possible that the PendingUrb is
3384 // finished just before stopping the end point, but after the looping check.
3386 // The PendingUrb could be passed to XhcCmdTransfer to XhcExecTransfer to XhcCheckUrbResult
3387 // through function parameter, but That will cause every consumer of XhcCmdTransfer,
3388 // XhcExecTransfer and XhcCheckUrbResult pass a NULL PendingUrb.
3389 // But actually only XhcCheckUrbResult is aware of the PendingUrb.
3390 // So we choose to save the PendingUrb into the USB_XHCI_INSTANCE and use it in XhcCheckUrbResult.
3392 ASSERT (Xhc
->PendingUrb
== NULL
);
3393 Xhc
->PendingUrb
= PendingUrb
;
3395 // Reset the URB result from Timeout to NoError.
3396 // The USB result will be:
3397 // changed to Timeout when Stop/StopInvalidLength Transfer Event is received, or
3398 // remain NoError when Success/ShortPacket Transfer Event is received.
3400 if (PendingUrb
!= NULL
) {
3401 PendingUrb
->Result
= EFI_USB_NOERROR
;
3405 // Send stop endpoint command to transit Endpoint from running to stop state
3407 ZeroMem (&CmdTrbStopED
, sizeof (CmdTrbStopED
));
3408 CmdTrbStopED
.CycleBit
= 1;
3409 CmdTrbStopED
.Type
= TRB_TYPE_STOP_ENDPOINT
;
3410 CmdTrbStopED
.EDID
= Dci
;
3411 CmdTrbStopED
.SlotId
= SlotId
;
3412 Status
= XhcCmdTransfer (
3414 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbStopED
,
3415 XHC_GENERIC_TIMEOUT
,
3416 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3418 if (EFI_ERROR (Status
)) {
3419 DEBUG ((DEBUG_ERROR
, "XhcStopEndpoint: Stop Endpoint Failed, Status = %r\n", Status
));
3422 Xhc
->PendingUrb
= NULL
;
3428 Reset endpoint through XHCI's Reset_Endpoint cmd.
3430 @param Xhc The XHCI Instance.
3431 @param SlotId The slot id to be configured.
3432 @param Dci The device context index of endpoint.
3434 @retval EFI_SUCCESS Reset endpoint successfully.
3435 @retval Others Failed to reset endpoint.
3441 IN USB_XHCI_INSTANCE
*Xhc
,
3447 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3448 CMD_TRB_RESET_ENDPOINT CmdTrbResetED
;
3450 DEBUG ((DEBUG_INFO
, "XhcResetEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId
, Dci
));
3453 // Send stop endpoint command to transit Endpoint from running to stop state
3455 ZeroMem (&CmdTrbResetED
, sizeof (CmdTrbResetED
));
3456 CmdTrbResetED
.CycleBit
= 1;
3457 CmdTrbResetED
.Type
= TRB_TYPE_RESET_ENDPOINT
;
3458 CmdTrbResetED
.EDID
= Dci
;
3459 CmdTrbResetED
.SlotId
= SlotId
;
3460 Status
= XhcCmdTransfer (
3462 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbResetED
,
3463 XHC_GENERIC_TIMEOUT
,
3464 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3466 if (EFI_ERROR (Status
)) {
3467 DEBUG ((DEBUG_ERROR
, "XhcResetEndpoint: Reset Endpoint Failed, Status = %r\n", Status
));
3474 Set transfer ring dequeue pointer through XHCI's Set_Tr_Dequeue_Pointer cmd.
3476 @param Xhc The XHCI Instance.
3477 @param SlotId The slot id to be configured.
3478 @param Dci The device context index of endpoint.
3479 @param Urb The dequeue pointer of the transfer ring specified
3480 by the urb to be updated.
3482 @retval EFI_SUCCESS Set transfer ring dequeue pointer succeeds.
3483 @retval Others Failed to set transfer ring dequeue pointer.
3488 XhcSetTrDequeuePointer (
3489 IN USB_XHCI_INSTANCE
*Xhc
,
3496 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3497 CMD_SET_TR_DEQ_POINTER CmdSetTRDeq
;
3498 EFI_PHYSICAL_ADDRESS PhyAddr
;
3500 DEBUG ((DEBUG_INFO
, "XhcSetTrDequeuePointer: Slot = 0x%x, Dci = 0x%x, Urb = 0x%x\n", SlotId
, Dci
, Urb
));
3503 // Send stop endpoint command to transit Endpoint from running to stop state
3505 ZeroMem (&CmdSetTRDeq
, sizeof (CmdSetTRDeq
));
3506 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Urb
->Ring
->RingEnqueue
, sizeof (CMD_SET_TR_DEQ_POINTER
));
3507 CmdSetTRDeq
.PtrLo
= XHC_LOW_32BIT (PhyAddr
) | Urb
->Ring
->RingPCS
;
3508 CmdSetTRDeq
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3509 CmdSetTRDeq
.CycleBit
= 1;
3510 CmdSetTRDeq
.Type
= TRB_TYPE_SET_TR_DEQUE
;
3511 CmdSetTRDeq
.Endpoint
= Dci
;
3512 CmdSetTRDeq
.SlotId
= SlotId
;
3513 Status
= XhcCmdTransfer (
3515 (TRB_TEMPLATE
*)(UINTN
)&CmdSetTRDeq
,
3516 XHC_GENERIC_TIMEOUT
,
3517 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3519 if (EFI_ERROR (Status
)) {
3520 DEBUG ((DEBUG_ERROR
, "XhcSetTrDequeuePointer: Set TR Dequeue Pointer Failed, Status = %r\n", Status
));
3527 Set interface through XHCI's Configure_Endpoint cmd.
3529 @param Xhc The XHCI Instance.
3530 @param SlotId The slot id to be configured.
3531 @param DeviceSpeed The device's speed.
3532 @param ConfigDesc The pointer to the usb device configuration descriptor.
3533 @param Request USB device request to send.
3535 @retval EFI_SUCCESS Successfully set interface.
3541 IN USB_XHCI_INSTANCE
*Xhc
,
3543 IN UINT8 DeviceSpeed
,
3544 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
,
3545 IN EFI_USB_DEVICE_REQUEST
*Request
3549 USB_INTERFACE_DESCRIPTOR
*IfDescActive
;
3550 USB_INTERFACE_DESCRIPTOR
*IfDescSet
;
3551 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3552 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3559 EFI_PHYSICAL_ADDRESS PhyAddr
;
3562 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3563 INPUT_CONTEXT
*InputContext
;
3564 DEVICE_CONTEXT
*OutputContext
;
3565 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3567 Status
= EFI_SUCCESS
;
3569 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3570 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3572 // XHCI 4.6.6 Configure Endpoint
3573 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3574 // Context and Add Context flags as follows:
3575 // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
3576 // Context and Add Context flags to '0'.
3578 // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
3579 // So the default Drop Context and Add Context flags can be '0' to cover 1).
3581 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3582 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT
));
3584 ASSERT (ConfigDesc
!= NULL
);
3588 IfDescActive
= NULL
;
3591 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3592 while ((UINTN
)IfDesc
< ((UINTN
)ConfigDesc
+ ConfigDesc
->TotalLength
)) {
3593 if ((IfDesc
->DescriptorType
== USB_DESC_TYPE_INTERFACE
) && (IfDesc
->Length
>= sizeof (USB_INTERFACE_DESCRIPTOR
))) {
3594 if (IfDesc
->InterfaceNumber
== (UINT8
)Request
->Index
) {
3595 if (IfDesc
->AlternateSetting
== Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[IfDesc
->InterfaceNumber
]) {
3597 // Find out the active interface descriptor.
3599 IfDescActive
= IfDesc
;
3600 } else if (IfDesc
->AlternateSetting
== (UINT8
)Request
->Value
) {
3602 // Find out the interface descriptor to set.
3609 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3613 // XHCI 4.6.6 Configure Endpoint
3614 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3615 // Context and Add Context flags as follows:
3616 // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
3617 // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
3618 // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
3619 // the Drop Context flag to '1' and Add Context flag to '0'.
3620 // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
3621 // and Add Context flags shall be set to '1'.
3623 // Below codes are to cover 2), 3) and 4).
3626 if ((IfDescActive
!= NULL
) && (IfDescSet
!= NULL
)) {
3627 NumEp
= IfDescActive
->NumEndpoints
;
3628 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDescActive
+ 1);
3629 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3630 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3631 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3634 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3635 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3639 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
3640 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3642 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3649 // XHCI 4.3.6 - Setting Alternate Interfaces
3650 // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
3652 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, NULL
);
3653 if (EFI_ERROR (Status
)) {
3658 // XHCI 4.3.6 - Setting Alternate Interfaces
3659 // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
3661 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] != NULL
) {
3662 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1])->RingSeg0
;
3663 if (RingSeg
!= NULL
) {
3664 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
3667 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1]);
3668 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] = NULL
;
3672 // Set the Drop Context flag to '1'.
3674 InputContext
->InputControlContext
.Dword1
|= (BIT0
<< Dci
);
3676 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3680 // XHCI 4.3.6 - Setting Alternate Interfaces
3681 // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
3682 // Interface setting, to '0'.
3684 // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
3688 // XHCI 4.3.6 - Setting Alternate Interfaces
3689 // 4) For each endpoint enabled by the Configure Endpoint Command:
3690 // a. Allocate a Transfer Ring.
3691 // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
3692 // c. Initialize the Endpoint Context data structure.
3694 Dci
= XhcInitializeEndpointContext (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDescSet
);
3699 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3700 InputContext
->Slot
.ContextEntries
= MaxDci
;
3702 // XHCI 4.3.6 - Setting Alternate Interfaces
3703 // 5) Issue and successfully complete a Configure Endpoint Command.
3705 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3706 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3707 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3708 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3709 CmdTrbCfgEP
.CycleBit
= 1;
3710 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3711 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3712 DEBUG ((DEBUG_INFO
, "SetInterface: Configure Endpoint\n"));
3713 Status
= XhcCmdTransfer (
3715 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbCfgEP
,
3716 XHC_GENERIC_TIMEOUT
,
3717 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3719 if (EFI_ERROR (Status
)) {
3720 DEBUG ((DEBUG_ERROR
, "SetInterface: Config Endpoint Failed, Status = %r\n", Status
));
3723 // Update the active AlternateSetting.
3725 Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[(UINT8
)Request
->Index
] = (UINT8
)Request
->Value
;
3733 Set interface through XHCI's Configure_Endpoint cmd.
3735 @param Xhc The XHCI Instance.
3736 @param SlotId The slot id to be configured.
3737 @param DeviceSpeed The device's speed.
3738 @param ConfigDesc The pointer to the usb device configuration descriptor.
3739 @param Request USB device request to send.
3741 @retval EFI_SUCCESS Successfully set interface.
3747 IN USB_XHCI_INSTANCE
*Xhc
,
3749 IN UINT8 DeviceSpeed
,
3750 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
,
3751 IN EFI_USB_DEVICE_REQUEST
*Request
3755 USB_INTERFACE_DESCRIPTOR
*IfDescActive
;
3756 USB_INTERFACE_DESCRIPTOR
*IfDescSet
;
3757 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3758 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3765 EFI_PHYSICAL_ADDRESS PhyAddr
;
3768 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3769 INPUT_CONTEXT_64
*InputContext
;
3770 DEVICE_CONTEXT_64
*OutputContext
;
3771 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3773 Status
= EFI_SUCCESS
;
3775 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3776 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3778 // XHCI 4.6.6 Configure Endpoint
3779 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3780 // Context and Add Context flags as follows:
3781 // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
3782 // Context and Add Context flags to '0'.
3784 // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
3785 // So the default Drop Context and Add Context flags can be '0' to cover 1).
3787 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3788 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT_64
));
3790 ASSERT (ConfigDesc
!= NULL
);
3794 IfDescActive
= NULL
;
3797 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3798 while ((UINTN
)IfDesc
< ((UINTN
)ConfigDesc
+ ConfigDesc
->TotalLength
)) {
3799 if ((IfDesc
->DescriptorType
== USB_DESC_TYPE_INTERFACE
) && (IfDesc
->Length
>= sizeof (USB_INTERFACE_DESCRIPTOR
))) {
3800 if (IfDesc
->InterfaceNumber
== (UINT8
)Request
->Index
) {
3801 if (IfDesc
->AlternateSetting
== Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[IfDesc
->InterfaceNumber
]) {
3803 // Find out the active interface descriptor.
3805 IfDescActive
= IfDesc
;
3806 } else if (IfDesc
->AlternateSetting
== (UINT8
)Request
->Value
) {
3808 // Find out the interface descriptor to set.
3815 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3819 // XHCI 4.6.6 Configure Endpoint
3820 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3821 // Context and Add Context flags as follows:
3822 // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
3823 // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
3824 // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
3825 // the Drop Context flag to '1' and Add Context flag to '0'.
3826 // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
3827 // and Add Context flags shall be set to '1'.
3829 // Below codes are to cover 2), 3) and 4).
3832 if ((IfDescActive
!= NULL
) && (IfDescSet
!= NULL
)) {
3833 NumEp
= IfDescActive
->NumEndpoints
;
3834 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDescActive
+ 1);
3835 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3836 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3837 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3840 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3841 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3845 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
3846 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3848 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3855 // XHCI 4.3.6 - Setting Alternate Interfaces
3856 // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
3858 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, NULL
);
3859 if (EFI_ERROR (Status
)) {
3864 // XHCI 4.3.6 - Setting Alternate Interfaces
3865 // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
3867 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] != NULL
) {
3868 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1])->RingSeg0
;
3869 if (RingSeg
!= NULL
) {
3870 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
3873 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1]);
3874 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] = NULL
;
3878 // Set the Drop Context flag to '1'.
3880 InputContext
->InputControlContext
.Dword1
|= (BIT0
<< Dci
);
3882 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3886 // XHCI 4.3.6 - Setting Alternate Interfaces
3887 // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
3888 // Interface setting, to '0'.
3890 // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
3894 // XHCI 4.3.6 - Setting Alternate Interfaces
3895 // 4) For each endpoint enabled by the Configure Endpoint Command:
3896 // a. Allocate a Transfer Ring.
3897 // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
3898 // c. Initialize the Endpoint Context data structure.
3900 Dci
= XhcInitializeEndpointContext64 (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDescSet
);
3905 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3906 InputContext
->Slot
.ContextEntries
= MaxDci
;
3908 // XHCI 4.3.6 - Setting Alternate Interfaces
3909 // 5) Issue and successfully complete a Configure Endpoint Command.
3911 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3912 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3913 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3914 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3915 CmdTrbCfgEP
.CycleBit
= 1;
3916 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3917 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3918 DEBUG ((DEBUG_INFO
, "SetInterface64: Configure Endpoint\n"));
3919 Status
= XhcCmdTransfer (
3921 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbCfgEP
,
3922 XHC_GENERIC_TIMEOUT
,
3923 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3925 if (EFI_ERROR (Status
)) {
3926 DEBUG ((DEBUG_ERROR
, "SetInterface64: Config Endpoint Failed, Status = %r\n", Status
));
3929 // Update the active AlternateSetting.
3931 Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[(UINT8
)Request
->Index
] = (UINT8
)Request
->Value
;
3939 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
3941 @param Xhc The XHCI Instance.
3942 @param SlotId The slot id to be evaluated.
3943 @param MaxPacketSize The max packet size supported by the device control transfer.
3945 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.
3950 XhcEvaluateContext (
3951 IN USB_XHCI_INSTANCE
*Xhc
,
3953 IN UINT32 MaxPacketSize
3957 CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu
;
3958 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3959 INPUT_CONTEXT
*InputContext
;
3960 EFI_PHYSICAL_ADDRESS PhyAddr
;
3962 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3965 // 4.6.7 Evaluate Context
3967 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3968 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3970 InputContext
->InputControlContext
.Dword2
|= BIT1
;
3971 InputContext
->EP
[0].MaxPacketSize
= MaxPacketSize
;
3973 ZeroMem (&CmdTrbEvalu
, sizeof (CmdTrbEvalu
));
3974 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3975 CmdTrbEvalu
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3976 CmdTrbEvalu
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3977 CmdTrbEvalu
.CycleBit
= 1;
3978 CmdTrbEvalu
.Type
= TRB_TYPE_EVALU_CONTXT
;
3979 CmdTrbEvalu
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3980 DEBUG ((DEBUG_INFO
, "Evaluate context\n"));
3981 Status
= XhcCmdTransfer (
3983 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbEvalu
,
3984 XHC_GENERIC_TIMEOUT
,
3985 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
3987 if (EFI_ERROR (Status
)) {
3988 DEBUG ((DEBUG_ERROR
, "XhcEvaluateContext: Evaluate Context Failed, Status = %r\n", Status
));
3995 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
3997 @param Xhc The XHCI Instance.
3998 @param SlotId The slot id to be evaluated.
3999 @param MaxPacketSize The max packet size supported by the device control transfer.
4001 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.
4006 XhcEvaluateContext64 (
4007 IN USB_XHCI_INSTANCE
*Xhc
,
4009 IN UINT32 MaxPacketSize
4013 CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu
;
4014 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
4015 INPUT_CONTEXT_64
*InputContext
;
4016 EFI_PHYSICAL_ADDRESS PhyAddr
;
4018 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
4021 // 4.6.7 Evaluate Context
4023 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
4024 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
4026 InputContext
->InputControlContext
.Dword2
|= BIT1
;
4027 InputContext
->EP
[0].MaxPacketSize
= MaxPacketSize
;
4029 ZeroMem (&CmdTrbEvalu
, sizeof (CmdTrbEvalu
));
4030 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
4031 CmdTrbEvalu
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
4032 CmdTrbEvalu
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
4033 CmdTrbEvalu
.CycleBit
= 1;
4034 CmdTrbEvalu
.Type
= TRB_TYPE_EVALU_CONTXT
;
4035 CmdTrbEvalu
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
4036 DEBUG ((DEBUG_INFO
, "Evaluate context\n"));
4037 Status
= XhcCmdTransfer (
4039 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbEvalu
,
4040 XHC_GENERIC_TIMEOUT
,
4041 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
4043 if (EFI_ERROR (Status
)) {
4044 DEBUG ((DEBUG_ERROR
, "XhcEvaluateContext64: Evaluate Context Failed, Status = %r\n", Status
));
4051 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
4053 @param Xhc The XHCI Instance.
4054 @param SlotId The slot id to be configured.
4055 @param PortNum The total number of downstream port supported by the hub.
4056 @param TTT The TT think time of the hub device.
4057 @param MTT The multi-TT of the hub device.
4059 @retval EFI_SUCCESS Successfully configure the hub device's slot context.
4063 XhcConfigHubContext (
4064 IN USB_XHCI_INSTANCE
*Xhc
,
4072 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
4073 INPUT_CONTEXT
*InputContext
;
4074 DEVICE_CONTEXT
*OutputContext
;
4075 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
4076 EFI_PHYSICAL_ADDRESS PhyAddr
;
4078 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
4079 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
4080 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
4083 // 4.6.7 Evaluate Context
4085 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
4087 InputContext
->InputControlContext
.Dword2
|= BIT0
;
4090 // Copy the slot context from OutputContext to Input context
4092 CopyMem (&(InputContext
->Slot
), &(OutputContext
->Slot
), sizeof (SLOT_CONTEXT
));
4093 InputContext
->Slot
.Hub
= 1;
4094 InputContext
->Slot
.PortNum
= PortNum
;
4095 InputContext
->Slot
.TTT
= TTT
;
4096 InputContext
->Slot
.MTT
= MTT
;
4098 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
4099 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
4100 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
4101 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
4102 CmdTrbCfgEP
.CycleBit
= 1;
4103 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
4104 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
4105 DEBUG ((DEBUG_INFO
, "Configure Hub Slot Context\n"));
4106 Status
= XhcCmdTransfer (
4108 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbCfgEP
,
4109 XHC_GENERIC_TIMEOUT
,
4110 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
4112 if (EFI_ERROR (Status
)) {
4113 DEBUG ((DEBUG_ERROR
, "XhcConfigHubContext: Config Endpoint Failed, Status = %r\n", Status
));
4120 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
4122 @param Xhc The XHCI Instance.
4123 @param SlotId The slot id to be configured.
4124 @param PortNum The total number of downstream port supported by the hub.
4125 @param TTT The TT think time of the hub device.
4126 @param MTT The multi-TT of the hub device.
4128 @retval EFI_SUCCESS Successfully configure the hub device's slot context.
4132 XhcConfigHubContext64 (
4133 IN USB_XHCI_INSTANCE
*Xhc
,
4141 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
4142 INPUT_CONTEXT_64
*InputContext
;
4143 DEVICE_CONTEXT_64
*OutputContext
;
4144 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
4145 EFI_PHYSICAL_ADDRESS PhyAddr
;
4147 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
4148 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
4149 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
4152 // 4.6.7 Evaluate Context
4154 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
4156 InputContext
->InputControlContext
.Dword2
|= BIT0
;
4159 // Copy the slot context from OutputContext to Input context
4161 CopyMem (&(InputContext
->Slot
), &(OutputContext
->Slot
), sizeof (SLOT_CONTEXT_64
));
4162 InputContext
->Slot
.Hub
= 1;
4163 InputContext
->Slot
.PortNum
= PortNum
;
4164 InputContext
->Slot
.TTT
= TTT
;
4165 InputContext
->Slot
.MTT
= MTT
;
4167 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
4168 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
4169 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
4170 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
4171 CmdTrbCfgEP
.CycleBit
= 1;
4172 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
4173 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
4174 DEBUG ((DEBUG_INFO
, "Configure Hub Slot Context\n"));
4175 Status
= XhcCmdTransfer (
4177 (TRB_TEMPLATE
*)(UINTN
)&CmdTrbCfgEP
,
4178 XHC_GENERIC_TIMEOUT
,
4179 (TRB_TEMPLATE
**)(UINTN
)&EvtTrb
4181 if (EFI_ERROR (Status
)) {
4182 DEBUG ((DEBUG_ERROR
, "XhcConfigHubContext64: Config Endpoint Failed, Status = %r\n", Status
));