3 XHCI transfer scheduling routines.
5 Copyright (c) 2011 - 2018, 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 ((EFI_D_ERROR
, "XhcCmdTransfer: HC is halted\n"));
90 // Create a new URB, then poll the execution status.
92 Urb
= XhcCreateCmdTrb (Xhc
, CmdTrb
);
95 DEBUG ((EFI_D_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 ((EFI_D_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 ((EFI_D_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
->Ep
.Direction
== EfiUsbDataIn
) {
302 TrbStart
->TrbCtrSetup
.TRT
= 3;
303 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
304 TrbStart
->TrbCtrSetup
.TRT
= 2;
306 TrbStart
->TrbCtrSetup
.TRT
= 0;
309 // Update the cycle bit
311 TrbStart
->TrbCtrSetup
.CycleBit
= EPRing
->RingPCS
& BIT0
;
315 // For control transfer, create DATA_STAGE_TRB.
317 if (Urb
->DataLen
> 0) {
318 XhcSyncTrsRing (Xhc
, EPRing
);
319 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
320 TrbStart
->TrbCtrData
.TRBPtrLo
= XHC_LOW_32BIT(Urb
->DataPhy
);
321 TrbStart
->TrbCtrData
.TRBPtrHi
= XHC_HIGH_32BIT(Urb
->DataPhy
);
322 TrbStart
->TrbCtrData
.Length
= (UINT32
) Urb
->DataLen
;
323 TrbStart
->TrbCtrData
.TDSize
= 0;
324 TrbStart
->TrbCtrData
.IntTarget
= 0;
325 TrbStart
->TrbCtrData
.ISP
= 1;
326 TrbStart
->TrbCtrData
.IOC
= 1;
327 TrbStart
->TrbCtrData
.IDT
= 0;
328 TrbStart
->TrbCtrData
.CH
= 0;
329 TrbStart
->TrbCtrData
.Type
= TRB_TYPE_DATA_STAGE
;
330 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
331 TrbStart
->TrbCtrData
.DIR = 1;
332 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
333 TrbStart
->TrbCtrData
.DIR = 0;
335 TrbStart
->TrbCtrData
.DIR = 0;
338 // Update the cycle bit
340 TrbStart
->TrbCtrData
.CycleBit
= EPRing
->RingPCS
& BIT0
;
344 // For control transfer, create STATUS_STAGE_TRB.
345 // Get the pointer to next TRB for status stage use
347 XhcSyncTrsRing (Xhc
, EPRing
);
348 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
349 TrbStart
->TrbCtrStatus
.IntTarget
= 0;
350 TrbStart
->TrbCtrStatus
.IOC
= 1;
351 TrbStart
->TrbCtrStatus
.CH
= 0;
352 TrbStart
->TrbCtrStatus
.Type
= TRB_TYPE_STATUS_STAGE
;
353 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
354 TrbStart
->TrbCtrStatus
.DIR = 0;
355 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
356 TrbStart
->TrbCtrStatus
.DIR = 1;
358 TrbStart
->TrbCtrStatus
.DIR = 0;
361 // Update the cycle bit
363 TrbStart
->TrbCtrStatus
.CycleBit
= EPRing
->RingPCS
& BIT0
;
365 // Update the enqueue pointer
367 XhcSyncTrsRing (Xhc
, EPRing
);
369 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
378 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
379 while (TotalLen
< Urb
->DataLen
) {
380 if ((TotalLen
+ 0x10000) >= Urb
->DataLen
) {
381 Len
= Urb
->DataLen
- TotalLen
;
385 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
386 TrbStart
->TrbNormal
.TRBPtrLo
= XHC_LOW_32BIT((UINT8
*) Urb
->DataPhy
+ TotalLen
);
387 TrbStart
->TrbNormal
.TRBPtrHi
= XHC_HIGH_32BIT((UINT8
*) Urb
->DataPhy
+ TotalLen
);
388 TrbStart
->TrbNormal
.Length
= (UINT32
) Len
;
389 TrbStart
->TrbNormal
.TDSize
= 0;
390 TrbStart
->TrbNormal
.IntTarget
= 0;
391 TrbStart
->TrbNormal
.ISP
= 1;
392 TrbStart
->TrbNormal
.IOC
= 1;
393 TrbStart
->TrbNormal
.Type
= TRB_TYPE_NORMAL
;
395 // Update the cycle bit
397 TrbStart
->TrbNormal
.CycleBit
= EPRing
->RingPCS
& BIT0
;
399 XhcSyncTrsRing (Xhc
, EPRing
);
404 Urb
->TrbNum
= TrbNum
;
405 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
408 case ED_INTERRUPT_OUT
:
409 case ED_INTERRUPT_IN
:
413 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
414 while (TotalLen
< Urb
->DataLen
) {
415 if ((TotalLen
+ 0x10000) >= Urb
->DataLen
) {
416 Len
= Urb
->DataLen
- TotalLen
;
420 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
421 TrbStart
->TrbNormal
.TRBPtrLo
= XHC_LOW_32BIT((UINT8
*) Urb
->DataPhy
+ TotalLen
);
422 TrbStart
->TrbNormal
.TRBPtrHi
= XHC_HIGH_32BIT((UINT8
*) Urb
->DataPhy
+ TotalLen
);
423 TrbStart
->TrbNormal
.Length
= (UINT32
) Len
;
424 TrbStart
->TrbNormal
.TDSize
= 0;
425 TrbStart
->TrbNormal
.IntTarget
= 0;
426 TrbStart
->TrbNormal
.ISP
= 1;
427 TrbStart
->TrbNormal
.IOC
= 1;
428 TrbStart
->TrbNormal
.Type
= TRB_TYPE_NORMAL
;
430 // Update the cycle bit
432 TrbStart
->TrbNormal
.CycleBit
= EPRing
->RingPCS
& BIT0
;
434 XhcSyncTrsRing (Xhc
, EPRing
);
439 Urb
->TrbNum
= TrbNum
;
440 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
444 DEBUG ((EFI_D_INFO
, "Not supported EPType 0x%x!\n",EPType
));
454 Initialize the XHCI host controller for schedule.
456 @param Xhc The XHCI Instance to be initialized.
461 IN USB_XHCI_INSTANCE
*Xhc
465 EFI_PHYSICAL_ADDRESS DcbaaPhy
;
467 EFI_PHYSICAL_ADDRESS CmdRingPhy
;
469 UINT32 MaxScratchpadBufs
;
471 EFI_PHYSICAL_ADDRESS ScratchPhy
;
472 UINT64
*ScratchEntry
;
473 EFI_PHYSICAL_ADDRESS ScratchEntryPhy
;
475 UINTN
*ScratchEntryMap
;
479 // Initialize memory management.
481 Xhc
->MemPool
= UsbHcInitMemPool (Xhc
->PciIo
);
482 ASSERT (Xhc
->MemPool
!= NULL
);
485 // Program the Max Device Slots Enabled (MaxSlotsEn) field in the CONFIG register (5.4.7)
486 // to enable the device slots that system software is going to use.
488 Xhc
->MaxSlotsEn
= Xhc
->HcSParams1
.Data
.MaxSlots
;
489 ASSERT (Xhc
->MaxSlotsEn
>= 1 && Xhc
->MaxSlotsEn
<= 255);
490 XhcWriteOpReg (Xhc
, XHC_CONFIG_OFFSET
, Xhc
->MaxSlotsEn
);
493 // The Device Context Base Address Array entry associated with each allocated Device Slot
494 // shall contain a 64-bit pointer to the base of the associated Device Context.
495 // The Device Context Base Address Array shall contain MaxSlotsEn + 1 entries.
496 // Software shall set Device Context Base Address Array entries for unallocated Device Slots to '0'.
498 Entries
= (Xhc
->MaxSlotsEn
+ 1) * sizeof(UINT64
);
499 Dcbaa
= UsbHcAllocateMem (Xhc
->MemPool
, Entries
);
500 ASSERT (Dcbaa
!= NULL
);
501 ZeroMem (Dcbaa
, Entries
);
504 // A Scratchpad Buffer is a PAGESIZE block of system memory located on a PAGESIZE boundary.
505 // System software shall allocate the Scratchpad Buffer(s) before placing the xHC in to Run
506 // mode (Run/Stop(R/S) ='1').
508 MaxScratchpadBufs
= ((Xhc
->HcSParams2
.Data
.ScratchBufHi
) << 5) | (Xhc
->HcSParams2
.Data
.ScratchBufLo
);
509 Xhc
->MaxScratchpadBufs
= MaxScratchpadBufs
;
510 ASSERT (MaxScratchpadBufs
<= 1023);
511 if (MaxScratchpadBufs
!= 0) {
513 // Allocate the buffer to record the Mapping for each scratch buffer in order to Unmap them
515 ScratchEntryMap
= AllocateZeroPool (sizeof (UINTN
) * MaxScratchpadBufs
);
516 ASSERT (ScratchEntryMap
!= NULL
);
517 Xhc
->ScratchEntryMap
= ScratchEntryMap
;
520 // Allocate the buffer to record the host address for each entry
522 ScratchEntry
= AllocateZeroPool (sizeof (UINT64
) * MaxScratchpadBufs
);
523 ASSERT (ScratchEntry
!= NULL
);
524 Xhc
->ScratchEntry
= ScratchEntry
;
527 Status
= UsbHcAllocateAlignedPages (
529 EFI_SIZE_TO_PAGES (MaxScratchpadBufs
* sizeof (UINT64
)),
531 (VOID
**) &ScratchBuf
,
535 ASSERT_EFI_ERROR (Status
);
537 ZeroMem (ScratchBuf
, MaxScratchpadBufs
* sizeof (UINT64
));
538 Xhc
->ScratchBuf
= ScratchBuf
;
541 // Allocate each scratch buffer
543 for (Index
= 0; Index
< MaxScratchpadBufs
; Index
++) {
545 Status
= UsbHcAllocateAlignedPages (
547 EFI_SIZE_TO_PAGES (Xhc
->PageSize
),
549 (VOID
**) &ScratchEntry
[Index
],
551 (VOID
**) &ScratchEntryMap
[Index
]
553 ASSERT_EFI_ERROR (Status
);
554 ZeroMem ((VOID
*)(UINTN
)ScratchEntry
[Index
], Xhc
->PageSize
);
556 // Fill with the PCI device address
558 *ScratchBuf
++ = ScratchEntryPhy
;
561 // The Scratchpad Buffer Array contains pointers to the Scratchpad Buffers. Entry 0 of the
562 // Device Context Base Address Array points to the Scratchpad Buffer Array.
564 *(UINT64
*)Dcbaa
= (UINT64
)(UINTN
) ScratchPhy
;
568 // Program the Device Context Base Address Array Pointer (DCBAAP) register (5.4.6) with
569 // a 64-bit address pointing to where the Device Context Base Address Array is located.
571 Xhc
->DCBAA
= (UINT64
*)(UINTN
)Dcbaa
;
573 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
574 // So divide it to two 32-bytes width register access.
576 DcbaaPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Dcbaa
, Entries
);
577 XhcWriteOpReg (Xhc
, XHC_DCBAAP_OFFSET
, XHC_LOW_32BIT(DcbaaPhy
));
578 XhcWriteOpReg (Xhc
, XHC_DCBAAP_OFFSET
+ 4, XHC_HIGH_32BIT (DcbaaPhy
));
580 DEBUG ((EFI_D_INFO
, "XhcInitSched:DCBAA=0x%x\n", (UINT64
)(UINTN
)Xhc
->DCBAA
));
583 // Define the Command Ring Dequeue Pointer by programming the Command Ring Control Register
584 // (5.4.5) with a 64-bit address pointing to the starting address of the first TRB of the Command Ring.
585 // Note: The Command Ring is 64 byte aligned, so the low order 6 bits of the Command Ring Pointer shall
588 CreateTransferRing (Xhc
, CMD_RING_TRB_NUMBER
, &Xhc
->CmdRing
);
590 // The xHC uses the Enqueue Pointer to determine when a Transfer Ring is empty. As it fetches TRBs from a
591 // Transfer Ring it checks for a Cycle bit transition. If a transition detected, the ring is empty.
592 // So we set RCS as inverted PCS init value to let Command Ring empty
594 CmdRing
= (UINT64
)(UINTN
)Xhc
->CmdRing
.RingSeg0
;
595 CmdRingPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, (VOID
*)(UINTN
) CmdRing
, sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
);
596 ASSERT ((CmdRingPhy
& 0x3F) == 0);
597 CmdRingPhy
|= XHC_CRCR_RCS
;
599 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
600 // So divide it to two 32-bytes width register access.
602 XhcWriteOpReg (Xhc
, XHC_CRCR_OFFSET
, XHC_LOW_32BIT(CmdRingPhy
));
603 XhcWriteOpReg (Xhc
, XHC_CRCR_OFFSET
+ 4, XHC_HIGH_32BIT (CmdRingPhy
));
606 // Disable the 'interrupter enable' bit in USB_CMD
607 // and clear IE & IP bit in all Interrupter X Management Registers.
609 XhcClearOpRegBit (Xhc
, XHC_USBCMD_OFFSET
, XHC_USBCMD_INTE
);
610 for (Index
= 0; Index
< (UINT16
)(Xhc
->HcSParams1
.Data
.MaxIntrs
); Index
++) {
611 XhcClearRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
+ (Index
* 32), XHC_IMAN_IE
);
612 XhcSetRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
+ (Index
* 32), XHC_IMAN_IP
);
616 // Allocate EventRing for Cmd, Ctrl, Bulk, Interrupt, AsynInterrupt transfer
618 CreateEventRing (Xhc
, &Xhc
->EventRing
);
619 DEBUG ((DEBUG_INFO
, "XhcInitSched: Created CMD ring [%p~%p) EVENT ring [%p~%p)\n",
620 Xhc
->CmdRing
.RingSeg0
, (UINTN
)Xhc
->CmdRing
.RingSeg0
+ sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
,
621 Xhc
->EventRing
.EventRingSeg0
, (UINTN
)Xhc
->EventRing
.EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
626 System software shall use a Reset Endpoint Command (section 4.11.4.7) to remove the Halted
627 condition in the xHC. After the successful completion of the Reset Endpoint Command, the Endpoint
628 Context is transitioned from the Halted to the Stopped state and the Transfer Ring of the endpoint is
629 reenabled. The next write to the Doorbell of the Endpoint will transition the Endpoint Context from the
630 Stopped to the Running state.
632 @param Xhc The XHCI Instance.
633 @param Urb The urb which makes the endpoint halted.
635 @retval EFI_SUCCESS The recovery is successful.
636 @retval Others Failed to recovery halted endpoint.
641 XhcRecoverHaltedEndpoint (
642 IN USB_XHCI_INSTANCE
*Xhc
,
650 Status
= EFI_SUCCESS
;
651 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
653 return EFI_DEVICE_ERROR
;
655 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
658 DEBUG ((EFI_D_INFO
, "Recovery Halted Slot = %x,Dci = %x\n", SlotId
, Dci
));
661 // 1) Send Reset endpoint command to transit from halt to stop state
663 Status
= XhcResetEndpoint(Xhc
, SlotId
, Dci
);
664 if (EFI_ERROR(Status
)) {
665 DEBUG ((EFI_D_ERROR
, "XhcRecoverHaltedEndpoint: Reset Endpoint Failed, Status = %r\n", Status
));
670 // 2)Set dequeue pointer
672 Status
= XhcSetTrDequeuePointer(Xhc
, SlotId
, Dci
, Urb
);
673 if (EFI_ERROR(Status
)) {
674 DEBUG ((EFI_D_ERROR
, "XhcRecoverHaltedEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status
));
679 // 3)Ring the doorbell to transit from stop to active
681 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
688 System software shall use a Stop Endpoint Command (section 4.6.9) and the Set TR Dequeue Pointer
689 Command (section 4.6.10) to remove the timed-out TDs from the xHC transfer ring. The next write to
690 the Doorbell of the Endpoint will transition the Endpoint Context from the Stopped to the Running
693 @param Xhc The XHCI Instance.
694 @param Urb The urb which doesn't get completed in a specified timeout range.
696 @retval EFI_SUCCESS The dequeuing of the TDs is successful.
697 @retval EFI_ALREADY_STARTED The Urb is finished so no deque is needed.
698 @retval Others Failed to stop the endpoint and dequeue the TDs.
703 XhcDequeueTrbFromEndpoint (
704 IN USB_XHCI_INSTANCE
*Xhc
,
712 Status
= EFI_SUCCESS
;
713 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
715 return EFI_DEVICE_ERROR
;
717 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
720 DEBUG ((EFI_D_INFO
, "Stop Slot = %x,Dci = %x\n", SlotId
, Dci
));
723 // 1) Send Stop endpoint command to stop xHC from executing of the TDs on the endpoint
725 Status
= XhcStopEndpoint(Xhc
, SlotId
, Dci
, Urb
);
726 if (EFI_ERROR(Status
)) {
727 DEBUG ((EFI_D_ERROR
, "XhcDequeueTrbFromEndpoint: Stop Endpoint Failed, Status = %r\n", Status
));
732 // 2)Set dequeue pointer
734 if (Urb
->Finished
&& Urb
->Result
== EFI_USB_NOERROR
) {
736 // Return Already Started to indicate the pending URB is finished.
737 // This fixes BULK data loss when transfer is detected as timeout
738 // but finished just before stopping endpoint.
740 Status
= EFI_ALREADY_STARTED
;
741 DEBUG ((DEBUG_INFO
, "XhcDequeueTrbFromEndpoint: Pending URB is finished: Length Actual/Expect = %d/%d!\n", Urb
->Completed
, Urb
->DataLen
));
743 Status
= XhcSetTrDequeuePointer(Xhc
, SlotId
, Dci
, Urb
);
744 if (EFI_ERROR (Status
)) {
745 DEBUG ((DEBUG_ERROR
, "XhcDequeueTrbFromEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status
));
751 // 3)Ring the doorbell to transit from stop to active
753 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
760 Create XHCI event ring.
762 @param Xhc The XHCI Instance.
763 @param EventRing The created event ring.
768 IN USB_XHCI_INSTANCE
*Xhc
,
769 OUT EVENT_RING
*EventRing
773 EVENT_RING_SEG_TABLE_ENTRY
*ERSTBase
;
775 EFI_PHYSICAL_ADDRESS ERSTPhy
;
776 EFI_PHYSICAL_ADDRESS DequeuePhy
;
778 ASSERT (EventRing
!= NULL
);
780 Size
= sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
;
781 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, Size
);
782 ASSERT (Buf
!= NULL
);
783 ASSERT (((UINTN
) Buf
& 0x3F) == 0);
786 EventRing
->EventRingSeg0
= Buf
;
787 EventRing
->TrbNumber
= EVENT_RING_TRB_NUMBER
;
788 EventRing
->EventRingDequeue
= (TRB_TEMPLATE
*) EventRing
->EventRingSeg0
;
789 EventRing
->EventRingEnqueue
= (TRB_TEMPLATE
*) EventRing
->EventRingSeg0
;
791 DequeuePhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Buf
, Size
);
794 // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1'
795 // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring.
797 EventRing
->EventRingCCS
= 1;
799 Size
= sizeof (EVENT_RING_SEG_TABLE_ENTRY
) * ERST_NUMBER
;
800 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, Size
);
801 ASSERT (Buf
!= NULL
);
802 ASSERT (((UINTN
) Buf
& 0x3F) == 0);
805 ERSTBase
= (EVENT_RING_SEG_TABLE_ENTRY
*) Buf
;
806 EventRing
->ERSTBase
= ERSTBase
;
807 ERSTBase
->PtrLo
= XHC_LOW_32BIT (DequeuePhy
);
808 ERSTBase
->PtrHi
= XHC_HIGH_32BIT (DequeuePhy
);
809 ERSTBase
->RingTrbSize
= EVENT_RING_TRB_NUMBER
;
811 ERSTPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, ERSTBase
, Size
);
814 // Program the Interrupter Event Ring Segment Table Size (ERSTSZ) register (5.5.2.3.1)
822 // Program the Interrupter Event Ring Dequeue Pointer (ERDP) register (5.5.2.3.3)
824 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
825 // So divide it to two 32-bytes width register access.
830 XHC_LOW_32BIT((UINT64
)(UINTN
)DequeuePhy
)
835 XHC_HIGH_32BIT((UINT64
)(UINTN
)DequeuePhy
)
838 // Program the Interrupter Event Ring Segment Table Base Address (ERSTBA) register(5.5.2.3.2)
840 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
841 // So divide it to two 32-bytes width register access.
846 XHC_LOW_32BIT((UINT64
)(UINTN
)ERSTPhy
)
850 XHC_ERSTBA_OFFSET
+ 4,
851 XHC_HIGH_32BIT((UINT64
)(UINTN
)ERSTPhy
)
854 // Need set IMAN IE bit to enble the ring interrupt
856 XhcSetRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
, XHC_IMAN_IE
);
860 Create XHCI transfer ring.
862 @param Xhc The XHCI Instance.
863 @param TrbNum The number of TRB in the ring.
864 @param TransferRing The created transfer ring.
869 IN USB_XHCI_INSTANCE
*Xhc
,
871 OUT TRANSFER_RING
*TransferRing
876 EFI_PHYSICAL_ADDRESS PhyAddr
;
878 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (TRB_TEMPLATE
) * TrbNum
);
879 ASSERT (Buf
!= NULL
);
880 ASSERT (((UINTN
) Buf
& 0x3F) == 0);
881 ZeroMem (Buf
, sizeof (TRB_TEMPLATE
) * TrbNum
);
883 TransferRing
->RingSeg0
= Buf
;
884 TransferRing
->TrbNumber
= TrbNum
;
885 TransferRing
->RingEnqueue
= (TRB_TEMPLATE
*) TransferRing
->RingSeg0
;
886 TransferRing
->RingDequeue
= (TRB_TEMPLATE
*) TransferRing
->RingSeg0
;
887 TransferRing
->RingPCS
= 1;
889 // 4.9.2 Transfer Ring Management
890 // To form a ring (or circular queue) a Link TRB may be inserted at the end of a ring to
891 // point to the first TRB in the ring.
893 EndTrb
= (LINK_TRB
*) ((UINTN
)Buf
+ sizeof (TRB_TEMPLATE
) * (TrbNum
- 1));
894 EndTrb
->Type
= TRB_TYPE_LINK
;
895 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Buf
, sizeof (TRB_TEMPLATE
) * TrbNum
);
896 EndTrb
->PtrLo
= XHC_LOW_32BIT (PhyAddr
);
897 EndTrb
->PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
899 // Toggle Cycle (TC). When set to '1', the xHC shall toggle its interpretation of the Cycle bit.
903 // Set Cycle bit as other TRB PCS init value
905 EndTrb
->CycleBit
= 0;
909 Free XHCI event ring.
911 @param Xhc The XHCI Instance.
912 @param EventRing The event ring to be freed.
918 IN USB_XHCI_INSTANCE
*Xhc
,
919 IN EVENT_RING
*EventRing
922 if(EventRing
->EventRingSeg0
== NULL
) {
927 // Free EventRing Segment 0
929 UsbHcFreeMem (Xhc
->MemPool
, EventRing
->EventRingSeg0
, sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
);
934 UsbHcFreeMem (Xhc
->MemPool
, EventRing
->ERSTBase
, sizeof (EVENT_RING_SEG_TABLE_ENTRY
) * ERST_NUMBER
);
939 Free the resouce allocated at initializing schedule.
941 @param Xhc The XHCI Instance.
946 IN USB_XHCI_INSTANCE
*Xhc
950 UINT64
*ScratchEntry
;
952 if (Xhc
->ScratchBuf
!= NULL
) {
953 ScratchEntry
= Xhc
->ScratchEntry
;
954 for (Index
= 0; Index
< Xhc
->MaxScratchpadBufs
; Index
++) {
956 // Free Scratchpad Buffers
958 UsbHcFreeAlignedPages (Xhc
->PciIo
, (VOID
*)(UINTN
)ScratchEntry
[Index
], EFI_SIZE_TO_PAGES (Xhc
->PageSize
), (VOID
*) Xhc
->ScratchEntryMap
[Index
]);
961 // Free Scratchpad Buffer Array
963 UsbHcFreeAlignedPages (Xhc
->PciIo
, Xhc
->ScratchBuf
, EFI_SIZE_TO_PAGES (Xhc
->MaxScratchpadBufs
* sizeof (UINT64
)), Xhc
->ScratchMap
);
964 FreePool (Xhc
->ScratchEntryMap
);
965 FreePool (Xhc
->ScratchEntry
);
968 if (Xhc
->CmdRing
.RingSeg0
!= NULL
) {
969 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->CmdRing
.RingSeg0
, sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
);
970 Xhc
->CmdRing
.RingSeg0
= NULL
;
973 XhcFreeEventRing (Xhc
,&Xhc
->EventRing
);
975 if (Xhc
->DCBAA
!= NULL
) {
976 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->DCBAA
, (Xhc
->MaxSlotsEn
+ 1) * sizeof(UINT64
));
981 // Free memory pool at last
983 if (Xhc
->MemPool
!= NULL
) {
984 UsbHcFreeMemPool (Xhc
->MemPool
);
990 Check if the Trb is a transaction of the URB.
992 @param Xhc The XHCI Instance.
993 @param Trb The TRB to be checked
994 @param Urb The URB to be checked.
996 @retval TRUE It is a transaction of the URB.
997 @retval FALSE It is not any transaction of the URB.
1002 IN USB_XHCI_INSTANCE
*Xhc
,
1003 IN TRB_TEMPLATE
*Trb
,
1008 TRB_TEMPLATE
*CheckedTrb
;
1010 EFI_PHYSICAL_ADDRESS PhyAddr
;
1012 CheckedTrb
= Urb
->TrbStart
;
1013 for (Index
= 0; Index
< Urb
->TrbNum
; Index
++) {
1014 if (Trb
== CheckedTrb
) {
1019 // If the checked TRB is the link TRB at the end of the transfer ring,
1020 // recircle it to the head of the ring.
1022 if (CheckedTrb
->Type
== TRB_TYPE_LINK
) {
1023 LinkTrb
= (LINK_TRB
*) CheckedTrb
;
1024 PhyAddr
= (EFI_PHYSICAL_ADDRESS
)(LinkTrb
->PtrLo
| LShiftU64 ((UINT64
) LinkTrb
->PtrHi
, 32));
1025 CheckedTrb
= (TRB_TEMPLATE
*)(UINTN
) UsbHcGetHostAddrForPciAddr (Xhc
->MemPool
, (VOID
*)(UINTN
) PhyAddr
, sizeof (TRB_TEMPLATE
));
1026 ASSERT (CheckedTrb
== Urb
->Ring
->RingSeg0
);
1034 Check if the Trb is a transaction of the URBs in XHCI's asynchronous transfer list.
1036 @param Xhc The XHCI Instance.
1037 @param Trb The TRB to be checked.
1038 @param Urb The pointer to the matched Urb.
1040 @retval TRUE The Trb is matched with a transaction of the URBs in the async list.
1041 @retval FALSE The Trb is not matched with any URBs in the async list.
1046 IN USB_XHCI_INSTANCE
*Xhc
,
1047 IN TRB_TEMPLATE
*Trb
,
1055 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1056 CheckedUrb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1057 if (IsTransferRingTrb (Xhc
, Trb
, CheckedUrb
)) {
1068 Check the URB's execution result and update the URB's
1071 @param Xhc The XHCI Instance.
1072 @param Urb The URB to check result.
1074 @return Whether the result of URB transfer is finialized.
1079 IN USB_XHCI_INSTANCE
*Xhc
,
1083 EVT_TRB_TRANSFER
*EvtTrb
;
1084 TRB_TEMPLATE
*TRBPtr
;
1093 EFI_PHYSICAL_ADDRESS PhyAddr
;
1095 ASSERT ((Xhc
!= NULL
) && (Urb
!= NULL
));
1097 Status
= EFI_SUCCESS
;
1100 if (Urb
->Finished
) {
1106 if (XhcIsHalt (Xhc
) || XhcIsSysError (Xhc
)) {
1107 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
1112 // Traverse the event ring to find out all new events from the previous check.
1114 XhcSyncEventRing (Xhc
, &Xhc
->EventRing
);
1115 for (Index
= 0; Index
< Xhc
->EventRing
.TrbNumber
; Index
++) {
1116 Status
= XhcCheckNewEvent (Xhc
, &Xhc
->EventRing
, ((TRB_TEMPLATE
**)&EvtTrb
));
1117 if (Status
== EFI_NOT_READY
) {
1119 // All new events are handled, return directly.
1125 // Only handle COMMAND_COMPLETETION_EVENT and TRANSFER_EVENT.
1127 if ((EvtTrb
->Type
!= TRB_TYPE_COMMAND_COMPLT_EVENT
) && (EvtTrb
->Type
!= TRB_TYPE_TRANS_EVENT
)) {
1132 // Need convert pci device address to host address
1134 PhyAddr
= (EFI_PHYSICAL_ADDRESS
)(EvtTrb
->TRBPtrLo
| LShiftU64 ((UINT64
) EvtTrb
->TRBPtrHi
, 32));
1135 TRBPtr
= (TRB_TEMPLATE
*)(UINTN
) UsbHcGetHostAddrForPciAddr (Xhc
->MemPool
, (VOID
*)(UINTN
) PhyAddr
, sizeof (TRB_TEMPLATE
));
1138 // Update the status of URB including the pending URB, the URB that is currently checked,
1139 // and URBs in the XHCI's async interrupt transfer list.
1140 // This way is used to avoid that those completed async transfer events don't get
1141 // handled in time and are flushed by newer coming events.
1143 if (Xhc
->PendingUrb
!= NULL
&& IsTransferRingTrb (Xhc
, TRBPtr
, Xhc
->PendingUrb
)) {
1144 CheckedUrb
= Xhc
->PendingUrb
;
1145 } else if (IsTransferRingTrb (Xhc
, TRBPtr
, Urb
)) {
1147 } else if (IsAsyncIntTrb (Xhc
, TRBPtr
, &AsyncUrb
)) {
1148 CheckedUrb
= AsyncUrb
;
1153 switch (EvtTrb
->Completecode
) {
1154 case TRB_COMPLETION_STALL_ERROR
:
1155 CheckedUrb
->Result
|= EFI_USB_ERR_STALL
;
1156 CheckedUrb
->Finished
= TRUE
;
1157 DEBUG ((EFI_D_ERROR
, "XhcCheckUrbResult: STALL_ERROR! Completecode = %x\n",EvtTrb
->Completecode
));
1160 case TRB_COMPLETION_BABBLE_ERROR
:
1161 CheckedUrb
->Result
|= EFI_USB_ERR_BABBLE
;
1162 CheckedUrb
->Finished
= TRUE
;
1163 DEBUG ((EFI_D_ERROR
, "XhcCheckUrbResult: BABBLE_ERROR! Completecode = %x\n",EvtTrb
->Completecode
));
1166 case TRB_COMPLETION_DATA_BUFFER_ERROR
:
1167 CheckedUrb
->Result
|= EFI_USB_ERR_BUFFER
;
1168 CheckedUrb
->Finished
= TRUE
;
1169 DEBUG ((EFI_D_ERROR
, "XhcCheckUrbResult: ERR_BUFFER! Completecode = %x\n",EvtTrb
->Completecode
));
1172 case TRB_COMPLETION_USB_TRANSACTION_ERROR
:
1173 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1174 CheckedUrb
->Finished
= TRUE
;
1175 DEBUG ((EFI_D_ERROR
, "XhcCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n",EvtTrb
->Completecode
));
1178 case TRB_COMPLETION_STOPPED
:
1179 case TRB_COMPLETION_STOPPED_LENGTH_INVALID
:
1180 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1181 CheckedUrb
->Finished
= TRUE
;
1183 // The pending URB is timeout and force stopped when stopping endpoint.
1184 // Continue the loop to receive the Command Complete Event for stopping endpoint.
1188 case TRB_COMPLETION_SHORT_PACKET
:
1189 case TRB_COMPLETION_SUCCESS
:
1190 if (EvtTrb
->Completecode
== TRB_COMPLETION_SHORT_PACKET
) {
1191 DEBUG ((EFI_D_VERBOSE
, "XhcCheckUrbResult: short packet happens!\n"));
1194 TRBType
= (UINT8
) (TRBPtr
->Type
);
1195 if ((TRBType
== TRB_TYPE_DATA_STAGE
) ||
1196 (TRBType
== TRB_TYPE_NORMAL
) ||
1197 (TRBType
== TRB_TYPE_ISOCH
)) {
1198 CheckedUrb
->Completed
+= (((TRANSFER_TRB_NORMAL
*)TRBPtr
)->Length
- EvtTrb
->Length
);
1204 DEBUG ((EFI_D_ERROR
, "Transfer Default Error Occur! Completecode = 0x%x!\n",EvtTrb
->Completecode
));
1205 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1206 CheckedUrb
->Finished
= TRUE
;
1211 // Only check first and end Trb event address
1213 if (TRBPtr
== CheckedUrb
->TrbStart
) {
1214 CheckedUrb
->StartDone
= TRUE
;
1217 if (TRBPtr
== CheckedUrb
->TrbEnd
) {
1218 CheckedUrb
->EndDone
= TRUE
;
1221 if (CheckedUrb
->StartDone
&& CheckedUrb
->EndDone
) {
1222 CheckedUrb
->Finished
= TRUE
;
1223 CheckedUrb
->EvtTrb
= (TRB_TEMPLATE
*)EvtTrb
;
1230 // Advance event ring to last available entry
1232 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
1233 // So divide it to two 32-bytes width register access.
1235 Low
= XhcReadRuntimeReg (Xhc
, XHC_ERDP_OFFSET
);
1236 High
= XhcReadRuntimeReg (Xhc
, XHC_ERDP_OFFSET
+ 4);
1237 XhcDequeue
= (UINT64
)(LShiftU64((UINT64
)High
, 32) | Low
);
1239 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->EventRing
.EventRingDequeue
, sizeof (TRB_TEMPLATE
));
1241 if ((XhcDequeue
& (~0x0F)) != (PhyAddr
& (~0x0F))) {
1243 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
1244 // So divide it to two 32-bytes width register access.
1246 XhcWriteRuntimeReg (Xhc
, XHC_ERDP_OFFSET
, XHC_LOW_32BIT (PhyAddr
) | BIT3
);
1247 XhcWriteRuntimeReg (Xhc
, XHC_ERDP_OFFSET
+ 4, XHC_HIGH_32BIT (PhyAddr
));
1250 return Urb
->Finished
;
1255 Execute the transfer by polling the URB. This is a synchronous operation.
1257 @param Xhc The XHCI Instance.
1258 @param CmdTransfer The executed URB is for cmd transfer or not.
1259 @param Urb The URB to execute.
1260 @param Timeout The time to wait before abort, in millisecond.
1262 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.
1263 @return EFI_TIMEOUT The transfer failed due to time out.
1264 @return EFI_SUCCESS The transfer finished OK.
1265 @retval EFI_OUT_OF_RESOURCES Memory for the timer event could not be allocated.
1270 IN USB_XHCI_INSTANCE
*Xhc
,
1271 IN BOOLEAN CmdTransfer
,
1280 EFI_EVENT TimeoutEvent
;
1281 BOOLEAN IndefiniteTimeout
;
1283 Status
= EFI_SUCCESS
;
1285 TimeoutEvent
= NULL
;
1286 IndefiniteTimeout
= FALSE
;
1292 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
1294 return EFI_DEVICE_ERROR
;
1296 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
1301 IndefiniteTimeout
= TRUE
;
1305 Status
= gBS
->CreateEvent (
1313 if (EFI_ERROR (Status
)) {
1317 Status
= gBS
->SetTimer (TimeoutEvent
,
1319 EFI_TIMER_PERIOD_MILLISECONDS(Timeout
));
1321 if (EFI_ERROR (Status
)) {
1326 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
1329 Finished
= XhcCheckUrbResult (Xhc
, Urb
);
1333 gBS
->Stall (XHC_1_MICROSECOND
);
1334 } while (IndefiniteTimeout
|| EFI_ERROR(gBS
->CheckEvent (TimeoutEvent
)));
1337 if (EFI_ERROR(Status
)) {
1338 Urb
->Result
= EFI_USB_ERR_NOTEXECUTE
;
1339 } else if (!Finished
) {
1340 Urb
->Result
= EFI_USB_ERR_TIMEOUT
;
1341 Status
= EFI_TIMEOUT
;
1342 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
1343 Status
= EFI_DEVICE_ERROR
;
1346 if (TimeoutEvent
!= NULL
) {
1347 gBS
->CloseEvent (TimeoutEvent
);
1354 Delete a single asynchronous interrupt transfer for
1355 the device and endpoint.
1357 @param Xhc The XHCI Instance.
1358 @param BusAddr The logical device address assigned by UsbBus driver.
1359 @param EpNum The endpoint of the target.
1361 @retval EFI_SUCCESS An asynchronous transfer is removed.
1362 @retval EFI_NOT_FOUND No transfer for the device is found.
1366 XhciDelAsyncIntTransfer (
1367 IN USB_XHCI_INSTANCE
*Xhc
,
1375 EFI_USB_DATA_DIRECTION Direction
;
1378 Direction
= ((EpNum
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
;
1383 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1384 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1385 if ((Urb
->Ep
.BusAddr
== BusAddr
) &&
1386 (Urb
->Ep
.EpAddr
== EpNum
) &&
1387 (Urb
->Ep
.Direction
== Direction
)) {
1389 // Device doesn't finish the IntTransfer until real data comes
1390 // So the TRB should be removed as well.
1392 Status
= XhcDequeueTrbFromEndpoint (Xhc
, Urb
);
1393 if (EFI_ERROR (Status
)) {
1394 DEBUG ((EFI_D_ERROR
, "XhciDelAsyncIntTransfer: XhcDequeueTrbFromEndpoint failed\n"));
1397 RemoveEntryList (&Urb
->UrbList
);
1398 FreePool (Urb
->Data
);
1399 XhcFreeUrb (Xhc
, Urb
);
1404 return EFI_NOT_FOUND
;
1408 Remove all the asynchronous interrutp transfers.
1410 @param Xhc The XHCI Instance.
1414 XhciDelAllAsyncIntTransfers (
1415 IN USB_XHCI_INSTANCE
*Xhc
1423 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1424 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1427 // Device doesn't finish the IntTransfer until real data comes
1428 // So the TRB should be removed as well.
1430 Status
= XhcDequeueTrbFromEndpoint (Xhc
, Urb
);
1431 if (EFI_ERROR (Status
)) {
1432 DEBUG ((EFI_D_ERROR
, "XhciDelAllAsyncIntTransfers: XhcDequeueTrbFromEndpoint failed\n"));
1435 RemoveEntryList (&Urb
->UrbList
);
1436 FreePool (Urb
->Data
);
1437 XhcFreeUrb (Xhc
, Urb
);
1442 Insert a single asynchronous interrupt transfer for
1443 the device and endpoint.
1445 @param Xhc The XHCI Instance
1446 @param BusAddr The logical device address assigned by UsbBus driver
1447 @param EpAddr Endpoint addrress
1448 @param DevSpeed The device speed
1449 @param MaxPacket The max packet length of the endpoint
1450 @param DataLen The length of data buffer
1451 @param Callback The function to call when data is transferred
1452 @param Context The context to the callback
1454 @return Created URB or NULL
1458 XhciInsertAsyncIntTransfer (
1459 IN USB_XHCI_INSTANCE
*Xhc
,
1465 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback
,
1472 Data
= AllocateZeroPool (DataLen
);
1474 DEBUG ((DEBUG_ERROR
, "%a: failed to allocate buffer\n", __FUNCTION__
));
1478 Urb
= XhcCreateUrb (
1484 XHC_INT_TRANSFER_ASYNC
,
1492 DEBUG ((DEBUG_ERROR
, "%a: failed to create URB\n", __FUNCTION__
));
1498 // New asynchronous transfer must inserted to the head.
1499 // Check the comments in XhcMoniteAsyncRequests
1501 InsertHeadList (&Xhc
->AsyncIntTransfers
, &Urb
->UrbList
);
1507 Update the queue head for next round of asynchronous transfer
1509 @param Xhc The XHCI Instance.
1510 @param Urb The URB to update
1514 XhcUpdateAsyncRequest (
1515 IN USB_XHCI_INSTANCE
*Xhc
,
1521 if (Urb
->Result
== EFI_USB_NOERROR
) {
1522 Status
= XhcCreateTransferTrb (Xhc
, Urb
);
1523 if (EFI_ERROR (Status
)) {
1526 Status
= RingIntTransferDoorBell (Xhc
, Urb
);
1527 if (EFI_ERROR (Status
)) {
1534 Flush data from PCI controller specific address to mapped system
1537 @param Xhc The XHCI device.
1538 @param Urb The URB to unmap.
1540 @retval EFI_SUCCESS Success to flush data to mapped system memory.
1541 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
1545 XhcFlushAsyncIntMap (
1546 IN USB_XHCI_INSTANCE
*Xhc
,
1551 EFI_PHYSICAL_ADDRESS PhyAddr
;
1552 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
1553 EFI_PCI_IO_PROTOCOL
*PciIo
;
1560 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
1561 MapOp
= EfiPciIoOperationBusMasterWrite
;
1563 MapOp
= EfiPciIoOperationBusMasterRead
;
1566 if (Urb
->DataMap
!= NULL
) {
1567 Status
= PciIo
->Unmap (PciIo
, Urb
->DataMap
);
1568 if (EFI_ERROR (Status
)) {
1573 Urb
->DataMap
= NULL
;
1575 Status
= PciIo
->Map (PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
1576 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
1580 Urb
->DataPhy
= (VOID
*) ((UINTN
) PhyAddr
);
1585 return EFI_DEVICE_ERROR
;
1589 Interrupt transfer periodic check handler.
1591 @param Event Interrupt event.
1592 @param Context Pointer to USB_XHCI_INSTANCE.
1597 XhcMonitorAsyncRequests (
1602 USB_XHCI_INSTANCE
*Xhc
;
1611 OldTpl
= gBS
->RaiseTPL (XHC_TPL
);
1613 Xhc
= (USB_XHCI_INSTANCE
*) Context
;
1615 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1616 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1619 // Make sure that the device is available before every check.
1621 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
1627 // Check the result of URB execution. If it is still
1628 // active, check the next one.
1630 XhcCheckUrbResult (Xhc
, Urb
);
1632 if (!Urb
->Finished
) {
1637 // Flush any PCI posted write transactions from a PCI host
1638 // bridge to system memory.
1640 Status
= XhcFlushAsyncIntMap (Xhc
, Urb
);
1641 if (EFI_ERROR (Status
)) {
1642 DEBUG ((EFI_D_ERROR
, "XhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
1646 // Allocate a buffer then copy the transferred data for user.
1647 // If failed to allocate the buffer, update the URB for next
1648 // round of transfer. Ignore the data of this round.
1651 if (Urb
->Result
== EFI_USB_NOERROR
) {
1653 // Make sure the data received from HW is no more than expected.
1655 if (Urb
->Completed
<= Urb
->DataLen
) {
1656 ProcBuf
= AllocateZeroPool (Urb
->Completed
);
1659 if (ProcBuf
== NULL
) {
1660 XhcUpdateAsyncRequest (Xhc
, Urb
);
1664 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
1668 // Leave error recovery to its related device driver. A
1669 // common case of the error recovery is to re-submit the
1670 // interrupt transfer which is linked to the head of the
1671 // list. This function scans from head to tail. So the
1672 // re-submitted interrupt transfer's callback function
1673 // will not be called again in this round. Don't touch this
1674 // URB after the callback, it may have been removed by the
1677 if (Urb
->Callback
!= NULL
) {
1679 // Restore the old TPL, USB bus maybe connect device in
1680 // his callback. Some drivers may has a lower TPL restriction.
1682 gBS
->RestoreTPL (OldTpl
);
1683 (Urb
->Callback
) (ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
1684 OldTpl
= gBS
->RaiseTPL (XHC_TPL
);
1687 if (ProcBuf
!= NULL
) {
1688 gBS
->FreePool (ProcBuf
);
1691 XhcUpdateAsyncRequest (Xhc
, Urb
);
1693 gBS
->RestoreTPL (OldTpl
);
1697 Monitor the port status change. Enable/Disable device slot if there is a device attached/detached.
1699 @param Xhc The XHCI Instance.
1700 @param ParentRouteChart The route string pointed to the parent device if it exists.
1701 @param Port The port to be polled.
1702 @param PortState The port state.
1704 @retval EFI_SUCCESS Successfully enable/disable device slot according to port state.
1705 @retval Others Should not appear.
1710 XhcPollPortStatusChange (
1711 IN USB_XHCI_INSTANCE
*Xhc
,
1712 IN USB_DEV_ROUTE ParentRouteChart
,
1714 IN EFI_USB_PORT_STATUS
*PortState
1720 USB_DEV_ROUTE RouteChart
;
1722 Status
= EFI_SUCCESS
;
1724 if ((PortState
->PortChangeStatus
& (USB_PORT_STAT_C_CONNECTION
| USB_PORT_STAT_C_ENABLE
| USB_PORT_STAT_C_OVERCURRENT
| USB_PORT_STAT_C_RESET
)) == 0) {
1728 if (ParentRouteChart
.Dword
== 0) {
1729 RouteChart
.Route
.RouteString
= 0;
1730 RouteChart
.Route
.RootPortNum
= Port
+ 1;
1731 RouteChart
.Route
.TierNum
= 1;
1734 RouteChart
.Route
.RouteString
= ParentRouteChart
.Route
.RouteString
| (Port
<< (4 * (ParentRouteChart
.Route
.TierNum
- 1)));
1736 RouteChart
.Route
.RouteString
= ParentRouteChart
.Route
.RouteString
| (15 << (4 * (ParentRouteChart
.Route
.TierNum
- 1)));
1738 RouteChart
.Route
.RootPortNum
= ParentRouteChart
.Route
.RootPortNum
;
1739 RouteChart
.Route
.TierNum
= ParentRouteChart
.Route
.TierNum
+ 1;
1742 SlotId
= XhcRouteStringToSlotId (Xhc
, RouteChart
);
1744 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
1745 Status
= XhcDisableSlotCmd (Xhc
, SlotId
);
1747 Status
= XhcDisableSlotCmd64 (Xhc
, SlotId
);
1751 if (((PortState
->PortStatus
& USB_PORT_STAT_ENABLE
) != 0) &&
1752 ((PortState
->PortStatus
& USB_PORT_STAT_CONNECTION
) != 0)) {
1754 // Has a device attached, Identify device speed after port is enabled.
1756 Speed
= EFI_USB_SPEED_FULL
;
1757 if ((PortState
->PortStatus
& USB_PORT_STAT_LOW_SPEED
) != 0) {
1758 Speed
= EFI_USB_SPEED_LOW
;
1759 } else if ((PortState
->PortStatus
& USB_PORT_STAT_HIGH_SPEED
) != 0) {
1760 Speed
= EFI_USB_SPEED_HIGH
;
1761 } else if ((PortState
->PortStatus
& USB_PORT_STAT_SUPER_SPEED
) != 0) {
1762 Speed
= EFI_USB_SPEED_SUPER
;
1765 // Execute Enable_Slot cmd for attached device, initialize device context and assign device address.
1767 SlotId
= XhcRouteStringToSlotId (Xhc
, RouteChart
);
1768 if ((SlotId
== 0) && ((PortState
->PortChangeStatus
& USB_PORT_STAT_C_RESET
) != 0)) {
1769 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
1770 Status
= XhcInitializeDeviceSlot (Xhc
, ParentRouteChart
, Port
, RouteChart
, Speed
);
1772 Status
= XhcInitializeDeviceSlot64 (Xhc
, ParentRouteChart
, Port
, RouteChart
, Speed
);
1782 Calculate the device context index by endpoint address and direction.
1784 @param EpAddr The target endpoint number.
1785 @param Direction The direction of the target endpoint.
1787 @return The device context index of endpoint.
1801 Index
= (UINT8
) (2 * EpAddr
);
1802 if (Direction
== EfiUsbDataIn
) {
1810 Find out the actual device address according to the requested device address from UsbBus.
1812 @param Xhc The XHCI Instance.
1813 @param BusDevAddr The requested device address by UsbBus upper driver.
1815 @return The actual device address assigned to the device.
1820 XhcBusDevAddrToSlotId (
1821 IN USB_XHCI_INSTANCE
*Xhc
,
1827 for (Index
= 0; Index
< 255; Index
++) {
1828 if (Xhc
->UsbDevContext
[Index
+ 1].Enabled
&&
1829 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
!= 0) &&
1830 (Xhc
->UsbDevContext
[Index
+ 1].BusDevAddr
== BusDevAddr
)) {
1839 return Xhc
->UsbDevContext
[Index
+ 1].SlotId
;
1843 Find out the slot id according to the device's route string.
1845 @param Xhc The XHCI Instance.
1846 @param RouteString The route string described the device location.
1848 @return The slot id used by the device.
1853 XhcRouteStringToSlotId (
1854 IN USB_XHCI_INSTANCE
*Xhc
,
1855 IN USB_DEV_ROUTE RouteString
1860 for (Index
= 0; Index
< 255; Index
++) {
1861 if (Xhc
->UsbDevContext
[Index
+ 1].Enabled
&&
1862 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
!= 0) &&
1863 (Xhc
->UsbDevContext
[Index
+ 1].RouteString
.Dword
== RouteString
.Dword
)) {
1872 return Xhc
->UsbDevContext
[Index
+ 1].SlotId
;
1876 Synchronize the specified event ring to update the enqueue and dequeue pointer.
1878 @param Xhc The XHCI Instance.
1879 @param EvtRing The event ring to sync.
1881 @retval EFI_SUCCESS The event ring is synchronized successfully.
1887 IN USB_XHCI_INSTANCE
*Xhc
,
1888 IN EVENT_RING
*EvtRing
1892 TRB_TEMPLATE
*EvtTrb1
;
1894 ASSERT (EvtRing
!= NULL
);
1897 // Calculate the EventRingEnqueue and EventRingCCS.
1898 // Note: only support single Segment
1900 EvtTrb1
= EvtRing
->EventRingDequeue
;
1902 for (Index
= 0; Index
< EvtRing
->TrbNumber
; Index
++) {
1903 if (EvtTrb1
->CycleBit
!= EvtRing
->EventRingCCS
) {
1909 if ((UINTN
)EvtTrb1
>= ((UINTN
) EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
1910 EvtTrb1
= EvtRing
->EventRingSeg0
;
1911 EvtRing
->EventRingCCS
= (EvtRing
->EventRingCCS
) ? 0 : 1;
1915 if (Index
< EvtRing
->TrbNumber
) {
1916 EvtRing
->EventRingEnqueue
= EvtTrb1
;
1925 Synchronize the specified transfer ring to update the enqueue and dequeue pointer.
1927 @param Xhc The XHCI Instance.
1928 @param TrsRing The transfer ring to sync.
1930 @retval EFI_SUCCESS The transfer ring is synchronized successfully.
1936 IN USB_XHCI_INSTANCE
*Xhc
,
1937 IN TRANSFER_RING
*TrsRing
1941 TRB_TEMPLATE
*TrsTrb
;
1943 ASSERT (TrsRing
!= NULL
);
1945 // Calculate the latest RingEnqueue and RingPCS
1947 TrsTrb
= TrsRing
->RingEnqueue
;
1948 ASSERT (TrsTrb
!= NULL
);
1950 for (Index
= 0; Index
< TrsRing
->TrbNumber
; Index
++) {
1951 if (TrsTrb
->CycleBit
!= (TrsRing
->RingPCS
& BIT0
)) {
1955 if ((UINT8
) TrsTrb
->Type
== TRB_TYPE_LINK
) {
1956 ASSERT (((LINK_TRB
*)TrsTrb
)->TC
!= 0);
1958 // set cycle bit in Link TRB as normal
1960 ((LINK_TRB
*)TrsTrb
)->CycleBit
= TrsRing
->RingPCS
& BIT0
;
1962 // Toggle PCS maintained by software
1964 TrsRing
->RingPCS
= (TrsRing
->RingPCS
& BIT0
) ? 0 : 1;
1965 TrsTrb
= (TRB_TEMPLATE
*) TrsRing
->RingSeg0
; // Use host address
1969 ASSERT (Index
!= TrsRing
->TrbNumber
);
1971 if (TrsTrb
!= TrsRing
->RingEnqueue
) {
1972 TrsRing
->RingEnqueue
= TrsTrb
;
1976 // Clear the Trb context for enqueue, but reserve the PCS bit
1978 TrsTrb
->Parameter1
= 0;
1979 TrsTrb
->Parameter2
= 0;
1983 TrsTrb
->Control
= 0;
1989 Check if there is a new generated event.
1991 @param Xhc The XHCI Instance.
1992 @param EvtRing The event ring to check.
1993 @param NewEvtTrb The new event TRB found.
1995 @retval EFI_SUCCESS Found a new event TRB at the event ring.
1996 @retval EFI_NOT_READY The event ring has no new event.
2002 IN USB_XHCI_INSTANCE
*Xhc
,
2003 IN EVENT_RING
*EvtRing
,
2004 OUT TRB_TEMPLATE
**NewEvtTrb
2007 ASSERT (EvtRing
!= NULL
);
2009 *NewEvtTrb
= EvtRing
->EventRingDequeue
;
2011 if (EvtRing
->EventRingDequeue
== EvtRing
->EventRingEnqueue
) {
2012 return EFI_NOT_READY
;
2015 EvtRing
->EventRingDequeue
++;
2017 // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.
2019 if ((UINTN
)EvtRing
->EventRingDequeue
>= ((UINTN
) EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
2020 EvtRing
->EventRingDequeue
= EvtRing
->EventRingSeg0
;
2027 Ring the door bell to notify XHCI there is a transaction to be executed.
2029 @param Xhc The XHCI Instance.
2030 @param SlotId The slot id of the target device.
2031 @param Dci The device context index of the target slot or endpoint.
2033 @retval EFI_SUCCESS Successfully ring the door bell.
2039 IN USB_XHCI_INSTANCE
*Xhc
,
2045 XhcWriteDoorBellReg (Xhc
, 0, 0);
2047 XhcWriteDoorBellReg (Xhc
, SlotId
* sizeof (UINT32
), Dci
);
2054 Ring the door bell to notify XHCI there is a transaction to be executed through URB.
2056 @param Xhc The XHCI Instance.
2057 @param Urb The URB to be rung.
2059 @retval EFI_SUCCESS Successfully ring the door bell.
2063 RingIntTransferDoorBell (
2064 IN USB_XHCI_INSTANCE
*Xhc
,
2071 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
2072 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
2073 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
2078 Assign and initialize the device slot for a new device.
2080 @param Xhc The XHCI Instance.
2081 @param ParentRouteChart The route string pointed to the parent device.
2082 @param ParentPort The port at which the device is located.
2083 @param RouteChart The route string pointed to the device.
2084 @param DeviceSpeed The device speed.
2086 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.
2091 XhcInitializeDeviceSlot (
2092 IN USB_XHCI_INSTANCE
*Xhc
,
2093 IN USB_DEV_ROUTE ParentRouteChart
,
2094 IN UINT16 ParentPort
,
2095 IN USB_DEV_ROUTE RouteChart
,
2096 IN UINT8 DeviceSpeed
2100 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
2101 INPUT_CONTEXT
*InputContext
;
2102 DEVICE_CONTEXT
*OutputContext
;
2103 TRANSFER_RING
*EndpointTransferRing
;
2104 CMD_TRB_ADDRESS_DEVICE CmdTrbAddr
;
2105 UINT8 DeviceAddress
;
2106 CMD_TRB_ENABLE_SLOT CmdTrb
;
2109 DEVICE_CONTEXT
*ParentDeviceContext
;
2110 EFI_PHYSICAL_ADDRESS PhyAddr
;
2112 ZeroMem (&CmdTrb
, sizeof (CMD_TRB_ENABLE_SLOT
));
2113 CmdTrb
.CycleBit
= 1;
2114 CmdTrb
.Type
= TRB_TYPE_EN_SLOT
;
2116 Status
= XhcCmdTransfer (
2118 (TRB_TEMPLATE
*) (UINTN
) &CmdTrb
,
2119 XHC_GENERIC_TIMEOUT
,
2120 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2122 if (EFI_ERROR (Status
)) {
2123 DEBUG ((EFI_D_ERROR
, "XhcInitializeDeviceSlot: Enable Slot Failed, Status = %r\n", Status
));
2126 ASSERT (EvtTrb
->SlotId
<= Xhc
->MaxSlotsEn
);
2127 DEBUG ((EFI_D_INFO
, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb
->SlotId
));
2128 SlotId
= (UINT8
)EvtTrb
->SlotId
;
2129 ASSERT (SlotId
!= 0);
2131 ZeroMem (&Xhc
->UsbDevContext
[SlotId
], sizeof (USB_DEV_CONTEXT
));
2132 Xhc
->UsbDevContext
[SlotId
].Enabled
= TRUE
;
2133 Xhc
->UsbDevContext
[SlotId
].SlotId
= SlotId
;
2134 Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
= RouteChart
.Dword
;
2135 Xhc
->UsbDevContext
[SlotId
].ParentRouteString
.Dword
= ParentRouteChart
.Dword
;
2138 // 4.3.3 Device Slot Initialization
2139 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.
2141 InputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (INPUT_CONTEXT
));
2142 ASSERT (InputContext
!= NULL
);
2143 ASSERT (((UINTN
) InputContext
& 0x3F) == 0);
2144 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
2146 Xhc
->UsbDevContext
[SlotId
].InputContext
= (VOID
*) InputContext
;
2149 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1
2150 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input
2151 // Context are affected by the command.
2153 InputContext
->InputControlContext
.Dword2
|= (BIT0
| BIT1
);
2156 // 3) Initialize the Input Slot Context data structure
2158 InputContext
->Slot
.RouteString
= RouteChart
.Route
.RouteString
;
2159 InputContext
->Slot
.Speed
= DeviceSpeed
+ 1;
2160 InputContext
->Slot
.ContextEntries
= 1;
2161 InputContext
->Slot
.RootHubPortNum
= RouteChart
.Route
.RootPortNum
;
2163 if (RouteChart
.Route
.RouteString
) {
2165 // The device is behind of hub device.
2167 ParentSlotId
= XhcRouteStringToSlotId(Xhc
, ParentRouteChart
);
2168 ASSERT (ParentSlotId
!= 0);
2170 //if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context
2172 ParentDeviceContext
= (DEVICE_CONTEXT
*)Xhc
->UsbDevContext
[ParentSlotId
].OutputContext
;
2173 if ((ParentDeviceContext
->Slot
.TTPortNum
== 0) &&
2174 (ParentDeviceContext
->Slot
.TTHubSlotId
== 0)) {
2175 if ((ParentDeviceContext
->Slot
.Speed
== (EFI_USB_SPEED_HIGH
+ 1)) && (DeviceSpeed
< EFI_USB_SPEED_HIGH
)) {
2177 // Full/Low device attached to High speed hub port that isolates the high speed signaling
2178 // environment from Full/Low speed signaling environment for a device
2180 InputContext
->Slot
.TTPortNum
= ParentPort
;
2181 InputContext
->Slot
.TTHubSlotId
= ParentSlotId
;
2185 // Inherit the TT parameters from parent device.
2187 InputContext
->Slot
.TTPortNum
= ParentDeviceContext
->Slot
.TTPortNum
;
2188 InputContext
->Slot
.TTHubSlotId
= ParentDeviceContext
->Slot
.TTHubSlotId
;
2190 // If the device is a High speed device then down the speed to be the same as its parent Hub
2192 if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2193 InputContext
->Slot
.Speed
= ParentDeviceContext
->Slot
.Speed
;
2199 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.
2201 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2202 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0] = EndpointTransferRing
;
2203 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0]);
2205 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).
2207 InputContext
->EP
[0].EPType
= ED_CONTROL_BIDIR
;
2209 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2210 InputContext
->EP
[0].MaxPacketSize
= 512;
2211 } else if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2212 InputContext
->EP
[0].MaxPacketSize
= 64;
2214 InputContext
->EP
[0].MaxPacketSize
= 8;
2217 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints
2218 // 1KB, and Bulk and Isoch endpoints 3KB.
2220 InputContext
->EP
[0].AverageTRBLength
= 8;
2221 InputContext
->EP
[0].MaxBurstSize
= 0;
2222 InputContext
->EP
[0].Interval
= 0;
2223 InputContext
->EP
[0].MaxPStreams
= 0;
2224 InputContext
->EP
[0].Mult
= 0;
2225 InputContext
->EP
[0].CErr
= 3;
2228 // Init the DCS(dequeue cycle state) as the transfer ring's CCS
2230 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2232 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0])->RingSeg0
,
2233 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2235 InputContext
->EP
[0].PtrLo
= XHC_LOW_32BIT (PhyAddr
) | BIT0
;
2236 InputContext
->EP
[0].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2239 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
2241 OutputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (DEVICE_CONTEXT
));
2242 ASSERT (OutputContext
!= NULL
);
2243 ASSERT (((UINTN
) OutputContext
& 0x3F) == 0);
2244 ZeroMem (OutputContext
, sizeof (DEVICE_CONTEXT
));
2246 Xhc
->UsbDevContext
[SlotId
].OutputContext
= OutputContext
;
2248 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with
2249 // a pointer to the Output Device Context data structure (6.2.1).
2251 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, OutputContext
, sizeof (DEVICE_CONTEXT
));
2253 // Fill DCBAA with PCI device address
2255 Xhc
->DCBAA
[SlotId
] = (UINT64
) (UINTN
) PhyAddr
;
2258 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input
2259 // Context data structure described above.
2261 // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request
2264 gBS
->Stall (XHC_RESET_RECOVERY_DELAY
);
2265 ZeroMem (&CmdTrbAddr
, sizeof (CmdTrbAddr
));
2266 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT
));
2267 CmdTrbAddr
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2268 CmdTrbAddr
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2269 CmdTrbAddr
.CycleBit
= 1;
2270 CmdTrbAddr
.Type
= TRB_TYPE_ADDRESS_DEV
;
2271 CmdTrbAddr
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
2272 Status
= XhcCmdTransfer (
2274 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbAddr
,
2275 XHC_GENERIC_TIMEOUT
,
2276 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2278 if (!EFI_ERROR (Status
)) {
2279 DeviceAddress
= (UINT8
) ((DEVICE_CONTEXT
*) OutputContext
)->Slot
.DeviceAddress
;
2280 DEBUG ((EFI_D_INFO
, " Address %d assigned successfully\n", DeviceAddress
));
2281 Xhc
->UsbDevContext
[SlotId
].XhciDevAddr
= DeviceAddress
;
2288 Assign and initialize the device slot for a new device.
2290 @param Xhc The XHCI Instance.
2291 @param ParentRouteChart The route string pointed to the parent device.
2292 @param ParentPort The port at which the device is located.
2293 @param RouteChart The route string pointed to the device.
2294 @param DeviceSpeed The device speed.
2296 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.
2301 XhcInitializeDeviceSlot64 (
2302 IN USB_XHCI_INSTANCE
*Xhc
,
2303 IN USB_DEV_ROUTE ParentRouteChart
,
2304 IN UINT16 ParentPort
,
2305 IN USB_DEV_ROUTE RouteChart
,
2306 IN UINT8 DeviceSpeed
2310 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
2311 INPUT_CONTEXT_64
*InputContext
;
2312 DEVICE_CONTEXT_64
*OutputContext
;
2313 TRANSFER_RING
*EndpointTransferRing
;
2314 CMD_TRB_ADDRESS_DEVICE CmdTrbAddr
;
2315 UINT8 DeviceAddress
;
2316 CMD_TRB_ENABLE_SLOT CmdTrb
;
2319 DEVICE_CONTEXT_64
*ParentDeviceContext
;
2320 EFI_PHYSICAL_ADDRESS PhyAddr
;
2322 ZeroMem (&CmdTrb
, sizeof (CMD_TRB_ENABLE_SLOT
));
2323 CmdTrb
.CycleBit
= 1;
2324 CmdTrb
.Type
= TRB_TYPE_EN_SLOT
;
2326 Status
= XhcCmdTransfer (
2328 (TRB_TEMPLATE
*) (UINTN
) &CmdTrb
,
2329 XHC_GENERIC_TIMEOUT
,
2330 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2332 if (EFI_ERROR (Status
)) {
2333 DEBUG ((EFI_D_ERROR
, "XhcInitializeDeviceSlot64: Enable Slot Failed, Status = %r\n", Status
));
2336 ASSERT (EvtTrb
->SlotId
<= Xhc
->MaxSlotsEn
);
2337 DEBUG ((EFI_D_INFO
, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb
->SlotId
));
2338 SlotId
= (UINT8
)EvtTrb
->SlotId
;
2339 ASSERT (SlotId
!= 0);
2341 ZeroMem (&Xhc
->UsbDevContext
[SlotId
], sizeof (USB_DEV_CONTEXT
));
2342 Xhc
->UsbDevContext
[SlotId
].Enabled
= TRUE
;
2343 Xhc
->UsbDevContext
[SlotId
].SlotId
= SlotId
;
2344 Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
= RouteChart
.Dword
;
2345 Xhc
->UsbDevContext
[SlotId
].ParentRouteString
.Dword
= ParentRouteChart
.Dword
;
2348 // 4.3.3 Device Slot Initialization
2349 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.
2351 InputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (INPUT_CONTEXT_64
));
2352 ASSERT (InputContext
!= NULL
);
2353 ASSERT (((UINTN
) InputContext
& 0x3F) == 0);
2354 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
2356 Xhc
->UsbDevContext
[SlotId
].InputContext
= (VOID
*) InputContext
;
2359 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1
2360 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input
2361 // Context are affected by the command.
2363 InputContext
->InputControlContext
.Dword2
|= (BIT0
| BIT1
);
2366 // 3) Initialize the Input Slot Context data structure
2368 InputContext
->Slot
.RouteString
= RouteChart
.Route
.RouteString
;
2369 InputContext
->Slot
.Speed
= DeviceSpeed
+ 1;
2370 InputContext
->Slot
.ContextEntries
= 1;
2371 InputContext
->Slot
.RootHubPortNum
= RouteChart
.Route
.RootPortNum
;
2373 if (RouteChart
.Route
.RouteString
) {
2375 // The device is behind of hub device.
2377 ParentSlotId
= XhcRouteStringToSlotId(Xhc
, ParentRouteChart
);
2378 ASSERT (ParentSlotId
!= 0);
2380 //if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context
2382 ParentDeviceContext
= (DEVICE_CONTEXT_64
*)Xhc
->UsbDevContext
[ParentSlotId
].OutputContext
;
2383 if ((ParentDeviceContext
->Slot
.TTPortNum
== 0) &&
2384 (ParentDeviceContext
->Slot
.TTHubSlotId
== 0)) {
2385 if ((ParentDeviceContext
->Slot
.Speed
== (EFI_USB_SPEED_HIGH
+ 1)) && (DeviceSpeed
< EFI_USB_SPEED_HIGH
)) {
2387 // Full/Low device attached to High speed hub port that isolates the high speed signaling
2388 // environment from Full/Low speed signaling environment for a device
2390 InputContext
->Slot
.TTPortNum
= ParentPort
;
2391 InputContext
->Slot
.TTHubSlotId
= ParentSlotId
;
2395 // Inherit the TT parameters from parent device.
2397 InputContext
->Slot
.TTPortNum
= ParentDeviceContext
->Slot
.TTPortNum
;
2398 InputContext
->Slot
.TTHubSlotId
= ParentDeviceContext
->Slot
.TTHubSlotId
;
2400 // If the device is a High speed device then down the speed to be the same as its parent Hub
2402 if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2403 InputContext
->Slot
.Speed
= ParentDeviceContext
->Slot
.Speed
;
2409 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.
2411 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2412 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0] = EndpointTransferRing
;
2413 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0]);
2415 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).
2417 InputContext
->EP
[0].EPType
= ED_CONTROL_BIDIR
;
2419 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2420 InputContext
->EP
[0].MaxPacketSize
= 512;
2421 } else if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2422 InputContext
->EP
[0].MaxPacketSize
= 64;
2424 InputContext
->EP
[0].MaxPacketSize
= 8;
2427 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints
2428 // 1KB, and Bulk and Isoch endpoints 3KB.
2430 InputContext
->EP
[0].AverageTRBLength
= 8;
2431 InputContext
->EP
[0].MaxBurstSize
= 0;
2432 InputContext
->EP
[0].Interval
= 0;
2433 InputContext
->EP
[0].MaxPStreams
= 0;
2434 InputContext
->EP
[0].Mult
= 0;
2435 InputContext
->EP
[0].CErr
= 3;
2438 // Init the DCS(dequeue cycle state) as the transfer ring's CCS
2440 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2442 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0])->RingSeg0
,
2443 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2445 InputContext
->EP
[0].PtrLo
= XHC_LOW_32BIT (PhyAddr
) | BIT0
;
2446 InputContext
->EP
[0].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2449 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
2451 OutputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (DEVICE_CONTEXT_64
));
2452 ASSERT (OutputContext
!= NULL
);
2453 ASSERT (((UINTN
) OutputContext
& 0x3F) == 0);
2454 ZeroMem (OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2456 Xhc
->UsbDevContext
[SlotId
].OutputContext
= OutputContext
;
2458 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with
2459 // a pointer to the Output Device Context data structure (6.2.1).
2461 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2463 // Fill DCBAA with PCI device address
2465 Xhc
->DCBAA
[SlotId
] = (UINT64
) (UINTN
) PhyAddr
;
2468 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input
2469 // Context data structure described above.
2471 // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request
2474 gBS
->Stall (XHC_RESET_RECOVERY_DELAY
);
2475 ZeroMem (&CmdTrbAddr
, sizeof (CmdTrbAddr
));
2476 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT_64
));
2477 CmdTrbAddr
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2478 CmdTrbAddr
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2479 CmdTrbAddr
.CycleBit
= 1;
2480 CmdTrbAddr
.Type
= TRB_TYPE_ADDRESS_DEV
;
2481 CmdTrbAddr
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
2482 Status
= XhcCmdTransfer (
2484 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbAddr
,
2485 XHC_GENERIC_TIMEOUT
,
2486 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2488 if (!EFI_ERROR (Status
)) {
2489 DeviceAddress
= (UINT8
) ((DEVICE_CONTEXT_64
*) OutputContext
)->Slot
.DeviceAddress
;
2490 DEBUG ((EFI_D_INFO
, " Address %d assigned successfully\n", DeviceAddress
));
2491 Xhc
->UsbDevContext
[SlotId
].XhciDevAddr
= DeviceAddress
;
2498 Disable the specified device slot.
2500 @param Xhc The XHCI Instance.
2501 @param SlotId The slot id to be disabled.
2503 @retval EFI_SUCCESS Successfully disable the device slot.
2509 IN USB_XHCI_INSTANCE
*Xhc
,
2514 TRB_TEMPLATE
*EvtTrb
;
2515 CMD_TRB_DISABLE_SLOT CmdTrbDisSlot
;
2520 // Disable the device slots occupied by these devices on its downstream ports.
2521 // Entry 0 is reserved.
2523 for (Index
= 0; Index
< 255; Index
++) {
2524 if (!Xhc
->UsbDevContext
[Index
+ 1].Enabled
||
2525 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
== 0) ||
2526 (Xhc
->UsbDevContext
[Index
+ 1].ParentRouteString
.Dword
!= Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
)) {
2530 Status
= XhcDisableSlotCmd (Xhc
, Xhc
->UsbDevContext
[Index
+ 1].SlotId
);
2532 if (EFI_ERROR (Status
)) {
2533 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
2534 Xhc
->UsbDevContext
[Index
+ 1].SlotId
= 0;
2539 // Construct the disable slot command
2541 DEBUG ((EFI_D_INFO
, "Disable device slot %d!\n", SlotId
));
2543 ZeroMem (&CmdTrbDisSlot
, sizeof (CmdTrbDisSlot
));
2544 CmdTrbDisSlot
.CycleBit
= 1;
2545 CmdTrbDisSlot
.Type
= TRB_TYPE_DIS_SLOT
;
2546 CmdTrbDisSlot
.SlotId
= SlotId
;
2547 Status
= XhcCmdTransfer (
2549 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbDisSlot
,
2550 XHC_GENERIC_TIMEOUT
,
2551 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2553 if (EFI_ERROR (Status
)) {
2554 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status
));
2558 // Free the slot's device context entry
2560 Xhc
->DCBAA
[SlotId
] = 0;
2563 // Free the slot related data structure
2565 for (Index
= 0; Index
< 31; Index
++) {
2566 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] != NULL
) {
2567 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
])->RingSeg0
;
2568 if (RingSeg
!= NULL
) {
2569 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
2571 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
]);
2572 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] = NULL
;
2576 for (Index
= 0; Index
< Xhc
->UsbDevContext
[SlotId
].DevDesc
.NumConfigurations
; Index
++) {
2577 if (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
] != NULL
) {
2578 FreePool (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
]);
2582 if (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
!= NULL
) {
2583 FreePool (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
);
2586 if (Xhc
->UsbDevContext
[SlotId
].InputContext
!= NULL
) {
2587 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT
));
2590 if (Xhc
->UsbDevContext
[SlotId
].OutputContext
!= NULL
) {
2591 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].OutputContext
, sizeof (DEVICE_CONTEXT
));
2594 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established
2595 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to
2596 // remove urb from XHCI's asynchronous transfer list.
2598 Xhc
->UsbDevContext
[SlotId
].Enabled
= FALSE
;
2599 Xhc
->UsbDevContext
[SlotId
].SlotId
= 0;
2605 Disable the specified device slot.
2607 @param Xhc The XHCI Instance.
2608 @param SlotId The slot id to be disabled.
2610 @retval EFI_SUCCESS Successfully disable the device slot.
2615 XhcDisableSlotCmd64 (
2616 IN USB_XHCI_INSTANCE
*Xhc
,
2621 TRB_TEMPLATE
*EvtTrb
;
2622 CMD_TRB_DISABLE_SLOT CmdTrbDisSlot
;
2627 // Disable the device slots occupied by these devices on its downstream ports.
2628 // Entry 0 is reserved.
2630 for (Index
= 0; Index
< 255; Index
++) {
2631 if (!Xhc
->UsbDevContext
[Index
+ 1].Enabled
||
2632 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
== 0) ||
2633 (Xhc
->UsbDevContext
[Index
+ 1].ParentRouteString
.Dword
!= Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
)) {
2637 Status
= XhcDisableSlotCmd64 (Xhc
, Xhc
->UsbDevContext
[Index
+ 1].SlotId
);
2639 if (EFI_ERROR (Status
)) {
2640 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
2641 Xhc
->UsbDevContext
[Index
+ 1].SlotId
= 0;
2646 // Construct the disable slot command
2648 DEBUG ((EFI_D_INFO
, "Disable device slot %d!\n", SlotId
));
2650 ZeroMem (&CmdTrbDisSlot
, sizeof (CmdTrbDisSlot
));
2651 CmdTrbDisSlot
.CycleBit
= 1;
2652 CmdTrbDisSlot
.Type
= TRB_TYPE_DIS_SLOT
;
2653 CmdTrbDisSlot
.SlotId
= SlotId
;
2654 Status
= XhcCmdTransfer (
2656 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbDisSlot
,
2657 XHC_GENERIC_TIMEOUT
,
2658 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2660 if (EFI_ERROR (Status
)) {
2661 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status
));
2665 // Free the slot's device context entry
2667 Xhc
->DCBAA
[SlotId
] = 0;
2670 // Free the slot related data structure
2672 for (Index
= 0; Index
< 31; Index
++) {
2673 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] != NULL
) {
2674 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
])->RingSeg0
;
2675 if (RingSeg
!= NULL
) {
2676 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
2678 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
]);
2679 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] = NULL
;
2683 for (Index
= 0; Index
< Xhc
->UsbDevContext
[SlotId
].DevDesc
.NumConfigurations
; Index
++) {
2684 if (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
] != NULL
) {
2685 FreePool (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
]);
2689 if (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
!= NULL
) {
2690 FreePool (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
);
2693 if (Xhc
->UsbDevContext
[SlotId
].InputContext
!= NULL
) {
2694 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT_64
));
2697 if (Xhc
->UsbDevContext
[SlotId
].OutputContext
!= NULL
) {
2698 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2701 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established
2702 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to
2703 // remove urb from XHCI's asynchronous transfer list.
2705 Xhc
->UsbDevContext
[SlotId
].Enabled
= FALSE
;
2706 Xhc
->UsbDevContext
[SlotId
].SlotId
= 0;
2712 Initialize endpoint context in input context.
2714 @param Xhc The XHCI Instance.
2715 @param SlotId The slot id to be configured.
2716 @param DeviceSpeed The device's speed.
2717 @param InputContext The pointer to the input context.
2718 @param IfDesc The pointer to the usb device interface descriptor.
2720 @return The maximum device context index of endpoint.
2725 XhcInitializeEndpointContext (
2726 IN USB_XHCI_INSTANCE
*Xhc
,
2728 IN UINT8 DeviceSpeed
,
2729 IN INPUT_CONTEXT
*InputContext
,
2730 IN USB_INTERFACE_DESCRIPTOR
*IfDesc
2733 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
2740 EFI_PHYSICAL_ADDRESS PhyAddr
;
2742 TRANSFER_RING
*EndpointTransferRing
;
2746 NumEp
= IfDesc
->NumEndpoints
;
2748 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDesc
+ 1);
2749 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
2750 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
2751 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2754 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
2755 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2759 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
2760 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
2762 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
2768 InputContext
->InputControlContext
.Dword2
|= (BIT0
<< Dci
);
2769 InputContext
->EP
[Dci
-1].MaxPacketSize
= EpDesc
->MaxPacketSize
;
2771 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2773 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.
2775 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2777 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2780 switch (EpDesc
->Attributes
& USB_ENDPOINT_TYPE_MASK
) {
2781 case USB_ENDPOINT_BULK
:
2782 if (Direction
== EfiUsbDataIn
) {
2783 InputContext
->EP
[Dci
-1].CErr
= 3;
2784 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_IN
;
2786 InputContext
->EP
[Dci
-1].CErr
= 3;
2787 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_OUT
;
2790 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2791 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2792 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
2793 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
2794 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2795 DEBUG ((DEBUG_INFO
, "Endpoint[%x]: Created BULK ring [%p~%p)\n",
2796 EpDesc
->EndpointAddress
,
2797 EndpointTransferRing
->RingSeg0
,
2798 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2803 case USB_ENDPOINT_ISO
:
2804 if (Direction
== EfiUsbDataIn
) {
2805 InputContext
->EP
[Dci
-1].CErr
= 0;
2806 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_IN
;
2808 InputContext
->EP
[Dci
-1].CErr
= 0;
2809 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_OUT
;
2812 // Get the bInterval from descriptor and init the the interval field of endpoint context.
2813 // Refer to XHCI 1.1 spec section 6.2.3.6.
2815 if (DeviceSpeed
== EFI_USB_SPEED_FULL
) {
2816 Interval
= EpDesc
->Interval
;
2817 ASSERT (Interval
>= 1 && Interval
<= 16);
2818 InputContext
->EP
[Dci
-1].Interval
= Interval
+ 2;
2819 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2820 Interval
= EpDesc
->Interval
;
2821 ASSERT (Interval
>= 1 && Interval
<= 16);
2822 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2826 // Do not support isochronous transfer now.
2828 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
2829 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2831 case USB_ENDPOINT_INTERRUPT
:
2832 if (Direction
== EfiUsbDataIn
) {
2833 InputContext
->EP
[Dci
-1].CErr
= 3;
2834 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_IN
;
2836 InputContext
->EP
[Dci
-1].CErr
= 3;
2837 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_OUT
;
2839 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2840 InputContext
->EP
[Dci
-1].MaxESITPayload
= EpDesc
->MaxPacketSize
;
2842 // Get the bInterval from descriptor and init the the interval field of endpoint context
2844 if ((DeviceSpeed
== EFI_USB_SPEED_FULL
) || (DeviceSpeed
== EFI_USB_SPEED_LOW
)) {
2845 Interval
= EpDesc
->Interval
;
2847 // Calculate through the bInterval field of Endpoint descriptor.
2849 ASSERT (Interval
!= 0);
2850 InputContext
->EP
[Dci
-1].Interval
= (UINT32
)HighBitSet32((UINT32
)Interval
) + 3;
2851 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2852 Interval
= EpDesc
->Interval
;
2853 ASSERT (Interval
>= 1 && Interval
<= 16);
2855 // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
2857 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2858 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2859 InputContext
->EP
[Dci
-1].MaxESITPayload
= 0x0002;
2860 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2861 InputContext
->EP
[Dci
-1].CErr
= 3;
2864 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2865 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
2866 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
2867 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2868 DEBUG ((DEBUG_INFO
, "Endpoint[%x]: Created INT ring [%p~%p)\n",
2869 EpDesc
->EndpointAddress
,
2870 EndpointTransferRing
->RingSeg0
,
2871 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2876 case USB_ENDPOINT_CONTROL
:
2878 // Do not support control transfer now.
2880 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext: Unsupport Control EP found, Transfer ring is not allocated.\n"));
2882 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext: Unknown EP found, Transfer ring is not allocated.\n"));
2883 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2887 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2889 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingSeg0
,
2890 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2892 PhyAddr
&= ~((EFI_PHYSICAL_ADDRESS
)0x0F);
2893 PhyAddr
|= (EFI_PHYSICAL_ADDRESS
)((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingPCS
;
2894 InputContext
->EP
[Dci
-1].PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2895 InputContext
->EP
[Dci
-1].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2897 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2904 Initialize endpoint context in input context.
2906 @param Xhc The XHCI Instance.
2907 @param SlotId The slot id to be configured.
2908 @param DeviceSpeed The device's speed.
2909 @param InputContext The pointer to the input context.
2910 @param IfDesc The pointer to the usb device interface descriptor.
2912 @return The maximum device context index of endpoint.
2917 XhcInitializeEndpointContext64 (
2918 IN USB_XHCI_INSTANCE
*Xhc
,
2920 IN UINT8 DeviceSpeed
,
2921 IN INPUT_CONTEXT_64
*InputContext
,
2922 IN USB_INTERFACE_DESCRIPTOR
*IfDesc
2925 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
2932 EFI_PHYSICAL_ADDRESS PhyAddr
;
2934 TRANSFER_RING
*EndpointTransferRing
;
2938 NumEp
= IfDesc
->NumEndpoints
;
2940 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDesc
+ 1);
2941 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
2942 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
2943 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2946 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
2947 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2951 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
2952 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
2954 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
2960 InputContext
->InputControlContext
.Dword2
|= (BIT0
<< Dci
);
2961 InputContext
->EP
[Dci
-1].MaxPacketSize
= EpDesc
->MaxPacketSize
;
2963 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2965 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.
2967 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2969 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2972 switch (EpDesc
->Attributes
& USB_ENDPOINT_TYPE_MASK
) {
2973 case USB_ENDPOINT_BULK
:
2974 if (Direction
== EfiUsbDataIn
) {
2975 InputContext
->EP
[Dci
-1].CErr
= 3;
2976 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_IN
;
2978 InputContext
->EP
[Dci
-1].CErr
= 3;
2979 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_OUT
;
2982 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2983 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2984 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
2985 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
2986 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2987 DEBUG ((DEBUG_INFO
, "Endpoint64[%x]: Created BULK ring [%p~%p)\n",
2988 EpDesc
->EndpointAddress
,
2989 EndpointTransferRing
->RingSeg0
,
2990 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2995 case USB_ENDPOINT_ISO
:
2996 if (Direction
== EfiUsbDataIn
) {
2997 InputContext
->EP
[Dci
-1].CErr
= 0;
2998 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_IN
;
3000 InputContext
->EP
[Dci
-1].CErr
= 0;
3001 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_OUT
;
3004 // Get the bInterval from descriptor and init the the interval field of endpoint context.
3005 // Refer to XHCI 1.1 spec section 6.2.3.6.
3007 if (DeviceSpeed
== EFI_USB_SPEED_FULL
) {
3008 Interval
= EpDesc
->Interval
;
3009 ASSERT (Interval
>= 1 && Interval
<= 16);
3010 InputContext
->EP
[Dci
-1].Interval
= Interval
+ 2;
3011 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
3012 Interval
= EpDesc
->Interval
;
3013 ASSERT (Interval
>= 1 && Interval
<= 16);
3014 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
3018 // Do not support isochronous transfer now.
3020 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext64: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
3021 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3023 case USB_ENDPOINT_INTERRUPT
:
3024 if (Direction
== EfiUsbDataIn
) {
3025 InputContext
->EP
[Dci
-1].CErr
= 3;
3026 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_IN
;
3028 InputContext
->EP
[Dci
-1].CErr
= 3;
3029 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_OUT
;
3031 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3032 InputContext
->EP
[Dci
-1].MaxESITPayload
= EpDesc
->MaxPacketSize
;
3034 // Get the bInterval from descriptor and init the the interval field of endpoint context
3036 if ((DeviceSpeed
== EFI_USB_SPEED_FULL
) || (DeviceSpeed
== EFI_USB_SPEED_LOW
)) {
3037 Interval
= EpDesc
->Interval
;
3039 // Calculate through the bInterval field of Endpoint descriptor.
3041 ASSERT (Interval
!= 0);
3042 InputContext
->EP
[Dci
-1].Interval
= (UINT32
)HighBitSet32((UINT32
)Interval
) + 3;
3043 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
3044 Interval
= EpDesc
->Interval
;
3045 ASSERT (Interval
>= 1 && Interval
<= 16);
3047 // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
3049 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
3050 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3051 InputContext
->EP
[Dci
-1].MaxESITPayload
= 0x0002;
3052 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
3053 InputContext
->EP
[Dci
-1].CErr
= 3;
3056 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
3057 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
3058 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
3059 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
3060 DEBUG ((DEBUG_INFO
, "Endpoint64[%x]: Created INT ring [%p~%p)\n",
3061 EpDesc
->EndpointAddress
,
3062 EndpointTransferRing
->RingSeg0
,
3063 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
3068 case USB_ENDPOINT_CONTROL
:
3070 // Do not support control transfer now.
3072 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext64: Unsupport Control EP found, Transfer ring is not allocated.\n"));
3074 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext64: Unknown EP found, Transfer ring is not allocated.\n"));
3075 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3079 PhyAddr
= UsbHcGetPciAddrForHostAddr (
3081 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingSeg0
,
3082 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
3084 PhyAddr
&= ~((EFI_PHYSICAL_ADDRESS
)0x0F);
3085 PhyAddr
|= (EFI_PHYSICAL_ADDRESS
)((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingPCS
;
3086 InputContext
->EP
[Dci
-1].PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3087 InputContext
->EP
[Dci
-1].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3089 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3096 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
3098 @param Xhc The XHCI Instance.
3099 @param SlotId The slot id to be configured.
3100 @param DeviceSpeed The device's speed.
3101 @param ConfigDesc The pointer to the usb device configuration descriptor.
3103 @retval EFI_SUCCESS Successfully configure all the device endpoints.
3109 IN USB_XHCI_INSTANCE
*Xhc
,
3111 IN UINT8 DeviceSpeed
,
3112 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
3116 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3120 EFI_PHYSICAL_ADDRESS PhyAddr
;
3122 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3123 INPUT_CONTEXT
*InputContext
;
3124 DEVICE_CONTEXT
*OutputContext
;
3125 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3127 // 4.6.6 Configure Endpoint
3129 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3130 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3131 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3132 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT
));
3134 ASSERT (ConfigDesc
!= NULL
);
3138 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3139 for (Index
= 0; Index
< ConfigDesc
->NumInterfaces
; Index
++) {
3140 while ((IfDesc
->DescriptorType
!= USB_DESC_TYPE_INTERFACE
) || (IfDesc
->AlternateSetting
!= 0)) {
3141 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3144 if (IfDesc
->Length
< sizeof (USB_INTERFACE_DESCRIPTOR
)) {
3145 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3149 Dci
= XhcInitializeEndpointContext (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDesc
);
3154 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3157 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3158 InputContext
->Slot
.ContextEntries
= MaxDci
;
3160 // configure endpoint
3162 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3163 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3164 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3165 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3166 CmdTrbCfgEP
.CycleBit
= 1;
3167 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3168 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3169 DEBUG ((EFI_D_INFO
, "Configure Endpoint\n"));
3170 Status
= XhcCmdTransfer (
3172 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3173 XHC_GENERIC_TIMEOUT
,
3174 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3176 if (EFI_ERROR (Status
)) {
3177 DEBUG ((EFI_D_ERROR
, "XhcSetConfigCmd: Config Endpoint Failed, Status = %r\n", Status
));
3179 Xhc
->UsbDevContext
[SlotId
].ActiveConfiguration
= ConfigDesc
->ConfigurationValue
;
3186 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
3188 @param Xhc The XHCI Instance.
3189 @param SlotId The slot id to be configured.
3190 @param DeviceSpeed The device's speed.
3191 @param ConfigDesc The pointer to the usb device configuration descriptor.
3193 @retval EFI_SUCCESS Successfully configure all the device endpoints.
3199 IN USB_XHCI_INSTANCE
*Xhc
,
3201 IN UINT8 DeviceSpeed
,
3202 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
3206 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3210 EFI_PHYSICAL_ADDRESS PhyAddr
;
3212 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3213 INPUT_CONTEXT_64
*InputContext
;
3214 DEVICE_CONTEXT_64
*OutputContext
;
3215 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3217 // 4.6.6 Configure Endpoint
3219 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3220 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3221 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3222 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT_64
));
3224 ASSERT (ConfigDesc
!= NULL
);
3228 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3229 for (Index
= 0; Index
< ConfigDesc
->NumInterfaces
; Index
++) {
3230 while ((IfDesc
->DescriptorType
!= USB_DESC_TYPE_INTERFACE
) || (IfDesc
->AlternateSetting
!= 0)) {
3231 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3234 if (IfDesc
->Length
< sizeof (USB_INTERFACE_DESCRIPTOR
)) {
3235 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3239 Dci
= XhcInitializeEndpointContext64 (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDesc
);
3244 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3247 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3248 InputContext
->Slot
.ContextEntries
= MaxDci
;
3250 // configure endpoint
3252 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3253 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3254 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3255 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3256 CmdTrbCfgEP
.CycleBit
= 1;
3257 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3258 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3259 DEBUG ((EFI_D_INFO
, "Configure Endpoint\n"));
3260 Status
= XhcCmdTransfer (
3262 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3263 XHC_GENERIC_TIMEOUT
,
3264 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3266 if (EFI_ERROR (Status
)) {
3267 DEBUG ((EFI_D_ERROR
, "XhcSetConfigCmd64: Config Endpoint Failed, Status = %r\n", Status
));
3269 Xhc
->UsbDevContext
[SlotId
].ActiveConfiguration
= ConfigDesc
->ConfigurationValue
;
3276 Stop endpoint through XHCI's Stop_Endpoint cmd.
3278 @param Xhc The XHCI Instance.
3279 @param SlotId The slot id to be configured.
3280 @param Dci The device context index of endpoint.
3281 @param PendingUrb The pending URB to check completion status when stopping the end point.
3283 @retval EFI_SUCCESS Stop endpoint successfully.
3284 @retval Others Failed to stop endpoint.
3290 IN USB_XHCI_INSTANCE
*Xhc
,
3293 IN URB
*PendingUrb OPTIONAL
3297 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3298 CMD_TRB_STOP_ENDPOINT CmdTrbStopED
;
3300 DEBUG ((EFI_D_INFO
, "XhcStopEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId
, Dci
));
3303 // When XhcCheckUrbResult waits for the Stop_Endpoint completion, it also checks
3304 // the PendingUrb completion status, because it's possible that the PendingUrb is
3305 // finished just before stopping the end point, but after the looping check.
3307 // The PendingUrb could be passed to XhcCmdTransfer to XhcExecTransfer to XhcCheckUrbResult
3308 // through function parameter, but That will cause every consumer of XhcCmdTransfer,
3309 // XhcExecTransfer and XhcCheckUrbResult pass a NULL PendingUrb.
3310 // But actually only XhcCheckUrbResult is aware of the PendingUrb.
3311 // So we choose to save the PendingUrb into the USB_XHCI_INSTANCE and use it in XhcCheckUrbResult.
3313 ASSERT (Xhc
->PendingUrb
== NULL
);
3314 Xhc
->PendingUrb
= PendingUrb
;
3316 // Reset the URB result from Timeout to NoError.
3317 // The USB result will be:
3318 // changed to Timeout when Stop/StopInvalidLength Transfer Event is received, or
3319 // remain NoError when Success/ShortPacket Transfer Event is received.
3321 if (PendingUrb
!= NULL
) {
3322 PendingUrb
->Result
= EFI_USB_NOERROR
;
3326 // Send stop endpoint command to transit Endpoint from running to stop state
3328 ZeroMem (&CmdTrbStopED
, sizeof (CmdTrbStopED
));
3329 CmdTrbStopED
.CycleBit
= 1;
3330 CmdTrbStopED
.Type
= TRB_TYPE_STOP_ENDPOINT
;
3331 CmdTrbStopED
.EDID
= Dci
;
3332 CmdTrbStopED
.SlotId
= SlotId
;
3333 Status
= XhcCmdTransfer (
3335 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbStopED
,
3336 XHC_GENERIC_TIMEOUT
,
3337 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3339 if (EFI_ERROR(Status
)) {
3340 DEBUG ((EFI_D_ERROR
, "XhcStopEndpoint: Stop Endpoint Failed, Status = %r\n", Status
));
3343 Xhc
->PendingUrb
= NULL
;
3349 Reset endpoint through XHCI's Reset_Endpoint cmd.
3351 @param Xhc The XHCI Instance.
3352 @param SlotId The slot id to be configured.
3353 @param Dci The device context index of endpoint.
3355 @retval EFI_SUCCESS Reset endpoint successfully.
3356 @retval Others Failed to reset endpoint.
3362 IN USB_XHCI_INSTANCE
*Xhc
,
3368 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3369 CMD_TRB_RESET_ENDPOINT CmdTrbResetED
;
3371 DEBUG ((EFI_D_INFO
, "XhcResetEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId
, Dci
));
3374 // Send stop endpoint command to transit Endpoint from running to stop state
3376 ZeroMem (&CmdTrbResetED
, sizeof (CmdTrbResetED
));
3377 CmdTrbResetED
.CycleBit
= 1;
3378 CmdTrbResetED
.Type
= TRB_TYPE_RESET_ENDPOINT
;
3379 CmdTrbResetED
.EDID
= Dci
;
3380 CmdTrbResetED
.SlotId
= SlotId
;
3381 Status
= XhcCmdTransfer (
3383 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbResetED
,
3384 XHC_GENERIC_TIMEOUT
,
3385 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3387 if (EFI_ERROR(Status
)) {
3388 DEBUG ((EFI_D_ERROR
, "XhcResetEndpoint: Reset Endpoint Failed, Status = %r\n", Status
));
3395 Set transfer ring dequeue pointer through XHCI's Set_Tr_Dequeue_Pointer cmd.
3397 @param Xhc The XHCI Instance.
3398 @param SlotId The slot id to be configured.
3399 @param Dci The device context index of endpoint.
3400 @param Urb The dequeue pointer of the transfer ring specified
3401 by the urb to be updated.
3403 @retval EFI_SUCCESS Set transfer ring dequeue pointer succeeds.
3404 @retval Others Failed to set transfer ring dequeue pointer.
3409 XhcSetTrDequeuePointer (
3410 IN USB_XHCI_INSTANCE
*Xhc
,
3417 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3418 CMD_SET_TR_DEQ_POINTER CmdSetTRDeq
;
3419 EFI_PHYSICAL_ADDRESS PhyAddr
;
3421 DEBUG ((EFI_D_INFO
, "XhcSetTrDequeuePointer: Slot = 0x%x, Dci = 0x%x, Urb = 0x%x\n", SlotId
, Dci
, Urb
));
3424 // Send stop endpoint command to transit Endpoint from running to stop state
3426 ZeroMem (&CmdSetTRDeq
, sizeof (CmdSetTRDeq
));
3427 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Urb
->Ring
->RingEnqueue
, sizeof (CMD_SET_TR_DEQ_POINTER
));
3428 CmdSetTRDeq
.PtrLo
= XHC_LOW_32BIT (PhyAddr
) | Urb
->Ring
->RingPCS
;
3429 CmdSetTRDeq
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3430 CmdSetTRDeq
.CycleBit
= 1;
3431 CmdSetTRDeq
.Type
= TRB_TYPE_SET_TR_DEQUE
;
3432 CmdSetTRDeq
.Endpoint
= Dci
;
3433 CmdSetTRDeq
.SlotId
= SlotId
;
3434 Status
= XhcCmdTransfer (
3436 (TRB_TEMPLATE
*) (UINTN
) &CmdSetTRDeq
,
3437 XHC_GENERIC_TIMEOUT
,
3438 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3440 if (EFI_ERROR(Status
)) {
3441 DEBUG ((EFI_D_ERROR
, "XhcSetTrDequeuePointer: Set TR Dequeue Pointer Failed, Status = %r\n", Status
));
3448 Set interface through XHCI's Configure_Endpoint cmd.
3450 @param Xhc The XHCI Instance.
3451 @param SlotId The slot id to be configured.
3452 @param DeviceSpeed The device's speed.
3453 @param ConfigDesc The pointer to the usb device configuration descriptor.
3454 @param Request USB device request to send.
3456 @retval EFI_SUCCESS Successfully set interface.
3462 IN USB_XHCI_INSTANCE
*Xhc
,
3464 IN UINT8 DeviceSpeed
,
3465 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
,
3466 IN EFI_USB_DEVICE_REQUEST
*Request
3470 USB_INTERFACE_DESCRIPTOR
*IfDescActive
;
3471 USB_INTERFACE_DESCRIPTOR
*IfDescSet
;
3472 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3473 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3480 EFI_PHYSICAL_ADDRESS PhyAddr
;
3483 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3484 INPUT_CONTEXT
*InputContext
;
3485 DEVICE_CONTEXT
*OutputContext
;
3486 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3488 Status
= EFI_SUCCESS
;
3490 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3491 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3493 // XHCI 4.6.6 Configure Endpoint
3494 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3495 // Context and Add Context flags as follows:
3496 // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
3497 // Context and Add Context flags to '0'.
3499 // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
3500 // So the default Drop Context and Add Context flags can be '0' to cover 1).
3502 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3503 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT
));
3505 ASSERT (ConfigDesc
!= NULL
);
3509 IfDescActive
= NULL
;
3512 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3513 while ((UINTN
) IfDesc
< ((UINTN
) ConfigDesc
+ ConfigDesc
->TotalLength
)) {
3514 if ((IfDesc
->DescriptorType
== USB_DESC_TYPE_INTERFACE
) && (IfDesc
->Length
>= sizeof (USB_INTERFACE_DESCRIPTOR
))) {
3515 if (IfDesc
->InterfaceNumber
== (UINT8
) Request
->Index
) {
3516 if (IfDesc
->AlternateSetting
== Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[IfDesc
->InterfaceNumber
]) {
3518 // Find out the active interface descriptor.
3520 IfDescActive
= IfDesc
;
3521 } else if (IfDesc
->AlternateSetting
== (UINT8
) Request
->Value
) {
3523 // Find out the interface descriptor to set.
3529 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3533 // XHCI 4.6.6 Configure Endpoint
3534 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3535 // Context and Add Context flags as follows:
3536 // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
3537 // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
3538 // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
3539 // the Drop Context flag to '1' and Add Context flag to '0'.
3540 // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
3541 // and Add Context flags shall be set to '1'.
3543 // Below codes are to cover 2), 3) and 4).
3546 if ((IfDescActive
!= NULL
) && (IfDescSet
!= NULL
)) {
3547 NumEp
= IfDescActive
->NumEndpoints
;
3548 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*) (IfDescActive
+ 1);
3549 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3550 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3551 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3554 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3555 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3559 EpAddr
= (UINT8
) (EpDesc
->EndpointAddress
& 0x0F);
3560 Direction
= (UINT8
) ((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3562 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3568 // XHCI 4.3.6 - Setting Alternate Interfaces
3569 // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
3571 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, NULL
);
3572 if (EFI_ERROR (Status
)) {
3576 // XHCI 4.3.6 - Setting Alternate Interfaces
3577 // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
3579 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] != NULL
) {
3580 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1])->RingSeg0
;
3581 if (RingSeg
!= NULL
) {
3582 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
3584 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1]);
3585 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] = NULL
;
3589 // Set the Drop Context flag to '1'.
3591 InputContext
->InputControlContext
.Dword1
|= (BIT0
<< Dci
);
3593 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3597 // XHCI 4.3.6 - Setting Alternate Interfaces
3598 // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
3599 // Interface setting, to '0'.
3601 // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
3605 // XHCI 4.3.6 - Setting Alternate Interfaces
3606 // 4) For each endpoint enabled by the Configure Endpoint Command:
3607 // a. Allocate a Transfer Ring.
3608 // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
3609 // c. Initialize the Endpoint Context data structure.
3611 Dci
= XhcInitializeEndpointContext (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDescSet
);
3616 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3617 InputContext
->Slot
.ContextEntries
= MaxDci
;
3619 // XHCI 4.3.6 - Setting Alternate Interfaces
3620 // 5) Issue and successfully complete a Configure Endpoint Command.
3622 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3623 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3624 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3625 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3626 CmdTrbCfgEP
.CycleBit
= 1;
3627 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3628 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3629 DEBUG ((EFI_D_INFO
, "SetInterface: Configure Endpoint\n"));
3630 Status
= XhcCmdTransfer (
3632 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3633 XHC_GENERIC_TIMEOUT
,
3634 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3636 if (EFI_ERROR (Status
)) {
3637 DEBUG ((EFI_D_ERROR
, "SetInterface: Config Endpoint Failed, Status = %r\n", Status
));
3640 // Update the active AlternateSetting.
3642 Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[(UINT8
) Request
->Index
] = (UINT8
) Request
->Value
;
3650 Set interface through XHCI's Configure_Endpoint cmd.
3652 @param Xhc The XHCI Instance.
3653 @param SlotId The slot id to be configured.
3654 @param DeviceSpeed The device's speed.
3655 @param ConfigDesc The pointer to the usb device configuration descriptor.
3656 @param Request USB device request to send.
3658 @retval EFI_SUCCESS Successfully set interface.
3664 IN USB_XHCI_INSTANCE
*Xhc
,
3666 IN UINT8 DeviceSpeed
,
3667 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
,
3668 IN EFI_USB_DEVICE_REQUEST
*Request
3672 USB_INTERFACE_DESCRIPTOR
*IfDescActive
;
3673 USB_INTERFACE_DESCRIPTOR
*IfDescSet
;
3674 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3675 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3682 EFI_PHYSICAL_ADDRESS PhyAddr
;
3685 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3686 INPUT_CONTEXT_64
*InputContext
;
3687 DEVICE_CONTEXT_64
*OutputContext
;
3688 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3690 Status
= EFI_SUCCESS
;
3692 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3693 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3695 // XHCI 4.6.6 Configure Endpoint
3696 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3697 // Context and Add Context flags as follows:
3698 // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
3699 // Context and Add Context flags to '0'.
3701 // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
3702 // So the default Drop Context and Add Context flags can be '0' to cover 1).
3704 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3705 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT_64
));
3707 ASSERT (ConfigDesc
!= NULL
);
3711 IfDescActive
= NULL
;
3714 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3715 while ((UINTN
) IfDesc
< ((UINTN
) ConfigDesc
+ ConfigDesc
->TotalLength
)) {
3716 if ((IfDesc
->DescriptorType
== USB_DESC_TYPE_INTERFACE
) && (IfDesc
->Length
>= sizeof (USB_INTERFACE_DESCRIPTOR
))) {
3717 if (IfDesc
->InterfaceNumber
== (UINT8
) Request
->Index
) {
3718 if (IfDesc
->AlternateSetting
== Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[IfDesc
->InterfaceNumber
]) {
3720 // Find out the active interface descriptor.
3722 IfDescActive
= IfDesc
;
3723 } else if (IfDesc
->AlternateSetting
== (UINT8
) Request
->Value
) {
3725 // Find out the interface descriptor to set.
3731 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3735 // XHCI 4.6.6 Configure Endpoint
3736 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3737 // Context and Add Context flags as follows:
3738 // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
3739 // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
3740 // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
3741 // the Drop Context flag to '1' and Add Context flag to '0'.
3742 // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
3743 // and Add Context flags shall be set to '1'.
3745 // Below codes are to cover 2), 3) and 4).
3748 if ((IfDescActive
!= NULL
) && (IfDescSet
!= NULL
)) {
3749 NumEp
= IfDescActive
->NumEndpoints
;
3750 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*) (IfDescActive
+ 1);
3751 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3752 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3753 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3756 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3757 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3761 EpAddr
= (UINT8
) (EpDesc
->EndpointAddress
& 0x0F);
3762 Direction
= (UINT8
) ((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3764 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3770 // XHCI 4.3.6 - Setting Alternate Interfaces
3771 // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
3773 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, NULL
);
3774 if (EFI_ERROR (Status
)) {
3778 // XHCI 4.3.6 - Setting Alternate Interfaces
3779 // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
3781 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] != NULL
) {
3782 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1])->RingSeg0
;
3783 if (RingSeg
!= NULL
) {
3784 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
3786 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1]);
3787 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] = NULL
;
3791 // Set the Drop Context flag to '1'.
3793 InputContext
->InputControlContext
.Dword1
|= (BIT0
<< Dci
);
3795 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3799 // XHCI 4.3.6 - Setting Alternate Interfaces
3800 // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
3801 // Interface setting, to '0'.
3803 // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
3807 // XHCI 4.3.6 - Setting Alternate Interfaces
3808 // 4) For each endpoint enabled by the Configure Endpoint Command:
3809 // a. Allocate a Transfer Ring.
3810 // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
3811 // c. Initialize the Endpoint Context data structure.
3813 Dci
= XhcInitializeEndpointContext64 (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDescSet
);
3818 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3819 InputContext
->Slot
.ContextEntries
= MaxDci
;
3821 // XHCI 4.3.6 - Setting Alternate Interfaces
3822 // 5) Issue and successfully complete a Configure Endpoint Command.
3824 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3825 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3826 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3827 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3828 CmdTrbCfgEP
.CycleBit
= 1;
3829 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3830 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3831 DEBUG ((EFI_D_INFO
, "SetInterface64: Configure Endpoint\n"));
3832 Status
= XhcCmdTransfer (
3834 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3835 XHC_GENERIC_TIMEOUT
,
3836 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3838 if (EFI_ERROR (Status
)) {
3839 DEBUG ((EFI_D_ERROR
, "SetInterface64: Config Endpoint Failed, Status = %r\n", Status
));
3842 // Update the active AlternateSetting.
3844 Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[(UINT8
) Request
->Index
] = (UINT8
) Request
->Value
;
3852 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
3854 @param Xhc The XHCI Instance.
3855 @param SlotId The slot id to be evaluated.
3856 @param MaxPacketSize The max packet size supported by the device control transfer.
3858 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.
3863 XhcEvaluateContext (
3864 IN USB_XHCI_INSTANCE
*Xhc
,
3866 IN UINT32 MaxPacketSize
3870 CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu
;
3871 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3872 INPUT_CONTEXT
*InputContext
;
3873 EFI_PHYSICAL_ADDRESS PhyAddr
;
3875 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3878 // 4.6.7 Evaluate Context
3880 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3881 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3883 InputContext
->InputControlContext
.Dword2
|= BIT1
;
3884 InputContext
->EP
[0].MaxPacketSize
= MaxPacketSize
;
3886 ZeroMem (&CmdTrbEvalu
, sizeof (CmdTrbEvalu
));
3887 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3888 CmdTrbEvalu
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3889 CmdTrbEvalu
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3890 CmdTrbEvalu
.CycleBit
= 1;
3891 CmdTrbEvalu
.Type
= TRB_TYPE_EVALU_CONTXT
;
3892 CmdTrbEvalu
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3893 DEBUG ((EFI_D_INFO
, "Evaluate context\n"));
3894 Status
= XhcCmdTransfer (
3896 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbEvalu
,
3897 XHC_GENERIC_TIMEOUT
,
3898 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3900 if (EFI_ERROR (Status
)) {
3901 DEBUG ((EFI_D_ERROR
, "XhcEvaluateContext: Evaluate Context Failed, Status = %r\n", Status
));
3907 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
3909 @param Xhc The XHCI Instance.
3910 @param SlotId The slot id to be evaluated.
3911 @param MaxPacketSize The max packet size supported by the device control transfer.
3913 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.
3918 XhcEvaluateContext64 (
3919 IN USB_XHCI_INSTANCE
*Xhc
,
3921 IN UINT32 MaxPacketSize
3925 CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu
;
3926 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3927 INPUT_CONTEXT_64
*InputContext
;
3928 EFI_PHYSICAL_ADDRESS PhyAddr
;
3930 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3933 // 4.6.7 Evaluate Context
3935 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3936 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3938 InputContext
->InputControlContext
.Dword2
|= BIT1
;
3939 InputContext
->EP
[0].MaxPacketSize
= MaxPacketSize
;
3941 ZeroMem (&CmdTrbEvalu
, sizeof (CmdTrbEvalu
));
3942 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3943 CmdTrbEvalu
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3944 CmdTrbEvalu
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3945 CmdTrbEvalu
.CycleBit
= 1;
3946 CmdTrbEvalu
.Type
= TRB_TYPE_EVALU_CONTXT
;
3947 CmdTrbEvalu
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3948 DEBUG ((EFI_D_INFO
, "Evaluate context\n"));
3949 Status
= XhcCmdTransfer (
3951 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbEvalu
,
3952 XHC_GENERIC_TIMEOUT
,
3953 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3955 if (EFI_ERROR (Status
)) {
3956 DEBUG ((EFI_D_ERROR
, "XhcEvaluateContext64: Evaluate Context Failed, Status = %r\n", Status
));
3963 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
3965 @param Xhc The XHCI Instance.
3966 @param SlotId The slot id to be configured.
3967 @param PortNum The total number of downstream port supported by the hub.
3968 @param TTT The TT think time of the hub device.
3969 @param MTT The multi-TT of the hub device.
3971 @retval EFI_SUCCESS Successfully configure the hub device's slot context.
3975 XhcConfigHubContext (
3976 IN USB_XHCI_INSTANCE
*Xhc
,
3984 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3985 INPUT_CONTEXT
*InputContext
;
3986 DEVICE_CONTEXT
*OutputContext
;
3987 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3988 EFI_PHYSICAL_ADDRESS PhyAddr
;
3990 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3991 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3992 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3995 // 4.6.7 Evaluate Context
3997 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3999 InputContext
->InputControlContext
.Dword2
|= BIT0
;
4002 // Copy the slot context from OutputContext to Input context
4004 CopyMem(&(InputContext
->Slot
), &(OutputContext
->Slot
), sizeof (SLOT_CONTEXT
));
4005 InputContext
->Slot
.Hub
= 1;
4006 InputContext
->Slot
.PortNum
= PortNum
;
4007 InputContext
->Slot
.TTT
= TTT
;
4008 InputContext
->Slot
.MTT
= MTT
;
4010 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
4011 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
4012 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
4013 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
4014 CmdTrbCfgEP
.CycleBit
= 1;
4015 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
4016 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
4017 DEBUG ((EFI_D_INFO
, "Configure Hub Slot Context\n"));
4018 Status
= XhcCmdTransfer (
4020 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
4021 XHC_GENERIC_TIMEOUT
,
4022 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
4024 if (EFI_ERROR (Status
)) {
4025 DEBUG ((EFI_D_ERROR
, "XhcConfigHubContext: Config Endpoint Failed, Status = %r\n", Status
));
4031 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
4033 @param Xhc The XHCI Instance.
4034 @param SlotId The slot id to be configured.
4035 @param PortNum The total number of downstream port supported by the hub.
4036 @param TTT The TT think time of the hub device.
4037 @param MTT The multi-TT of the hub device.
4039 @retval EFI_SUCCESS Successfully configure the hub device's slot context.
4043 XhcConfigHubContext64 (
4044 IN USB_XHCI_INSTANCE
*Xhc
,
4052 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
4053 INPUT_CONTEXT_64
*InputContext
;
4054 DEVICE_CONTEXT_64
*OutputContext
;
4055 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
4056 EFI_PHYSICAL_ADDRESS PhyAddr
;
4058 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
4059 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
4060 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
4063 // 4.6.7 Evaluate Context
4065 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
4067 InputContext
->InputControlContext
.Dword2
|= BIT0
;
4070 // Copy the slot context from OutputContext to Input context
4072 CopyMem(&(InputContext
->Slot
), &(OutputContext
->Slot
), sizeof (SLOT_CONTEXT_64
));
4073 InputContext
->Slot
.Hub
= 1;
4074 InputContext
->Slot
.PortNum
= PortNum
;
4075 InputContext
->Slot
.TTT
= TTT
;
4076 InputContext
->Slot
.MTT
= MTT
;
4078 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
4079 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
4080 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
4081 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
4082 CmdTrbCfgEP
.CycleBit
= 1;
4083 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
4084 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
4085 DEBUG ((EFI_D_INFO
, "Configure Hub Slot Context\n"));
4086 Status
= XhcCmdTransfer (
4088 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
4089 XHC_GENERIC_TIMEOUT
,
4090 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
4092 if (EFI_ERROR (Status
)) {
4093 DEBUG ((EFI_D_ERROR
, "XhcConfigHubContext64: Config Endpoint Failed, Status = %r\n", Status
));