3 XHCI transfer scheduling routines.
5 Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
6 Copyright (c) Microsoft Corporation.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
14 Create a command transfer TRB to support XHCI command interfaces.
16 @param Xhc The XHCI Instance.
17 @param CmdTrb The cmd TRB to be executed.
19 @return Created URB or NULL.
24 IN USB_XHCI_INSTANCE
*Xhc
,
25 IN TRB_TEMPLATE
*CmdTrb
30 Urb
= AllocateZeroPool (sizeof (URB
));
35 Urb
->Signature
= XHC_URB_SIG
;
37 Urb
->Ring
= &Xhc
->CmdRing
;
38 XhcSyncTrsRing (Xhc
, Urb
->Ring
);
40 Urb
->TrbStart
= Urb
->Ring
->RingEnqueue
;
41 CopyMem (Urb
->TrbStart
, CmdTrb
, sizeof (TRB_TEMPLATE
));
42 Urb
->TrbStart
->CycleBit
= Urb
->Ring
->RingPCS
& BIT0
;
43 Urb
->TrbEnd
= Urb
->TrbStart
;
49 Execute a XHCI cmd TRB pointed by CmdTrb.
51 @param Xhc The XHCI Instance.
52 @param CmdTrb The cmd TRB to be executed.
53 @param Timeout Indicates the maximum time, in millisecond, which the
54 transfer is allowed to complete.
55 @param EvtTrb The event TRB corresponding to the cmd TRB.
57 @retval EFI_SUCCESS The transfer was completed successfully.
58 @retval EFI_INVALID_PARAMETER Some parameters are invalid.
59 @retval EFI_TIMEOUT The transfer failed due to timeout.
60 @retval EFI_DEVICE_ERROR The transfer failed due to host controller error.
66 IN USB_XHCI_INSTANCE
*Xhc
,
67 IN TRB_TEMPLATE
*CmdTrb
,
69 OUT TRB_TEMPLATE
**EvtTrb
76 // Validate the parameters
78 if ((Xhc
== NULL
) || (CmdTrb
== NULL
)) {
79 return EFI_INVALID_PARAMETER
;
82 Status
= EFI_DEVICE_ERROR
;
84 if (XhcIsHalt (Xhc
) || XhcIsSysError (Xhc
)) {
85 DEBUG ((EFI_D_ERROR
, "XhcCmdTransfer: HC is halted\n"));
90 // Create a new URB, then poll the execution status.
92 Urb
= XhcCreateCmdTrb (Xhc
, CmdTrb
);
95 DEBUG ((EFI_D_ERROR
, "XhcCmdTransfer: failed to create URB\n"));
96 Status
= EFI_OUT_OF_RESOURCES
;
100 Status
= XhcExecTransfer (Xhc
, TRUE
, Urb
, Timeout
);
101 *EvtTrb
= Urb
->EvtTrb
;
103 if (Urb
->Result
== EFI_USB_NOERROR
) {
104 Status
= EFI_SUCCESS
;
107 XhcFreeUrb (Xhc
, Urb
);
114 Create a new URB for a new transaction.
116 @param Xhc The XHCI Instance
117 @param BusAddr The logical device address assigned by UsbBus driver
118 @param EpAddr Endpoint addrress
119 @param DevSpeed The device speed
120 @param MaxPacket The max packet length of the endpoint
121 @param Type The transaction type
122 @param Request The standard USB request for control transfer
123 @param Data The user data to transfer
124 @param DataLen The length of data buffer
125 @param Callback The function to call when data is transferred
126 @param Context The context to the callback
128 @return Created URB or NULL
133 IN USB_XHCI_INSTANCE
*Xhc
,
139 IN EFI_USB_DEVICE_REQUEST
*Request
,
142 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback
,
150 Urb
= AllocateZeroPool (sizeof (URB
));
155 Urb
->Signature
= XHC_URB_SIG
;
156 InitializeListHead (&Urb
->UrbList
);
159 Ep
->BusAddr
= BusAddr
;
160 Ep
->EpAddr
= (UINT8
)(EpAddr
& 0x0F);
161 Ep
->Direction
= ((EpAddr
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
;
162 Ep
->DevSpeed
= DevSpeed
;
163 Ep
->MaxPacket
= MaxPacket
;
166 Urb
->Request
= Request
;
168 Urb
->DataLen
= DataLen
;
169 Urb
->Callback
= Callback
;
170 Urb
->Context
= Context
;
172 Status
= XhcCreateTransferTrb (Xhc
, Urb
);
173 ASSERT_EFI_ERROR (Status
);
174 if (EFI_ERROR (Status
)) {
175 DEBUG ((EFI_D_ERROR
, "XhcCreateUrb: XhcCreateTransferTrb Failed, Status = %r\n", Status
));
184 Free an allocated URB.
186 @param Xhc The XHCI device.
187 @param Urb The URB to free.
192 IN USB_XHCI_INSTANCE
*Xhc
,
196 if ((Xhc
== NULL
) || (Urb
== NULL
)) {
200 if (Urb
->DataMap
!= NULL
) {
201 Xhc
->PciIo
->Unmap (Xhc
->PciIo
, Urb
->DataMap
);
208 Create a transfer TRB.
210 @param Xhc The XHCI Instance
211 @param Urb The urb used to construct the transfer TRB.
213 @return Created TRB or NULL
217 XhcCreateTransferTrb (
218 IN USB_XHCI_INSTANCE
*Xhc
,
223 TRANSFER_RING
*EPRing
;
231 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
232 EFI_PHYSICAL_ADDRESS PhyAddr
;
236 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
238 return EFI_DEVICE_ERROR
;
241 Urb
->Finished
= FALSE
;
242 Urb
->StartDone
= FALSE
;
243 Urb
->EndDone
= FALSE
;
245 Urb
->Result
= EFI_USB_NOERROR
;
247 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
249 EPRing
= (TRANSFER_RING
*)(UINTN
) Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1];
251 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
252 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
253 EPType
= (UINT8
) ((DEVICE_CONTEXT
*)OutputContext
)->EP
[Dci
-1].EPType
;
255 EPType
= (UINT8
) ((DEVICE_CONTEXT_64
*)OutputContext
)->EP
[Dci
-1].EPType
;
261 if ((Urb
->Data
!= NULL
) && (Urb
->DataMap
== NULL
)) {
262 if (((UINT8
) (Urb
->Ep
.Direction
)) == EfiUsbDataIn
) {
263 MapOp
= EfiPciIoOperationBusMasterWrite
;
265 MapOp
= EfiPciIoOperationBusMasterRead
;
269 Status
= Xhc
->PciIo
->Map (Xhc
->PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
271 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
272 DEBUG ((EFI_D_ERROR
, "XhcCreateTransferTrb: Fail to map Urb->Data.\n"));
273 return EFI_OUT_OF_RESOURCES
;
276 Urb
->DataPhy
= (VOID
*) ((UINTN
) PhyAddr
);
283 XhcSyncTrsRing (Xhc
, EPRing
);
284 Urb
->TrbStart
= EPRing
->RingEnqueue
;
286 case ED_CONTROL_BIDIR
:
288 // For control transfer, create SETUP_STAGE_TRB first.
290 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
291 TrbStart
->TrbCtrSetup
.bmRequestType
= Urb
->Request
->RequestType
;
292 TrbStart
->TrbCtrSetup
.bRequest
= Urb
->Request
->Request
;
293 TrbStart
->TrbCtrSetup
.wValue
= Urb
->Request
->Value
;
294 TrbStart
->TrbCtrSetup
.wIndex
= Urb
->Request
->Index
;
295 TrbStart
->TrbCtrSetup
.wLength
= Urb
->Request
->Length
;
296 TrbStart
->TrbCtrSetup
.Length
= 8;
297 TrbStart
->TrbCtrSetup
.IntTarget
= 0;
298 TrbStart
->TrbCtrSetup
.IOC
= 1;
299 TrbStart
->TrbCtrSetup
.IDT
= 1;
300 TrbStart
->TrbCtrSetup
.Type
= TRB_TYPE_SETUP_STAGE
;
301 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
302 TrbStart
->TrbCtrSetup
.TRT
= 3;
303 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
304 TrbStart
->TrbCtrSetup
.TRT
= 2;
306 TrbStart
->TrbCtrSetup
.TRT
= 0;
309 // Update the cycle bit
311 TrbStart
->TrbCtrSetup
.CycleBit
= EPRing
->RingPCS
& BIT0
;
315 // For control transfer, create DATA_STAGE_TRB.
317 if (Urb
->DataLen
> 0) {
318 XhcSyncTrsRing (Xhc
, EPRing
);
319 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
320 TrbStart
->TrbCtrData
.TRBPtrLo
= XHC_LOW_32BIT(Urb
->DataPhy
);
321 TrbStart
->TrbCtrData
.TRBPtrHi
= XHC_HIGH_32BIT(Urb
->DataPhy
);
322 TrbStart
->TrbCtrData
.Length
= (UINT32
) Urb
->DataLen
;
323 TrbStart
->TrbCtrData
.TDSize
= 0;
324 TrbStart
->TrbCtrData
.IntTarget
= 0;
325 TrbStart
->TrbCtrData
.ISP
= 1;
326 TrbStart
->TrbCtrData
.IOC
= 1;
327 TrbStart
->TrbCtrData
.IDT
= 0;
328 TrbStart
->TrbCtrData
.CH
= 0;
329 TrbStart
->TrbCtrData
.Type
= TRB_TYPE_DATA_STAGE
;
330 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
331 TrbStart
->TrbCtrData
.DIR = 1;
332 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
333 TrbStart
->TrbCtrData
.DIR = 0;
335 TrbStart
->TrbCtrData
.DIR = 0;
338 // Update the cycle bit
340 TrbStart
->TrbCtrData
.CycleBit
= EPRing
->RingPCS
& BIT0
;
344 // For control transfer, create STATUS_STAGE_TRB.
345 // Get the pointer to next TRB for status stage use
347 XhcSyncTrsRing (Xhc
, EPRing
);
348 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
349 TrbStart
->TrbCtrStatus
.IntTarget
= 0;
350 TrbStart
->TrbCtrStatus
.IOC
= 1;
351 TrbStart
->TrbCtrStatus
.CH
= 0;
352 TrbStart
->TrbCtrStatus
.Type
= TRB_TYPE_STATUS_STAGE
;
353 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
354 TrbStart
->TrbCtrStatus
.DIR = 0;
355 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
356 TrbStart
->TrbCtrStatus
.DIR = 1;
358 TrbStart
->TrbCtrStatus
.DIR = 0;
361 // Update the cycle bit
363 TrbStart
->TrbCtrStatus
.CycleBit
= EPRing
->RingPCS
& BIT0
;
365 // Update the enqueue pointer
367 XhcSyncTrsRing (Xhc
, EPRing
);
369 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
378 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
379 while (TotalLen
< Urb
->DataLen
) {
380 if ((TotalLen
+ 0x10000) >= Urb
->DataLen
) {
381 Len
= Urb
->DataLen
- TotalLen
;
385 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
386 TrbStart
->TrbNormal
.TRBPtrLo
= XHC_LOW_32BIT((UINT8
*) Urb
->DataPhy
+ TotalLen
);
387 TrbStart
->TrbNormal
.TRBPtrHi
= XHC_HIGH_32BIT((UINT8
*) Urb
->DataPhy
+ TotalLen
);
388 TrbStart
->TrbNormal
.Length
= (UINT32
) Len
;
389 TrbStart
->TrbNormal
.TDSize
= 0;
390 TrbStart
->TrbNormal
.IntTarget
= 0;
391 TrbStart
->TrbNormal
.ISP
= 1;
392 TrbStart
->TrbNormal
.IOC
= 1;
393 TrbStart
->TrbNormal
.Type
= TRB_TYPE_NORMAL
;
395 // Update the cycle bit
397 TrbStart
->TrbNormal
.CycleBit
= EPRing
->RingPCS
& BIT0
;
399 XhcSyncTrsRing (Xhc
, EPRing
);
404 Urb
->TrbNum
= TrbNum
;
405 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
408 case ED_INTERRUPT_OUT
:
409 case ED_INTERRUPT_IN
:
413 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
414 while (TotalLen
< Urb
->DataLen
) {
415 if ((TotalLen
+ 0x10000) >= Urb
->DataLen
) {
416 Len
= Urb
->DataLen
- TotalLen
;
420 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
421 TrbStart
->TrbNormal
.TRBPtrLo
= XHC_LOW_32BIT((UINT8
*) Urb
->DataPhy
+ TotalLen
);
422 TrbStart
->TrbNormal
.TRBPtrHi
= XHC_HIGH_32BIT((UINT8
*) Urb
->DataPhy
+ TotalLen
);
423 TrbStart
->TrbNormal
.Length
= (UINT32
) Len
;
424 TrbStart
->TrbNormal
.TDSize
= 0;
425 TrbStart
->TrbNormal
.IntTarget
= 0;
426 TrbStart
->TrbNormal
.ISP
= 1;
427 TrbStart
->TrbNormal
.IOC
= 1;
428 TrbStart
->TrbNormal
.Type
= TRB_TYPE_NORMAL
;
430 // Update the cycle bit
432 TrbStart
->TrbNormal
.CycleBit
= EPRing
->RingPCS
& BIT0
;
434 XhcSyncTrsRing (Xhc
, EPRing
);
439 Urb
->TrbNum
= TrbNum
;
440 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
444 DEBUG ((EFI_D_INFO
, "Not supported EPType 0x%x!\n",EPType
));
454 Initialize the XHCI host controller for schedule.
456 @param Xhc The XHCI Instance to be initialized.
461 IN USB_XHCI_INSTANCE
*Xhc
465 EFI_PHYSICAL_ADDRESS DcbaaPhy
;
467 EFI_PHYSICAL_ADDRESS CmdRingPhy
;
469 UINT32 MaxScratchpadBufs
;
471 EFI_PHYSICAL_ADDRESS ScratchPhy
;
472 UINT64
*ScratchEntry
;
473 EFI_PHYSICAL_ADDRESS ScratchEntryPhy
;
475 UINTN
*ScratchEntryMap
;
479 // Initialize memory management.
481 Xhc
->MemPool
= UsbHcInitMemPool (Xhc
->PciIo
);
482 ASSERT (Xhc
->MemPool
!= NULL
);
485 // Program the Max Device Slots Enabled (MaxSlotsEn) field in the CONFIG register (5.4.7)
486 // to enable the device slots that system software is going to use.
488 Xhc
->MaxSlotsEn
= Xhc
->HcSParams1
.Data
.MaxSlots
;
489 ASSERT (Xhc
->MaxSlotsEn
>= 1 && Xhc
->MaxSlotsEn
<= 255);
490 XhcWriteOpReg (Xhc
, XHC_CONFIG_OFFSET
, Xhc
->MaxSlotsEn
);
493 // The Device Context Base Address Array entry associated with each allocated Device Slot
494 // shall contain a 64-bit pointer to the base of the associated Device Context.
495 // The Device Context Base Address Array shall contain MaxSlotsEn + 1 entries.
496 // Software shall set Device Context Base Address Array entries for unallocated Device Slots to '0'.
498 Entries
= (Xhc
->MaxSlotsEn
+ 1) * sizeof(UINT64
);
499 Dcbaa
= UsbHcAllocateMem (Xhc
->MemPool
, Entries
);
500 ASSERT (Dcbaa
!= NULL
);
501 ZeroMem (Dcbaa
, Entries
);
504 // A Scratchpad Buffer is a PAGESIZE block of system memory located on a PAGESIZE boundary.
505 // System software shall allocate the Scratchpad Buffer(s) before placing the xHC in to Run
506 // mode (Run/Stop(R/S) ='1').
508 MaxScratchpadBufs
= ((Xhc
->HcSParams2
.Data
.ScratchBufHi
) << 5) | (Xhc
->HcSParams2
.Data
.ScratchBufLo
);
509 Xhc
->MaxScratchpadBufs
= MaxScratchpadBufs
;
510 ASSERT (MaxScratchpadBufs
<= 1023);
511 if (MaxScratchpadBufs
!= 0) {
513 // Allocate the buffer to record the Mapping for each scratch buffer in order to Unmap them
515 ScratchEntryMap
= AllocateZeroPool (sizeof (UINTN
) * MaxScratchpadBufs
);
516 ASSERT (ScratchEntryMap
!= NULL
);
517 Xhc
->ScratchEntryMap
= ScratchEntryMap
;
520 // Allocate the buffer to record the host address for each entry
522 ScratchEntry
= AllocateZeroPool (sizeof (UINT64
) * MaxScratchpadBufs
);
523 ASSERT (ScratchEntry
!= NULL
);
524 Xhc
->ScratchEntry
= ScratchEntry
;
527 Status
= UsbHcAllocateAlignedPages (
529 EFI_SIZE_TO_PAGES (MaxScratchpadBufs
* sizeof (UINT64
)),
531 (VOID
**) &ScratchBuf
,
535 ASSERT_EFI_ERROR (Status
);
537 ZeroMem (ScratchBuf
, MaxScratchpadBufs
* sizeof (UINT64
));
538 Xhc
->ScratchBuf
= ScratchBuf
;
541 // Allocate each scratch buffer
543 for (Index
= 0; Index
< MaxScratchpadBufs
; Index
++) {
545 Status
= UsbHcAllocateAlignedPages (
547 EFI_SIZE_TO_PAGES (Xhc
->PageSize
),
549 (VOID
**) &ScratchEntry
[Index
],
551 (VOID
**) &ScratchEntryMap
[Index
]
553 ASSERT_EFI_ERROR (Status
);
554 ZeroMem ((VOID
*)(UINTN
)ScratchEntry
[Index
], Xhc
->PageSize
);
556 // Fill with the PCI device address
558 *ScratchBuf
++ = ScratchEntryPhy
;
561 // The Scratchpad Buffer Array contains pointers to the Scratchpad Buffers. Entry 0 of the
562 // Device Context Base Address Array points to the Scratchpad Buffer Array.
564 *(UINT64
*)Dcbaa
= (UINT64
)(UINTN
) ScratchPhy
;
568 // Program the Device Context Base Address Array Pointer (DCBAAP) register (5.4.6) with
569 // a 64-bit address pointing to where the Device Context Base Address Array is located.
571 Xhc
->DCBAA
= (UINT64
*)(UINTN
)Dcbaa
;
573 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
574 // So divide it to two 32-bytes width register access.
576 DcbaaPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Dcbaa
, Entries
);
577 XhcWriteOpReg (Xhc
, XHC_DCBAAP_OFFSET
, XHC_LOW_32BIT(DcbaaPhy
));
578 XhcWriteOpReg (Xhc
, XHC_DCBAAP_OFFSET
+ 4, XHC_HIGH_32BIT (DcbaaPhy
));
580 DEBUG ((EFI_D_INFO
, "XhcInitSched:DCBAA=0x%x\n", (UINT64
)(UINTN
)Xhc
->DCBAA
));
583 // Define the Command Ring Dequeue Pointer by programming the Command Ring Control Register
584 // (5.4.5) with a 64-bit address pointing to the starting address of the first TRB of the Command Ring.
585 // Note: The Command Ring is 64 byte aligned, so the low order 6 bits of the Command Ring Pointer shall
588 CreateTransferRing (Xhc
, CMD_RING_TRB_NUMBER
, &Xhc
->CmdRing
);
590 // The xHC uses the Enqueue Pointer to determine when a Transfer Ring is empty. As it fetches TRBs from a
591 // Transfer Ring it checks for a Cycle bit transition. If a transition detected, the ring is empty.
592 // So we set RCS as inverted PCS init value to let Command Ring empty
594 CmdRing
= (UINT64
)(UINTN
)Xhc
->CmdRing
.RingSeg0
;
595 CmdRingPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, (VOID
*)(UINTN
) CmdRing
, sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
);
596 ASSERT ((CmdRingPhy
& 0x3F) == 0);
597 CmdRingPhy
|= XHC_CRCR_RCS
;
599 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
600 // So divide it to two 32-bytes width register access.
602 XhcWriteOpReg (Xhc
, XHC_CRCR_OFFSET
, XHC_LOW_32BIT(CmdRingPhy
));
603 XhcWriteOpReg (Xhc
, XHC_CRCR_OFFSET
+ 4, XHC_HIGH_32BIT (CmdRingPhy
));
606 // Disable the 'interrupter enable' bit in USB_CMD
607 // and clear IE & IP bit in all Interrupter X Management Registers.
609 XhcClearOpRegBit (Xhc
, XHC_USBCMD_OFFSET
, XHC_USBCMD_INTE
);
610 for (Index
= 0; Index
< (UINT16
)(Xhc
->HcSParams1
.Data
.MaxIntrs
); Index
++) {
611 XhcClearRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
+ (Index
* 32), XHC_IMAN_IE
);
612 XhcSetRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
+ (Index
* 32), XHC_IMAN_IP
);
616 // Allocate EventRing for Cmd, Ctrl, Bulk, Interrupt, AsynInterrupt transfer
618 CreateEventRing (Xhc
, &Xhc
->EventRing
);
619 DEBUG ((DEBUG_INFO
, "XhcInitSched: Created CMD ring [%p~%p) EVENT ring [%p~%p)\n",
620 Xhc
->CmdRing
.RingSeg0
, (UINTN
)Xhc
->CmdRing
.RingSeg0
+ sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
,
621 Xhc
->EventRing
.EventRingSeg0
, (UINTN
)Xhc
->EventRing
.EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
626 System software shall use a Reset Endpoint Command (section 4.11.4.7) to remove the Halted
627 condition in the xHC. After the successful completion of the Reset Endpoint Command, the Endpoint
628 Context is transitioned from the Halted to the Stopped state and the Transfer Ring of the endpoint is
629 reenabled. The next write to the Doorbell of the Endpoint will transition the Endpoint Context from the
630 Stopped to the Running state.
632 @param Xhc The XHCI Instance.
633 @param Urb The urb which makes the endpoint halted.
635 @retval EFI_SUCCESS The recovery is successful.
636 @retval Others Failed to recovery halted endpoint.
641 XhcRecoverHaltedEndpoint (
642 IN USB_XHCI_INSTANCE
*Xhc
,
650 Status
= EFI_SUCCESS
;
651 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
653 return EFI_DEVICE_ERROR
;
655 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
658 DEBUG ((EFI_D_INFO
, "Recovery Halted Slot = %x,Dci = %x\n", SlotId
, Dci
));
661 // 1) Send Reset endpoint command to transit from halt to stop state
663 Status
= XhcResetEndpoint(Xhc
, SlotId
, Dci
);
664 if (EFI_ERROR(Status
)) {
665 DEBUG ((EFI_D_ERROR
, "XhcRecoverHaltedEndpoint: Reset Endpoint Failed, Status = %r\n", Status
));
670 // 2)Set dequeue pointer
672 Status
= XhcSetTrDequeuePointer(Xhc
, SlotId
, Dci
, Urb
);
673 if (EFI_ERROR(Status
)) {
674 DEBUG ((EFI_D_ERROR
, "XhcRecoverHaltedEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status
));
679 // 3)Ring the doorbell to transit from stop to active
681 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
688 System software shall use a Stop Endpoint Command (section 4.6.9) and the Set TR Dequeue Pointer
689 Command (section 4.6.10) to remove the timed-out TDs from the xHC transfer ring. The next write to
690 the Doorbell of the Endpoint will transition the Endpoint Context from the Stopped to the Running
693 @param Xhc The XHCI Instance.
694 @param Urb The urb which doesn't get completed in a specified timeout range.
696 @retval EFI_SUCCESS The dequeuing of the TDs is successful.
697 @retval EFI_ALREADY_STARTED The Urb is finished so no deque is needed.
698 @retval Others Failed to stop the endpoint and dequeue the TDs.
703 XhcDequeueTrbFromEndpoint (
704 IN USB_XHCI_INSTANCE
*Xhc
,
712 Status
= EFI_SUCCESS
;
713 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
715 return EFI_DEVICE_ERROR
;
717 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
720 DEBUG ((EFI_D_INFO
, "Stop Slot = %x,Dci = %x\n", SlotId
, Dci
));
723 // 1) Send Stop endpoint command to stop xHC from executing of the TDs on the endpoint
725 Status
= XhcStopEndpoint(Xhc
, SlotId
, Dci
, Urb
);
726 if (EFI_ERROR(Status
)) {
727 DEBUG ((EFI_D_ERROR
, "XhcDequeueTrbFromEndpoint: Stop Endpoint Failed, Status = %r\n", Status
));
732 // 2)Set dequeue pointer
734 if (Urb
->Finished
&& Urb
->Result
== EFI_USB_NOERROR
) {
736 // Return Already Started to indicate the pending URB is finished.
737 // This fixes BULK data loss when transfer is detected as timeout
738 // but finished just before stopping endpoint.
740 Status
= EFI_ALREADY_STARTED
;
741 DEBUG ((DEBUG_INFO
, "XhcDequeueTrbFromEndpoint: Pending URB is finished: Length Actual/Expect = %d/%d!\n", Urb
->Completed
, Urb
->DataLen
));
743 Status
= XhcSetTrDequeuePointer(Xhc
, SlotId
, Dci
, Urb
);
744 if (EFI_ERROR (Status
)) {
745 DEBUG ((DEBUG_ERROR
, "XhcDequeueTrbFromEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status
));
751 // 3)Ring the doorbell to transit from stop to active
753 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
760 Create XHCI event ring.
762 @param Xhc The XHCI Instance.
763 @param EventRing The created event ring.
768 IN USB_XHCI_INSTANCE
*Xhc
,
769 OUT EVENT_RING
*EventRing
773 EVENT_RING_SEG_TABLE_ENTRY
*ERSTBase
;
775 EFI_PHYSICAL_ADDRESS ERSTPhy
;
776 EFI_PHYSICAL_ADDRESS DequeuePhy
;
778 ASSERT (EventRing
!= NULL
);
780 Size
= sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
;
781 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, Size
);
782 ASSERT (Buf
!= NULL
);
783 ASSERT (((UINTN
) Buf
& 0x3F) == 0);
786 EventRing
->EventRingSeg0
= Buf
;
787 EventRing
->TrbNumber
= EVENT_RING_TRB_NUMBER
;
788 EventRing
->EventRingDequeue
= (TRB_TEMPLATE
*) EventRing
->EventRingSeg0
;
789 EventRing
->EventRingEnqueue
= (TRB_TEMPLATE
*) EventRing
->EventRingSeg0
;
791 DequeuePhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Buf
, Size
);
794 // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1'
795 // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring.
797 EventRing
->EventRingCCS
= 1;
799 Size
= sizeof (EVENT_RING_SEG_TABLE_ENTRY
) * ERST_NUMBER
;
800 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, Size
);
801 ASSERT (Buf
!= NULL
);
802 ASSERT (((UINTN
) Buf
& 0x3F) == 0);
805 ERSTBase
= (EVENT_RING_SEG_TABLE_ENTRY
*) Buf
;
806 EventRing
->ERSTBase
= ERSTBase
;
807 ERSTBase
->PtrLo
= XHC_LOW_32BIT (DequeuePhy
);
808 ERSTBase
->PtrHi
= XHC_HIGH_32BIT (DequeuePhy
);
809 ERSTBase
->RingTrbSize
= EVENT_RING_TRB_NUMBER
;
811 ERSTPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, ERSTBase
, Size
);
814 // Program the Interrupter Event Ring Segment Table Size (ERSTSZ) register (5.5.2.3.1)
822 // Program the Interrupter Event Ring Dequeue Pointer (ERDP) register (5.5.2.3.3)
824 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
825 // So divide it to two 32-bytes width register access.
830 XHC_LOW_32BIT((UINT64
)(UINTN
)DequeuePhy
)
835 XHC_HIGH_32BIT((UINT64
)(UINTN
)DequeuePhy
)
838 // Program the Interrupter Event Ring Segment Table Base Address (ERSTBA) register(5.5.2.3.2)
840 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
841 // So divide it to two 32-bytes width register access.
846 XHC_LOW_32BIT((UINT64
)(UINTN
)ERSTPhy
)
850 XHC_ERSTBA_OFFSET
+ 4,
851 XHC_HIGH_32BIT((UINT64
)(UINTN
)ERSTPhy
)
854 // Need set IMAN IE bit to enble the ring interrupt
856 XhcSetRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
, XHC_IMAN_IE
);
860 Create XHCI transfer ring.
862 @param Xhc The XHCI Instance.
863 @param TrbNum The number of TRB in the ring.
864 @param TransferRing The created transfer ring.
869 IN USB_XHCI_INSTANCE
*Xhc
,
871 OUT TRANSFER_RING
*TransferRing
876 EFI_PHYSICAL_ADDRESS PhyAddr
;
878 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (TRB_TEMPLATE
) * TrbNum
);
879 ASSERT (Buf
!= NULL
);
880 ASSERT (((UINTN
) Buf
& 0x3F) == 0);
881 ZeroMem (Buf
, sizeof (TRB_TEMPLATE
) * TrbNum
);
883 TransferRing
->RingSeg0
= Buf
;
884 TransferRing
->TrbNumber
= TrbNum
;
885 TransferRing
->RingEnqueue
= (TRB_TEMPLATE
*) TransferRing
->RingSeg0
;
886 TransferRing
->RingDequeue
= (TRB_TEMPLATE
*) TransferRing
->RingSeg0
;
887 TransferRing
->RingPCS
= 1;
889 // 4.9.2 Transfer Ring Management
890 // To form a ring (or circular queue) a Link TRB may be inserted at the end of a ring to
891 // point to the first TRB in the ring.
893 EndTrb
= (LINK_TRB
*) ((UINTN
)Buf
+ sizeof (TRB_TEMPLATE
) * (TrbNum
- 1));
894 EndTrb
->Type
= TRB_TYPE_LINK
;
895 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Buf
, sizeof (TRB_TEMPLATE
) * TrbNum
);
896 EndTrb
->PtrLo
= XHC_LOW_32BIT (PhyAddr
);
897 EndTrb
->PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
899 // Toggle Cycle (TC). When set to '1', the xHC shall toggle its interpretation of the Cycle bit.
903 // Set Cycle bit as other TRB PCS init value
905 EndTrb
->CycleBit
= 0;
909 Free XHCI event ring.
911 @param Xhc The XHCI Instance.
912 @param EventRing The event ring to be freed.
918 IN USB_XHCI_INSTANCE
*Xhc
,
919 IN EVENT_RING
*EventRing
922 if(EventRing
->EventRingSeg0
== NULL
) {
927 // Free EventRing Segment 0
929 UsbHcFreeMem (Xhc
->MemPool
, EventRing
->EventRingSeg0
, sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
);
934 UsbHcFreeMem (Xhc
->MemPool
, EventRing
->ERSTBase
, sizeof (EVENT_RING_SEG_TABLE_ENTRY
) * ERST_NUMBER
);
939 Free the resouce allocated at initializing schedule.
941 @param Xhc The XHCI Instance.
946 IN USB_XHCI_INSTANCE
*Xhc
950 UINT64
*ScratchEntry
;
952 if (Xhc
->ScratchBuf
!= NULL
) {
953 ScratchEntry
= Xhc
->ScratchEntry
;
954 for (Index
= 0; Index
< Xhc
->MaxScratchpadBufs
; Index
++) {
956 // Free Scratchpad Buffers
958 UsbHcFreeAlignedPages (Xhc
->PciIo
, (VOID
*)(UINTN
)ScratchEntry
[Index
], EFI_SIZE_TO_PAGES (Xhc
->PageSize
), (VOID
*) Xhc
->ScratchEntryMap
[Index
]);
961 // Free Scratchpad Buffer Array
963 UsbHcFreeAlignedPages (Xhc
->PciIo
, Xhc
->ScratchBuf
, EFI_SIZE_TO_PAGES (Xhc
->MaxScratchpadBufs
* sizeof (UINT64
)), Xhc
->ScratchMap
);
964 FreePool (Xhc
->ScratchEntryMap
);
965 FreePool (Xhc
->ScratchEntry
);
968 if (Xhc
->CmdRing
.RingSeg0
!= NULL
) {
969 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->CmdRing
.RingSeg0
, sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
);
970 Xhc
->CmdRing
.RingSeg0
= NULL
;
973 XhcFreeEventRing (Xhc
,&Xhc
->EventRing
);
975 if (Xhc
->DCBAA
!= NULL
) {
976 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->DCBAA
, (Xhc
->MaxSlotsEn
+ 1) * sizeof(UINT64
));
981 // Free memory pool at last
983 if (Xhc
->MemPool
!= NULL
) {
984 UsbHcFreeMemPool (Xhc
->MemPool
);
990 Check if the Trb is a transaction of the URB.
992 @param Xhc The XHCI Instance.
993 @param Trb The TRB to be checked
994 @param Urb The URB to be checked.
996 @retval TRUE It is a transaction of the URB.
997 @retval FALSE It is not any transaction of the URB.
1002 IN USB_XHCI_INSTANCE
*Xhc
,
1003 IN TRB_TEMPLATE
*Trb
,
1008 TRB_TEMPLATE
*CheckedTrb
;
1010 EFI_PHYSICAL_ADDRESS PhyAddr
;
1012 CheckedTrb
= Urb
->TrbStart
;
1013 for (Index
= 0; Index
< Urb
->TrbNum
; Index
++) {
1014 if (Trb
== CheckedTrb
) {
1019 // If the checked TRB is the link TRB at the end of the transfer ring,
1020 // recircle it to the head of the ring.
1022 if (CheckedTrb
->Type
== TRB_TYPE_LINK
) {
1023 LinkTrb
= (LINK_TRB
*) CheckedTrb
;
1024 PhyAddr
= (EFI_PHYSICAL_ADDRESS
)(LinkTrb
->PtrLo
| LShiftU64 ((UINT64
) LinkTrb
->PtrHi
, 32));
1025 CheckedTrb
= (TRB_TEMPLATE
*)(UINTN
) UsbHcGetHostAddrForPciAddr (Xhc
->MemPool
, (VOID
*)(UINTN
) PhyAddr
, sizeof (TRB_TEMPLATE
));
1026 ASSERT (CheckedTrb
== Urb
->Ring
->RingSeg0
);
1034 Check if the Trb is a transaction of the URBs in XHCI's asynchronous transfer list.
1036 @param Xhc The XHCI Instance.
1037 @param Trb The TRB to be checked.
1038 @param Urb The pointer to the matched Urb.
1040 @retval TRUE The Trb is matched with a transaction of the URBs in the async list.
1041 @retval FALSE The Trb is not matched with any URBs in the async list.
1046 IN USB_XHCI_INSTANCE
*Xhc
,
1047 IN TRB_TEMPLATE
*Trb
,
1055 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1056 CheckedUrb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1057 if (IsTransferRingTrb (Xhc
, Trb
, CheckedUrb
)) {
1068 Check the URB's execution result and update the URB's
1071 @param Xhc The XHCI Instance.
1072 @param Urb The URB to check result.
1074 @return Whether the result of URB transfer is finialized.
1079 IN USB_XHCI_INSTANCE
*Xhc
,
1083 EVT_TRB_TRANSFER
*EvtTrb
;
1084 TRB_TEMPLATE
*TRBPtr
;
1093 EFI_PHYSICAL_ADDRESS PhyAddr
;
1095 ASSERT ((Xhc
!= NULL
) && (Urb
!= NULL
));
1097 Status
= EFI_SUCCESS
;
1100 if (Urb
->Finished
) {
1106 if (XhcIsHalt (Xhc
) || XhcIsSysError (Xhc
)) {
1107 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
1112 // Traverse the event ring to find out all new events from the previous check.
1114 XhcSyncEventRing (Xhc
, &Xhc
->EventRing
);
1115 for (Index
= 0; Index
< Xhc
->EventRing
.TrbNumber
; Index
++) {
1116 Status
= XhcCheckNewEvent (Xhc
, &Xhc
->EventRing
, ((TRB_TEMPLATE
**)&EvtTrb
));
1117 if (Status
== EFI_NOT_READY
) {
1119 // All new events are handled, return directly.
1125 // Only handle COMMAND_COMPLETETION_EVENT and TRANSFER_EVENT.
1127 if ((EvtTrb
->Type
!= TRB_TYPE_COMMAND_COMPLT_EVENT
) && (EvtTrb
->Type
!= TRB_TYPE_TRANS_EVENT
)) {
1132 // Need convert pci device address to host address
1134 PhyAddr
= (EFI_PHYSICAL_ADDRESS
)(EvtTrb
->TRBPtrLo
| LShiftU64 ((UINT64
) EvtTrb
->TRBPtrHi
, 32));
1135 TRBPtr
= (TRB_TEMPLATE
*)(UINTN
) UsbHcGetHostAddrForPciAddr (Xhc
->MemPool
, (VOID
*)(UINTN
) PhyAddr
, sizeof (TRB_TEMPLATE
));
1138 // Update the status of URB including the pending URB, the URB that is currently checked,
1139 // and URBs in the XHCI's async interrupt transfer list.
1140 // This way is used to avoid that those completed async transfer events don't get
1141 // handled in time and are flushed by newer coming events.
1143 if (Xhc
->PendingUrb
!= NULL
&& IsTransferRingTrb (Xhc
, TRBPtr
, Xhc
->PendingUrb
)) {
1144 CheckedUrb
= Xhc
->PendingUrb
;
1145 } else if (IsTransferRingTrb (Xhc
, TRBPtr
, Urb
)) {
1147 } else if (IsAsyncIntTrb (Xhc
, TRBPtr
, &AsyncUrb
)) {
1148 CheckedUrb
= AsyncUrb
;
1153 switch (EvtTrb
->Completecode
) {
1154 case TRB_COMPLETION_STALL_ERROR
:
1155 CheckedUrb
->Result
|= EFI_USB_ERR_STALL
;
1156 CheckedUrb
->Finished
= TRUE
;
1157 DEBUG ((EFI_D_ERROR
, "XhcCheckUrbResult: STALL_ERROR! Completecode = %x\n",EvtTrb
->Completecode
));
1160 case TRB_COMPLETION_BABBLE_ERROR
:
1161 CheckedUrb
->Result
|= EFI_USB_ERR_BABBLE
;
1162 CheckedUrb
->Finished
= TRUE
;
1163 DEBUG ((EFI_D_ERROR
, "XhcCheckUrbResult: BABBLE_ERROR! Completecode = %x\n",EvtTrb
->Completecode
));
1166 case TRB_COMPLETION_DATA_BUFFER_ERROR
:
1167 CheckedUrb
->Result
|= EFI_USB_ERR_BUFFER
;
1168 CheckedUrb
->Finished
= TRUE
;
1169 DEBUG ((EFI_D_ERROR
, "XhcCheckUrbResult: ERR_BUFFER! Completecode = %x\n",EvtTrb
->Completecode
));
1172 case TRB_COMPLETION_USB_TRANSACTION_ERROR
:
1173 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1174 CheckedUrb
->Finished
= TRUE
;
1175 DEBUG ((EFI_D_ERROR
, "XhcCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n",EvtTrb
->Completecode
));
1178 case TRB_COMPLETION_STOPPED
:
1179 case TRB_COMPLETION_STOPPED_LENGTH_INVALID
:
1180 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1181 CheckedUrb
->Finished
= TRUE
;
1183 // The pending URB is timeout and force stopped when stopping endpoint.
1184 // Continue the loop to receive the Command Complete Event for stopping endpoint.
1188 case TRB_COMPLETION_SHORT_PACKET
:
1189 case TRB_COMPLETION_SUCCESS
:
1190 if (EvtTrb
->Completecode
== TRB_COMPLETION_SHORT_PACKET
) {
1191 DEBUG ((EFI_D_VERBOSE
, "XhcCheckUrbResult: short packet happens!\n"));
1194 TRBType
= (UINT8
) (TRBPtr
->Type
);
1195 if ((TRBType
== TRB_TYPE_DATA_STAGE
) ||
1196 (TRBType
== TRB_TYPE_NORMAL
) ||
1197 (TRBType
== TRB_TYPE_ISOCH
)) {
1198 CheckedUrb
->Completed
+= (((TRANSFER_TRB_NORMAL
*)TRBPtr
)->Length
- EvtTrb
->Length
);
1204 DEBUG ((EFI_D_ERROR
, "Transfer Default Error Occur! Completecode = 0x%x!\n",EvtTrb
->Completecode
));
1205 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1206 CheckedUrb
->Finished
= TRUE
;
1211 // Only check first and end Trb event address
1213 if (TRBPtr
== CheckedUrb
->TrbStart
) {
1214 CheckedUrb
->StartDone
= TRUE
;
1217 if (TRBPtr
== CheckedUrb
->TrbEnd
) {
1218 CheckedUrb
->EndDone
= TRUE
;
1221 if (CheckedUrb
->StartDone
&& CheckedUrb
->EndDone
) {
1222 CheckedUrb
->Finished
= TRUE
;
1223 CheckedUrb
->EvtTrb
= (TRB_TEMPLATE
*)EvtTrb
;
1230 // Advance event ring to last available entry
1232 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
1233 // So divide it to two 32-bytes width register access.
1235 Low
= XhcReadRuntimeReg (Xhc
, XHC_ERDP_OFFSET
);
1236 High
= XhcReadRuntimeReg (Xhc
, XHC_ERDP_OFFSET
+ 4);
1237 XhcDequeue
= (UINT64
)(LShiftU64((UINT64
)High
, 32) | Low
);
1239 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->EventRing
.EventRingDequeue
, sizeof (TRB_TEMPLATE
));
1241 if ((XhcDequeue
& (~0x0F)) != (PhyAddr
& (~0x0F))) {
1243 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
1244 // So divide it to two 32-bytes width register access.
1246 XhcWriteRuntimeReg (Xhc
, XHC_ERDP_OFFSET
, XHC_LOW_32BIT (PhyAddr
) | BIT3
);
1247 XhcWriteRuntimeReg (Xhc
, XHC_ERDP_OFFSET
+ 4, XHC_HIGH_32BIT (PhyAddr
));
1250 return Urb
->Finished
;
1255 Execute the transfer by polling the URB. This is a synchronous operation.
1257 @param Xhc The XHCI Instance.
1258 @param CmdTransfer The executed URB is for cmd transfer or not.
1259 @param Urb The URB to execute.
1260 @param Timeout The time to wait before abort, in millisecond.
1262 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.
1263 @return EFI_TIMEOUT The transfer failed due to time out.
1264 @return EFI_SUCCESS The transfer finished OK.
1269 IN USB_XHCI_INSTANCE
*Xhc
,
1270 IN BOOLEAN CmdTransfer
,
1286 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
1288 return EFI_DEVICE_ERROR
;
1290 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
1294 Status
= EFI_SUCCESS
;
1295 Loop
= Timeout
* XHC_1_MILLISECOND
;
1300 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
1302 for (Index
= 0; Index
< Loop
; Index
++) {
1303 Finished
= XhcCheckUrbResult (Xhc
, Urb
);
1307 gBS
->Stall (XHC_1_MICROSECOND
);
1310 if (Index
== Loop
) {
1311 Urb
->Result
= EFI_USB_ERR_TIMEOUT
;
1312 Status
= EFI_TIMEOUT
;
1313 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
1314 Status
= EFI_DEVICE_ERROR
;
1321 Delete a single asynchronous interrupt transfer for
1322 the device and endpoint.
1324 @param Xhc The XHCI Instance.
1325 @param BusAddr The logical device address assigned by UsbBus driver.
1326 @param EpNum The endpoint of the target.
1328 @retval EFI_SUCCESS An asynchronous transfer is removed.
1329 @retval EFI_NOT_FOUND No transfer for the device is found.
1333 XhciDelAsyncIntTransfer (
1334 IN USB_XHCI_INSTANCE
*Xhc
,
1342 EFI_USB_DATA_DIRECTION Direction
;
1345 Direction
= ((EpNum
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
;
1350 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1351 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1352 if ((Urb
->Ep
.BusAddr
== BusAddr
) &&
1353 (Urb
->Ep
.EpAddr
== EpNum
) &&
1354 (Urb
->Ep
.Direction
== Direction
)) {
1356 // Device doesn't finish the IntTransfer until real data comes
1357 // So the TRB should be removed as well.
1359 Status
= XhcDequeueTrbFromEndpoint (Xhc
, Urb
);
1360 if (EFI_ERROR (Status
)) {
1361 DEBUG ((EFI_D_ERROR
, "XhciDelAsyncIntTransfer: XhcDequeueTrbFromEndpoint failed\n"));
1364 RemoveEntryList (&Urb
->UrbList
);
1365 FreePool (Urb
->Data
);
1366 XhcFreeUrb (Xhc
, Urb
);
1371 return EFI_NOT_FOUND
;
1375 Remove all the asynchronous interrutp transfers.
1377 @param Xhc The XHCI Instance.
1381 XhciDelAllAsyncIntTransfers (
1382 IN USB_XHCI_INSTANCE
*Xhc
1390 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1391 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1394 // Device doesn't finish the IntTransfer until real data comes
1395 // So the TRB should be removed as well.
1397 Status
= XhcDequeueTrbFromEndpoint (Xhc
, Urb
);
1398 if (EFI_ERROR (Status
)) {
1399 DEBUG ((EFI_D_ERROR
, "XhciDelAllAsyncIntTransfers: XhcDequeueTrbFromEndpoint failed\n"));
1402 RemoveEntryList (&Urb
->UrbList
);
1403 FreePool (Urb
->Data
);
1404 XhcFreeUrb (Xhc
, Urb
);
1409 Insert a single asynchronous interrupt transfer for
1410 the device and endpoint.
1412 @param Xhc The XHCI Instance
1413 @param BusAddr The logical device address assigned by UsbBus driver
1414 @param EpAddr Endpoint addrress
1415 @param DevSpeed The device speed
1416 @param MaxPacket The max packet length of the endpoint
1417 @param DataLen The length of data buffer
1418 @param Callback The function to call when data is transferred
1419 @param Context The context to the callback
1421 @return Created URB or NULL
1425 XhciInsertAsyncIntTransfer (
1426 IN USB_XHCI_INSTANCE
*Xhc
,
1432 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback
,
1439 Data
= AllocateZeroPool (DataLen
);
1441 DEBUG ((DEBUG_ERROR
, "%a: failed to allocate buffer\n", __FUNCTION__
));
1445 Urb
= XhcCreateUrb (
1451 XHC_INT_TRANSFER_ASYNC
,
1459 DEBUG ((DEBUG_ERROR
, "%a: failed to create URB\n", __FUNCTION__
));
1465 // New asynchronous transfer must inserted to the head.
1466 // Check the comments in XhcMoniteAsyncRequests
1468 InsertHeadList (&Xhc
->AsyncIntTransfers
, &Urb
->UrbList
);
1474 Update the queue head for next round of asynchronous transfer
1476 @param Xhc The XHCI Instance.
1477 @param Urb The URB to update
1481 XhcUpdateAsyncRequest (
1482 IN USB_XHCI_INSTANCE
*Xhc
,
1488 if (Urb
->Result
== EFI_USB_NOERROR
) {
1489 Status
= XhcCreateTransferTrb (Xhc
, Urb
);
1490 if (EFI_ERROR (Status
)) {
1493 Status
= RingIntTransferDoorBell (Xhc
, Urb
);
1494 if (EFI_ERROR (Status
)) {
1501 Flush data from PCI controller specific address to mapped system
1504 @param Xhc The XHCI device.
1505 @param Urb The URB to unmap.
1507 @retval EFI_SUCCESS Success to flush data to mapped system memory.
1508 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
1512 XhcFlushAsyncIntMap (
1513 IN USB_XHCI_INSTANCE
*Xhc
,
1518 EFI_PHYSICAL_ADDRESS PhyAddr
;
1519 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
1520 EFI_PCI_IO_PROTOCOL
*PciIo
;
1527 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
1528 MapOp
= EfiPciIoOperationBusMasterWrite
;
1530 MapOp
= EfiPciIoOperationBusMasterRead
;
1533 if (Urb
->DataMap
!= NULL
) {
1534 Status
= PciIo
->Unmap (PciIo
, Urb
->DataMap
);
1535 if (EFI_ERROR (Status
)) {
1540 Urb
->DataMap
= NULL
;
1542 Status
= PciIo
->Map (PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
1543 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
1547 Urb
->DataPhy
= (VOID
*) ((UINTN
) PhyAddr
);
1552 return EFI_DEVICE_ERROR
;
1556 Interrupt transfer periodic check handler.
1558 @param Event Interrupt event.
1559 @param Context Pointer to USB_XHCI_INSTANCE.
1564 XhcMonitorAsyncRequests (
1569 USB_XHCI_INSTANCE
*Xhc
;
1578 OldTpl
= gBS
->RaiseTPL (XHC_TPL
);
1580 Xhc
= (USB_XHCI_INSTANCE
*) Context
;
1582 BASE_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1583 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1586 // Make sure that the device is available before every check.
1588 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
1594 // Check the result of URB execution. If it is still
1595 // active, check the next one.
1597 XhcCheckUrbResult (Xhc
, Urb
);
1599 if (!Urb
->Finished
) {
1604 // Flush any PCI posted write transactions from a PCI host
1605 // bridge to system memory.
1607 Status
= XhcFlushAsyncIntMap (Xhc
, Urb
);
1608 if (EFI_ERROR (Status
)) {
1609 DEBUG ((EFI_D_ERROR
, "XhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
1613 // Allocate a buffer then copy the transferred data for user.
1614 // If failed to allocate the buffer, update the URB for next
1615 // round of transfer. Ignore the data of this round.
1618 if (Urb
->Result
== EFI_USB_NOERROR
) {
1620 // Make sure the data received from HW is no more than expected.
1622 if (Urb
->Completed
<= Urb
->DataLen
) {
1623 ProcBuf
= AllocateZeroPool (Urb
->Completed
);
1626 if (ProcBuf
== NULL
) {
1627 XhcUpdateAsyncRequest (Xhc
, Urb
);
1631 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
1635 // Leave error recovery to its related device driver. A
1636 // common case of the error recovery is to re-submit the
1637 // interrupt transfer which is linked to the head of the
1638 // list. This function scans from head to tail. So the
1639 // re-submitted interrupt transfer's callback function
1640 // will not be called again in this round. Don't touch this
1641 // URB after the callback, it may have been removed by the
1644 if (Urb
->Callback
!= NULL
) {
1646 // Restore the old TPL, USB bus maybe connect device in
1647 // his callback. Some drivers may has a lower TPL restriction.
1649 gBS
->RestoreTPL (OldTpl
);
1650 (Urb
->Callback
) (ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
1651 OldTpl
= gBS
->RaiseTPL (XHC_TPL
);
1654 if (ProcBuf
!= NULL
) {
1655 gBS
->FreePool (ProcBuf
);
1658 XhcUpdateAsyncRequest (Xhc
, Urb
);
1660 gBS
->RestoreTPL (OldTpl
);
1664 Monitor the port status change. Enable/Disable device slot if there is a device attached/detached.
1666 @param Xhc The XHCI Instance.
1667 @param ParentRouteChart The route string pointed to the parent device if it exists.
1668 @param Port The port to be polled.
1669 @param PortState The port state.
1671 @retval EFI_SUCCESS Successfully enable/disable device slot according to port state.
1672 @retval Others Should not appear.
1677 XhcPollPortStatusChange (
1678 IN USB_XHCI_INSTANCE
*Xhc
,
1679 IN USB_DEV_ROUTE ParentRouteChart
,
1681 IN EFI_USB_PORT_STATUS
*PortState
1687 USB_DEV_ROUTE RouteChart
;
1689 Status
= EFI_SUCCESS
;
1691 if ((PortState
->PortChangeStatus
& (USB_PORT_STAT_C_CONNECTION
| USB_PORT_STAT_C_ENABLE
| USB_PORT_STAT_C_OVERCURRENT
| USB_PORT_STAT_C_RESET
)) == 0) {
1695 if (ParentRouteChart
.Dword
== 0) {
1696 RouteChart
.Route
.RouteString
= 0;
1697 RouteChart
.Route
.RootPortNum
= Port
+ 1;
1698 RouteChart
.Route
.TierNum
= 1;
1701 RouteChart
.Route
.RouteString
= ParentRouteChart
.Route
.RouteString
| (Port
<< (4 * (ParentRouteChart
.Route
.TierNum
- 1)));
1703 RouteChart
.Route
.RouteString
= ParentRouteChart
.Route
.RouteString
| (15 << (4 * (ParentRouteChart
.Route
.TierNum
- 1)));
1705 RouteChart
.Route
.RootPortNum
= ParentRouteChart
.Route
.RootPortNum
;
1706 RouteChart
.Route
.TierNum
= ParentRouteChart
.Route
.TierNum
+ 1;
1709 SlotId
= XhcRouteStringToSlotId (Xhc
, RouteChart
);
1711 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
1712 Status
= XhcDisableSlotCmd (Xhc
, SlotId
);
1714 Status
= XhcDisableSlotCmd64 (Xhc
, SlotId
);
1718 if (((PortState
->PortStatus
& USB_PORT_STAT_ENABLE
) != 0) &&
1719 ((PortState
->PortStatus
& USB_PORT_STAT_CONNECTION
) != 0)) {
1721 // Has a device attached, Identify device speed after port is enabled.
1723 Speed
= EFI_USB_SPEED_FULL
;
1724 if ((PortState
->PortStatus
& USB_PORT_STAT_LOW_SPEED
) != 0) {
1725 Speed
= EFI_USB_SPEED_LOW
;
1726 } else if ((PortState
->PortStatus
& USB_PORT_STAT_HIGH_SPEED
) != 0) {
1727 Speed
= EFI_USB_SPEED_HIGH
;
1728 } else if ((PortState
->PortStatus
& USB_PORT_STAT_SUPER_SPEED
) != 0) {
1729 Speed
= EFI_USB_SPEED_SUPER
;
1732 // Execute Enable_Slot cmd for attached device, initialize device context and assign device address.
1734 SlotId
= XhcRouteStringToSlotId (Xhc
, RouteChart
);
1735 if ((SlotId
== 0) && ((PortState
->PortChangeStatus
& USB_PORT_STAT_C_RESET
) != 0)) {
1736 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
1737 Status
= XhcInitializeDeviceSlot (Xhc
, ParentRouteChart
, Port
, RouteChart
, Speed
);
1739 Status
= XhcInitializeDeviceSlot64 (Xhc
, ParentRouteChart
, Port
, RouteChart
, Speed
);
1749 Calculate the device context index by endpoint address and direction.
1751 @param EpAddr The target endpoint number.
1752 @param Direction The direction of the target endpoint.
1754 @return The device context index of endpoint.
1768 Index
= (UINT8
) (2 * EpAddr
);
1769 if (Direction
== EfiUsbDataIn
) {
1777 Find out the actual device address according to the requested device address from UsbBus.
1779 @param Xhc The XHCI Instance.
1780 @param BusDevAddr The requested device address by UsbBus upper driver.
1782 @return The actual device address assigned to the device.
1787 XhcBusDevAddrToSlotId (
1788 IN USB_XHCI_INSTANCE
*Xhc
,
1794 for (Index
= 0; Index
< 255; Index
++) {
1795 if (Xhc
->UsbDevContext
[Index
+ 1].Enabled
&&
1796 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
!= 0) &&
1797 (Xhc
->UsbDevContext
[Index
+ 1].BusDevAddr
== BusDevAddr
)) {
1806 return Xhc
->UsbDevContext
[Index
+ 1].SlotId
;
1810 Find out the slot id according to the device's route string.
1812 @param Xhc The XHCI Instance.
1813 @param RouteString The route string described the device location.
1815 @return The slot id used by the device.
1820 XhcRouteStringToSlotId (
1821 IN USB_XHCI_INSTANCE
*Xhc
,
1822 IN USB_DEV_ROUTE RouteString
1827 for (Index
= 0; Index
< 255; Index
++) {
1828 if (Xhc
->UsbDevContext
[Index
+ 1].Enabled
&&
1829 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
!= 0) &&
1830 (Xhc
->UsbDevContext
[Index
+ 1].RouteString
.Dword
== RouteString
.Dword
)) {
1839 return Xhc
->UsbDevContext
[Index
+ 1].SlotId
;
1843 Synchronize the specified event ring to update the enqueue and dequeue pointer.
1845 @param Xhc The XHCI Instance.
1846 @param EvtRing The event ring to sync.
1848 @retval EFI_SUCCESS The event ring is synchronized successfully.
1854 IN USB_XHCI_INSTANCE
*Xhc
,
1855 IN EVENT_RING
*EvtRing
1859 TRB_TEMPLATE
*EvtTrb1
;
1861 ASSERT (EvtRing
!= NULL
);
1864 // Calculate the EventRingEnqueue and EventRingCCS.
1865 // Note: only support single Segment
1867 EvtTrb1
= EvtRing
->EventRingDequeue
;
1869 for (Index
= 0; Index
< EvtRing
->TrbNumber
; Index
++) {
1870 if (EvtTrb1
->CycleBit
!= EvtRing
->EventRingCCS
) {
1876 if ((UINTN
)EvtTrb1
>= ((UINTN
) EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
1877 EvtTrb1
= EvtRing
->EventRingSeg0
;
1878 EvtRing
->EventRingCCS
= (EvtRing
->EventRingCCS
) ? 0 : 1;
1882 if (Index
< EvtRing
->TrbNumber
) {
1883 EvtRing
->EventRingEnqueue
= EvtTrb1
;
1892 Synchronize the specified transfer ring to update the enqueue and dequeue pointer.
1894 @param Xhc The XHCI Instance.
1895 @param TrsRing The transfer ring to sync.
1897 @retval EFI_SUCCESS The transfer ring is synchronized successfully.
1903 IN USB_XHCI_INSTANCE
*Xhc
,
1904 IN TRANSFER_RING
*TrsRing
1908 TRB_TEMPLATE
*TrsTrb
;
1910 ASSERT (TrsRing
!= NULL
);
1912 // Calculate the latest RingEnqueue and RingPCS
1914 TrsTrb
= TrsRing
->RingEnqueue
;
1915 ASSERT (TrsTrb
!= NULL
);
1917 for (Index
= 0; Index
< TrsRing
->TrbNumber
; Index
++) {
1918 if (TrsTrb
->CycleBit
!= (TrsRing
->RingPCS
& BIT0
)) {
1922 if ((UINT8
) TrsTrb
->Type
== TRB_TYPE_LINK
) {
1923 ASSERT (((LINK_TRB
*)TrsTrb
)->TC
!= 0);
1925 // set cycle bit in Link TRB as normal
1927 ((LINK_TRB
*)TrsTrb
)->CycleBit
= TrsRing
->RingPCS
& BIT0
;
1929 // Toggle PCS maintained by software
1931 TrsRing
->RingPCS
= (TrsRing
->RingPCS
& BIT0
) ? 0 : 1;
1932 TrsTrb
= (TRB_TEMPLATE
*) TrsRing
->RingSeg0
; // Use host address
1936 ASSERT (Index
!= TrsRing
->TrbNumber
);
1938 if (TrsTrb
!= TrsRing
->RingEnqueue
) {
1939 TrsRing
->RingEnqueue
= TrsTrb
;
1943 // Clear the Trb context for enqueue, but reserve the PCS bit
1945 TrsTrb
->Parameter1
= 0;
1946 TrsTrb
->Parameter2
= 0;
1950 TrsTrb
->Control
= 0;
1956 Check if there is a new generated event.
1958 @param Xhc The XHCI Instance.
1959 @param EvtRing The event ring to check.
1960 @param NewEvtTrb The new event TRB found.
1962 @retval EFI_SUCCESS Found a new event TRB at the event ring.
1963 @retval EFI_NOT_READY The event ring has no new event.
1969 IN USB_XHCI_INSTANCE
*Xhc
,
1970 IN EVENT_RING
*EvtRing
,
1971 OUT TRB_TEMPLATE
**NewEvtTrb
1974 ASSERT (EvtRing
!= NULL
);
1976 *NewEvtTrb
= EvtRing
->EventRingDequeue
;
1978 if (EvtRing
->EventRingDequeue
== EvtRing
->EventRingEnqueue
) {
1979 return EFI_NOT_READY
;
1982 EvtRing
->EventRingDequeue
++;
1984 // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.
1986 if ((UINTN
)EvtRing
->EventRingDequeue
>= ((UINTN
) EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
1987 EvtRing
->EventRingDequeue
= EvtRing
->EventRingSeg0
;
1994 Ring the door bell to notify XHCI there is a transaction to be executed.
1996 @param Xhc The XHCI Instance.
1997 @param SlotId The slot id of the target device.
1998 @param Dci The device context index of the target slot or endpoint.
2000 @retval EFI_SUCCESS Successfully ring the door bell.
2006 IN USB_XHCI_INSTANCE
*Xhc
,
2012 XhcWriteDoorBellReg (Xhc
, 0, 0);
2014 XhcWriteDoorBellReg (Xhc
, SlotId
* sizeof (UINT32
), Dci
);
2021 Ring the door bell to notify XHCI there is a transaction to be executed through URB.
2023 @param Xhc The XHCI Instance.
2024 @param Urb The URB to be rung.
2026 @retval EFI_SUCCESS Successfully ring the door bell.
2030 RingIntTransferDoorBell (
2031 IN USB_XHCI_INSTANCE
*Xhc
,
2038 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
2039 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
2040 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
2045 Assign and initialize the device slot for a new device.
2047 @param Xhc The XHCI Instance.
2048 @param ParentRouteChart The route string pointed to the parent device.
2049 @param ParentPort The port at which the device is located.
2050 @param RouteChart The route string pointed to the device.
2051 @param DeviceSpeed The device speed.
2053 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.
2058 XhcInitializeDeviceSlot (
2059 IN USB_XHCI_INSTANCE
*Xhc
,
2060 IN USB_DEV_ROUTE ParentRouteChart
,
2061 IN UINT16 ParentPort
,
2062 IN USB_DEV_ROUTE RouteChart
,
2063 IN UINT8 DeviceSpeed
2067 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
2068 INPUT_CONTEXT
*InputContext
;
2069 DEVICE_CONTEXT
*OutputContext
;
2070 TRANSFER_RING
*EndpointTransferRing
;
2071 CMD_TRB_ADDRESS_DEVICE CmdTrbAddr
;
2072 UINT8 DeviceAddress
;
2073 CMD_TRB_ENABLE_SLOT CmdTrb
;
2076 DEVICE_CONTEXT
*ParentDeviceContext
;
2077 EFI_PHYSICAL_ADDRESS PhyAddr
;
2079 ZeroMem (&CmdTrb
, sizeof (CMD_TRB_ENABLE_SLOT
));
2080 CmdTrb
.CycleBit
= 1;
2081 CmdTrb
.Type
= TRB_TYPE_EN_SLOT
;
2083 Status
= XhcCmdTransfer (
2085 (TRB_TEMPLATE
*) (UINTN
) &CmdTrb
,
2086 XHC_GENERIC_TIMEOUT
,
2087 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2089 if (EFI_ERROR (Status
)) {
2090 DEBUG ((EFI_D_ERROR
, "XhcInitializeDeviceSlot: Enable Slot Failed, Status = %r\n", Status
));
2093 ASSERT (EvtTrb
->SlotId
<= Xhc
->MaxSlotsEn
);
2094 DEBUG ((EFI_D_INFO
, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb
->SlotId
));
2095 SlotId
= (UINT8
)EvtTrb
->SlotId
;
2096 ASSERT (SlotId
!= 0);
2098 ZeroMem (&Xhc
->UsbDevContext
[SlotId
], sizeof (USB_DEV_CONTEXT
));
2099 Xhc
->UsbDevContext
[SlotId
].Enabled
= TRUE
;
2100 Xhc
->UsbDevContext
[SlotId
].SlotId
= SlotId
;
2101 Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
= RouteChart
.Dword
;
2102 Xhc
->UsbDevContext
[SlotId
].ParentRouteString
.Dword
= ParentRouteChart
.Dword
;
2105 // 4.3.3 Device Slot Initialization
2106 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.
2108 InputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (INPUT_CONTEXT
));
2109 ASSERT (InputContext
!= NULL
);
2110 ASSERT (((UINTN
) InputContext
& 0x3F) == 0);
2111 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
2113 Xhc
->UsbDevContext
[SlotId
].InputContext
= (VOID
*) InputContext
;
2116 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1
2117 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input
2118 // Context are affected by the command.
2120 InputContext
->InputControlContext
.Dword2
|= (BIT0
| BIT1
);
2123 // 3) Initialize the Input Slot Context data structure
2125 InputContext
->Slot
.RouteString
= RouteChart
.Route
.RouteString
;
2126 InputContext
->Slot
.Speed
= DeviceSpeed
+ 1;
2127 InputContext
->Slot
.ContextEntries
= 1;
2128 InputContext
->Slot
.RootHubPortNum
= RouteChart
.Route
.RootPortNum
;
2130 if (RouteChart
.Route
.RouteString
) {
2132 // The device is behind of hub device.
2134 ParentSlotId
= XhcRouteStringToSlotId(Xhc
, ParentRouteChart
);
2135 ASSERT (ParentSlotId
!= 0);
2137 //if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context
2139 ParentDeviceContext
= (DEVICE_CONTEXT
*)Xhc
->UsbDevContext
[ParentSlotId
].OutputContext
;
2140 if ((ParentDeviceContext
->Slot
.TTPortNum
== 0) &&
2141 (ParentDeviceContext
->Slot
.TTHubSlotId
== 0)) {
2142 if ((ParentDeviceContext
->Slot
.Speed
== (EFI_USB_SPEED_HIGH
+ 1)) && (DeviceSpeed
< EFI_USB_SPEED_HIGH
)) {
2144 // Full/Low device attached to High speed hub port that isolates the high speed signaling
2145 // environment from Full/Low speed signaling environment for a device
2147 InputContext
->Slot
.TTPortNum
= ParentPort
;
2148 InputContext
->Slot
.TTHubSlotId
= ParentSlotId
;
2152 // Inherit the TT parameters from parent device.
2154 InputContext
->Slot
.TTPortNum
= ParentDeviceContext
->Slot
.TTPortNum
;
2155 InputContext
->Slot
.TTHubSlotId
= ParentDeviceContext
->Slot
.TTHubSlotId
;
2157 // If the device is a High speed device then down the speed to be the same as its parent Hub
2159 if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2160 InputContext
->Slot
.Speed
= ParentDeviceContext
->Slot
.Speed
;
2166 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.
2168 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2169 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0] = EndpointTransferRing
;
2170 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0]);
2172 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).
2174 InputContext
->EP
[0].EPType
= ED_CONTROL_BIDIR
;
2176 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2177 InputContext
->EP
[0].MaxPacketSize
= 512;
2178 } else if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2179 InputContext
->EP
[0].MaxPacketSize
= 64;
2181 InputContext
->EP
[0].MaxPacketSize
= 8;
2184 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints
2185 // 1KB, and Bulk and Isoch endpoints 3KB.
2187 InputContext
->EP
[0].AverageTRBLength
= 8;
2188 InputContext
->EP
[0].MaxBurstSize
= 0;
2189 InputContext
->EP
[0].Interval
= 0;
2190 InputContext
->EP
[0].MaxPStreams
= 0;
2191 InputContext
->EP
[0].Mult
= 0;
2192 InputContext
->EP
[0].CErr
= 3;
2195 // Init the DCS(dequeue cycle state) as the transfer ring's CCS
2197 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2199 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0])->RingSeg0
,
2200 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2202 InputContext
->EP
[0].PtrLo
= XHC_LOW_32BIT (PhyAddr
) | BIT0
;
2203 InputContext
->EP
[0].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2206 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
2208 OutputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (DEVICE_CONTEXT
));
2209 ASSERT (OutputContext
!= NULL
);
2210 ASSERT (((UINTN
) OutputContext
& 0x3F) == 0);
2211 ZeroMem (OutputContext
, sizeof (DEVICE_CONTEXT
));
2213 Xhc
->UsbDevContext
[SlotId
].OutputContext
= OutputContext
;
2215 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with
2216 // a pointer to the Output Device Context data structure (6.2.1).
2218 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, OutputContext
, sizeof (DEVICE_CONTEXT
));
2220 // Fill DCBAA with PCI device address
2222 Xhc
->DCBAA
[SlotId
] = (UINT64
) (UINTN
) PhyAddr
;
2225 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input
2226 // Context data structure described above.
2228 // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request
2231 gBS
->Stall (XHC_RESET_RECOVERY_DELAY
);
2232 ZeroMem (&CmdTrbAddr
, sizeof (CmdTrbAddr
));
2233 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT
));
2234 CmdTrbAddr
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2235 CmdTrbAddr
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2236 CmdTrbAddr
.CycleBit
= 1;
2237 CmdTrbAddr
.Type
= TRB_TYPE_ADDRESS_DEV
;
2238 CmdTrbAddr
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
2239 Status
= XhcCmdTransfer (
2241 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbAddr
,
2242 XHC_GENERIC_TIMEOUT
,
2243 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2245 if (!EFI_ERROR (Status
)) {
2246 DeviceAddress
= (UINT8
) ((DEVICE_CONTEXT
*) OutputContext
)->Slot
.DeviceAddress
;
2247 DEBUG ((EFI_D_INFO
, " Address %d assigned successfully\n", DeviceAddress
));
2248 Xhc
->UsbDevContext
[SlotId
].XhciDevAddr
= DeviceAddress
;
2255 Assign and initialize the device slot for a new device.
2257 @param Xhc The XHCI Instance.
2258 @param ParentRouteChart The route string pointed to the parent device.
2259 @param ParentPort The port at which the device is located.
2260 @param RouteChart The route string pointed to the device.
2261 @param DeviceSpeed The device speed.
2263 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.
2268 XhcInitializeDeviceSlot64 (
2269 IN USB_XHCI_INSTANCE
*Xhc
,
2270 IN USB_DEV_ROUTE ParentRouteChart
,
2271 IN UINT16 ParentPort
,
2272 IN USB_DEV_ROUTE RouteChart
,
2273 IN UINT8 DeviceSpeed
2277 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
2278 INPUT_CONTEXT_64
*InputContext
;
2279 DEVICE_CONTEXT_64
*OutputContext
;
2280 TRANSFER_RING
*EndpointTransferRing
;
2281 CMD_TRB_ADDRESS_DEVICE CmdTrbAddr
;
2282 UINT8 DeviceAddress
;
2283 CMD_TRB_ENABLE_SLOT CmdTrb
;
2286 DEVICE_CONTEXT_64
*ParentDeviceContext
;
2287 EFI_PHYSICAL_ADDRESS PhyAddr
;
2289 ZeroMem (&CmdTrb
, sizeof (CMD_TRB_ENABLE_SLOT
));
2290 CmdTrb
.CycleBit
= 1;
2291 CmdTrb
.Type
= TRB_TYPE_EN_SLOT
;
2293 Status
= XhcCmdTransfer (
2295 (TRB_TEMPLATE
*) (UINTN
) &CmdTrb
,
2296 XHC_GENERIC_TIMEOUT
,
2297 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2299 if (EFI_ERROR (Status
)) {
2300 DEBUG ((EFI_D_ERROR
, "XhcInitializeDeviceSlot64: Enable Slot Failed, Status = %r\n", Status
));
2303 ASSERT (EvtTrb
->SlotId
<= Xhc
->MaxSlotsEn
);
2304 DEBUG ((EFI_D_INFO
, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb
->SlotId
));
2305 SlotId
= (UINT8
)EvtTrb
->SlotId
;
2306 ASSERT (SlotId
!= 0);
2308 ZeroMem (&Xhc
->UsbDevContext
[SlotId
], sizeof (USB_DEV_CONTEXT
));
2309 Xhc
->UsbDevContext
[SlotId
].Enabled
= TRUE
;
2310 Xhc
->UsbDevContext
[SlotId
].SlotId
= SlotId
;
2311 Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
= RouteChart
.Dword
;
2312 Xhc
->UsbDevContext
[SlotId
].ParentRouteString
.Dword
= ParentRouteChart
.Dword
;
2315 // 4.3.3 Device Slot Initialization
2316 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.
2318 InputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (INPUT_CONTEXT_64
));
2319 ASSERT (InputContext
!= NULL
);
2320 ASSERT (((UINTN
) InputContext
& 0x3F) == 0);
2321 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
2323 Xhc
->UsbDevContext
[SlotId
].InputContext
= (VOID
*) InputContext
;
2326 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1
2327 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input
2328 // Context are affected by the command.
2330 InputContext
->InputControlContext
.Dword2
|= (BIT0
| BIT1
);
2333 // 3) Initialize the Input Slot Context data structure
2335 InputContext
->Slot
.RouteString
= RouteChart
.Route
.RouteString
;
2336 InputContext
->Slot
.Speed
= DeviceSpeed
+ 1;
2337 InputContext
->Slot
.ContextEntries
= 1;
2338 InputContext
->Slot
.RootHubPortNum
= RouteChart
.Route
.RootPortNum
;
2340 if (RouteChart
.Route
.RouteString
) {
2342 // The device is behind of hub device.
2344 ParentSlotId
= XhcRouteStringToSlotId(Xhc
, ParentRouteChart
);
2345 ASSERT (ParentSlotId
!= 0);
2347 //if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context
2349 ParentDeviceContext
= (DEVICE_CONTEXT_64
*)Xhc
->UsbDevContext
[ParentSlotId
].OutputContext
;
2350 if ((ParentDeviceContext
->Slot
.TTPortNum
== 0) &&
2351 (ParentDeviceContext
->Slot
.TTHubSlotId
== 0)) {
2352 if ((ParentDeviceContext
->Slot
.Speed
== (EFI_USB_SPEED_HIGH
+ 1)) && (DeviceSpeed
< EFI_USB_SPEED_HIGH
)) {
2354 // Full/Low device attached to High speed hub port that isolates the high speed signaling
2355 // environment from Full/Low speed signaling environment for a device
2357 InputContext
->Slot
.TTPortNum
= ParentPort
;
2358 InputContext
->Slot
.TTHubSlotId
= ParentSlotId
;
2362 // Inherit the TT parameters from parent device.
2364 InputContext
->Slot
.TTPortNum
= ParentDeviceContext
->Slot
.TTPortNum
;
2365 InputContext
->Slot
.TTHubSlotId
= ParentDeviceContext
->Slot
.TTHubSlotId
;
2367 // If the device is a High speed device then down the speed to be the same as its parent Hub
2369 if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2370 InputContext
->Slot
.Speed
= ParentDeviceContext
->Slot
.Speed
;
2376 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.
2378 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2379 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0] = EndpointTransferRing
;
2380 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0]);
2382 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).
2384 InputContext
->EP
[0].EPType
= ED_CONTROL_BIDIR
;
2386 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2387 InputContext
->EP
[0].MaxPacketSize
= 512;
2388 } else if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2389 InputContext
->EP
[0].MaxPacketSize
= 64;
2391 InputContext
->EP
[0].MaxPacketSize
= 8;
2394 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints
2395 // 1KB, and Bulk and Isoch endpoints 3KB.
2397 InputContext
->EP
[0].AverageTRBLength
= 8;
2398 InputContext
->EP
[0].MaxBurstSize
= 0;
2399 InputContext
->EP
[0].Interval
= 0;
2400 InputContext
->EP
[0].MaxPStreams
= 0;
2401 InputContext
->EP
[0].Mult
= 0;
2402 InputContext
->EP
[0].CErr
= 3;
2405 // Init the DCS(dequeue cycle state) as the transfer ring's CCS
2407 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2409 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0])->RingSeg0
,
2410 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2412 InputContext
->EP
[0].PtrLo
= XHC_LOW_32BIT (PhyAddr
) | BIT0
;
2413 InputContext
->EP
[0].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2416 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
2418 OutputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (DEVICE_CONTEXT_64
));
2419 ASSERT (OutputContext
!= NULL
);
2420 ASSERT (((UINTN
) OutputContext
& 0x3F) == 0);
2421 ZeroMem (OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2423 Xhc
->UsbDevContext
[SlotId
].OutputContext
= OutputContext
;
2425 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with
2426 // a pointer to the Output Device Context data structure (6.2.1).
2428 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2430 // Fill DCBAA with PCI device address
2432 Xhc
->DCBAA
[SlotId
] = (UINT64
) (UINTN
) PhyAddr
;
2435 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input
2436 // Context data structure described above.
2438 // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request
2441 gBS
->Stall (XHC_RESET_RECOVERY_DELAY
);
2442 ZeroMem (&CmdTrbAddr
, sizeof (CmdTrbAddr
));
2443 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT_64
));
2444 CmdTrbAddr
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2445 CmdTrbAddr
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2446 CmdTrbAddr
.CycleBit
= 1;
2447 CmdTrbAddr
.Type
= TRB_TYPE_ADDRESS_DEV
;
2448 CmdTrbAddr
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
2449 Status
= XhcCmdTransfer (
2451 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbAddr
,
2452 XHC_GENERIC_TIMEOUT
,
2453 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2455 if (!EFI_ERROR (Status
)) {
2456 DeviceAddress
= (UINT8
) ((DEVICE_CONTEXT_64
*) OutputContext
)->Slot
.DeviceAddress
;
2457 DEBUG ((EFI_D_INFO
, " Address %d assigned successfully\n", DeviceAddress
));
2458 Xhc
->UsbDevContext
[SlotId
].XhciDevAddr
= DeviceAddress
;
2465 Disable the specified device slot.
2467 @param Xhc The XHCI Instance.
2468 @param SlotId The slot id to be disabled.
2470 @retval EFI_SUCCESS Successfully disable the device slot.
2476 IN USB_XHCI_INSTANCE
*Xhc
,
2481 TRB_TEMPLATE
*EvtTrb
;
2482 CMD_TRB_DISABLE_SLOT CmdTrbDisSlot
;
2487 // Disable the device slots occupied by these devices on its downstream ports.
2488 // Entry 0 is reserved.
2490 for (Index
= 0; Index
< 255; Index
++) {
2491 if (!Xhc
->UsbDevContext
[Index
+ 1].Enabled
||
2492 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
== 0) ||
2493 (Xhc
->UsbDevContext
[Index
+ 1].ParentRouteString
.Dword
!= Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
)) {
2497 Status
= XhcDisableSlotCmd (Xhc
, Xhc
->UsbDevContext
[Index
+ 1].SlotId
);
2499 if (EFI_ERROR (Status
)) {
2500 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
2501 Xhc
->UsbDevContext
[Index
+ 1].SlotId
= 0;
2506 // Construct the disable slot command
2508 DEBUG ((EFI_D_INFO
, "Disable device slot %d!\n", SlotId
));
2510 ZeroMem (&CmdTrbDisSlot
, sizeof (CmdTrbDisSlot
));
2511 CmdTrbDisSlot
.CycleBit
= 1;
2512 CmdTrbDisSlot
.Type
= TRB_TYPE_DIS_SLOT
;
2513 CmdTrbDisSlot
.SlotId
= SlotId
;
2514 Status
= XhcCmdTransfer (
2516 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbDisSlot
,
2517 XHC_GENERIC_TIMEOUT
,
2518 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2520 if (EFI_ERROR (Status
)) {
2521 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status
));
2525 // Free the slot's device context entry
2527 Xhc
->DCBAA
[SlotId
] = 0;
2530 // Free the slot related data structure
2532 for (Index
= 0; Index
< 31; Index
++) {
2533 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] != NULL
) {
2534 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
])->RingSeg0
;
2535 if (RingSeg
!= NULL
) {
2536 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
2538 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
]);
2539 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] = NULL
;
2543 for (Index
= 0; Index
< Xhc
->UsbDevContext
[SlotId
].DevDesc
.NumConfigurations
; Index
++) {
2544 if (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
] != NULL
) {
2545 FreePool (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
]);
2549 if (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
!= NULL
) {
2550 FreePool (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
);
2553 if (Xhc
->UsbDevContext
[SlotId
].InputContext
!= NULL
) {
2554 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT
));
2557 if (Xhc
->UsbDevContext
[SlotId
].OutputContext
!= NULL
) {
2558 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].OutputContext
, sizeof (DEVICE_CONTEXT
));
2561 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established
2562 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to
2563 // remove urb from XHCI's asynchronous transfer list.
2565 Xhc
->UsbDevContext
[SlotId
].Enabled
= FALSE
;
2566 Xhc
->UsbDevContext
[SlotId
].SlotId
= 0;
2572 Disable the specified device slot.
2574 @param Xhc The XHCI Instance.
2575 @param SlotId The slot id to be disabled.
2577 @retval EFI_SUCCESS Successfully disable the device slot.
2582 XhcDisableSlotCmd64 (
2583 IN USB_XHCI_INSTANCE
*Xhc
,
2588 TRB_TEMPLATE
*EvtTrb
;
2589 CMD_TRB_DISABLE_SLOT CmdTrbDisSlot
;
2594 // Disable the device slots occupied by these devices on its downstream ports.
2595 // Entry 0 is reserved.
2597 for (Index
= 0; Index
< 255; Index
++) {
2598 if (!Xhc
->UsbDevContext
[Index
+ 1].Enabled
||
2599 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
== 0) ||
2600 (Xhc
->UsbDevContext
[Index
+ 1].ParentRouteString
.Dword
!= Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
)) {
2604 Status
= XhcDisableSlotCmd64 (Xhc
, Xhc
->UsbDevContext
[Index
+ 1].SlotId
);
2606 if (EFI_ERROR (Status
)) {
2607 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
2608 Xhc
->UsbDevContext
[Index
+ 1].SlotId
= 0;
2613 // Construct the disable slot command
2615 DEBUG ((EFI_D_INFO
, "Disable device slot %d!\n", SlotId
));
2617 ZeroMem (&CmdTrbDisSlot
, sizeof (CmdTrbDisSlot
));
2618 CmdTrbDisSlot
.CycleBit
= 1;
2619 CmdTrbDisSlot
.Type
= TRB_TYPE_DIS_SLOT
;
2620 CmdTrbDisSlot
.SlotId
= SlotId
;
2621 Status
= XhcCmdTransfer (
2623 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbDisSlot
,
2624 XHC_GENERIC_TIMEOUT
,
2625 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2627 if (EFI_ERROR (Status
)) {
2628 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status
));
2632 // Free the slot's device context entry
2634 Xhc
->DCBAA
[SlotId
] = 0;
2637 // Free the slot related data structure
2639 for (Index
= 0; Index
< 31; Index
++) {
2640 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] != NULL
) {
2641 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
])->RingSeg0
;
2642 if (RingSeg
!= NULL
) {
2643 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
2645 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
]);
2646 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] = NULL
;
2650 for (Index
= 0; Index
< Xhc
->UsbDevContext
[SlotId
].DevDesc
.NumConfigurations
; Index
++) {
2651 if (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
] != NULL
) {
2652 FreePool (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
]);
2656 if (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
!= NULL
) {
2657 FreePool (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
);
2660 if (Xhc
->UsbDevContext
[SlotId
].InputContext
!= NULL
) {
2661 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT_64
));
2664 if (Xhc
->UsbDevContext
[SlotId
].OutputContext
!= NULL
) {
2665 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2668 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established
2669 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to
2670 // remove urb from XHCI's asynchronous transfer list.
2672 Xhc
->UsbDevContext
[SlotId
].Enabled
= FALSE
;
2673 Xhc
->UsbDevContext
[SlotId
].SlotId
= 0;
2679 Initialize endpoint context in input context.
2681 @param Xhc The XHCI Instance.
2682 @param SlotId The slot id to be configured.
2683 @param DeviceSpeed The device's speed.
2684 @param InputContext The pointer to the input context.
2685 @param IfDesc The pointer to the usb device interface descriptor.
2687 @return The maximum device context index of endpoint.
2692 XhcInitializeEndpointContext (
2693 IN USB_XHCI_INSTANCE
*Xhc
,
2695 IN UINT8 DeviceSpeed
,
2696 IN INPUT_CONTEXT
*InputContext
,
2697 IN USB_INTERFACE_DESCRIPTOR
*IfDesc
2700 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
2707 EFI_PHYSICAL_ADDRESS PhyAddr
;
2709 TRANSFER_RING
*EndpointTransferRing
;
2713 NumEp
= IfDesc
->NumEndpoints
;
2715 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDesc
+ 1);
2716 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
2717 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
2718 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2721 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
2722 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2726 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
2727 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
2729 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
2735 InputContext
->InputControlContext
.Dword2
|= (BIT0
<< Dci
);
2736 InputContext
->EP
[Dci
-1].MaxPacketSize
= EpDesc
->MaxPacketSize
;
2738 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2740 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.
2742 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2744 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2747 switch (EpDesc
->Attributes
& USB_ENDPOINT_TYPE_MASK
) {
2748 case USB_ENDPOINT_BULK
:
2749 if (Direction
== EfiUsbDataIn
) {
2750 InputContext
->EP
[Dci
-1].CErr
= 3;
2751 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_IN
;
2753 InputContext
->EP
[Dci
-1].CErr
= 3;
2754 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_OUT
;
2757 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2758 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2759 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
2760 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
2761 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2762 DEBUG ((DEBUG_INFO
, "Endpoint[%x]: Created BULK ring [%p~%p)\n",
2763 EpDesc
->EndpointAddress
,
2764 EndpointTransferRing
->RingSeg0
,
2765 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2770 case USB_ENDPOINT_ISO
:
2771 if (Direction
== EfiUsbDataIn
) {
2772 InputContext
->EP
[Dci
-1].CErr
= 0;
2773 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_IN
;
2775 InputContext
->EP
[Dci
-1].CErr
= 0;
2776 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_OUT
;
2779 // Get the bInterval from descriptor and init the the interval field of endpoint context.
2780 // Refer to XHCI 1.1 spec section 6.2.3.6.
2782 if (DeviceSpeed
== EFI_USB_SPEED_FULL
) {
2783 Interval
= EpDesc
->Interval
;
2784 ASSERT (Interval
>= 1 && Interval
<= 16);
2785 InputContext
->EP
[Dci
-1].Interval
= Interval
+ 2;
2786 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2787 Interval
= EpDesc
->Interval
;
2788 ASSERT (Interval
>= 1 && Interval
<= 16);
2789 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2793 // Do not support isochronous transfer now.
2795 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
2796 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2798 case USB_ENDPOINT_INTERRUPT
:
2799 if (Direction
== EfiUsbDataIn
) {
2800 InputContext
->EP
[Dci
-1].CErr
= 3;
2801 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_IN
;
2803 InputContext
->EP
[Dci
-1].CErr
= 3;
2804 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_OUT
;
2806 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2807 InputContext
->EP
[Dci
-1].MaxESITPayload
= EpDesc
->MaxPacketSize
;
2809 // Get the bInterval from descriptor and init the the interval field of endpoint context
2811 if ((DeviceSpeed
== EFI_USB_SPEED_FULL
) || (DeviceSpeed
== EFI_USB_SPEED_LOW
)) {
2812 Interval
= EpDesc
->Interval
;
2814 // Calculate through the bInterval field of Endpoint descriptor.
2816 ASSERT (Interval
!= 0);
2817 InputContext
->EP
[Dci
-1].Interval
= (UINT32
)HighBitSet32((UINT32
)Interval
) + 3;
2818 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2819 Interval
= EpDesc
->Interval
;
2820 ASSERT (Interval
>= 1 && Interval
<= 16);
2822 // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
2824 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2825 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2826 InputContext
->EP
[Dci
-1].MaxESITPayload
= 0x0002;
2827 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2828 InputContext
->EP
[Dci
-1].CErr
= 3;
2831 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2832 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
2833 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
2834 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2835 DEBUG ((DEBUG_INFO
, "Endpoint[%x]: Created INT ring [%p~%p)\n",
2836 EpDesc
->EndpointAddress
,
2837 EndpointTransferRing
->RingSeg0
,
2838 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2843 case USB_ENDPOINT_CONTROL
:
2845 // Do not support control transfer now.
2847 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext: Unsupport Control EP found, Transfer ring is not allocated.\n"));
2849 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext: Unknown EP found, Transfer ring is not allocated.\n"));
2850 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2854 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2856 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingSeg0
,
2857 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2859 PhyAddr
&= ~((EFI_PHYSICAL_ADDRESS
)0x0F);
2860 PhyAddr
|= (EFI_PHYSICAL_ADDRESS
)((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingPCS
;
2861 InputContext
->EP
[Dci
-1].PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2862 InputContext
->EP
[Dci
-1].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2864 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2871 Initialize endpoint context in input context.
2873 @param Xhc The XHCI Instance.
2874 @param SlotId The slot id to be configured.
2875 @param DeviceSpeed The device's speed.
2876 @param InputContext The pointer to the input context.
2877 @param IfDesc The pointer to the usb device interface descriptor.
2879 @return The maximum device context index of endpoint.
2884 XhcInitializeEndpointContext64 (
2885 IN USB_XHCI_INSTANCE
*Xhc
,
2887 IN UINT8 DeviceSpeed
,
2888 IN INPUT_CONTEXT_64
*InputContext
,
2889 IN USB_INTERFACE_DESCRIPTOR
*IfDesc
2892 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
2899 EFI_PHYSICAL_ADDRESS PhyAddr
;
2901 TRANSFER_RING
*EndpointTransferRing
;
2905 NumEp
= IfDesc
->NumEndpoints
;
2907 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDesc
+ 1);
2908 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
2909 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
2910 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2913 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
2914 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2918 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
2919 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
2921 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
2927 InputContext
->InputControlContext
.Dword2
|= (BIT0
<< Dci
);
2928 InputContext
->EP
[Dci
-1].MaxPacketSize
= EpDesc
->MaxPacketSize
;
2930 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2932 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.
2934 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2936 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2939 switch (EpDesc
->Attributes
& USB_ENDPOINT_TYPE_MASK
) {
2940 case USB_ENDPOINT_BULK
:
2941 if (Direction
== EfiUsbDataIn
) {
2942 InputContext
->EP
[Dci
-1].CErr
= 3;
2943 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_IN
;
2945 InputContext
->EP
[Dci
-1].CErr
= 3;
2946 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_OUT
;
2949 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2950 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2951 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
2952 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
2953 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2954 DEBUG ((DEBUG_INFO
, "Endpoint64[%x]: Created BULK ring [%p~%p)\n",
2955 EpDesc
->EndpointAddress
,
2956 EndpointTransferRing
->RingSeg0
,
2957 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2962 case USB_ENDPOINT_ISO
:
2963 if (Direction
== EfiUsbDataIn
) {
2964 InputContext
->EP
[Dci
-1].CErr
= 0;
2965 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_IN
;
2967 InputContext
->EP
[Dci
-1].CErr
= 0;
2968 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_OUT
;
2971 // Get the bInterval from descriptor and init the the interval field of endpoint context.
2972 // Refer to XHCI 1.1 spec section 6.2.3.6.
2974 if (DeviceSpeed
== EFI_USB_SPEED_FULL
) {
2975 Interval
= EpDesc
->Interval
;
2976 ASSERT (Interval
>= 1 && Interval
<= 16);
2977 InputContext
->EP
[Dci
-1].Interval
= Interval
+ 2;
2978 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2979 Interval
= EpDesc
->Interval
;
2980 ASSERT (Interval
>= 1 && Interval
<= 16);
2981 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2985 // Do not support isochronous transfer now.
2987 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext64: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
2988 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2990 case USB_ENDPOINT_INTERRUPT
:
2991 if (Direction
== EfiUsbDataIn
) {
2992 InputContext
->EP
[Dci
-1].CErr
= 3;
2993 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_IN
;
2995 InputContext
->EP
[Dci
-1].CErr
= 3;
2996 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_OUT
;
2998 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2999 InputContext
->EP
[Dci
-1].MaxESITPayload
= EpDesc
->MaxPacketSize
;
3001 // Get the bInterval from descriptor and init the the interval field of endpoint context
3003 if ((DeviceSpeed
== EFI_USB_SPEED_FULL
) || (DeviceSpeed
== EFI_USB_SPEED_LOW
)) {
3004 Interval
= EpDesc
->Interval
;
3006 // Calculate through the bInterval field of Endpoint descriptor.
3008 ASSERT (Interval
!= 0);
3009 InputContext
->EP
[Dci
-1].Interval
= (UINT32
)HighBitSet32((UINT32
)Interval
) + 3;
3010 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
3011 Interval
= EpDesc
->Interval
;
3012 ASSERT (Interval
>= 1 && Interval
<= 16);
3014 // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
3016 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
3017 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3018 InputContext
->EP
[Dci
-1].MaxESITPayload
= 0x0002;
3019 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
3020 InputContext
->EP
[Dci
-1].CErr
= 3;
3023 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
3024 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
3025 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
3026 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
3027 DEBUG ((DEBUG_INFO
, "Endpoint64[%x]: Created INT ring [%p~%p)\n",
3028 EpDesc
->EndpointAddress
,
3029 EndpointTransferRing
->RingSeg0
,
3030 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
3035 case USB_ENDPOINT_CONTROL
:
3037 // Do not support control transfer now.
3039 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext64: Unsupport Control EP found, Transfer ring is not allocated.\n"));
3041 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext64: Unknown EP found, Transfer ring is not allocated.\n"));
3042 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3046 PhyAddr
= UsbHcGetPciAddrForHostAddr (
3048 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingSeg0
,
3049 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
3051 PhyAddr
&= ~((EFI_PHYSICAL_ADDRESS
)0x0F);
3052 PhyAddr
|= (EFI_PHYSICAL_ADDRESS
)((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingPCS
;
3053 InputContext
->EP
[Dci
-1].PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3054 InputContext
->EP
[Dci
-1].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3056 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3063 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
3065 @param Xhc The XHCI Instance.
3066 @param SlotId The slot id to be configured.
3067 @param DeviceSpeed The device's speed.
3068 @param ConfigDesc The pointer to the usb device configuration descriptor.
3070 @retval EFI_SUCCESS Successfully configure all the device endpoints.
3076 IN USB_XHCI_INSTANCE
*Xhc
,
3078 IN UINT8 DeviceSpeed
,
3079 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
3083 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3087 EFI_PHYSICAL_ADDRESS PhyAddr
;
3089 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3090 INPUT_CONTEXT
*InputContext
;
3091 DEVICE_CONTEXT
*OutputContext
;
3092 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3094 // 4.6.6 Configure Endpoint
3096 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3097 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3098 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3099 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT
));
3101 ASSERT (ConfigDesc
!= NULL
);
3105 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3106 for (Index
= 0; Index
< ConfigDesc
->NumInterfaces
; Index
++) {
3107 while ((IfDesc
->DescriptorType
!= USB_DESC_TYPE_INTERFACE
) || (IfDesc
->AlternateSetting
!= 0)) {
3108 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3111 if (IfDesc
->Length
< sizeof (USB_INTERFACE_DESCRIPTOR
)) {
3112 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3116 Dci
= XhcInitializeEndpointContext (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDesc
);
3121 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3124 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3125 InputContext
->Slot
.ContextEntries
= MaxDci
;
3127 // configure endpoint
3129 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3130 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3131 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3132 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3133 CmdTrbCfgEP
.CycleBit
= 1;
3134 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3135 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3136 DEBUG ((EFI_D_INFO
, "Configure Endpoint\n"));
3137 Status
= XhcCmdTransfer (
3139 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3140 XHC_GENERIC_TIMEOUT
,
3141 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3143 if (EFI_ERROR (Status
)) {
3144 DEBUG ((EFI_D_ERROR
, "XhcSetConfigCmd: Config Endpoint Failed, Status = %r\n", Status
));
3146 Xhc
->UsbDevContext
[SlotId
].ActiveConfiguration
= ConfigDesc
->ConfigurationValue
;
3153 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
3155 @param Xhc The XHCI Instance.
3156 @param SlotId The slot id to be configured.
3157 @param DeviceSpeed The device's speed.
3158 @param ConfigDesc The pointer to the usb device configuration descriptor.
3160 @retval EFI_SUCCESS Successfully configure all the device endpoints.
3166 IN USB_XHCI_INSTANCE
*Xhc
,
3168 IN UINT8 DeviceSpeed
,
3169 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
3173 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3177 EFI_PHYSICAL_ADDRESS PhyAddr
;
3179 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3180 INPUT_CONTEXT_64
*InputContext
;
3181 DEVICE_CONTEXT_64
*OutputContext
;
3182 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3184 // 4.6.6 Configure Endpoint
3186 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3187 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3188 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3189 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT_64
));
3191 ASSERT (ConfigDesc
!= NULL
);
3195 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3196 for (Index
= 0; Index
< ConfigDesc
->NumInterfaces
; Index
++) {
3197 while ((IfDesc
->DescriptorType
!= USB_DESC_TYPE_INTERFACE
) || (IfDesc
->AlternateSetting
!= 0)) {
3198 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3201 if (IfDesc
->Length
< sizeof (USB_INTERFACE_DESCRIPTOR
)) {
3202 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3206 Dci
= XhcInitializeEndpointContext64 (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDesc
);
3211 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3214 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3215 InputContext
->Slot
.ContextEntries
= MaxDci
;
3217 // configure endpoint
3219 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3220 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3221 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3222 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3223 CmdTrbCfgEP
.CycleBit
= 1;
3224 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3225 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3226 DEBUG ((EFI_D_INFO
, "Configure Endpoint\n"));
3227 Status
= XhcCmdTransfer (
3229 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3230 XHC_GENERIC_TIMEOUT
,
3231 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3233 if (EFI_ERROR (Status
)) {
3234 DEBUG ((EFI_D_ERROR
, "XhcSetConfigCmd64: Config Endpoint Failed, Status = %r\n", Status
));
3236 Xhc
->UsbDevContext
[SlotId
].ActiveConfiguration
= ConfigDesc
->ConfigurationValue
;
3243 Stop endpoint through XHCI's Stop_Endpoint cmd.
3245 @param Xhc The XHCI Instance.
3246 @param SlotId The slot id to be configured.
3247 @param Dci The device context index of endpoint.
3248 @param PendingUrb The pending URB to check completion status when stopping the end point.
3250 @retval EFI_SUCCESS Stop endpoint successfully.
3251 @retval Others Failed to stop endpoint.
3257 IN USB_XHCI_INSTANCE
*Xhc
,
3260 IN URB
*PendingUrb OPTIONAL
3264 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3265 CMD_TRB_STOP_ENDPOINT CmdTrbStopED
;
3267 DEBUG ((EFI_D_INFO
, "XhcStopEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId
, Dci
));
3270 // When XhcCheckUrbResult waits for the Stop_Endpoint completion, it also checks
3271 // the PendingUrb completion status, because it's possible that the PendingUrb is
3272 // finished just before stopping the end point, but after the looping check.
3274 // The PendingUrb could be passed to XhcCmdTransfer to XhcExecTransfer to XhcCheckUrbResult
3275 // through function parameter, but That will cause every consumer of XhcCmdTransfer,
3276 // XhcExecTransfer and XhcCheckUrbResult pass a NULL PendingUrb.
3277 // But actually only XhcCheckUrbResult is aware of the PendingUrb.
3278 // So we choose to save the PendingUrb into the USB_XHCI_INSTANCE and use it in XhcCheckUrbResult.
3280 ASSERT (Xhc
->PendingUrb
== NULL
);
3281 Xhc
->PendingUrb
= PendingUrb
;
3283 // Reset the URB result from Timeout to NoError.
3284 // The USB result will be:
3285 // changed to Timeout when Stop/StopInvalidLength Transfer Event is received, or
3286 // remain NoError when Success/ShortPacket Transfer Event is received.
3288 if (PendingUrb
!= NULL
) {
3289 PendingUrb
->Result
= EFI_USB_NOERROR
;
3293 // Send stop endpoint command to transit Endpoint from running to stop state
3295 ZeroMem (&CmdTrbStopED
, sizeof (CmdTrbStopED
));
3296 CmdTrbStopED
.CycleBit
= 1;
3297 CmdTrbStopED
.Type
= TRB_TYPE_STOP_ENDPOINT
;
3298 CmdTrbStopED
.EDID
= Dci
;
3299 CmdTrbStopED
.SlotId
= SlotId
;
3300 Status
= XhcCmdTransfer (
3302 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbStopED
,
3303 XHC_GENERIC_TIMEOUT
,
3304 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3306 if (EFI_ERROR(Status
)) {
3307 DEBUG ((EFI_D_ERROR
, "XhcStopEndpoint: Stop Endpoint Failed, Status = %r\n", Status
));
3310 Xhc
->PendingUrb
= NULL
;
3316 Reset endpoint through XHCI's Reset_Endpoint cmd.
3318 @param Xhc The XHCI Instance.
3319 @param SlotId The slot id to be configured.
3320 @param Dci The device context index of endpoint.
3322 @retval EFI_SUCCESS Reset endpoint successfully.
3323 @retval Others Failed to reset endpoint.
3329 IN USB_XHCI_INSTANCE
*Xhc
,
3335 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3336 CMD_TRB_RESET_ENDPOINT CmdTrbResetED
;
3338 DEBUG ((EFI_D_INFO
, "XhcResetEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId
, Dci
));
3341 // Send stop endpoint command to transit Endpoint from running to stop state
3343 ZeroMem (&CmdTrbResetED
, sizeof (CmdTrbResetED
));
3344 CmdTrbResetED
.CycleBit
= 1;
3345 CmdTrbResetED
.Type
= TRB_TYPE_RESET_ENDPOINT
;
3346 CmdTrbResetED
.EDID
= Dci
;
3347 CmdTrbResetED
.SlotId
= SlotId
;
3348 Status
= XhcCmdTransfer (
3350 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbResetED
,
3351 XHC_GENERIC_TIMEOUT
,
3352 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3354 if (EFI_ERROR(Status
)) {
3355 DEBUG ((EFI_D_ERROR
, "XhcResetEndpoint: Reset Endpoint Failed, Status = %r\n", Status
));
3362 Set transfer ring dequeue pointer through XHCI's Set_Tr_Dequeue_Pointer cmd.
3364 @param Xhc The XHCI Instance.
3365 @param SlotId The slot id to be configured.
3366 @param Dci The device context index of endpoint.
3367 @param Urb The dequeue pointer of the transfer ring specified
3368 by the urb to be updated.
3370 @retval EFI_SUCCESS Set transfer ring dequeue pointer succeeds.
3371 @retval Others Failed to set transfer ring dequeue pointer.
3376 XhcSetTrDequeuePointer (
3377 IN USB_XHCI_INSTANCE
*Xhc
,
3384 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3385 CMD_SET_TR_DEQ_POINTER CmdSetTRDeq
;
3386 EFI_PHYSICAL_ADDRESS PhyAddr
;
3388 DEBUG ((EFI_D_INFO
, "XhcSetTrDequeuePointer: Slot = 0x%x, Dci = 0x%x, Urb = 0x%x\n", SlotId
, Dci
, Urb
));
3391 // Send stop endpoint command to transit Endpoint from running to stop state
3393 ZeroMem (&CmdSetTRDeq
, sizeof (CmdSetTRDeq
));
3394 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Urb
->Ring
->RingEnqueue
, sizeof (CMD_SET_TR_DEQ_POINTER
));
3395 CmdSetTRDeq
.PtrLo
= XHC_LOW_32BIT (PhyAddr
) | Urb
->Ring
->RingPCS
;
3396 CmdSetTRDeq
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3397 CmdSetTRDeq
.CycleBit
= 1;
3398 CmdSetTRDeq
.Type
= TRB_TYPE_SET_TR_DEQUE
;
3399 CmdSetTRDeq
.Endpoint
= Dci
;
3400 CmdSetTRDeq
.SlotId
= SlotId
;
3401 Status
= XhcCmdTransfer (
3403 (TRB_TEMPLATE
*) (UINTN
) &CmdSetTRDeq
,
3404 XHC_GENERIC_TIMEOUT
,
3405 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3407 if (EFI_ERROR(Status
)) {
3408 DEBUG ((EFI_D_ERROR
, "XhcSetTrDequeuePointer: Set TR Dequeue Pointer Failed, Status = %r\n", Status
));
3415 Set interface through XHCI's Configure_Endpoint cmd.
3417 @param Xhc The XHCI Instance.
3418 @param SlotId The slot id to be configured.
3419 @param DeviceSpeed The device's speed.
3420 @param ConfigDesc The pointer to the usb device configuration descriptor.
3421 @param Request USB device request to send.
3423 @retval EFI_SUCCESS Successfully set interface.
3429 IN USB_XHCI_INSTANCE
*Xhc
,
3431 IN UINT8 DeviceSpeed
,
3432 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
,
3433 IN EFI_USB_DEVICE_REQUEST
*Request
3437 USB_INTERFACE_DESCRIPTOR
*IfDescActive
;
3438 USB_INTERFACE_DESCRIPTOR
*IfDescSet
;
3439 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3440 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3447 EFI_PHYSICAL_ADDRESS PhyAddr
;
3450 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3451 INPUT_CONTEXT
*InputContext
;
3452 DEVICE_CONTEXT
*OutputContext
;
3453 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3455 Status
= EFI_SUCCESS
;
3457 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3458 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3460 // XHCI 4.6.6 Configure Endpoint
3461 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3462 // Context and Add Context flags as follows:
3463 // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
3464 // Context and Add Context flags to '0'.
3466 // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
3467 // So the default Drop Context and Add Context flags can be '0' to cover 1).
3469 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3470 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT
));
3472 ASSERT (ConfigDesc
!= NULL
);
3476 IfDescActive
= NULL
;
3479 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3480 while ((UINTN
) IfDesc
< ((UINTN
) ConfigDesc
+ ConfigDesc
->TotalLength
)) {
3481 if ((IfDesc
->DescriptorType
== USB_DESC_TYPE_INTERFACE
) && (IfDesc
->Length
>= sizeof (USB_INTERFACE_DESCRIPTOR
))) {
3482 if (IfDesc
->InterfaceNumber
== (UINT8
) Request
->Index
) {
3483 if (IfDesc
->AlternateSetting
== Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[IfDesc
->InterfaceNumber
]) {
3485 // Find out the active interface descriptor.
3487 IfDescActive
= IfDesc
;
3488 } else if (IfDesc
->AlternateSetting
== (UINT8
) Request
->Value
) {
3490 // Find out the interface descriptor to set.
3496 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3500 // XHCI 4.6.6 Configure Endpoint
3501 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3502 // Context and Add Context flags as follows:
3503 // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
3504 // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
3505 // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
3506 // the Drop Context flag to '1' and Add Context flag to '0'.
3507 // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
3508 // and Add Context flags shall be set to '1'.
3510 // Below codes are to cover 2), 3) and 4).
3513 if ((IfDescActive
!= NULL
) && (IfDescSet
!= NULL
)) {
3514 NumEp
= IfDescActive
->NumEndpoints
;
3515 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*) (IfDescActive
+ 1);
3516 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3517 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3518 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3521 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3522 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3526 EpAddr
= (UINT8
) (EpDesc
->EndpointAddress
& 0x0F);
3527 Direction
= (UINT8
) ((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3529 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3535 // XHCI 4.3.6 - Setting Alternate Interfaces
3536 // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
3538 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, NULL
);
3539 if (EFI_ERROR (Status
)) {
3543 // XHCI 4.3.6 - Setting Alternate Interfaces
3544 // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
3546 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] != NULL
) {
3547 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1])->RingSeg0
;
3548 if (RingSeg
!= NULL
) {
3549 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
3551 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1]);
3552 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] = NULL
;
3556 // Set the Drop Context flag to '1'.
3558 InputContext
->InputControlContext
.Dword1
|= (BIT0
<< Dci
);
3560 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3564 // XHCI 4.3.6 - Setting Alternate Interfaces
3565 // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
3566 // Interface setting, to '0'.
3568 // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
3572 // XHCI 4.3.6 - Setting Alternate Interfaces
3573 // 4) For each endpoint enabled by the Configure Endpoint Command:
3574 // a. Allocate a Transfer Ring.
3575 // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
3576 // c. Initialize the Endpoint Context data structure.
3578 Dci
= XhcInitializeEndpointContext (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDescSet
);
3583 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3584 InputContext
->Slot
.ContextEntries
= MaxDci
;
3586 // XHCI 4.3.6 - Setting Alternate Interfaces
3587 // 5) Issue and successfully complete a Configure Endpoint Command.
3589 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3590 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3591 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3592 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3593 CmdTrbCfgEP
.CycleBit
= 1;
3594 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3595 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3596 DEBUG ((EFI_D_INFO
, "SetInterface: Configure Endpoint\n"));
3597 Status
= XhcCmdTransfer (
3599 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3600 XHC_GENERIC_TIMEOUT
,
3601 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3603 if (EFI_ERROR (Status
)) {
3604 DEBUG ((EFI_D_ERROR
, "SetInterface: Config Endpoint Failed, Status = %r\n", Status
));
3607 // Update the active AlternateSetting.
3609 Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[(UINT8
) Request
->Index
] = (UINT8
) Request
->Value
;
3617 Set interface through XHCI's Configure_Endpoint cmd.
3619 @param Xhc The XHCI Instance.
3620 @param SlotId The slot id to be configured.
3621 @param DeviceSpeed The device's speed.
3622 @param ConfigDesc The pointer to the usb device configuration descriptor.
3623 @param Request USB device request to send.
3625 @retval EFI_SUCCESS Successfully set interface.
3631 IN USB_XHCI_INSTANCE
*Xhc
,
3633 IN UINT8 DeviceSpeed
,
3634 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
,
3635 IN EFI_USB_DEVICE_REQUEST
*Request
3639 USB_INTERFACE_DESCRIPTOR
*IfDescActive
;
3640 USB_INTERFACE_DESCRIPTOR
*IfDescSet
;
3641 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3642 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3649 EFI_PHYSICAL_ADDRESS PhyAddr
;
3652 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3653 INPUT_CONTEXT_64
*InputContext
;
3654 DEVICE_CONTEXT_64
*OutputContext
;
3655 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3657 Status
= EFI_SUCCESS
;
3659 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3660 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3662 // XHCI 4.6.6 Configure Endpoint
3663 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3664 // Context and Add Context flags as follows:
3665 // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
3666 // Context and Add Context flags to '0'.
3668 // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
3669 // So the default Drop Context and Add Context flags can be '0' to cover 1).
3671 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3672 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT_64
));
3674 ASSERT (ConfigDesc
!= NULL
);
3678 IfDescActive
= NULL
;
3681 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3682 while ((UINTN
) IfDesc
< ((UINTN
) ConfigDesc
+ ConfigDesc
->TotalLength
)) {
3683 if ((IfDesc
->DescriptorType
== USB_DESC_TYPE_INTERFACE
) && (IfDesc
->Length
>= sizeof (USB_INTERFACE_DESCRIPTOR
))) {
3684 if (IfDesc
->InterfaceNumber
== (UINT8
) Request
->Index
) {
3685 if (IfDesc
->AlternateSetting
== Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[IfDesc
->InterfaceNumber
]) {
3687 // Find out the active interface descriptor.
3689 IfDescActive
= IfDesc
;
3690 } else if (IfDesc
->AlternateSetting
== (UINT8
) Request
->Value
) {
3692 // Find out the interface descriptor to set.
3698 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3702 // XHCI 4.6.6 Configure Endpoint
3703 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3704 // Context and Add Context flags as follows:
3705 // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
3706 // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
3707 // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
3708 // the Drop Context flag to '1' and Add Context flag to '0'.
3709 // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
3710 // and Add Context flags shall be set to '1'.
3712 // Below codes are to cover 2), 3) and 4).
3715 if ((IfDescActive
!= NULL
) && (IfDescSet
!= NULL
)) {
3716 NumEp
= IfDescActive
->NumEndpoints
;
3717 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*) (IfDescActive
+ 1);
3718 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3719 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3720 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3723 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3724 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3728 EpAddr
= (UINT8
) (EpDesc
->EndpointAddress
& 0x0F);
3729 Direction
= (UINT8
) ((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3731 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3737 // XHCI 4.3.6 - Setting Alternate Interfaces
3738 // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
3740 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, NULL
);
3741 if (EFI_ERROR (Status
)) {
3745 // XHCI 4.3.6 - Setting Alternate Interfaces
3746 // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
3748 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] != NULL
) {
3749 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1])->RingSeg0
;
3750 if (RingSeg
!= NULL
) {
3751 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
3753 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1]);
3754 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] = NULL
;
3758 // Set the Drop Context flag to '1'.
3760 InputContext
->InputControlContext
.Dword1
|= (BIT0
<< Dci
);
3762 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3766 // XHCI 4.3.6 - Setting Alternate Interfaces
3767 // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
3768 // Interface setting, to '0'.
3770 // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
3774 // XHCI 4.3.6 - Setting Alternate Interfaces
3775 // 4) For each endpoint enabled by the Configure Endpoint Command:
3776 // a. Allocate a Transfer Ring.
3777 // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
3778 // c. Initialize the Endpoint Context data structure.
3780 Dci
= XhcInitializeEndpointContext64 (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDescSet
);
3785 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3786 InputContext
->Slot
.ContextEntries
= MaxDci
;
3788 // XHCI 4.3.6 - Setting Alternate Interfaces
3789 // 5) Issue and successfully complete a Configure Endpoint Command.
3791 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3792 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3793 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3794 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3795 CmdTrbCfgEP
.CycleBit
= 1;
3796 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3797 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3798 DEBUG ((EFI_D_INFO
, "SetInterface64: Configure Endpoint\n"));
3799 Status
= XhcCmdTransfer (
3801 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3802 XHC_GENERIC_TIMEOUT
,
3803 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3805 if (EFI_ERROR (Status
)) {
3806 DEBUG ((EFI_D_ERROR
, "SetInterface64: Config Endpoint Failed, Status = %r\n", Status
));
3809 // Update the active AlternateSetting.
3811 Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[(UINT8
) Request
->Index
] = (UINT8
) Request
->Value
;
3819 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
3821 @param Xhc The XHCI Instance.
3822 @param SlotId The slot id to be evaluated.
3823 @param MaxPacketSize The max packet size supported by the device control transfer.
3825 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.
3830 XhcEvaluateContext (
3831 IN USB_XHCI_INSTANCE
*Xhc
,
3833 IN UINT32 MaxPacketSize
3837 CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu
;
3838 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3839 INPUT_CONTEXT
*InputContext
;
3840 EFI_PHYSICAL_ADDRESS PhyAddr
;
3842 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3845 // 4.6.7 Evaluate Context
3847 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3848 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3850 InputContext
->InputControlContext
.Dword2
|= BIT1
;
3851 InputContext
->EP
[0].MaxPacketSize
= MaxPacketSize
;
3853 ZeroMem (&CmdTrbEvalu
, sizeof (CmdTrbEvalu
));
3854 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3855 CmdTrbEvalu
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3856 CmdTrbEvalu
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3857 CmdTrbEvalu
.CycleBit
= 1;
3858 CmdTrbEvalu
.Type
= TRB_TYPE_EVALU_CONTXT
;
3859 CmdTrbEvalu
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3860 DEBUG ((EFI_D_INFO
, "Evaluate context\n"));
3861 Status
= XhcCmdTransfer (
3863 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbEvalu
,
3864 XHC_GENERIC_TIMEOUT
,
3865 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3867 if (EFI_ERROR (Status
)) {
3868 DEBUG ((EFI_D_ERROR
, "XhcEvaluateContext: Evaluate Context Failed, Status = %r\n", Status
));
3874 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
3876 @param Xhc The XHCI Instance.
3877 @param SlotId The slot id to be evaluated.
3878 @param MaxPacketSize The max packet size supported by the device control transfer.
3880 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.
3885 XhcEvaluateContext64 (
3886 IN USB_XHCI_INSTANCE
*Xhc
,
3888 IN UINT32 MaxPacketSize
3892 CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu
;
3893 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3894 INPUT_CONTEXT_64
*InputContext
;
3895 EFI_PHYSICAL_ADDRESS PhyAddr
;
3897 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3900 // 4.6.7 Evaluate Context
3902 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3903 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3905 InputContext
->InputControlContext
.Dword2
|= BIT1
;
3906 InputContext
->EP
[0].MaxPacketSize
= MaxPacketSize
;
3908 ZeroMem (&CmdTrbEvalu
, sizeof (CmdTrbEvalu
));
3909 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3910 CmdTrbEvalu
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3911 CmdTrbEvalu
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3912 CmdTrbEvalu
.CycleBit
= 1;
3913 CmdTrbEvalu
.Type
= TRB_TYPE_EVALU_CONTXT
;
3914 CmdTrbEvalu
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3915 DEBUG ((EFI_D_INFO
, "Evaluate context\n"));
3916 Status
= XhcCmdTransfer (
3918 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbEvalu
,
3919 XHC_GENERIC_TIMEOUT
,
3920 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3922 if (EFI_ERROR (Status
)) {
3923 DEBUG ((EFI_D_ERROR
, "XhcEvaluateContext64: Evaluate Context Failed, Status = %r\n", Status
));
3930 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
3932 @param Xhc The XHCI Instance.
3933 @param SlotId The slot id to be configured.
3934 @param PortNum The total number of downstream port supported by the hub.
3935 @param TTT The TT think time of the hub device.
3936 @param MTT The multi-TT of the hub device.
3938 @retval EFI_SUCCESS Successfully configure the hub device's slot context.
3942 XhcConfigHubContext (
3943 IN USB_XHCI_INSTANCE
*Xhc
,
3951 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3952 INPUT_CONTEXT
*InputContext
;
3953 DEVICE_CONTEXT
*OutputContext
;
3954 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3955 EFI_PHYSICAL_ADDRESS PhyAddr
;
3957 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3958 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3959 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3962 // 4.6.7 Evaluate Context
3964 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3966 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3969 // Copy the slot context from OutputContext to Input context
3971 CopyMem(&(InputContext
->Slot
), &(OutputContext
->Slot
), sizeof (SLOT_CONTEXT
));
3972 InputContext
->Slot
.Hub
= 1;
3973 InputContext
->Slot
.PortNum
= PortNum
;
3974 InputContext
->Slot
.TTT
= TTT
;
3975 InputContext
->Slot
.MTT
= MTT
;
3977 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3978 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3979 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3980 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3981 CmdTrbCfgEP
.CycleBit
= 1;
3982 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3983 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3984 DEBUG ((EFI_D_INFO
, "Configure Hub Slot Context\n"));
3985 Status
= XhcCmdTransfer (
3987 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3988 XHC_GENERIC_TIMEOUT
,
3989 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3991 if (EFI_ERROR (Status
)) {
3992 DEBUG ((EFI_D_ERROR
, "XhcConfigHubContext: Config Endpoint Failed, Status = %r\n", Status
));
3998 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
4000 @param Xhc The XHCI Instance.
4001 @param SlotId The slot id to be configured.
4002 @param PortNum The total number of downstream port supported by the hub.
4003 @param TTT The TT think time of the hub device.
4004 @param MTT The multi-TT of the hub device.
4006 @retval EFI_SUCCESS Successfully configure the hub device's slot context.
4010 XhcConfigHubContext64 (
4011 IN USB_XHCI_INSTANCE
*Xhc
,
4019 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
4020 INPUT_CONTEXT_64
*InputContext
;
4021 DEVICE_CONTEXT_64
*OutputContext
;
4022 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
4023 EFI_PHYSICAL_ADDRESS PhyAddr
;
4025 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
4026 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
4027 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
4030 // 4.6.7 Evaluate Context
4032 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
4034 InputContext
->InputControlContext
.Dword2
|= BIT0
;
4037 // Copy the slot context from OutputContext to Input context
4039 CopyMem(&(InputContext
->Slot
), &(OutputContext
->Slot
), sizeof (SLOT_CONTEXT_64
));
4040 InputContext
->Slot
.Hub
= 1;
4041 InputContext
->Slot
.PortNum
= PortNum
;
4042 InputContext
->Slot
.TTT
= TTT
;
4043 InputContext
->Slot
.MTT
= MTT
;
4045 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
4046 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
4047 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
4048 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
4049 CmdTrbCfgEP
.CycleBit
= 1;
4050 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
4051 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
4052 DEBUG ((EFI_D_INFO
, "Configure Hub Slot Context\n"));
4053 Status
= XhcCmdTransfer (
4055 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
4056 XHC_GENERIC_TIMEOUT
,
4057 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
4059 if (EFI_ERROR (Status
)) {
4060 DEBUG ((EFI_D_ERROR
, "XhcConfigHubContext64: Config Endpoint Failed, Status = %r\n", Status
));