3 XHCI transfer scheduling routines.
5 Copyright (c) 2011 - 2020, Intel Corporation. All rights reserved.<BR>
6 Copyright (c) Microsoft Corporation.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
14 Create a command transfer TRB to support XHCI command interfaces.
16 @param Xhc The XHCI Instance.
17 @param CmdTrb The cmd TRB to be executed.
19 @return Created URB or NULL.
24 IN USB_XHCI_INSTANCE
*Xhc
,
25 IN TRB_TEMPLATE
*CmdTrb
30 Urb
= AllocateZeroPool (sizeof (URB
));
35 Urb
->Signature
= XHC_URB_SIG
;
37 Urb
->Ring
= &Xhc
->CmdRing
;
38 XhcSyncTrsRing (Xhc
, Urb
->Ring
);
40 Urb
->TrbStart
= Urb
->Ring
->RingEnqueue
;
41 CopyMem (Urb
->TrbStart
, CmdTrb
, sizeof (TRB_TEMPLATE
));
42 Urb
->TrbStart
->CycleBit
= Urb
->Ring
->RingPCS
& BIT0
;
43 Urb
->TrbEnd
= Urb
->TrbStart
;
49 Execute a XHCI cmd TRB pointed by CmdTrb.
51 @param Xhc The XHCI Instance.
52 @param CmdTrb The cmd TRB to be executed.
53 @param Timeout Indicates the maximum time, in millisecond, which the
54 transfer is allowed to complete.
55 @param EvtTrb The event TRB corresponding to the cmd TRB.
57 @retval EFI_SUCCESS The transfer was completed successfully.
58 @retval EFI_INVALID_PARAMETER Some parameters are invalid.
59 @retval EFI_TIMEOUT The transfer failed due to timeout.
60 @retval EFI_DEVICE_ERROR The transfer failed due to host controller error.
66 IN USB_XHCI_INSTANCE
*Xhc
,
67 IN TRB_TEMPLATE
*CmdTrb
,
69 OUT TRB_TEMPLATE
**EvtTrb
76 // Validate the parameters
78 if ((Xhc
== NULL
) || (CmdTrb
== NULL
)) {
79 return EFI_INVALID_PARAMETER
;
82 Status
= EFI_DEVICE_ERROR
;
84 if (XhcIsHalt (Xhc
) || XhcIsSysError (Xhc
)) {
85 DEBUG ((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
1721 USB_DEV_ROUTE RouteChart
;
1723 Status
= EFI_SUCCESS
;
1724 Retries
= XHC_INIT_DEVICE_SLOT_RETRIES
;
1726 if ((PortState
->PortChangeStatus
& (USB_PORT_STAT_C_CONNECTION
| USB_PORT_STAT_C_ENABLE
| USB_PORT_STAT_C_OVERCURRENT
| USB_PORT_STAT_C_RESET
)) == 0) {
1730 if (ParentRouteChart
.Dword
== 0) {
1731 RouteChart
.Route
.RouteString
= 0;
1732 RouteChart
.Route
.RootPortNum
= Port
+ 1;
1733 RouteChart
.Route
.TierNum
= 1;
1736 RouteChart
.Route
.RouteString
= ParentRouteChart
.Route
.RouteString
| (Port
<< (4 * (ParentRouteChart
.Route
.TierNum
- 1)));
1738 RouteChart
.Route
.RouteString
= ParentRouteChart
.Route
.RouteString
| (15 << (4 * (ParentRouteChart
.Route
.TierNum
- 1)));
1740 RouteChart
.Route
.RootPortNum
= ParentRouteChart
.Route
.RootPortNum
;
1741 RouteChart
.Route
.TierNum
= ParentRouteChart
.Route
.TierNum
+ 1;
1744 SlotId
= XhcRouteStringToSlotId (Xhc
, RouteChart
);
1746 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
1747 Status
= XhcDisableSlotCmd (Xhc
, SlotId
);
1749 Status
= XhcDisableSlotCmd64 (Xhc
, SlotId
);
1753 if (((PortState
->PortStatus
& USB_PORT_STAT_ENABLE
) != 0) &&
1754 ((PortState
->PortStatus
& USB_PORT_STAT_CONNECTION
) != 0)) {
1756 // Has a device attached, Identify device speed after port is enabled.
1758 Speed
= EFI_USB_SPEED_FULL
;
1759 if ((PortState
->PortStatus
& USB_PORT_STAT_LOW_SPEED
) != 0) {
1760 Speed
= EFI_USB_SPEED_LOW
;
1761 } else if ((PortState
->PortStatus
& USB_PORT_STAT_HIGH_SPEED
) != 0) {
1762 Speed
= EFI_USB_SPEED_HIGH
;
1763 } else if ((PortState
->PortStatus
& USB_PORT_STAT_SUPER_SPEED
) != 0) {
1764 Speed
= EFI_USB_SPEED_SUPER
;
1769 // Execute Enable_Slot cmd for attached device, initialize device context and assign device address.
1771 SlotId
= XhcRouteStringToSlotId (Xhc
, RouteChart
);
1772 if ((SlotId
== 0) && ((PortState
->PortChangeStatus
& USB_PORT_STAT_C_RESET
) != 0)) {
1773 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
1774 Status
= XhcInitializeDeviceSlot (Xhc
, ParentRouteChart
, Port
, RouteChart
, Speed
);
1776 Status
= XhcInitializeDeviceSlot64 (Xhc
, ParentRouteChart
, Port
, RouteChart
, Speed
);
1781 // According to the xHCI specification (section 4.6.5), "a USB Transaction
1782 // Error Completion Code for an Address Device Command may be due to a Stall
1783 // response from a device. Software should issue a Disable Slot Command for
1784 // the Device Slot then an Enable Slot Command to recover from this error."
1785 // Therefore, retry the device slot initialization if it fails due to a
1788 } while ((Status
== EFI_DEVICE_ERROR
) && (Retries
-- != 0));
1796 Calculate the device context index by endpoint address and direction.
1798 @param EpAddr The target endpoint number.
1799 @param Direction The direction of the target endpoint.
1801 @return The device context index of endpoint.
1815 Index
= (UINT8
) (2 * EpAddr
);
1816 if (Direction
== EfiUsbDataIn
) {
1824 Find out the actual device address according to the requested device address from UsbBus.
1826 @param Xhc The XHCI Instance.
1827 @param BusDevAddr The requested device address by UsbBus upper driver.
1829 @return The actual device address assigned to the device.
1834 XhcBusDevAddrToSlotId (
1835 IN USB_XHCI_INSTANCE
*Xhc
,
1841 for (Index
= 0; Index
< 255; Index
++) {
1842 if (Xhc
->UsbDevContext
[Index
+ 1].Enabled
&&
1843 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
!= 0) &&
1844 (Xhc
->UsbDevContext
[Index
+ 1].BusDevAddr
== BusDevAddr
)) {
1853 return Xhc
->UsbDevContext
[Index
+ 1].SlotId
;
1857 Find out the slot id according to the device's route string.
1859 @param Xhc The XHCI Instance.
1860 @param RouteString The route string described the device location.
1862 @return The slot id used by the device.
1867 XhcRouteStringToSlotId (
1868 IN USB_XHCI_INSTANCE
*Xhc
,
1869 IN USB_DEV_ROUTE RouteString
1874 for (Index
= 0; Index
< 255; Index
++) {
1875 if (Xhc
->UsbDevContext
[Index
+ 1].Enabled
&&
1876 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
!= 0) &&
1877 (Xhc
->UsbDevContext
[Index
+ 1].RouteString
.Dword
== RouteString
.Dword
)) {
1886 return Xhc
->UsbDevContext
[Index
+ 1].SlotId
;
1890 Synchronize the specified event ring to update the enqueue and dequeue pointer.
1892 @param Xhc The XHCI Instance.
1893 @param EvtRing The event ring to sync.
1895 @retval EFI_SUCCESS The event ring is synchronized successfully.
1901 IN USB_XHCI_INSTANCE
*Xhc
,
1902 IN EVENT_RING
*EvtRing
1906 TRB_TEMPLATE
*EvtTrb1
;
1908 ASSERT (EvtRing
!= NULL
);
1911 // Calculate the EventRingEnqueue and EventRingCCS.
1912 // Note: only support single Segment
1914 EvtTrb1
= EvtRing
->EventRingDequeue
;
1916 for (Index
= 0; Index
< EvtRing
->TrbNumber
; Index
++) {
1917 if (EvtTrb1
->CycleBit
!= EvtRing
->EventRingCCS
) {
1923 if ((UINTN
)EvtTrb1
>= ((UINTN
) EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
1924 EvtTrb1
= EvtRing
->EventRingSeg0
;
1925 EvtRing
->EventRingCCS
= (EvtRing
->EventRingCCS
) ? 0 : 1;
1929 if (Index
< EvtRing
->TrbNumber
) {
1930 EvtRing
->EventRingEnqueue
= EvtTrb1
;
1939 Synchronize the specified transfer ring to update the enqueue and dequeue pointer.
1941 @param Xhc The XHCI Instance.
1942 @param TrsRing The transfer ring to sync.
1944 @retval EFI_SUCCESS The transfer ring is synchronized successfully.
1950 IN USB_XHCI_INSTANCE
*Xhc
,
1951 IN TRANSFER_RING
*TrsRing
1955 TRB_TEMPLATE
*TrsTrb
;
1957 ASSERT (TrsRing
!= NULL
);
1959 // Calculate the latest RingEnqueue and RingPCS
1961 TrsTrb
= TrsRing
->RingEnqueue
;
1962 ASSERT (TrsTrb
!= NULL
);
1964 for (Index
= 0; Index
< TrsRing
->TrbNumber
; Index
++) {
1965 if (TrsTrb
->CycleBit
!= (TrsRing
->RingPCS
& BIT0
)) {
1969 if ((UINT8
) TrsTrb
->Type
== TRB_TYPE_LINK
) {
1970 ASSERT (((LINK_TRB
*)TrsTrb
)->TC
!= 0);
1972 // set cycle bit in Link TRB as normal
1974 ((LINK_TRB
*)TrsTrb
)->CycleBit
= TrsRing
->RingPCS
& BIT0
;
1976 // Toggle PCS maintained by software
1978 TrsRing
->RingPCS
= (TrsRing
->RingPCS
& BIT0
) ? 0 : 1;
1979 TrsTrb
= (TRB_TEMPLATE
*) TrsRing
->RingSeg0
; // Use host address
1983 ASSERT (Index
!= TrsRing
->TrbNumber
);
1985 if (TrsTrb
!= TrsRing
->RingEnqueue
) {
1986 TrsRing
->RingEnqueue
= TrsTrb
;
1990 // Clear the Trb context for enqueue, but reserve the PCS bit
1992 TrsTrb
->Parameter1
= 0;
1993 TrsTrb
->Parameter2
= 0;
1997 TrsTrb
->Control
= 0;
2003 Check if there is a new generated event.
2005 @param Xhc The XHCI Instance.
2006 @param EvtRing The event ring to check.
2007 @param NewEvtTrb The new event TRB found.
2009 @retval EFI_SUCCESS Found a new event TRB at the event ring.
2010 @retval EFI_NOT_READY The event ring has no new event.
2016 IN USB_XHCI_INSTANCE
*Xhc
,
2017 IN EVENT_RING
*EvtRing
,
2018 OUT TRB_TEMPLATE
**NewEvtTrb
2021 ASSERT (EvtRing
!= NULL
);
2023 *NewEvtTrb
= EvtRing
->EventRingDequeue
;
2025 if (EvtRing
->EventRingDequeue
== EvtRing
->EventRingEnqueue
) {
2026 return EFI_NOT_READY
;
2029 EvtRing
->EventRingDequeue
++;
2031 // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.
2033 if ((UINTN
)EvtRing
->EventRingDequeue
>= ((UINTN
) EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
2034 EvtRing
->EventRingDequeue
= EvtRing
->EventRingSeg0
;
2041 Ring the door bell to notify XHCI there is a transaction to be executed.
2043 @param Xhc The XHCI Instance.
2044 @param SlotId The slot id of the target device.
2045 @param Dci The device context index of the target slot or endpoint.
2047 @retval EFI_SUCCESS Successfully ring the door bell.
2053 IN USB_XHCI_INSTANCE
*Xhc
,
2059 XhcWriteDoorBellReg (Xhc
, 0, 0);
2061 XhcWriteDoorBellReg (Xhc
, SlotId
* sizeof (UINT32
), Dci
);
2068 Ring the door bell to notify XHCI there is a transaction to be executed through URB.
2070 @param Xhc The XHCI Instance.
2071 @param Urb The URB to be rung.
2073 @retval EFI_SUCCESS Successfully ring the door bell.
2077 RingIntTransferDoorBell (
2078 IN USB_XHCI_INSTANCE
*Xhc
,
2085 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
2086 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
2087 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
2092 Assign and initialize the device slot for a new device.
2094 @param Xhc The XHCI Instance.
2095 @param ParentRouteChart The route string pointed to the parent device.
2096 @param ParentPort The port at which the device is located.
2097 @param RouteChart The route string pointed to the device.
2098 @param DeviceSpeed The device speed.
2100 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.
2105 XhcInitializeDeviceSlot (
2106 IN USB_XHCI_INSTANCE
*Xhc
,
2107 IN USB_DEV_ROUTE ParentRouteChart
,
2108 IN UINT16 ParentPort
,
2109 IN USB_DEV_ROUTE RouteChart
,
2110 IN UINT8 DeviceSpeed
2114 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
2115 INPUT_CONTEXT
*InputContext
;
2116 DEVICE_CONTEXT
*OutputContext
;
2117 TRANSFER_RING
*EndpointTransferRing
;
2118 CMD_TRB_ADDRESS_DEVICE CmdTrbAddr
;
2119 UINT8 DeviceAddress
;
2120 CMD_TRB_ENABLE_SLOT CmdTrb
;
2123 DEVICE_CONTEXT
*ParentDeviceContext
;
2124 EFI_PHYSICAL_ADDRESS PhyAddr
;
2126 ZeroMem (&CmdTrb
, sizeof (CMD_TRB_ENABLE_SLOT
));
2127 CmdTrb
.CycleBit
= 1;
2128 CmdTrb
.Type
= TRB_TYPE_EN_SLOT
;
2130 Status
= XhcCmdTransfer (
2132 (TRB_TEMPLATE
*) (UINTN
) &CmdTrb
,
2133 XHC_GENERIC_TIMEOUT
,
2134 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2136 if (EFI_ERROR (Status
)) {
2137 DEBUG ((EFI_D_ERROR
, "XhcInitializeDeviceSlot: Enable Slot Failed, Status = %r\n", Status
));
2140 ASSERT (EvtTrb
->SlotId
<= Xhc
->MaxSlotsEn
);
2141 DEBUG ((EFI_D_INFO
, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb
->SlotId
));
2142 SlotId
= (UINT8
)EvtTrb
->SlotId
;
2143 ASSERT (SlotId
!= 0);
2145 ZeroMem (&Xhc
->UsbDevContext
[SlotId
], sizeof (USB_DEV_CONTEXT
));
2146 Xhc
->UsbDevContext
[SlotId
].Enabled
= TRUE
;
2147 Xhc
->UsbDevContext
[SlotId
].SlotId
= SlotId
;
2148 Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
= RouteChart
.Dword
;
2149 Xhc
->UsbDevContext
[SlotId
].ParentRouteString
.Dword
= ParentRouteChart
.Dword
;
2152 // 4.3.3 Device Slot Initialization
2153 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.
2155 InputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (INPUT_CONTEXT
));
2156 ASSERT (InputContext
!= NULL
);
2157 ASSERT (((UINTN
) InputContext
& 0x3F) == 0);
2158 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
2160 Xhc
->UsbDevContext
[SlotId
].InputContext
= (VOID
*) InputContext
;
2163 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1
2164 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input
2165 // Context are affected by the command.
2167 InputContext
->InputControlContext
.Dword2
|= (BIT0
| BIT1
);
2170 // 3) Initialize the Input Slot Context data structure
2172 InputContext
->Slot
.RouteString
= RouteChart
.Route
.RouteString
;
2173 InputContext
->Slot
.Speed
= DeviceSpeed
+ 1;
2174 InputContext
->Slot
.ContextEntries
= 1;
2175 InputContext
->Slot
.RootHubPortNum
= RouteChart
.Route
.RootPortNum
;
2177 if (RouteChart
.Route
.RouteString
) {
2179 // The device is behind of hub device.
2181 ParentSlotId
= XhcRouteStringToSlotId(Xhc
, ParentRouteChart
);
2182 ASSERT (ParentSlotId
!= 0);
2184 //if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context
2186 ParentDeviceContext
= (DEVICE_CONTEXT
*)Xhc
->UsbDevContext
[ParentSlotId
].OutputContext
;
2187 if ((ParentDeviceContext
->Slot
.TTPortNum
== 0) &&
2188 (ParentDeviceContext
->Slot
.TTHubSlotId
== 0)) {
2189 if ((ParentDeviceContext
->Slot
.Speed
== (EFI_USB_SPEED_HIGH
+ 1)) && (DeviceSpeed
< EFI_USB_SPEED_HIGH
)) {
2191 // Full/Low device attached to High speed hub port that isolates the high speed signaling
2192 // environment from Full/Low speed signaling environment for a device
2194 InputContext
->Slot
.TTPortNum
= ParentPort
;
2195 InputContext
->Slot
.TTHubSlotId
= ParentSlotId
;
2199 // Inherit the TT parameters from parent device.
2201 InputContext
->Slot
.TTPortNum
= ParentDeviceContext
->Slot
.TTPortNum
;
2202 InputContext
->Slot
.TTHubSlotId
= ParentDeviceContext
->Slot
.TTHubSlotId
;
2204 // If the device is a High speed device then down the speed to be the same as its parent Hub
2206 if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2207 InputContext
->Slot
.Speed
= ParentDeviceContext
->Slot
.Speed
;
2213 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.
2215 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2216 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0] = EndpointTransferRing
;
2217 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0]);
2219 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).
2221 InputContext
->EP
[0].EPType
= ED_CONTROL_BIDIR
;
2223 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2224 InputContext
->EP
[0].MaxPacketSize
= 512;
2225 } else if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2226 InputContext
->EP
[0].MaxPacketSize
= 64;
2228 InputContext
->EP
[0].MaxPacketSize
= 8;
2231 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints
2232 // 1KB, and Bulk and Isoch endpoints 3KB.
2234 InputContext
->EP
[0].AverageTRBLength
= 8;
2235 InputContext
->EP
[0].MaxBurstSize
= 0;
2236 InputContext
->EP
[0].Interval
= 0;
2237 InputContext
->EP
[0].MaxPStreams
= 0;
2238 InputContext
->EP
[0].Mult
= 0;
2239 InputContext
->EP
[0].CErr
= 3;
2242 // Init the DCS(dequeue cycle state) as the transfer ring's CCS
2244 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2246 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0])->RingSeg0
,
2247 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2249 InputContext
->EP
[0].PtrLo
= XHC_LOW_32BIT (PhyAddr
) | BIT0
;
2250 InputContext
->EP
[0].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2253 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
2255 OutputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (DEVICE_CONTEXT
));
2256 ASSERT (OutputContext
!= NULL
);
2257 ASSERT (((UINTN
) OutputContext
& 0x3F) == 0);
2258 ZeroMem (OutputContext
, sizeof (DEVICE_CONTEXT
));
2260 Xhc
->UsbDevContext
[SlotId
].OutputContext
= OutputContext
;
2262 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with
2263 // a pointer to the Output Device Context data structure (6.2.1).
2265 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, OutputContext
, sizeof (DEVICE_CONTEXT
));
2267 // Fill DCBAA with PCI device address
2269 Xhc
->DCBAA
[SlotId
] = (UINT64
) (UINTN
) PhyAddr
;
2272 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input
2273 // Context data structure described above.
2275 // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request
2278 gBS
->Stall (XHC_RESET_RECOVERY_DELAY
);
2279 ZeroMem (&CmdTrbAddr
, sizeof (CmdTrbAddr
));
2280 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT
));
2281 CmdTrbAddr
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2282 CmdTrbAddr
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2283 CmdTrbAddr
.CycleBit
= 1;
2284 CmdTrbAddr
.Type
= TRB_TYPE_ADDRESS_DEV
;
2285 CmdTrbAddr
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
2286 Status
= XhcCmdTransfer (
2288 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbAddr
,
2289 XHC_GENERIC_TIMEOUT
,
2290 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2292 if (!EFI_ERROR (Status
)) {
2293 DeviceAddress
= (UINT8
) ((DEVICE_CONTEXT
*) OutputContext
)->Slot
.DeviceAddress
;
2294 DEBUG ((EFI_D_INFO
, " Address %d assigned successfully\n", DeviceAddress
));
2295 Xhc
->UsbDevContext
[SlotId
].XhciDevAddr
= DeviceAddress
;
2297 DEBUG ((DEBUG_INFO
, " Address %d assigned unsuccessfully\n"));
2298 XhcDisableSlotCmd (Xhc
, SlotId
);
2305 Assign and initialize the device slot for a new device.
2307 @param Xhc The XHCI Instance.
2308 @param ParentRouteChart The route string pointed to the parent device.
2309 @param ParentPort The port at which the device is located.
2310 @param RouteChart The route string pointed to the device.
2311 @param DeviceSpeed The device speed.
2313 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.
2318 XhcInitializeDeviceSlot64 (
2319 IN USB_XHCI_INSTANCE
*Xhc
,
2320 IN USB_DEV_ROUTE ParentRouteChart
,
2321 IN UINT16 ParentPort
,
2322 IN USB_DEV_ROUTE RouteChart
,
2323 IN UINT8 DeviceSpeed
2327 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
2328 INPUT_CONTEXT_64
*InputContext
;
2329 DEVICE_CONTEXT_64
*OutputContext
;
2330 TRANSFER_RING
*EndpointTransferRing
;
2331 CMD_TRB_ADDRESS_DEVICE CmdTrbAddr
;
2332 UINT8 DeviceAddress
;
2333 CMD_TRB_ENABLE_SLOT CmdTrb
;
2336 DEVICE_CONTEXT_64
*ParentDeviceContext
;
2337 EFI_PHYSICAL_ADDRESS PhyAddr
;
2339 ZeroMem (&CmdTrb
, sizeof (CMD_TRB_ENABLE_SLOT
));
2340 CmdTrb
.CycleBit
= 1;
2341 CmdTrb
.Type
= TRB_TYPE_EN_SLOT
;
2343 Status
= XhcCmdTransfer (
2345 (TRB_TEMPLATE
*) (UINTN
) &CmdTrb
,
2346 XHC_GENERIC_TIMEOUT
,
2347 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2349 if (EFI_ERROR (Status
)) {
2350 DEBUG ((EFI_D_ERROR
, "XhcInitializeDeviceSlot64: Enable Slot Failed, Status = %r\n", Status
));
2353 ASSERT (EvtTrb
->SlotId
<= Xhc
->MaxSlotsEn
);
2354 DEBUG ((EFI_D_INFO
, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb
->SlotId
));
2355 SlotId
= (UINT8
)EvtTrb
->SlotId
;
2356 ASSERT (SlotId
!= 0);
2358 ZeroMem (&Xhc
->UsbDevContext
[SlotId
], sizeof (USB_DEV_CONTEXT
));
2359 Xhc
->UsbDevContext
[SlotId
].Enabled
= TRUE
;
2360 Xhc
->UsbDevContext
[SlotId
].SlotId
= SlotId
;
2361 Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
= RouteChart
.Dword
;
2362 Xhc
->UsbDevContext
[SlotId
].ParentRouteString
.Dword
= ParentRouteChart
.Dword
;
2365 // 4.3.3 Device Slot Initialization
2366 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.
2368 InputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (INPUT_CONTEXT_64
));
2369 ASSERT (InputContext
!= NULL
);
2370 ASSERT (((UINTN
) InputContext
& 0x3F) == 0);
2371 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
2373 Xhc
->UsbDevContext
[SlotId
].InputContext
= (VOID
*) InputContext
;
2376 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1
2377 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input
2378 // Context are affected by the command.
2380 InputContext
->InputControlContext
.Dword2
|= (BIT0
| BIT1
);
2383 // 3) Initialize the Input Slot Context data structure
2385 InputContext
->Slot
.RouteString
= RouteChart
.Route
.RouteString
;
2386 InputContext
->Slot
.Speed
= DeviceSpeed
+ 1;
2387 InputContext
->Slot
.ContextEntries
= 1;
2388 InputContext
->Slot
.RootHubPortNum
= RouteChart
.Route
.RootPortNum
;
2390 if (RouteChart
.Route
.RouteString
) {
2392 // The device is behind of hub device.
2394 ParentSlotId
= XhcRouteStringToSlotId(Xhc
, ParentRouteChart
);
2395 ASSERT (ParentSlotId
!= 0);
2397 //if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context
2399 ParentDeviceContext
= (DEVICE_CONTEXT_64
*)Xhc
->UsbDevContext
[ParentSlotId
].OutputContext
;
2400 if ((ParentDeviceContext
->Slot
.TTPortNum
== 0) &&
2401 (ParentDeviceContext
->Slot
.TTHubSlotId
== 0)) {
2402 if ((ParentDeviceContext
->Slot
.Speed
== (EFI_USB_SPEED_HIGH
+ 1)) && (DeviceSpeed
< EFI_USB_SPEED_HIGH
)) {
2404 // Full/Low device attached to High speed hub port that isolates the high speed signaling
2405 // environment from Full/Low speed signaling environment for a device
2407 InputContext
->Slot
.TTPortNum
= ParentPort
;
2408 InputContext
->Slot
.TTHubSlotId
= ParentSlotId
;
2412 // Inherit the TT parameters from parent device.
2414 InputContext
->Slot
.TTPortNum
= ParentDeviceContext
->Slot
.TTPortNum
;
2415 InputContext
->Slot
.TTHubSlotId
= ParentDeviceContext
->Slot
.TTHubSlotId
;
2417 // If the device is a High speed device then down the speed to be the same as its parent Hub
2419 if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2420 InputContext
->Slot
.Speed
= ParentDeviceContext
->Slot
.Speed
;
2426 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.
2428 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2429 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0] = EndpointTransferRing
;
2430 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0]);
2432 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).
2434 InputContext
->EP
[0].EPType
= ED_CONTROL_BIDIR
;
2436 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2437 InputContext
->EP
[0].MaxPacketSize
= 512;
2438 } else if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2439 InputContext
->EP
[0].MaxPacketSize
= 64;
2441 InputContext
->EP
[0].MaxPacketSize
= 8;
2444 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints
2445 // 1KB, and Bulk and Isoch endpoints 3KB.
2447 InputContext
->EP
[0].AverageTRBLength
= 8;
2448 InputContext
->EP
[0].MaxBurstSize
= 0;
2449 InputContext
->EP
[0].Interval
= 0;
2450 InputContext
->EP
[0].MaxPStreams
= 0;
2451 InputContext
->EP
[0].Mult
= 0;
2452 InputContext
->EP
[0].CErr
= 3;
2455 // Init the DCS(dequeue cycle state) as the transfer ring's CCS
2457 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2459 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0])->RingSeg0
,
2460 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2462 InputContext
->EP
[0].PtrLo
= XHC_LOW_32BIT (PhyAddr
) | BIT0
;
2463 InputContext
->EP
[0].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2466 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
2468 OutputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (DEVICE_CONTEXT_64
));
2469 ASSERT (OutputContext
!= NULL
);
2470 ASSERT (((UINTN
) OutputContext
& 0x3F) == 0);
2471 ZeroMem (OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2473 Xhc
->UsbDevContext
[SlotId
].OutputContext
= OutputContext
;
2475 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with
2476 // a pointer to the Output Device Context data structure (6.2.1).
2478 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2480 // Fill DCBAA with PCI device address
2482 Xhc
->DCBAA
[SlotId
] = (UINT64
) (UINTN
) PhyAddr
;
2485 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input
2486 // Context data structure described above.
2488 // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request
2491 gBS
->Stall (XHC_RESET_RECOVERY_DELAY
);
2492 ZeroMem (&CmdTrbAddr
, sizeof (CmdTrbAddr
));
2493 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT_64
));
2494 CmdTrbAddr
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2495 CmdTrbAddr
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2496 CmdTrbAddr
.CycleBit
= 1;
2497 CmdTrbAddr
.Type
= TRB_TYPE_ADDRESS_DEV
;
2498 CmdTrbAddr
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
2499 Status
= XhcCmdTransfer (
2501 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbAddr
,
2502 XHC_GENERIC_TIMEOUT
,
2503 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2505 if (!EFI_ERROR (Status
)) {
2506 DeviceAddress
= (UINT8
) ((DEVICE_CONTEXT_64
*) OutputContext
)->Slot
.DeviceAddress
;
2507 DEBUG ((EFI_D_INFO
, " Address %d assigned successfully\n", DeviceAddress
));
2508 Xhc
->UsbDevContext
[SlotId
].XhciDevAddr
= DeviceAddress
;
2510 DEBUG ((DEBUG_INFO
, " Address %d assigned unsuccessfully\n"));
2511 XhcDisableSlotCmd64 (Xhc
, SlotId
);
2519 Disable the specified device slot.
2521 @param Xhc The XHCI Instance.
2522 @param SlotId The slot id to be disabled.
2524 @retval EFI_SUCCESS Successfully disable the device slot.
2530 IN USB_XHCI_INSTANCE
*Xhc
,
2535 TRB_TEMPLATE
*EvtTrb
;
2536 CMD_TRB_DISABLE_SLOT CmdTrbDisSlot
;
2541 // Disable the device slots occupied by these devices on its downstream ports.
2542 // Entry 0 is reserved.
2544 for (Index
= 0; Index
< 255; Index
++) {
2545 if (!Xhc
->UsbDevContext
[Index
+ 1].Enabled
||
2546 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
== 0) ||
2547 (Xhc
->UsbDevContext
[Index
+ 1].ParentRouteString
.Dword
!= Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
)) {
2551 Status
= XhcDisableSlotCmd (Xhc
, Xhc
->UsbDevContext
[Index
+ 1].SlotId
);
2553 if (EFI_ERROR (Status
)) {
2554 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
2555 Xhc
->UsbDevContext
[Index
+ 1].SlotId
= 0;
2560 // Construct the disable slot command
2562 DEBUG ((EFI_D_INFO
, "Disable device slot %d!\n", SlotId
));
2564 ZeroMem (&CmdTrbDisSlot
, sizeof (CmdTrbDisSlot
));
2565 CmdTrbDisSlot
.CycleBit
= 1;
2566 CmdTrbDisSlot
.Type
= TRB_TYPE_DIS_SLOT
;
2567 CmdTrbDisSlot
.SlotId
= SlotId
;
2568 Status
= XhcCmdTransfer (
2570 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbDisSlot
,
2571 XHC_GENERIC_TIMEOUT
,
2572 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2574 if (EFI_ERROR (Status
)) {
2575 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status
));
2579 // Free the slot's device context entry
2581 Xhc
->DCBAA
[SlotId
] = 0;
2584 // Free the slot related data structure
2586 for (Index
= 0; Index
< 31; Index
++) {
2587 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] != NULL
) {
2588 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
])->RingSeg0
;
2589 if (RingSeg
!= NULL
) {
2590 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
2592 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
]);
2593 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] = NULL
;
2597 for (Index
= 0; Index
< Xhc
->UsbDevContext
[SlotId
].DevDesc
.NumConfigurations
; Index
++) {
2598 if (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
] != NULL
) {
2599 FreePool (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
]);
2603 if (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
!= NULL
) {
2604 FreePool (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
);
2607 if (Xhc
->UsbDevContext
[SlotId
].InputContext
!= NULL
) {
2608 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT
));
2611 if (Xhc
->UsbDevContext
[SlotId
].OutputContext
!= NULL
) {
2612 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].OutputContext
, sizeof (DEVICE_CONTEXT
));
2615 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established
2616 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to
2617 // remove urb from XHCI's asynchronous transfer list.
2619 Xhc
->UsbDevContext
[SlotId
].Enabled
= FALSE
;
2620 Xhc
->UsbDevContext
[SlotId
].SlotId
= 0;
2626 Disable the specified device slot.
2628 @param Xhc The XHCI Instance.
2629 @param SlotId The slot id to be disabled.
2631 @retval EFI_SUCCESS Successfully disable the device slot.
2636 XhcDisableSlotCmd64 (
2637 IN USB_XHCI_INSTANCE
*Xhc
,
2642 TRB_TEMPLATE
*EvtTrb
;
2643 CMD_TRB_DISABLE_SLOT CmdTrbDisSlot
;
2648 // Disable the device slots occupied by these devices on its downstream ports.
2649 // Entry 0 is reserved.
2651 for (Index
= 0; Index
< 255; Index
++) {
2652 if (!Xhc
->UsbDevContext
[Index
+ 1].Enabled
||
2653 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
== 0) ||
2654 (Xhc
->UsbDevContext
[Index
+ 1].ParentRouteString
.Dword
!= Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
)) {
2658 Status
= XhcDisableSlotCmd64 (Xhc
, Xhc
->UsbDevContext
[Index
+ 1].SlotId
);
2660 if (EFI_ERROR (Status
)) {
2661 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
2662 Xhc
->UsbDevContext
[Index
+ 1].SlotId
= 0;
2667 // Construct the disable slot command
2669 DEBUG ((EFI_D_INFO
, "Disable device slot %d!\n", SlotId
));
2671 ZeroMem (&CmdTrbDisSlot
, sizeof (CmdTrbDisSlot
));
2672 CmdTrbDisSlot
.CycleBit
= 1;
2673 CmdTrbDisSlot
.Type
= TRB_TYPE_DIS_SLOT
;
2674 CmdTrbDisSlot
.SlotId
= SlotId
;
2675 Status
= XhcCmdTransfer (
2677 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbDisSlot
,
2678 XHC_GENERIC_TIMEOUT
,
2679 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2681 if (EFI_ERROR (Status
)) {
2682 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status
));
2686 // Free the slot's device context entry
2688 Xhc
->DCBAA
[SlotId
] = 0;
2691 // Free the slot related data structure
2693 for (Index
= 0; Index
< 31; Index
++) {
2694 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] != NULL
) {
2695 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
])->RingSeg0
;
2696 if (RingSeg
!= NULL
) {
2697 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
2699 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
]);
2700 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] = NULL
;
2704 for (Index
= 0; Index
< Xhc
->UsbDevContext
[SlotId
].DevDesc
.NumConfigurations
; Index
++) {
2705 if (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
] != NULL
) {
2706 FreePool (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
]);
2710 if (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
!= NULL
) {
2711 FreePool (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
);
2714 if (Xhc
->UsbDevContext
[SlotId
].InputContext
!= NULL
) {
2715 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT_64
));
2718 if (Xhc
->UsbDevContext
[SlotId
].OutputContext
!= NULL
) {
2719 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2722 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established
2723 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to
2724 // remove urb from XHCI's asynchronous transfer list.
2726 Xhc
->UsbDevContext
[SlotId
].Enabled
= FALSE
;
2727 Xhc
->UsbDevContext
[SlotId
].SlotId
= 0;
2733 Initialize endpoint context in input context.
2735 @param Xhc The XHCI Instance.
2736 @param SlotId The slot id to be configured.
2737 @param DeviceSpeed The device's speed.
2738 @param InputContext The pointer to the input context.
2739 @param IfDesc The pointer to the usb device interface descriptor.
2741 @return The maximum device context index of endpoint.
2746 XhcInitializeEndpointContext (
2747 IN USB_XHCI_INSTANCE
*Xhc
,
2749 IN UINT8 DeviceSpeed
,
2750 IN INPUT_CONTEXT
*InputContext
,
2751 IN USB_INTERFACE_DESCRIPTOR
*IfDesc
2754 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
2761 EFI_PHYSICAL_ADDRESS PhyAddr
;
2763 TRANSFER_RING
*EndpointTransferRing
;
2767 NumEp
= IfDesc
->NumEndpoints
;
2769 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDesc
+ 1);
2770 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
2771 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
2772 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2775 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
2776 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2780 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
2781 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
2783 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
2789 InputContext
->InputControlContext
.Dword2
|= (BIT0
<< Dci
);
2790 InputContext
->EP
[Dci
-1].MaxPacketSize
= EpDesc
->MaxPacketSize
;
2792 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2794 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.
2796 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2798 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2801 switch (EpDesc
->Attributes
& USB_ENDPOINT_TYPE_MASK
) {
2802 case USB_ENDPOINT_BULK
:
2803 if (Direction
== EfiUsbDataIn
) {
2804 InputContext
->EP
[Dci
-1].CErr
= 3;
2805 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_IN
;
2807 InputContext
->EP
[Dci
-1].CErr
= 3;
2808 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_OUT
;
2811 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2812 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2813 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
2814 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
2815 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2816 DEBUG ((DEBUG_INFO
, "Endpoint[%x]: Created BULK ring [%p~%p)\n",
2817 EpDesc
->EndpointAddress
,
2818 EndpointTransferRing
->RingSeg0
,
2819 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2824 case USB_ENDPOINT_ISO
:
2825 if (Direction
== EfiUsbDataIn
) {
2826 InputContext
->EP
[Dci
-1].CErr
= 0;
2827 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_IN
;
2829 InputContext
->EP
[Dci
-1].CErr
= 0;
2830 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_OUT
;
2833 // Get the bInterval from descriptor and init the the interval field of endpoint context.
2834 // Refer to XHCI 1.1 spec section 6.2.3.6.
2836 if (DeviceSpeed
== EFI_USB_SPEED_FULL
) {
2837 Interval
= EpDesc
->Interval
;
2838 ASSERT (Interval
>= 1 && Interval
<= 16);
2839 InputContext
->EP
[Dci
-1].Interval
= Interval
+ 2;
2840 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2841 Interval
= EpDesc
->Interval
;
2842 ASSERT (Interval
>= 1 && Interval
<= 16);
2843 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2847 // Do not support isochronous transfer now.
2849 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
2850 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2852 case USB_ENDPOINT_INTERRUPT
:
2853 if (Direction
== EfiUsbDataIn
) {
2854 InputContext
->EP
[Dci
-1].CErr
= 3;
2855 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_IN
;
2857 InputContext
->EP
[Dci
-1].CErr
= 3;
2858 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_OUT
;
2860 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2861 InputContext
->EP
[Dci
-1].MaxESITPayload
= EpDesc
->MaxPacketSize
;
2863 // Get the bInterval from descriptor and init the the interval field of endpoint context
2865 if ((DeviceSpeed
== EFI_USB_SPEED_FULL
) || (DeviceSpeed
== EFI_USB_SPEED_LOW
)) {
2866 Interval
= EpDesc
->Interval
;
2868 // Calculate through the bInterval field of Endpoint descriptor.
2870 ASSERT (Interval
!= 0);
2871 InputContext
->EP
[Dci
-1].Interval
= (UINT32
)HighBitSet32((UINT32
)Interval
) + 3;
2872 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2873 Interval
= EpDesc
->Interval
;
2874 ASSERT (Interval
>= 1 && Interval
<= 16);
2876 // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
2878 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2879 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2880 InputContext
->EP
[Dci
-1].MaxESITPayload
= 0x0002;
2881 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2882 InputContext
->EP
[Dci
-1].CErr
= 3;
2885 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2886 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
2887 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
2888 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2889 DEBUG ((DEBUG_INFO
, "Endpoint[%x]: Created INT ring [%p~%p)\n",
2890 EpDesc
->EndpointAddress
,
2891 EndpointTransferRing
->RingSeg0
,
2892 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2897 case USB_ENDPOINT_CONTROL
:
2899 // Do not support control transfer now.
2901 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext: Unsupport Control EP found, Transfer ring is not allocated.\n"));
2903 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext: Unknown EP found, Transfer ring is not allocated.\n"));
2904 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2908 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2910 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingSeg0
,
2911 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2913 PhyAddr
&= ~((EFI_PHYSICAL_ADDRESS
)0x0F);
2914 PhyAddr
|= (EFI_PHYSICAL_ADDRESS
)((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingPCS
;
2915 InputContext
->EP
[Dci
-1].PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2916 InputContext
->EP
[Dci
-1].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2918 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2925 Initialize endpoint context in input context.
2927 @param Xhc The XHCI Instance.
2928 @param SlotId The slot id to be configured.
2929 @param DeviceSpeed The device's speed.
2930 @param InputContext The pointer to the input context.
2931 @param IfDesc The pointer to the usb device interface descriptor.
2933 @return The maximum device context index of endpoint.
2938 XhcInitializeEndpointContext64 (
2939 IN USB_XHCI_INSTANCE
*Xhc
,
2941 IN UINT8 DeviceSpeed
,
2942 IN INPUT_CONTEXT_64
*InputContext
,
2943 IN USB_INTERFACE_DESCRIPTOR
*IfDesc
2946 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
2953 EFI_PHYSICAL_ADDRESS PhyAddr
;
2955 TRANSFER_RING
*EndpointTransferRing
;
2959 NumEp
= IfDesc
->NumEndpoints
;
2961 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDesc
+ 1);
2962 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
2963 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
2964 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2967 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
2968 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2972 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
2973 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
2975 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
2981 InputContext
->InputControlContext
.Dword2
|= (BIT0
<< Dci
);
2982 InputContext
->EP
[Dci
-1].MaxPacketSize
= EpDesc
->MaxPacketSize
;
2984 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2986 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.
2988 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2990 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2993 switch (EpDesc
->Attributes
& USB_ENDPOINT_TYPE_MASK
) {
2994 case USB_ENDPOINT_BULK
:
2995 if (Direction
== EfiUsbDataIn
) {
2996 InputContext
->EP
[Dci
-1].CErr
= 3;
2997 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_IN
;
2999 InputContext
->EP
[Dci
-1].CErr
= 3;
3000 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_OUT
;
3003 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3004 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
3005 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
3006 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
3007 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
3008 DEBUG ((DEBUG_INFO
, "Endpoint64[%x]: Created BULK ring [%p~%p)\n",
3009 EpDesc
->EndpointAddress
,
3010 EndpointTransferRing
->RingSeg0
,
3011 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
3016 case USB_ENDPOINT_ISO
:
3017 if (Direction
== EfiUsbDataIn
) {
3018 InputContext
->EP
[Dci
-1].CErr
= 0;
3019 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_IN
;
3021 InputContext
->EP
[Dci
-1].CErr
= 0;
3022 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_OUT
;
3025 // Get the bInterval from descriptor and init the the interval field of endpoint context.
3026 // Refer to XHCI 1.1 spec section 6.2.3.6.
3028 if (DeviceSpeed
== EFI_USB_SPEED_FULL
) {
3029 Interval
= EpDesc
->Interval
;
3030 ASSERT (Interval
>= 1 && Interval
<= 16);
3031 InputContext
->EP
[Dci
-1].Interval
= Interval
+ 2;
3032 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
3033 Interval
= EpDesc
->Interval
;
3034 ASSERT (Interval
>= 1 && Interval
<= 16);
3035 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
3039 // Do not support isochronous transfer now.
3041 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext64: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
3042 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3044 case USB_ENDPOINT_INTERRUPT
:
3045 if (Direction
== EfiUsbDataIn
) {
3046 InputContext
->EP
[Dci
-1].CErr
= 3;
3047 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_IN
;
3049 InputContext
->EP
[Dci
-1].CErr
= 3;
3050 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_OUT
;
3052 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3053 InputContext
->EP
[Dci
-1].MaxESITPayload
= EpDesc
->MaxPacketSize
;
3055 // Get the bInterval from descriptor and init the the interval field of endpoint context
3057 if ((DeviceSpeed
== EFI_USB_SPEED_FULL
) || (DeviceSpeed
== EFI_USB_SPEED_LOW
)) {
3058 Interval
= EpDesc
->Interval
;
3060 // Calculate through the bInterval field of Endpoint descriptor.
3062 ASSERT (Interval
!= 0);
3063 InputContext
->EP
[Dci
-1].Interval
= (UINT32
)HighBitSet32((UINT32
)Interval
) + 3;
3064 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
3065 Interval
= EpDesc
->Interval
;
3066 ASSERT (Interval
>= 1 && Interval
<= 16);
3068 // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
3070 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
3071 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3072 InputContext
->EP
[Dci
-1].MaxESITPayload
= 0x0002;
3073 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
3074 InputContext
->EP
[Dci
-1].CErr
= 3;
3077 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
3078 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
3079 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
3080 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
3081 DEBUG ((DEBUG_INFO
, "Endpoint64[%x]: Created INT ring [%p~%p)\n",
3082 EpDesc
->EndpointAddress
,
3083 EndpointTransferRing
->RingSeg0
,
3084 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
3089 case USB_ENDPOINT_CONTROL
:
3091 // Do not support control transfer now.
3093 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext64: Unsupport Control EP found, Transfer ring is not allocated.\n"));
3095 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext64: Unknown EP found, Transfer ring is not allocated.\n"));
3096 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3100 PhyAddr
= UsbHcGetPciAddrForHostAddr (
3102 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingSeg0
,
3103 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
3105 PhyAddr
&= ~((EFI_PHYSICAL_ADDRESS
)0x0F);
3106 PhyAddr
|= (EFI_PHYSICAL_ADDRESS
)((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingPCS
;
3107 InputContext
->EP
[Dci
-1].PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3108 InputContext
->EP
[Dci
-1].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3110 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3117 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
3119 @param Xhc The XHCI Instance.
3120 @param SlotId The slot id to be configured.
3121 @param DeviceSpeed The device's speed.
3122 @param ConfigDesc The pointer to the usb device configuration descriptor.
3124 @retval EFI_SUCCESS Successfully configure all the device endpoints.
3130 IN USB_XHCI_INSTANCE
*Xhc
,
3132 IN UINT8 DeviceSpeed
,
3133 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
3137 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3141 EFI_PHYSICAL_ADDRESS PhyAddr
;
3143 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3144 INPUT_CONTEXT
*InputContext
;
3145 DEVICE_CONTEXT
*OutputContext
;
3146 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3148 // 4.6.6 Configure Endpoint
3150 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3151 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3152 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3153 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT
));
3155 ASSERT (ConfigDesc
!= NULL
);
3159 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3160 for (Index
= 0; Index
< ConfigDesc
->NumInterfaces
; Index
++) {
3161 while ((IfDesc
->DescriptorType
!= USB_DESC_TYPE_INTERFACE
) || (IfDesc
->AlternateSetting
!= 0)) {
3162 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3165 if (IfDesc
->Length
< sizeof (USB_INTERFACE_DESCRIPTOR
)) {
3166 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3170 Dci
= XhcInitializeEndpointContext (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDesc
);
3175 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3178 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3179 InputContext
->Slot
.ContextEntries
= MaxDci
;
3181 // configure endpoint
3183 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3184 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3185 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3186 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3187 CmdTrbCfgEP
.CycleBit
= 1;
3188 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3189 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3190 DEBUG ((EFI_D_INFO
, "Configure Endpoint\n"));
3191 Status
= XhcCmdTransfer (
3193 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3194 XHC_GENERIC_TIMEOUT
,
3195 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3197 if (EFI_ERROR (Status
)) {
3198 DEBUG ((EFI_D_ERROR
, "XhcSetConfigCmd: Config Endpoint Failed, Status = %r\n", Status
));
3200 Xhc
->UsbDevContext
[SlotId
].ActiveConfiguration
= ConfigDesc
->ConfigurationValue
;
3207 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
3209 @param Xhc The XHCI Instance.
3210 @param SlotId The slot id to be configured.
3211 @param DeviceSpeed The device's speed.
3212 @param ConfigDesc The pointer to the usb device configuration descriptor.
3214 @retval EFI_SUCCESS Successfully configure all the device endpoints.
3220 IN USB_XHCI_INSTANCE
*Xhc
,
3222 IN UINT8 DeviceSpeed
,
3223 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
3227 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3231 EFI_PHYSICAL_ADDRESS PhyAddr
;
3233 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3234 INPUT_CONTEXT_64
*InputContext
;
3235 DEVICE_CONTEXT_64
*OutputContext
;
3236 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3238 // 4.6.6 Configure Endpoint
3240 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3241 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3242 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3243 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT_64
));
3245 ASSERT (ConfigDesc
!= NULL
);
3249 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3250 for (Index
= 0; Index
< ConfigDesc
->NumInterfaces
; Index
++) {
3251 while ((IfDesc
->DescriptorType
!= USB_DESC_TYPE_INTERFACE
) || (IfDesc
->AlternateSetting
!= 0)) {
3252 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3255 if (IfDesc
->Length
< sizeof (USB_INTERFACE_DESCRIPTOR
)) {
3256 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3260 Dci
= XhcInitializeEndpointContext64 (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDesc
);
3265 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3268 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3269 InputContext
->Slot
.ContextEntries
= MaxDci
;
3271 // configure endpoint
3273 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3274 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3275 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3276 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3277 CmdTrbCfgEP
.CycleBit
= 1;
3278 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3279 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3280 DEBUG ((EFI_D_INFO
, "Configure Endpoint\n"));
3281 Status
= XhcCmdTransfer (
3283 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3284 XHC_GENERIC_TIMEOUT
,
3285 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3287 if (EFI_ERROR (Status
)) {
3288 DEBUG ((EFI_D_ERROR
, "XhcSetConfigCmd64: Config Endpoint Failed, Status = %r\n", Status
));
3290 Xhc
->UsbDevContext
[SlotId
].ActiveConfiguration
= ConfigDesc
->ConfigurationValue
;
3297 Stop endpoint through XHCI's Stop_Endpoint cmd.
3299 @param Xhc The XHCI Instance.
3300 @param SlotId The slot id to be configured.
3301 @param Dci The device context index of endpoint.
3302 @param PendingUrb The pending URB to check completion status when stopping the end point.
3304 @retval EFI_SUCCESS Stop endpoint successfully.
3305 @retval Others Failed to stop endpoint.
3311 IN USB_XHCI_INSTANCE
*Xhc
,
3314 IN URB
*PendingUrb OPTIONAL
3318 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3319 CMD_TRB_STOP_ENDPOINT CmdTrbStopED
;
3321 DEBUG ((EFI_D_INFO
, "XhcStopEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId
, Dci
));
3324 // When XhcCheckUrbResult waits for the Stop_Endpoint completion, it also checks
3325 // the PendingUrb completion status, because it's possible that the PendingUrb is
3326 // finished just before stopping the end point, but after the looping check.
3328 // The PendingUrb could be passed to XhcCmdTransfer to XhcExecTransfer to XhcCheckUrbResult
3329 // through function parameter, but That will cause every consumer of XhcCmdTransfer,
3330 // XhcExecTransfer and XhcCheckUrbResult pass a NULL PendingUrb.
3331 // But actually only XhcCheckUrbResult is aware of the PendingUrb.
3332 // So we choose to save the PendingUrb into the USB_XHCI_INSTANCE and use it in XhcCheckUrbResult.
3334 ASSERT (Xhc
->PendingUrb
== NULL
);
3335 Xhc
->PendingUrb
= PendingUrb
;
3337 // Reset the URB result from Timeout to NoError.
3338 // The USB result will be:
3339 // changed to Timeout when Stop/StopInvalidLength Transfer Event is received, or
3340 // remain NoError when Success/ShortPacket Transfer Event is received.
3342 if (PendingUrb
!= NULL
) {
3343 PendingUrb
->Result
= EFI_USB_NOERROR
;
3347 // Send stop endpoint command to transit Endpoint from running to stop state
3349 ZeroMem (&CmdTrbStopED
, sizeof (CmdTrbStopED
));
3350 CmdTrbStopED
.CycleBit
= 1;
3351 CmdTrbStopED
.Type
= TRB_TYPE_STOP_ENDPOINT
;
3352 CmdTrbStopED
.EDID
= Dci
;
3353 CmdTrbStopED
.SlotId
= SlotId
;
3354 Status
= XhcCmdTransfer (
3356 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbStopED
,
3357 XHC_GENERIC_TIMEOUT
,
3358 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3360 if (EFI_ERROR(Status
)) {
3361 DEBUG ((EFI_D_ERROR
, "XhcStopEndpoint: Stop Endpoint Failed, Status = %r\n", Status
));
3364 Xhc
->PendingUrb
= NULL
;
3370 Reset endpoint through XHCI's Reset_Endpoint cmd.
3372 @param Xhc The XHCI Instance.
3373 @param SlotId The slot id to be configured.
3374 @param Dci The device context index of endpoint.
3376 @retval EFI_SUCCESS Reset endpoint successfully.
3377 @retval Others Failed to reset endpoint.
3383 IN USB_XHCI_INSTANCE
*Xhc
,
3389 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3390 CMD_TRB_RESET_ENDPOINT CmdTrbResetED
;
3392 DEBUG ((EFI_D_INFO
, "XhcResetEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId
, Dci
));
3395 // Send stop endpoint command to transit Endpoint from running to stop state
3397 ZeroMem (&CmdTrbResetED
, sizeof (CmdTrbResetED
));
3398 CmdTrbResetED
.CycleBit
= 1;
3399 CmdTrbResetED
.Type
= TRB_TYPE_RESET_ENDPOINT
;
3400 CmdTrbResetED
.EDID
= Dci
;
3401 CmdTrbResetED
.SlotId
= SlotId
;
3402 Status
= XhcCmdTransfer (
3404 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbResetED
,
3405 XHC_GENERIC_TIMEOUT
,
3406 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3408 if (EFI_ERROR(Status
)) {
3409 DEBUG ((EFI_D_ERROR
, "XhcResetEndpoint: Reset Endpoint Failed, Status = %r\n", Status
));
3416 Set transfer ring dequeue pointer through XHCI's Set_Tr_Dequeue_Pointer cmd.
3418 @param Xhc The XHCI Instance.
3419 @param SlotId The slot id to be configured.
3420 @param Dci The device context index of endpoint.
3421 @param Urb The dequeue pointer of the transfer ring specified
3422 by the urb to be updated.
3424 @retval EFI_SUCCESS Set transfer ring dequeue pointer succeeds.
3425 @retval Others Failed to set transfer ring dequeue pointer.
3430 XhcSetTrDequeuePointer (
3431 IN USB_XHCI_INSTANCE
*Xhc
,
3438 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3439 CMD_SET_TR_DEQ_POINTER CmdSetTRDeq
;
3440 EFI_PHYSICAL_ADDRESS PhyAddr
;
3442 DEBUG ((EFI_D_INFO
, "XhcSetTrDequeuePointer: Slot = 0x%x, Dci = 0x%x, Urb = 0x%x\n", SlotId
, Dci
, Urb
));
3445 // Send stop endpoint command to transit Endpoint from running to stop state
3447 ZeroMem (&CmdSetTRDeq
, sizeof (CmdSetTRDeq
));
3448 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Urb
->Ring
->RingEnqueue
, sizeof (CMD_SET_TR_DEQ_POINTER
));
3449 CmdSetTRDeq
.PtrLo
= XHC_LOW_32BIT (PhyAddr
) | Urb
->Ring
->RingPCS
;
3450 CmdSetTRDeq
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3451 CmdSetTRDeq
.CycleBit
= 1;
3452 CmdSetTRDeq
.Type
= TRB_TYPE_SET_TR_DEQUE
;
3453 CmdSetTRDeq
.Endpoint
= Dci
;
3454 CmdSetTRDeq
.SlotId
= SlotId
;
3455 Status
= XhcCmdTransfer (
3457 (TRB_TEMPLATE
*) (UINTN
) &CmdSetTRDeq
,
3458 XHC_GENERIC_TIMEOUT
,
3459 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3461 if (EFI_ERROR(Status
)) {
3462 DEBUG ((EFI_D_ERROR
, "XhcSetTrDequeuePointer: Set TR Dequeue Pointer Failed, Status = %r\n", Status
));
3469 Set interface through XHCI's Configure_Endpoint cmd.
3471 @param Xhc The XHCI Instance.
3472 @param SlotId The slot id to be configured.
3473 @param DeviceSpeed The device's speed.
3474 @param ConfigDesc The pointer to the usb device configuration descriptor.
3475 @param Request USB device request to send.
3477 @retval EFI_SUCCESS Successfully set interface.
3483 IN USB_XHCI_INSTANCE
*Xhc
,
3485 IN UINT8 DeviceSpeed
,
3486 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
,
3487 IN EFI_USB_DEVICE_REQUEST
*Request
3491 USB_INTERFACE_DESCRIPTOR
*IfDescActive
;
3492 USB_INTERFACE_DESCRIPTOR
*IfDescSet
;
3493 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3494 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3501 EFI_PHYSICAL_ADDRESS PhyAddr
;
3504 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3505 INPUT_CONTEXT
*InputContext
;
3506 DEVICE_CONTEXT
*OutputContext
;
3507 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3509 Status
= EFI_SUCCESS
;
3511 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3512 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3514 // XHCI 4.6.6 Configure Endpoint
3515 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3516 // Context and Add Context flags as follows:
3517 // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
3518 // Context and Add Context flags to '0'.
3520 // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
3521 // So the default Drop Context and Add Context flags can be '0' to cover 1).
3523 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3524 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT
));
3526 ASSERT (ConfigDesc
!= NULL
);
3530 IfDescActive
= NULL
;
3533 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3534 while ((UINTN
) IfDesc
< ((UINTN
) ConfigDesc
+ ConfigDesc
->TotalLength
)) {
3535 if ((IfDesc
->DescriptorType
== USB_DESC_TYPE_INTERFACE
) && (IfDesc
->Length
>= sizeof (USB_INTERFACE_DESCRIPTOR
))) {
3536 if (IfDesc
->InterfaceNumber
== (UINT8
) Request
->Index
) {
3537 if (IfDesc
->AlternateSetting
== Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[IfDesc
->InterfaceNumber
]) {
3539 // Find out the active interface descriptor.
3541 IfDescActive
= IfDesc
;
3542 } else if (IfDesc
->AlternateSetting
== (UINT8
) Request
->Value
) {
3544 // Find out the interface descriptor to set.
3550 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3554 // XHCI 4.6.6 Configure Endpoint
3555 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3556 // Context and Add Context flags as follows:
3557 // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
3558 // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
3559 // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
3560 // the Drop Context flag to '1' and Add Context flag to '0'.
3561 // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
3562 // and Add Context flags shall be set to '1'.
3564 // Below codes are to cover 2), 3) and 4).
3567 if ((IfDescActive
!= NULL
) && (IfDescSet
!= NULL
)) {
3568 NumEp
= IfDescActive
->NumEndpoints
;
3569 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*) (IfDescActive
+ 1);
3570 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3571 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3572 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3575 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3576 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3580 EpAddr
= (UINT8
) (EpDesc
->EndpointAddress
& 0x0F);
3581 Direction
= (UINT8
) ((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3583 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3589 // XHCI 4.3.6 - Setting Alternate Interfaces
3590 // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
3592 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, NULL
);
3593 if (EFI_ERROR (Status
)) {
3597 // XHCI 4.3.6 - Setting Alternate Interfaces
3598 // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
3600 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] != NULL
) {
3601 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1])->RingSeg0
;
3602 if (RingSeg
!= NULL
) {
3603 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
3605 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1]);
3606 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] = NULL
;
3610 // Set the Drop Context flag to '1'.
3612 InputContext
->InputControlContext
.Dword1
|= (BIT0
<< Dci
);
3614 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3618 // XHCI 4.3.6 - Setting Alternate Interfaces
3619 // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
3620 // Interface setting, to '0'.
3622 // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
3626 // XHCI 4.3.6 - Setting Alternate Interfaces
3627 // 4) For each endpoint enabled by the Configure Endpoint Command:
3628 // a. Allocate a Transfer Ring.
3629 // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
3630 // c. Initialize the Endpoint Context data structure.
3632 Dci
= XhcInitializeEndpointContext (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDescSet
);
3637 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3638 InputContext
->Slot
.ContextEntries
= MaxDci
;
3640 // XHCI 4.3.6 - Setting Alternate Interfaces
3641 // 5) Issue and successfully complete a Configure Endpoint Command.
3643 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3644 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3645 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3646 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3647 CmdTrbCfgEP
.CycleBit
= 1;
3648 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3649 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3650 DEBUG ((EFI_D_INFO
, "SetInterface: Configure Endpoint\n"));
3651 Status
= XhcCmdTransfer (
3653 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3654 XHC_GENERIC_TIMEOUT
,
3655 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3657 if (EFI_ERROR (Status
)) {
3658 DEBUG ((EFI_D_ERROR
, "SetInterface: Config Endpoint Failed, Status = %r\n", Status
));
3661 // Update the active AlternateSetting.
3663 Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[(UINT8
) Request
->Index
] = (UINT8
) Request
->Value
;
3671 Set interface through XHCI's Configure_Endpoint cmd.
3673 @param Xhc The XHCI Instance.
3674 @param SlotId The slot id to be configured.
3675 @param DeviceSpeed The device's speed.
3676 @param ConfigDesc The pointer to the usb device configuration descriptor.
3677 @param Request USB device request to send.
3679 @retval EFI_SUCCESS Successfully set interface.
3685 IN USB_XHCI_INSTANCE
*Xhc
,
3687 IN UINT8 DeviceSpeed
,
3688 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
,
3689 IN EFI_USB_DEVICE_REQUEST
*Request
3693 USB_INTERFACE_DESCRIPTOR
*IfDescActive
;
3694 USB_INTERFACE_DESCRIPTOR
*IfDescSet
;
3695 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3696 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3703 EFI_PHYSICAL_ADDRESS PhyAddr
;
3706 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3707 INPUT_CONTEXT_64
*InputContext
;
3708 DEVICE_CONTEXT_64
*OutputContext
;
3709 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3711 Status
= EFI_SUCCESS
;
3713 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3714 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3716 // XHCI 4.6.6 Configure Endpoint
3717 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3718 // Context and Add Context flags as follows:
3719 // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
3720 // Context and Add Context flags to '0'.
3722 // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
3723 // So the default Drop Context and Add Context flags can be '0' to cover 1).
3725 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3726 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT_64
));
3728 ASSERT (ConfigDesc
!= NULL
);
3732 IfDescActive
= NULL
;
3735 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3736 while ((UINTN
) IfDesc
< ((UINTN
) ConfigDesc
+ ConfigDesc
->TotalLength
)) {
3737 if ((IfDesc
->DescriptorType
== USB_DESC_TYPE_INTERFACE
) && (IfDesc
->Length
>= sizeof (USB_INTERFACE_DESCRIPTOR
))) {
3738 if (IfDesc
->InterfaceNumber
== (UINT8
) Request
->Index
) {
3739 if (IfDesc
->AlternateSetting
== Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[IfDesc
->InterfaceNumber
]) {
3741 // Find out the active interface descriptor.
3743 IfDescActive
= IfDesc
;
3744 } else if (IfDesc
->AlternateSetting
== (UINT8
) Request
->Value
) {
3746 // Find out the interface descriptor to set.
3752 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3756 // XHCI 4.6.6 Configure Endpoint
3757 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3758 // Context and Add Context flags as follows:
3759 // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
3760 // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
3761 // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
3762 // the Drop Context flag to '1' and Add Context flag to '0'.
3763 // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
3764 // and Add Context flags shall be set to '1'.
3766 // Below codes are to cover 2), 3) and 4).
3769 if ((IfDescActive
!= NULL
) && (IfDescSet
!= NULL
)) {
3770 NumEp
= IfDescActive
->NumEndpoints
;
3771 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*) (IfDescActive
+ 1);
3772 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3773 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3774 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3777 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3778 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3782 EpAddr
= (UINT8
) (EpDesc
->EndpointAddress
& 0x0F);
3783 Direction
= (UINT8
) ((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3785 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3791 // XHCI 4.3.6 - Setting Alternate Interfaces
3792 // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
3794 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, NULL
);
3795 if (EFI_ERROR (Status
)) {
3799 // XHCI 4.3.6 - Setting Alternate Interfaces
3800 // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
3802 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] != NULL
) {
3803 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1])->RingSeg0
;
3804 if (RingSeg
!= NULL
) {
3805 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
3807 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1]);
3808 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] = NULL
;
3812 // Set the Drop Context flag to '1'.
3814 InputContext
->InputControlContext
.Dword1
|= (BIT0
<< Dci
);
3816 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3820 // XHCI 4.3.6 - Setting Alternate Interfaces
3821 // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
3822 // Interface setting, to '0'.
3824 // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
3828 // XHCI 4.3.6 - Setting Alternate Interfaces
3829 // 4) For each endpoint enabled by the Configure Endpoint Command:
3830 // a. Allocate a Transfer Ring.
3831 // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
3832 // c. Initialize the Endpoint Context data structure.
3834 Dci
= XhcInitializeEndpointContext64 (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDescSet
);
3839 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3840 InputContext
->Slot
.ContextEntries
= MaxDci
;
3842 // XHCI 4.3.6 - Setting Alternate Interfaces
3843 // 5) Issue and successfully complete a Configure Endpoint Command.
3845 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3846 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3847 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3848 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3849 CmdTrbCfgEP
.CycleBit
= 1;
3850 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3851 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3852 DEBUG ((EFI_D_INFO
, "SetInterface64: Configure Endpoint\n"));
3853 Status
= XhcCmdTransfer (
3855 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3856 XHC_GENERIC_TIMEOUT
,
3857 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3859 if (EFI_ERROR (Status
)) {
3860 DEBUG ((EFI_D_ERROR
, "SetInterface64: Config Endpoint Failed, Status = %r\n", Status
));
3863 // Update the active AlternateSetting.
3865 Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[(UINT8
) Request
->Index
] = (UINT8
) Request
->Value
;
3873 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
3875 @param Xhc The XHCI Instance.
3876 @param SlotId The slot id to be evaluated.
3877 @param MaxPacketSize The max packet size supported by the device control transfer.
3879 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.
3884 XhcEvaluateContext (
3885 IN USB_XHCI_INSTANCE
*Xhc
,
3887 IN UINT32 MaxPacketSize
3891 CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu
;
3892 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3893 INPUT_CONTEXT
*InputContext
;
3894 EFI_PHYSICAL_ADDRESS PhyAddr
;
3896 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3899 // 4.6.7 Evaluate Context
3901 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3902 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3904 InputContext
->InputControlContext
.Dword2
|= BIT1
;
3905 InputContext
->EP
[0].MaxPacketSize
= MaxPacketSize
;
3907 ZeroMem (&CmdTrbEvalu
, sizeof (CmdTrbEvalu
));
3908 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3909 CmdTrbEvalu
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3910 CmdTrbEvalu
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3911 CmdTrbEvalu
.CycleBit
= 1;
3912 CmdTrbEvalu
.Type
= TRB_TYPE_EVALU_CONTXT
;
3913 CmdTrbEvalu
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3914 DEBUG ((EFI_D_INFO
, "Evaluate context\n"));
3915 Status
= XhcCmdTransfer (
3917 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbEvalu
,
3918 XHC_GENERIC_TIMEOUT
,
3919 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3921 if (EFI_ERROR (Status
)) {
3922 DEBUG ((EFI_D_ERROR
, "XhcEvaluateContext: Evaluate Context Failed, Status = %r\n", Status
));
3928 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
3930 @param Xhc The XHCI Instance.
3931 @param SlotId The slot id to be evaluated.
3932 @param MaxPacketSize The max packet size supported by the device control transfer.
3934 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.
3939 XhcEvaluateContext64 (
3940 IN USB_XHCI_INSTANCE
*Xhc
,
3942 IN UINT32 MaxPacketSize
3946 CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu
;
3947 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3948 INPUT_CONTEXT_64
*InputContext
;
3949 EFI_PHYSICAL_ADDRESS PhyAddr
;
3951 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3954 // 4.6.7 Evaluate Context
3956 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3957 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3959 InputContext
->InputControlContext
.Dword2
|= BIT1
;
3960 InputContext
->EP
[0].MaxPacketSize
= MaxPacketSize
;
3962 ZeroMem (&CmdTrbEvalu
, sizeof (CmdTrbEvalu
));
3963 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3964 CmdTrbEvalu
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3965 CmdTrbEvalu
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3966 CmdTrbEvalu
.CycleBit
= 1;
3967 CmdTrbEvalu
.Type
= TRB_TYPE_EVALU_CONTXT
;
3968 CmdTrbEvalu
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3969 DEBUG ((EFI_D_INFO
, "Evaluate context\n"));
3970 Status
= XhcCmdTransfer (
3972 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbEvalu
,
3973 XHC_GENERIC_TIMEOUT
,
3974 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3976 if (EFI_ERROR (Status
)) {
3977 DEBUG ((EFI_D_ERROR
, "XhcEvaluateContext64: Evaluate Context Failed, Status = %r\n", Status
));
3984 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
3986 @param Xhc The XHCI Instance.
3987 @param SlotId The slot id to be configured.
3988 @param PortNum The total number of downstream port supported by the hub.
3989 @param TTT The TT think time of the hub device.
3990 @param MTT The multi-TT of the hub device.
3992 @retval EFI_SUCCESS Successfully configure the hub device's slot context.
3996 XhcConfigHubContext (
3997 IN USB_XHCI_INSTANCE
*Xhc
,
4005 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
4006 INPUT_CONTEXT
*InputContext
;
4007 DEVICE_CONTEXT
*OutputContext
;
4008 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
4009 EFI_PHYSICAL_ADDRESS PhyAddr
;
4011 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
4012 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
4013 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
4016 // 4.6.7 Evaluate Context
4018 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
4020 InputContext
->InputControlContext
.Dword2
|= BIT0
;
4023 // Copy the slot context from OutputContext to Input context
4025 CopyMem(&(InputContext
->Slot
), &(OutputContext
->Slot
), sizeof (SLOT_CONTEXT
));
4026 InputContext
->Slot
.Hub
= 1;
4027 InputContext
->Slot
.PortNum
= PortNum
;
4028 InputContext
->Slot
.TTT
= TTT
;
4029 InputContext
->Slot
.MTT
= MTT
;
4031 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
4032 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
4033 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
4034 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
4035 CmdTrbCfgEP
.CycleBit
= 1;
4036 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
4037 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
4038 DEBUG ((EFI_D_INFO
, "Configure Hub Slot Context\n"));
4039 Status
= XhcCmdTransfer (
4041 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
4042 XHC_GENERIC_TIMEOUT
,
4043 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
4045 if (EFI_ERROR (Status
)) {
4046 DEBUG ((EFI_D_ERROR
, "XhcConfigHubContext: Config Endpoint Failed, Status = %r\n", Status
));
4052 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
4054 @param Xhc The XHCI Instance.
4055 @param SlotId The slot id to be configured.
4056 @param PortNum The total number of downstream port supported by the hub.
4057 @param TTT The TT think time of the hub device.
4058 @param MTT The multi-TT of the hub device.
4060 @retval EFI_SUCCESS Successfully configure the hub device's slot context.
4064 XhcConfigHubContext64 (
4065 IN USB_XHCI_INSTANCE
*Xhc
,
4073 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
4074 INPUT_CONTEXT_64
*InputContext
;
4075 DEVICE_CONTEXT_64
*OutputContext
;
4076 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
4077 EFI_PHYSICAL_ADDRESS PhyAddr
;
4079 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
4080 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
4081 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
4084 // 4.6.7 Evaluate Context
4086 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
4088 InputContext
->InputControlContext
.Dword2
|= BIT0
;
4091 // Copy the slot context from OutputContext to Input context
4093 CopyMem(&(InputContext
->Slot
), &(OutputContext
->Slot
), sizeof (SLOT_CONTEXT_64
));
4094 InputContext
->Slot
.Hub
= 1;
4095 InputContext
->Slot
.PortNum
= PortNum
;
4096 InputContext
->Slot
.TTT
= TTT
;
4097 InputContext
->Slot
.MTT
= MTT
;
4099 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
4100 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
4101 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
4102 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
4103 CmdTrbCfgEP
.CycleBit
= 1;
4104 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
4105 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
4106 DEBUG ((EFI_D_INFO
, "Configure Hub Slot Context\n"));
4107 Status
= XhcCmdTransfer (
4109 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
4110 XHC_GENERIC_TIMEOUT
,
4111 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
4113 if (EFI_ERROR (Status
)) {
4114 DEBUG ((EFI_D_ERROR
, "XhcConfigHubContext64: Config Endpoint Failed, Status = %r\n", Status
));