3 XHCI transfer scheduling routines.
5 Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
13 Create a command transfer TRB to support XHCI command interfaces.
15 @param Xhc The XHCI Instance.
16 @param CmdTrb The cmd TRB to be executed.
18 @return Created URB or NULL.
23 IN USB_XHCI_INSTANCE
*Xhc
,
24 IN TRB_TEMPLATE
*CmdTrb
29 Urb
= AllocateZeroPool (sizeof (URB
));
34 Urb
->Signature
= XHC_URB_SIG
;
36 Urb
->Ring
= &Xhc
->CmdRing
;
37 XhcSyncTrsRing (Xhc
, Urb
->Ring
);
39 Urb
->TrbStart
= Urb
->Ring
->RingEnqueue
;
40 CopyMem (Urb
->TrbStart
, CmdTrb
, sizeof (TRB_TEMPLATE
));
41 Urb
->TrbStart
->CycleBit
= Urb
->Ring
->RingPCS
& BIT0
;
42 Urb
->TrbEnd
= Urb
->TrbStart
;
48 Execute a XHCI cmd TRB pointed by CmdTrb.
50 @param Xhc The XHCI Instance.
51 @param CmdTrb The cmd TRB to be executed.
52 @param Timeout Indicates the maximum time, in millisecond, which the
53 transfer is allowed to complete.
54 @param EvtTrb The event TRB corresponding to the cmd TRB.
56 @retval EFI_SUCCESS The transfer was completed successfully.
57 @retval EFI_INVALID_PARAMETER Some parameters are invalid.
58 @retval EFI_TIMEOUT The transfer failed due to timeout.
59 @retval EFI_DEVICE_ERROR The transfer failed due to host controller error.
65 IN USB_XHCI_INSTANCE
*Xhc
,
66 IN TRB_TEMPLATE
*CmdTrb
,
68 OUT TRB_TEMPLATE
**EvtTrb
75 // Validate the parameters
77 if ((Xhc
== NULL
) || (CmdTrb
== NULL
)) {
78 return EFI_INVALID_PARAMETER
;
81 Status
= EFI_DEVICE_ERROR
;
83 if (XhcIsHalt (Xhc
) || XhcIsSysError (Xhc
)) {
84 DEBUG ((EFI_D_ERROR
, "XhcCmdTransfer: HC is halted\n"));
89 // Create a new URB, then poll the execution status.
91 Urb
= XhcCreateCmdTrb (Xhc
, CmdTrb
);
94 DEBUG ((EFI_D_ERROR
, "XhcCmdTransfer: failed to create URB\n"));
95 Status
= EFI_OUT_OF_RESOURCES
;
99 Status
= XhcExecTransfer (Xhc
, TRUE
, Urb
, Timeout
);
100 *EvtTrb
= Urb
->EvtTrb
;
102 if (Urb
->Result
== EFI_USB_NOERROR
) {
103 Status
= EFI_SUCCESS
;
106 XhcFreeUrb (Xhc
, Urb
);
113 Create a new URB for a new transaction.
115 @param Xhc The XHCI Instance
116 @param BusAddr The logical device address assigned by UsbBus driver
117 @param EpAddr Endpoint addrress
118 @param DevSpeed The device speed
119 @param MaxPacket The max packet length of the endpoint
120 @param Type The transaction type
121 @param Request The standard USB request for control transfer
122 @param Data The user data to transfer
123 @param DataLen The length of data buffer
124 @param Callback The function to call when data is transferred
125 @param Context The context to the callback
127 @return Created URB or NULL
132 IN USB_XHCI_INSTANCE
*Xhc
,
138 IN EFI_USB_DEVICE_REQUEST
*Request
,
141 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback
,
149 Urb
= AllocateZeroPool (sizeof (URB
));
154 Urb
->Signature
= XHC_URB_SIG
;
155 InitializeListHead (&Urb
->UrbList
);
158 Ep
->BusAddr
= BusAddr
;
159 Ep
->EpAddr
= (UINT8
)(EpAddr
& 0x0F);
160 Ep
->Direction
= ((EpAddr
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
;
161 Ep
->DevSpeed
= DevSpeed
;
162 Ep
->MaxPacket
= MaxPacket
;
165 Urb
->Request
= Request
;
167 Urb
->DataLen
= DataLen
;
168 Urb
->Callback
= Callback
;
169 Urb
->Context
= Context
;
171 Status
= XhcCreateTransferTrb (Xhc
, Urb
);
172 ASSERT_EFI_ERROR (Status
);
173 if (EFI_ERROR (Status
)) {
174 DEBUG ((EFI_D_ERROR
, "XhcCreateUrb: XhcCreateTransferTrb Failed, Status = %r\n", Status
));
183 Free an allocated URB.
185 @param Xhc The XHCI device.
186 @param Urb The URB to free.
191 IN USB_XHCI_INSTANCE
*Xhc
,
195 if ((Xhc
== NULL
) || (Urb
== NULL
)) {
199 if (Urb
->DataMap
!= NULL
) {
200 Xhc
->PciIo
->Unmap (Xhc
->PciIo
, Urb
->DataMap
);
207 Create a transfer TRB.
209 @param Xhc The XHCI Instance
210 @param Urb The urb used to construct the transfer TRB.
212 @return Created TRB or NULL
216 XhcCreateTransferTrb (
217 IN USB_XHCI_INSTANCE
*Xhc
,
222 TRANSFER_RING
*EPRing
;
230 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
231 EFI_PHYSICAL_ADDRESS PhyAddr
;
235 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
237 return EFI_DEVICE_ERROR
;
240 Urb
->Finished
= FALSE
;
241 Urb
->StartDone
= FALSE
;
242 Urb
->EndDone
= FALSE
;
244 Urb
->Result
= EFI_USB_NOERROR
;
246 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
248 EPRing
= (TRANSFER_RING
*)(UINTN
) Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1];
250 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
251 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
252 EPType
= (UINT8
) ((DEVICE_CONTEXT
*)OutputContext
)->EP
[Dci
-1].EPType
;
254 EPType
= (UINT8
) ((DEVICE_CONTEXT_64
*)OutputContext
)->EP
[Dci
-1].EPType
;
260 if ((Urb
->Data
!= NULL
) && (Urb
->DataMap
== NULL
)) {
261 if (((UINT8
) (Urb
->Ep
.Direction
)) == EfiUsbDataIn
) {
262 MapOp
= EfiPciIoOperationBusMasterWrite
;
264 MapOp
= EfiPciIoOperationBusMasterRead
;
268 Status
= Xhc
->PciIo
->Map (Xhc
->PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
270 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
271 DEBUG ((EFI_D_ERROR
, "XhcCreateTransferTrb: Fail to map Urb->Data.\n"));
272 return EFI_OUT_OF_RESOURCES
;
275 Urb
->DataPhy
= (VOID
*) ((UINTN
) PhyAddr
);
282 XhcSyncTrsRing (Xhc
, EPRing
);
283 Urb
->TrbStart
= EPRing
->RingEnqueue
;
285 case ED_CONTROL_BIDIR
:
287 // For control transfer, create SETUP_STAGE_TRB first.
289 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
290 TrbStart
->TrbCtrSetup
.bmRequestType
= Urb
->Request
->RequestType
;
291 TrbStart
->TrbCtrSetup
.bRequest
= Urb
->Request
->Request
;
292 TrbStart
->TrbCtrSetup
.wValue
= Urb
->Request
->Value
;
293 TrbStart
->TrbCtrSetup
.wIndex
= Urb
->Request
->Index
;
294 TrbStart
->TrbCtrSetup
.wLength
= Urb
->Request
->Length
;
295 TrbStart
->TrbCtrSetup
.Length
= 8;
296 TrbStart
->TrbCtrSetup
.IntTarget
= 0;
297 TrbStart
->TrbCtrSetup
.IOC
= 1;
298 TrbStart
->TrbCtrSetup
.IDT
= 1;
299 TrbStart
->TrbCtrSetup
.Type
= TRB_TYPE_SETUP_STAGE
;
300 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
301 TrbStart
->TrbCtrSetup
.TRT
= 3;
302 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
303 TrbStart
->TrbCtrSetup
.TRT
= 2;
305 TrbStart
->TrbCtrSetup
.TRT
= 0;
308 // Update the cycle bit
310 TrbStart
->TrbCtrSetup
.CycleBit
= EPRing
->RingPCS
& BIT0
;
314 // For control transfer, create DATA_STAGE_TRB.
316 if (Urb
->DataLen
> 0) {
317 XhcSyncTrsRing (Xhc
, EPRing
);
318 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
319 TrbStart
->TrbCtrData
.TRBPtrLo
= XHC_LOW_32BIT(Urb
->DataPhy
);
320 TrbStart
->TrbCtrData
.TRBPtrHi
= XHC_HIGH_32BIT(Urb
->DataPhy
);
321 TrbStart
->TrbCtrData
.Length
= (UINT32
) Urb
->DataLen
;
322 TrbStart
->TrbCtrData
.TDSize
= 0;
323 TrbStart
->TrbCtrData
.IntTarget
= 0;
324 TrbStart
->TrbCtrData
.ISP
= 1;
325 TrbStart
->TrbCtrData
.IOC
= 1;
326 TrbStart
->TrbCtrData
.IDT
= 0;
327 TrbStart
->TrbCtrData
.CH
= 0;
328 TrbStart
->TrbCtrData
.Type
= TRB_TYPE_DATA_STAGE
;
329 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
330 TrbStart
->TrbCtrData
.DIR = 1;
331 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
332 TrbStart
->TrbCtrData
.DIR = 0;
334 TrbStart
->TrbCtrData
.DIR = 0;
337 // Update the cycle bit
339 TrbStart
->TrbCtrData
.CycleBit
= EPRing
->RingPCS
& BIT0
;
343 // For control transfer, create STATUS_STAGE_TRB.
344 // Get the pointer to next TRB for status stage use
346 XhcSyncTrsRing (Xhc
, EPRing
);
347 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
348 TrbStart
->TrbCtrStatus
.IntTarget
= 0;
349 TrbStart
->TrbCtrStatus
.IOC
= 1;
350 TrbStart
->TrbCtrStatus
.CH
= 0;
351 TrbStart
->TrbCtrStatus
.Type
= TRB_TYPE_STATUS_STAGE
;
352 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
353 TrbStart
->TrbCtrStatus
.DIR = 0;
354 } else if (Urb
->Ep
.Direction
== EfiUsbDataOut
) {
355 TrbStart
->TrbCtrStatus
.DIR = 1;
357 TrbStart
->TrbCtrStatus
.DIR = 0;
360 // Update the cycle bit
362 TrbStart
->TrbCtrStatus
.CycleBit
= EPRing
->RingPCS
& BIT0
;
364 // Update the enqueue pointer
366 XhcSyncTrsRing (Xhc
, EPRing
);
368 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
377 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
378 while (TotalLen
< Urb
->DataLen
) {
379 if ((TotalLen
+ 0x10000) >= Urb
->DataLen
) {
380 Len
= Urb
->DataLen
- TotalLen
;
384 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
385 TrbStart
->TrbNormal
.TRBPtrLo
= XHC_LOW_32BIT((UINT8
*) Urb
->DataPhy
+ TotalLen
);
386 TrbStart
->TrbNormal
.TRBPtrHi
= XHC_HIGH_32BIT((UINT8
*) Urb
->DataPhy
+ TotalLen
);
387 TrbStart
->TrbNormal
.Length
= (UINT32
) Len
;
388 TrbStart
->TrbNormal
.TDSize
= 0;
389 TrbStart
->TrbNormal
.IntTarget
= 0;
390 TrbStart
->TrbNormal
.ISP
= 1;
391 TrbStart
->TrbNormal
.IOC
= 1;
392 TrbStart
->TrbNormal
.Type
= TRB_TYPE_NORMAL
;
394 // Update the cycle bit
396 TrbStart
->TrbNormal
.CycleBit
= EPRing
->RingPCS
& BIT0
;
398 XhcSyncTrsRing (Xhc
, EPRing
);
403 Urb
->TrbNum
= TrbNum
;
404 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
407 case ED_INTERRUPT_OUT
:
408 case ED_INTERRUPT_IN
:
412 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
413 while (TotalLen
< Urb
->DataLen
) {
414 if ((TotalLen
+ 0x10000) >= Urb
->DataLen
) {
415 Len
= Urb
->DataLen
- TotalLen
;
419 TrbStart
= (TRB
*)(UINTN
)EPRing
->RingEnqueue
;
420 TrbStart
->TrbNormal
.TRBPtrLo
= XHC_LOW_32BIT((UINT8
*) Urb
->DataPhy
+ TotalLen
);
421 TrbStart
->TrbNormal
.TRBPtrHi
= XHC_HIGH_32BIT((UINT8
*) Urb
->DataPhy
+ TotalLen
);
422 TrbStart
->TrbNormal
.Length
= (UINT32
) Len
;
423 TrbStart
->TrbNormal
.TDSize
= 0;
424 TrbStart
->TrbNormal
.IntTarget
= 0;
425 TrbStart
->TrbNormal
.ISP
= 1;
426 TrbStart
->TrbNormal
.IOC
= 1;
427 TrbStart
->TrbNormal
.Type
= TRB_TYPE_NORMAL
;
429 // Update the cycle bit
431 TrbStart
->TrbNormal
.CycleBit
= EPRing
->RingPCS
& BIT0
;
433 XhcSyncTrsRing (Xhc
, EPRing
);
438 Urb
->TrbNum
= TrbNum
;
439 Urb
->TrbEnd
= (TRB_TEMPLATE
*)(UINTN
)TrbStart
;
443 DEBUG ((EFI_D_INFO
, "Not supported EPType 0x%x!\n",EPType
));
453 Initialize the XHCI host controller for schedule.
455 @param Xhc The XHCI Instance to be initialized.
460 IN USB_XHCI_INSTANCE
*Xhc
464 EFI_PHYSICAL_ADDRESS DcbaaPhy
;
466 EFI_PHYSICAL_ADDRESS CmdRingPhy
;
468 UINT32 MaxScratchpadBufs
;
470 EFI_PHYSICAL_ADDRESS ScratchPhy
;
471 UINT64
*ScratchEntry
;
472 EFI_PHYSICAL_ADDRESS ScratchEntryPhy
;
474 UINTN
*ScratchEntryMap
;
478 // Initialize memory management.
480 Xhc
->MemPool
= UsbHcInitMemPool (Xhc
->PciIo
);
481 ASSERT (Xhc
->MemPool
!= NULL
);
484 // Program the Max Device Slots Enabled (MaxSlotsEn) field in the CONFIG register (5.4.7)
485 // to enable the device slots that system software is going to use.
487 Xhc
->MaxSlotsEn
= Xhc
->HcSParams1
.Data
.MaxSlots
;
488 ASSERT (Xhc
->MaxSlotsEn
>= 1 && Xhc
->MaxSlotsEn
<= 255);
489 XhcWriteOpReg (Xhc
, XHC_CONFIG_OFFSET
, Xhc
->MaxSlotsEn
);
492 // The Device Context Base Address Array entry associated with each allocated Device Slot
493 // shall contain a 64-bit pointer to the base of the associated Device Context.
494 // The Device Context Base Address Array shall contain MaxSlotsEn + 1 entries.
495 // Software shall set Device Context Base Address Array entries for unallocated Device Slots to '0'.
497 Entries
= (Xhc
->MaxSlotsEn
+ 1) * sizeof(UINT64
);
498 Dcbaa
= UsbHcAllocateMem (Xhc
->MemPool
, Entries
);
499 ASSERT (Dcbaa
!= NULL
);
500 ZeroMem (Dcbaa
, Entries
);
503 // A Scratchpad Buffer is a PAGESIZE block of system memory located on a PAGESIZE boundary.
504 // System software shall allocate the Scratchpad Buffer(s) before placing the xHC in to Run
505 // mode (Run/Stop(R/S) ='1').
507 MaxScratchpadBufs
= ((Xhc
->HcSParams2
.Data
.ScratchBufHi
) << 5) | (Xhc
->HcSParams2
.Data
.ScratchBufLo
);
508 Xhc
->MaxScratchpadBufs
= MaxScratchpadBufs
;
509 ASSERT (MaxScratchpadBufs
<= 1023);
510 if (MaxScratchpadBufs
!= 0) {
512 // Allocate the buffer to record the Mapping for each scratch buffer in order to Unmap them
514 ScratchEntryMap
= AllocateZeroPool (sizeof (UINTN
) * MaxScratchpadBufs
);
515 ASSERT (ScratchEntryMap
!= NULL
);
516 Xhc
->ScratchEntryMap
= ScratchEntryMap
;
519 // Allocate the buffer to record the host address for each entry
521 ScratchEntry
= AllocateZeroPool (sizeof (UINT64
) * MaxScratchpadBufs
);
522 ASSERT (ScratchEntry
!= NULL
);
523 Xhc
->ScratchEntry
= ScratchEntry
;
526 Status
= UsbHcAllocateAlignedPages (
528 EFI_SIZE_TO_PAGES (MaxScratchpadBufs
* sizeof (UINT64
)),
530 (VOID
**) &ScratchBuf
,
534 ASSERT_EFI_ERROR (Status
);
536 ZeroMem (ScratchBuf
, MaxScratchpadBufs
* sizeof (UINT64
));
537 Xhc
->ScratchBuf
= ScratchBuf
;
540 // Allocate each scratch buffer
542 for (Index
= 0; Index
< MaxScratchpadBufs
; Index
++) {
544 Status
= UsbHcAllocateAlignedPages (
546 EFI_SIZE_TO_PAGES (Xhc
->PageSize
),
548 (VOID
**) &ScratchEntry
[Index
],
550 (VOID
**) &ScratchEntryMap
[Index
]
552 ASSERT_EFI_ERROR (Status
);
553 ZeroMem ((VOID
*)(UINTN
)ScratchEntry
[Index
], Xhc
->PageSize
);
555 // Fill with the PCI device address
557 *ScratchBuf
++ = ScratchEntryPhy
;
560 // The Scratchpad Buffer Array contains pointers to the Scratchpad Buffers. Entry 0 of the
561 // Device Context Base Address Array points to the Scratchpad Buffer Array.
563 *(UINT64
*)Dcbaa
= (UINT64
)(UINTN
) ScratchPhy
;
567 // Program the Device Context Base Address Array Pointer (DCBAAP) register (5.4.6) with
568 // a 64-bit address pointing to where the Device Context Base Address Array is located.
570 Xhc
->DCBAA
= (UINT64
*)(UINTN
)Dcbaa
;
572 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
573 // So divide it to two 32-bytes width register access.
575 DcbaaPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Dcbaa
, Entries
);
576 XhcWriteOpReg (Xhc
, XHC_DCBAAP_OFFSET
, XHC_LOW_32BIT(DcbaaPhy
));
577 XhcWriteOpReg (Xhc
, XHC_DCBAAP_OFFSET
+ 4, XHC_HIGH_32BIT (DcbaaPhy
));
579 DEBUG ((EFI_D_INFO
, "XhcInitSched:DCBAA=0x%x\n", (UINT64
)(UINTN
)Xhc
->DCBAA
));
582 // Define the Command Ring Dequeue Pointer by programming the Command Ring Control Register
583 // (5.4.5) with a 64-bit address pointing to the starting address of the first TRB of the Command Ring.
584 // Note: The Command Ring is 64 byte aligned, so the low order 6 bits of the Command Ring Pointer shall
587 CreateTransferRing (Xhc
, CMD_RING_TRB_NUMBER
, &Xhc
->CmdRing
);
589 // The xHC uses the Enqueue Pointer to determine when a Transfer Ring is empty. As it fetches TRBs from a
590 // Transfer Ring it checks for a Cycle bit transition. If a transition detected, the ring is empty.
591 // So we set RCS as inverted PCS init value to let Command Ring empty
593 CmdRing
= (UINT64
)(UINTN
)Xhc
->CmdRing
.RingSeg0
;
594 CmdRingPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, (VOID
*)(UINTN
) CmdRing
, sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
);
595 ASSERT ((CmdRingPhy
& 0x3F) == 0);
596 CmdRingPhy
|= XHC_CRCR_RCS
;
598 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
599 // So divide it to two 32-bytes width register access.
601 XhcWriteOpReg (Xhc
, XHC_CRCR_OFFSET
, XHC_LOW_32BIT(CmdRingPhy
));
602 XhcWriteOpReg (Xhc
, XHC_CRCR_OFFSET
+ 4, XHC_HIGH_32BIT (CmdRingPhy
));
605 // Disable the 'interrupter enable' bit in USB_CMD
606 // and clear IE & IP bit in all Interrupter X Management Registers.
608 XhcClearOpRegBit (Xhc
, XHC_USBCMD_OFFSET
, XHC_USBCMD_INTE
);
609 for (Index
= 0; Index
< (UINT16
)(Xhc
->HcSParams1
.Data
.MaxIntrs
); Index
++) {
610 XhcClearRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
+ (Index
* 32), XHC_IMAN_IE
);
611 XhcSetRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
+ (Index
* 32), XHC_IMAN_IP
);
615 // Allocate EventRing for Cmd, Ctrl, Bulk, Interrupt, AsynInterrupt transfer
617 CreateEventRing (Xhc
, &Xhc
->EventRing
);
618 DEBUG ((DEBUG_INFO
, "XhcInitSched: Created CMD ring [%p~%p) EVENT ring [%p~%p)\n",
619 Xhc
->CmdRing
.RingSeg0
, (UINTN
)Xhc
->CmdRing
.RingSeg0
+ sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
,
620 Xhc
->EventRing
.EventRingSeg0
, (UINTN
)Xhc
->EventRing
.EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
625 System software shall use a Reset Endpoint Command (section 4.11.4.7) to remove the Halted
626 condition in the xHC. After the successful completion of the Reset Endpoint Command, the Endpoint
627 Context is transitioned from the Halted to the Stopped state and the Transfer Ring of the endpoint is
628 reenabled. The next write to the Doorbell of the Endpoint will transition the Endpoint Context from the
629 Stopped to the Running state.
631 @param Xhc The XHCI Instance.
632 @param Urb The urb which makes the endpoint halted.
634 @retval EFI_SUCCESS The recovery is successful.
635 @retval Others Failed to recovery halted endpoint.
640 XhcRecoverHaltedEndpoint (
641 IN USB_XHCI_INSTANCE
*Xhc
,
649 Status
= EFI_SUCCESS
;
650 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
652 return EFI_DEVICE_ERROR
;
654 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
657 DEBUG ((EFI_D_INFO
, "Recovery Halted Slot = %x,Dci = %x\n", SlotId
, Dci
));
660 // 1) Send Reset endpoint command to transit from halt to stop state
662 Status
= XhcResetEndpoint(Xhc
, SlotId
, Dci
);
663 if (EFI_ERROR(Status
)) {
664 DEBUG ((EFI_D_ERROR
, "XhcRecoverHaltedEndpoint: Reset Endpoint Failed, Status = %r\n", Status
));
669 // 2)Set dequeue pointer
671 Status
= XhcSetTrDequeuePointer(Xhc
, SlotId
, Dci
, Urb
);
672 if (EFI_ERROR(Status
)) {
673 DEBUG ((EFI_D_ERROR
, "XhcRecoverHaltedEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status
));
678 // 3)Ring the doorbell to transit from stop to active
680 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
687 System software shall use a Stop Endpoint Command (section 4.6.9) and the Set TR Dequeue Pointer
688 Command (section 4.6.10) to remove the timed-out TDs from the xHC transfer ring. The next write to
689 the Doorbell of the Endpoint will transition the Endpoint Context from the Stopped to the Running
692 @param Xhc The XHCI Instance.
693 @param Urb The urb which doesn't get completed in a specified timeout range.
695 @retval EFI_SUCCESS The dequeuing of the TDs is successful.
696 @retval EFI_ALREADY_STARTED The Urb is finished so no deque is needed.
697 @retval Others Failed to stop the endpoint and dequeue the TDs.
702 XhcDequeueTrbFromEndpoint (
703 IN USB_XHCI_INSTANCE
*Xhc
,
711 Status
= EFI_SUCCESS
;
712 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
714 return EFI_DEVICE_ERROR
;
716 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
719 DEBUG ((EFI_D_INFO
, "Stop Slot = %x,Dci = %x\n", SlotId
, Dci
));
722 // 1) Send Stop endpoint command to stop xHC from executing of the TDs on the endpoint
724 Status
= XhcStopEndpoint(Xhc
, SlotId
, Dci
, Urb
);
725 if (EFI_ERROR(Status
)) {
726 DEBUG ((EFI_D_ERROR
, "XhcDequeueTrbFromEndpoint: Stop Endpoint Failed, Status = %r\n", Status
));
731 // 2)Set dequeue pointer
733 if (Urb
->Finished
&& Urb
->Result
== EFI_USB_NOERROR
) {
735 // Return Already Started to indicate the pending URB is finished.
736 // This fixes BULK data loss when transfer is detected as timeout
737 // but finished just before stopping endpoint.
739 Status
= EFI_ALREADY_STARTED
;
740 DEBUG ((DEBUG_INFO
, "XhcDequeueTrbFromEndpoint: Pending URB is finished: Length Actual/Expect = %d/%d!\n", Urb
->Completed
, Urb
->DataLen
));
742 Status
= XhcSetTrDequeuePointer(Xhc
, SlotId
, Dci
, Urb
);
743 if (EFI_ERROR (Status
)) {
744 DEBUG ((DEBUG_ERROR
, "XhcDequeueTrbFromEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status
));
750 // 3)Ring the doorbell to transit from stop to active
752 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
759 Create XHCI event ring.
761 @param Xhc The XHCI Instance.
762 @param EventRing The created event ring.
767 IN USB_XHCI_INSTANCE
*Xhc
,
768 OUT EVENT_RING
*EventRing
772 EVENT_RING_SEG_TABLE_ENTRY
*ERSTBase
;
774 EFI_PHYSICAL_ADDRESS ERSTPhy
;
775 EFI_PHYSICAL_ADDRESS DequeuePhy
;
777 ASSERT (EventRing
!= NULL
);
779 Size
= sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
;
780 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, Size
);
781 ASSERT (Buf
!= NULL
);
782 ASSERT (((UINTN
) Buf
& 0x3F) == 0);
785 EventRing
->EventRingSeg0
= Buf
;
786 EventRing
->TrbNumber
= EVENT_RING_TRB_NUMBER
;
787 EventRing
->EventRingDequeue
= (TRB_TEMPLATE
*) EventRing
->EventRingSeg0
;
788 EventRing
->EventRingEnqueue
= (TRB_TEMPLATE
*) EventRing
->EventRingSeg0
;
790 DequeuePhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Buf
, Size
);
793 // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1'
794 // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring.
796 EventRing
->EventRingCCS
= 1;
798 Size
= sizeof (EVENT_RING_SEG_TABLE_ENTRY
) * ERST_NUMBER
;
799 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, Size
);
800 ASSERT (Buf
!= NULL
);
801 ASSERT (((UINTN
) Buf
& 0x3F) == 0);
804 ERSTBase
= (EVENT_RING_SEG_TABLE_ENTRY
*) Buf
;
805 EventRing
->ERSTBase
= ERSTBase
;
806 ERSTBase
->PtrLo
= XHC_LOW_32BIT (DequeuePhy
);
807 ERSTBase
->PtrHi
= XHC_HIGH_32BIT (DequeuePhy
);
808 ERSTBase
->RingTrbSize
= EVENT_RING_TRB_NUMBER
;
810 ERSTPhy
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, ERSTBase
, Size
);
813 // Program the Interrupter Event Ring Segment Table Size (ERSTSZ) register (5.5.2.3.1)
821 // Program the Interrupter Event Ring Dequeue Pointer (ERDP) register (5.5.2.3.3)
823 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
824 // So divide it to two 32-bytes width register access.
829 XHC_LOW_32BIT((UINT64
)(UINTN
)DequeuePhy
)
834 XHC_HIGH_32BIT((UINT64
)(UINTN
)DequeuePhy
)
837 // Program the Interrupter Event Ring Segment Table Base Address (ERSTBA) register(5.5.2.3.2)
839 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
840 // So divide it to two 32-bytes width register access.
845 XHC_LOW_32BIT((UINT64
)(UINTN
)ERSTPhy
)
849 XHC_ERSTBA_OFFSET
+ 4,
850 XHC_HIGH_32BIT((UINT64
)(UINTN
)ERSTPhy
)
853 // Need set IMAN IE bit to enble the ring interrupt
855 XhcSetRuntimeRegBit (Xhc
, XHC_IMAN_OFFSET
, XHC_IMAN_IE
);
859 Create XHCI transfer ring.
861 @param Xhc The XHCI Instance.
862 @param TrbNum The number of TRB in the ring.
863 @param TransferRing The created transfer ring.
868 IN USB_XHCI_INSTANCE
*Xhc
,
870 OUT TRANSFER_RING
*TransferRing
875 EFI_PHYSICAL_ADDRESS PhyAddr
;
877 Buf
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (TRB_TEMPLATE
) * TrbNum
);
878 ASSERT (Buf
!= NULL
);
879 ASSERT (((UINTN
) Buf
& 0x3F) == 0);
880 ZeroMem (Buf
, sizeof (TRB_TEMPLATE
) * TrbNum
);
882 TransferRing
->RingSeg0
= Buf
;
883 TransferRing
->TrbNumber
= TrbNum
;
884 TransferRing
->RingEnqueue
= (TRB_TEMPLATE
*) TransferRing
->RingSeg0
;
885 TransferRing
->RingDequeue
= (TRB_TEMPLATE
*) TransferRing
->RingSeg0
;
886 TransferRing
->RingPCS
= 1;
888 // 4.9.2 Transfer Ring Management
889 // To form a ring (or circular queue) a Link TRB may be inserted at the end of a ring to
890 // point to the first TRB in the ring.
892 EndTrb
= (LINK_TRB
*) ((UINTN
)Buf
+ sizeof (TRB_TEMPLATE
) * (TrbNum
- 1));
893 EndTrb
->Type
= TRB_TYPE_LINK
;
894 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Buf
, sizeof (TRB_TEMPLATE
) * TrbNum
);
895 EndTrb
->PtrLo
= XHC_LOW_32BIT (PhyAddr
);
896 EndTrb
->PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
898 // Toggle Cycle (TC). When set to '1', the xHC shall toggle its interpretation of the Cycle bit.
902 // Set Cycle bit as other TRB PCS init value
904 EndTrb
->CycleBit
= 0;
908 Free XHCI event ring.
910 @param Xhc The XHCI Instance.
911 @param EventRing The event ring to be freed.
917 IN USB_XHCI_INSTANCE
*Xhc
,
918 IN EVENT_RING
*EventRing
921 if(EventRing
->EventRingSeg0
== NULL
) {
926 // Free EventRing Segment 0
928 UsbHcFreeMem (Xhc
->MemPool
, EventRing
->EventRingSeg0
, sizeof (TRB_TEMPLATE
) * EVENT_RING_TRB_NUMBER
);
933 UsbHcFreeMem (Xhc
->MemPool
, EventRing
->ERSTBase
, sizeof (EVENT_RING_SEG_TABLE_ENTRY
) * ERST_NUMBER
);
938 Free the resouce allocated at initializing schedule.
940 @param Xhc The XHCI Instance.
945 IN USB_XHCI_INSTANCE
*Xhc
949 UINT64
*ScratchEntry
;
951 if (Xhc
->ScratchBuf
!= NULL
) {
952 ScratchEntry
= Xhc
->ScratchEntry
;
953 for (Index
= 0; Index
< Xhc
->MaxScratchpadBufs
; Index
++) {
955 // Free Scratchpad Buffers
957 UsbHcFreeAlignedPages (Xhc
->PciIo
, (VOID
*)(UINTN
)ScratchEntry
[Index
], EFI_SIZE_TO_PAGES (Xhc
->PageSize
), (VOID
*) Xhc
->ScratchEntryMap
[Index
]);
960 // Free Scratchpad Buffer Array
962 UsbHcFreeAlignedPages (Xhc
->PciIo
, Xhc
->ScratchBuf
, EFI_SIZE_TO_PAGES (Xhc
->MaxScratchpadBufs
* sizeof (UINT64
)), Xhc
->ScratchMap
);
963 FreePool (Xhc
->ScratchEntryMap
);
964 FreePool (Xhc
->ScratchEntry
);
967 if (Xhc
->CmdRing
.RingSeg0
!= NULL
) {
968 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->CmdRing
.RingSeg0
, sizeof (TRB_TEMPLATE
) * CMD_RING_TRB_NUMBER
);
969 Xhc
->CmdRing
.RingSeg0
= NULL
;
972 XhcFreeEventRing (Xhc
,&Xhc
->EventRing
);
974 if (Xhc
->DCBAA
!= NULL
) {
975 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->DCBAA
, (Xhc
->MaxSlotsEn
+ 1) * sizeof(UINT64
));
980 // Free memory pool at last
982 if (Xhc
->MemPool
!= NULL
) {
983 UsbHcFreeMemPool (Xhc
->MemPool
);
989 Check if the Trb is a transaction of the URB.
991 @param Xhc The XHCI Instance.
992 @param Trb The TRB to be checked
993 @param Urb The URB to be checked.
995 @retval TRUE It is a transaction of the URB.
996 @retval FALSE It is not any transaction of the URB.
1001 IN USB_XHCI_INSTANCE
*Xhc
,
1002 IN TRB_TEMPLATE
*Trb
,
1007 TRB_TEMPLATE
*CheckedTrb
;
1009 EFI_PHYSICAL_ADDRESS PhyAddr
;
1011 CheckedTrb
= Urb
->TrbStart
;
1012 for (Index
= 0; Index
< Urb
->TrbNum
; Index
++) {
1013 if (Trb
== CheckedTrb
) {
1018 // If the checked TRB is the link TRB at the end of the transfer ring,
1019 // recircle it to the head of the ring.
1021 if (CheckedTrb
->Type
== TRB_TYPE_LINK
) {
1022 LinkTrb
= (LINK_TRB
*) CheckedTrb
;
1023 PhyAddr
= (EFI_PHYSICAL_ADDRESS
)(LinkTrb
->PtrLo
| LShiftU64 ((UINT64
) LinkTrb
->PtrHi
, 32));
1024 CheckedTrb
= (TRB_TEMPLATE
*)(UINTN
) UsbHcGetHostAddrForPciAddr (Xhc
->MemPool
, (VOID
*)(UINTN
) PhyAddr
, sizeof (TRB_TEMPLATE
));
1025 ASSERT (CheckedTrb
== Urb
->Ring
->RingSeg0
);
1033 Check if the Trb is a transaction of the URBs in XHCI's asynchronous transfer list.
1035 @param Xhc The XHCI Instance.
1036 @param Trb The TRB to be checked.
1037 @param Urb The pointer to the matched Urb.
1039 @retval TRUE The Trb is matched with a transaction of the URBs in the async list.
1040 @retval FALSE The Trb is not matched with any URBs in the async list.
1045 IN USB_XHCI_INSTANCE
*Xhc
,
1046 IN TRB_TEMPLATE
*Trb
,
1054 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1055 CheckedUrb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1056 if (IsTransferRingTrb (Xhc
, Trb
, CheckedUrb
)) {
1067 Check the URB's execution result and update the URB's
1070 @param Xhc The XHCI Instance.
1071 @param Urb The URB to check result.
1073 @return Whether the result of URB transfer is finialized.
1078 IN USB_XHCI_INSTANCE
*Xhc
,
1082 EVT_TRB_TRANSFER
*EvtTrb
;
1083 TRB_TEMPLATE
*TRBPtr
;
1092 EFI_PHYSICAL_ADDRESS PhyAddr
;
1094 ASSERT ((Xhc
!= NULL
) && (Urb
!= NULL
));
1096 Status
= EFI_SUCCESS
;
1099 if (Urb
->Finished
) {
1105 if (XhcIsHalt (Xhc
) || XhcIsSysError (Xhc
)) {
1106 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
1111 // Traverse the event ring to find out all new events from the previous check.
1113 XhcSyncEventRing (Xhc
, &Xhc
->EventRing
);
1114 for (Index
= 0; Index
< Xhc
->EventRing
.TrbNumber
; Index
++) {
1115 Status
= XhcCheckNewEvent (Xhc
, &Xhc
->EventRing
, ((TRB_TEMPLATE
**)&EvtTrb
));
1116 if (Status
== EFI_NOT_READY
) {
1118 // All new events are handled, return directly.
1124 // Only handle COMMAND_COMPLETETION_EVENT and TRANSFER_EVENT.
1126 if ((EvtTrb
->Type
!= TRB_TYPE_COMMAND_COMPLT_EVENT
) && (EvtTrb
->Type
!= TRB_TYPE_TRANS_EVENT
)) {
1131 // Need convert pci device address to host address
1133 PhyAddr
= (EFI_PHYSICAL_ADDRESS
)(EvtTrb
->TRBPtrLo
| LShiftU64 ((UINT64
) EvtTrb
->TRBPtrHi
, 32));
1134 TRBPtr
= (TRB_TEMPLATE
*)(UINTN
) UsbHcGetHostAddrForPciAddr (Xhc
->MemPool
, (VOID
*)(UINTN
) PhyAddr
, sizeof (TRB_TEMPLATE
));
1137 // Update the status of URB including the pending URB, the URB that is currently checked,
1138 // and URBs in the XHCI's async interrupt transfer list.
1139 // This way is used to avoid that those completed async transfer events don't get
1140 // handled in time and are flushed by newer coming events.
1142 if (Xhc
->PendingUrb
!= NULL
&& IsTransferRingTrb (Xhc
, TRBPtr
, Xhc
->PendingUrb
)) {
1143 CheckedUrb
= Xhc
->PendingUrb
;
1144 } else if (IsTransferRingTrb (Xhc
, TRBPtr
, Urb
)) {
1146 } else if (IsAsyncIntTrb (Xhc
, TRBPtr
, &AsyncUrb
)) {
1147 CheckedUrb
= AsyncUrb
;
1152 switch (EvtTrb
->Completecode
) {
1153 case TRB_COMPLETION_STALL_ERROR
:
1154 CheckedUrb
->Result
|= EFI_USB_ERR_STALL
;
1155 CheckedUrb
->Finished
= TRUE
;
1156 DEBUG ((EFI_D_ERROR
, "XhcCheckUrbResult: STALL_ERROR! Completecode = %x\n",EvtTrb
->Completecode
));
1159 case TRB_COMPLETION_BABBLE_ERROR
:
1160 CheckedUrb
->Result
|= EFI_USB_ERR_BABBLE
;
1161 CheckedUrb
->Finished
= TRUE
;
1162 DEBUG ((EFI_D_ERROR
, "XhcCheckUrbResult: BABBLE_ERROR! Completecode = %x\n",EvtTrb
->Completecode
));
1165 case TRB_COMPLETION_DATA_BUFFER_ERROR
:
1166 CheckedUrb
->Result
|= EFI_USB_ERR_BUFFER
;
1167 CheckedUrb
->Finished
= TRUE
;
1168 DEBUG ((EFI_D_ERROR
, "XhcCheckUrbResult: ERR_BUFFER! Completecode = %x\n",EvtTrb
->Completecode
));
1171 case TRB_COMPLETION_USB_TRANSACTION_ERROR
:
1172 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1173 CheckedUrb
->Finished
= TRUE
;
1174 DEBUG ((EFI_D_ERROR
, "XhcCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n",EvtTrb
->Completecode
));
1177 case TRB_COMPLETION_STOPPED
:
1178 case TRB_COMPLETION_STOPPED_LENGTH_INVALID
:
1179 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1180 CheckedUrb
->Finished
= TRUE
;
1182 // The pending URB is timeout and force stopped when stopping endpoint.
1183 // Continue the loop to receive the Command Complete Event for stopping endpoint.
1187 case TRB_COMPLETION_SHORT_PACKET
:
1188 case TRB_COMPLETION_SUCCESS
:
1189 if (EvtTrb
->Completecode
== TRB_COMPLETION_SHORT_PACKET
) {
1190 DEBUG ((EFI_D_VERBOSE
, "XhcCheckUrbResult: short packet happens!\n"));
1193 TRBType
= (UINT8
) (TRBPtr
->Type
);
1194 if ((TRBType
== TRB_TYPE_DATA_STAGE
) ||
1195 (TRBType
== TRB_TYPE_NORMAL
) ||
1196 (TRBType
== TRB_TYPE_ISOCH
)) {
1197 CheckedUrb
->Completed
+= (((TRANSFER_TRB_NORMAL
*)TRBPtr
)->Length
- EvtTrb
->Length
);
1203 DEBUG ((EFI_D_ERROR
, "Transfer Default Error Occur! Completecode = 0x%x!\n",EvtTrb
->Completecode
));
1204 CheckedUrb
->Result
|= EFI_USB_ERR_TIMEOUT
;
1205 CheckedUrb
->Finished
= TRUE
;
1210 // Only check first and end Trb event address
1212 if (TRBPtr
== CheckedUrb
->TrbStart
) {
1213 CheckedUrb
->StartDone
= TRUE
;
1216 if (TRBPtr
== CheckedUrb
->TrbEnd
) {
1217 CheckedUrb
->EndDone
= TRUE
;
1220 if (CheckedUrb
->StartDone
&& CheckedUrb
->EndDone
) {
1221 CheckedUrb
->Finished
= TRUE
;
1222 CheckedUrb
->EvtTrb
= (TRB_TEMPLATE
*)EvtTrb
;
1229 // Advance event ring to last available entry
1231 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
1232 // So divide it to two 32-bytes width register access.
1234 Low
= XhcReadRuntimeReg (Xhc
, XHC_ERDP_OFFSET
);
1235 High
= XhcReadRuntimeReg (Xhc
, XHC_ERDP_OFFSET
+ 4);
1236 XhcDequeue
= (UINT64
)(LShiftU64((UINT64
)High
, 32) | Low
);
1238 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->EventRing
.EventRingDequeue
, sizeof (TRB_TEMPLATE
));
1240 if ((XhcDequeue
& (~0x0F)) != (PhyAddr
& (~0x0F))) {
1242 // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
1243 // So divide it to two 32-bytes width register access.
1245 XhcWriteRuntimeReg (Xhc
, XHC_ERDP_OFFSET
, XHC_LOW_32BIT (PhyAddr
) | BIT3
);
1246 XhcWriteRuntimeReg (Xhc
, XHC_ERDP_OFFSET
+ 4, XHC_HIGH_32BIT (PhyAddr
));
1249 return Urb
->Finished
;
1254 Execute the transfer by polling the URB. This is a synchronous operation.
1256 @param Xhc The XHCI Instance.
1257 @param CmdTransfer The executed URB is for cmd transfer or not.
1258 @param Urb The URB to execute.
1259 @param Timeout The time to wait before abort, in millisecond.
1261 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.
1262 @return EFI_TIMEOUT The transfer failed due to time out.
1263 @return EFI_SUCCESS The transfer finished OK.
1268 IN USB_XHCI_INSTANCE
*Xhc
,
1269 IN BOOLEAN CmdTransfer
,
1285 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
1287 return EFI_DEVICE_ERROR
;
1289 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
1293 Status
= EFI_SUCCESS
;
1294 Loop
= Timeout
* XHC_1_MILLISECOND
;
1299 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
1301 for (Index
= 0; Index
< Loop
; Index
++) {
1302 Finished
= XhcCheckUrbResult (Xhc
, Urb
);
1306 gBS
->Stall (XHC_1_MICROSECOND
);
1309 if (Index
== Loop
) {
1310 Urb
->Result
= EFI_USB_ERR_TIMEOUT
;
1311 Status
= EFI_TIMEOUT
;
1312 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
1313 Status
= EFI_DEVICE_ERROR
;
1320 Delete a single asynchronous interrupt transfer for
1321 the device and endpoint.
1323 @param Xhc The XHCI Instance.
1324 @param BusAddr The logical device address assigned by UsbBus driver.
1325 @param EpNum The endpoint of the target.
1327 @retval EFI_SUCCESS An asynchronous transfer is removed.
1328 @retval EFI_NOT_FOUND No transfer for the device is found.
1332 XhciDelAsyncIntTransfer (
1333 IN USB_XHCI_INSTANCE
*Xhc
,
1341 EFI_USB_DATA_DIRECTION Direction
;
1344 Direction
= ((EpNum
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
;
1349 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1350 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1351 if ((Urb
->Ep
.BusAddr
== BusAddr
) &&
1352 (Urb
->Ep
.EpAddr
== EpNum
) &&
1353 (Urb
->Ep
.Direction
== Direction
)) {
1355 // Device doesn't finish the IntTransfer until real data comes
1356 // So the TRB should be removed as well.
1358 Status
= XhcDequeueTrbFromEndpoint (Xhc
, Urb
);
1359 if (EFI_ERROR (Status
)) {
1360 DEBUG ((EFI_D_ERROR
, "XhciDelAsyncIntTransfer: XhcDequeueTrbFromEndpoint failed\n"));
1363 RemoveEntryList (&Urb
->UrbList
);
1364 FreePool (Urb
->Data
);
1365 XhcFreeUrb (Xhc
, Urb
);
1370 return EFI_NOT_FOUND
;
1374 Remove all the asynchronous interrutp transfers.
1376 @param Xhc The XHCI Instance.
1380 XhciDelAllAsyncIntTransfers (
1381 IN USB_XHCI_INSTANCE
*Xhc
1389 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1390 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1393 // Device doesn't finish the IntTransfer until real data comes
1394 // So the TRB should be removed as well.
1396 Status
= XhcDequeueTrbFromEndpoint (Xhc
, Urb
);
1397 if (EFI_ERROR (Status
)) {
1398 DEBUG ((EFI_D_ERROR
, "XhciDelAllAsyncIntTransfers: XhcDequeueTrbFromEndpoint failed\n"));
1401 RemoveEntryList (&Urb
->UrbList
);
1402 FreePool (Urb
->Data
);
1403 XhcFreeUrb (Xhc
, Urb
);
1408 Insert a single asynchronous interrupt transfer for
1409 the device and endpoint.
1411 @param Xhc The XHCI Instance
1412 @param BusAddr The logical device address assigned by UsbBus driver
1413 @param EpAddr Endpoint addrress
1414 @param DevSpeed The device speed
1415 @param MaxPacket The max packet length of the endpoint
1416 @param DataLen The length of data buffer
1417 @param Callback The function to call when data is transferred
1418 @param Context The context to the callback
1420 @return Created URB or NULL
1424 XhciInsertAsyncIntTransfer (
1425 IN USB_XHCI_INSTANCE
*Xhc
,
1431 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback
,
1438 Data
= AllocateZeroPool (DataLen
);
1440 DEBUG ((DEBUG_ERROR
, "%a: failed to allocate buffer\n", __FUNCTION__
));
1444 Urb
= XhcCreateUrb (
1450 XHC_INT_TRANSFER_ASYNC
,
1458 DEBUG ((DEBUG_ERROR
, "%a: failed to create URB\n", __FUNCTION__
));
1464 // New asynchronous transfer must inserted to the head.
1465 // Check the comments in XhcMoniteAsyncRequests
1467 InsertHeadList (&Xhc
->AsyncIntTransfers
, &Urb
->UrbList
);
1473 Update the queue head for next round of asynchronous transfer
1475 @param Xhc The XHCI Instance.
1476 @param Urb The URB to update
1480 XhcUpdateAsyncRequest (
1481 IN USB_XHCI_INSTANCE
*Xhc
,
1487 if (Urb
->Result
== EFI_USB_NOERROR
) {
1488 Status
= XhcCreateTransferTrb (Xhc
, Urb
);
1489 if (EFI_ERROR (Status
)) {
1492 Status
= RingIntTransferDoorBell (Xhc
, Urb
);
1493 if (EFI_ERROR (Status
)) {
1500 Flush data from PCI controller specific address to mapped system
1503 @param Xhc The XHCI device.
1504 @param Urb The URB to unmap.
1506 @retval EFI_SUCCESS Success to flush data to mapped system memory.
1507 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
1511 XhcFlushAsyncIntMap (
1512 IN USB_XHCI_INSTANCE
*Xhc
,
1517 EFI_PHYSICAL_ADDRESS PhyAddr
;
1518 EFI_PCI_IO_PROTOCOL_OPERATION MapOp
;
1519 EFI_PCI_IO_PROTOCOL
*PciIo
;
1526 if (Urb
->Ep
.Direction
== EfiUsbDataIn
) {
1527 MapOp
= EfiPciIoOperationBusMasterWrite
;
1529 MapOp
= EfiPciIoOperationBusMasterRead
;
1532 if (Urb
->DataMap
!= NULL
) {
1533 Status
= PciIo
->Unmap (PciIo
, Urb
->DataMap
);
1534 if (EFI_ERROR (Status
)) {
1539 Urb
->DataMap
= NULL
;
1541 Status
= PciIo
->Map (PciIo
, MapOp
, Urb
->Data
, &Len
, &PhyAddr
, &Map
);
1542 if (EFI_ERROR (Status
) || (Len
!= Urb
->DataLen
)) {
1546 Urb
->DataPhy
= (VOID
*) ((UINTN
) PhyAddr
);
1551 return EFI_DEVICE_ERROR
;
1555 Interrupt transfer periodic check handler.
1557 @param Event Interrupt event.
1558 @param Context Pointer to USB_XHCI_INSTANCE.
1563 XhcMonitorAsyncRequests (
1568 USB_XHCI_INSTANCE
*Xhc
;
1577 OldTpl
= gBS
->RaiseTPL (XHC_TPL
);
1579 Xhc
= (USB_XHCI_INSTANCE
*) Context
;
1581 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Xhc
->AsyncIntTransfers
) {
1582 Urb
= EFI_LIST_CONTAINER (Entry
, URB
, UrbList
);
1585 // Make sure that the device is available before every check.
1587 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
1593 // Check the result of URB execution. If it is still
1594 // active, check the next one.
1596 XhcCheckUrbResult (Xhc
, Urb
);
1598 if (!Urb
->Finished
) {
1603 // Flush any PCI posted write transactions from a PCI host
1604 // bridge to system memory.
1606 Status
= XhcFlushAsyncIntMap (Xhc
, Urb
);
1607 if (EFI_ERROR (Status
)) {
1608 DEBUG ((EFI_D_ERROR
, "XhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
1612 // Allocate a buffer then copy the transferred data for user.
1613 // If failed to allocate the buffer, update the URB for next
1614 // round of transfer. Ignore the data of this round.
1617 if (Urb
->Result
== EFI_USB_NOERROR
) {
1619 // Make sure the data received from HW is no more than expected.
1621 if (Urb
->Completed
<= Urb
->DataLen
) {
1622 ProcBuf
= AllocateZeroPool (Urb
->Completed
);
1625 if (ProcBuf
== NULL
) {
1626 XhcUpdateAsyncRequest (Xhc
, Urb
);
1630 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
1634 // Leave error recovery to its related device driver. A
1635 // common case of the error recovery is to re-submit the
1636 // interrupt transfer which is linked to the head of the
1637 // list. This function scans from head to tail. So the
1638 // re-submitted interrupt transfer's callback function
1639 // will not be called again in this round. Don't touch this
1640 // URB after the callback, it may have been removed by the
1643 if (Urb
->Callback
!= NULL
) {
1645 // Restore the old TPL, USB bus maybe connect device in
1646 // his callback. Some drivers may has a lower TPL restriction.
1648 gBS
->RestoreTPL (OldTpl
);
1649 (Urb
->Callback
) (ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
1650 OldTpl
= gBS
->RaiseTPL (XHC_TPL
);
1653 if (ProcBuf
!= NULL
) {
1654 gBS
->FreePool (ProcBuf
);
1657 XhcUpdateAsyncRequest (Xhc
, Urb
);
1659 gBS
->RestoreTPL (OldTpl
);
1663 Monitor the port status change. Enable/Disable device slot if there is a device attached/detached.
1665 @param Xhc The XHCI Instance.
1666 @param ParentRouteChart The route string pointed to the parent device if it exists.
1667 @param Port The port to be polled.
1668 @param PortState The port state.
1670 @retval EFI_SUCCESS Successfully enable/disable device slot according to port state.
1671 @retval Others Should not appear.
1676 XhcPollPortStatusChange (
1677 IN USB_XHCI_INSTANCE
*Xhc
,
1678 IN USB_DEV_ROUTE ParentRouteChart
,
1680 IN EFI_USB_PORT_STATUS
*PortState
1686 USB_DEV_ROUTE RouteChart
;
1688 Status
= EFI_SUCCESS
;
1690 if ((PortState
->PortChangeStatus
& (USB_PORT_STAT_C_CONNECTION
| USB_PORT_STAT_C_ENABLE
| USB_PORT_STAT_C_OVERCURRENT
| USB_PORT_STAT_C_RESET
)) == 0) {
1694 if (ParentRouteChart
.Dword
== 0) {
1695 RouteChart
.Route
.RouteString
= 0;
1696 RouteChart
.Route
.RootPortNum
= Port
+ 1;
1697 RouteChart
.Route
.TierNum
= 1;
1700 RouteChart
.Route
.RouteString
= ParentRouteChart
.Route
.RouteString
| (Port
<< (4 * (ParentRouteChart
.Route
.TierNum
- 1)));
1702 RouteChart
.Route
.RouteString
= ParentRouteChart
.Route
.RouteString
| (15 << (4 * (ParentRouteChart
.Route
.TierNum
- 1)));
1704 RouteChart
.Route
.RootPortNum
= ParentRouteChart
.Route
.RootPortNum
;
1705 RouteChart
.Route
.TierNum
= ParentRouteChart
.Route
.TierNum
+ 1;
1708 SlotId
= XhcRouteStringToSlotId (Xhc
, RouteChart
);
1710 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
1711 Status
= XhcDisableSlotCmd (Xhc
, SlotId
);
1713 Status
= XhcDisableSlotCmd64 (Xhc
, SlotId
);
1717 if (((PortState
->PortStatus
& USB_PORT_STAT_ENABLE
) != 0) &&
1718 ((PortState
->PortStatus
& USB_PORT_STAT_CONNECTION
) != 0)) {
1720 // Has a device attached, Identify device speed after port is enabled.
1722 Speed
= EFI_USB_SPEED_FULL
;
1723 if ((PortState
->PortStatus
& USB_PORT_STAT_LOW_SPEED
) != 0) {
1724 Speed
= EFI_USB_SPEED_LOW
;
1725 } else if ((PortState
->PortStatus
& USB_PORT_STAT_HIGH_SPEED
) != 0) {
1726 Speed
= EFI_USB_SPEED_HIGH
;
1727 } else if ((PortState
->PortStatus
& USB_PORT_STAT_SUPER_SPEED
) != 0) {
1728 Speed
= EFI_USB_SPEED_SUPER
;
1731 // Execute Enable_Slot cmd for attached device, initialize device context and assign device address.
1733 SlotId
= XhcRouteStringToSlotId (Xhc
, RouteChart
);
1734 if ((SlotId
== 0) && ((PortState
->PortChangeStatus
& USB_PORT_STAT_C_RESET
) != 0)) {
1735 if (Xhc
->HcCParams
.Data
.Csz
== 0) {
1736 Status
= XhcInitializeDeviceSlot (Xhc
, ParentRouteChart
, Port
, RouteChart
, Speed
);
1738 Status
= XhcInitializeDeviceSlot64 (Xhc
, ParentRouteChart
, Port
, RouteChart
, Speed
);
1748 Calculate the device context index by endpoint address and direction.
1750 @param EpAddr The target endpoint number.
1751 @param Direction The direction of the target endpoint.
1753 @return The device context index of endpoint.
1767 Index
= (UINT8
) (2 * EpAddr
);
1768 if (Direction
== EfiUsbDataIn
) {
1776 Find out the actual device address according to the requested device address from UsbBus.
1778 @param Xhc The XHCI Instance.
1779 @param BusDevAddr The requested device address by UsbBus upper driver.
1781 @return The actual device address assigned to the device.
1786 XhcBusDevAddrToSlotId (
1787 IN USB_XHCI_INSTANCE
*Xhc
,
1793 for (Index
= 0; Index
< 255; Index
++) {
1794 if (Xhc
->UsbDevContext
[Index
+ 1].Enabled
&&
1795 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
!= 0) &&
1796 (Xhc
->UsbDevContext
[Index
+ 1].BusDevAddr
== BusDevAddr
)) {
1805 return Xhc
->UsbDevContext
[Index
+ 1].SlotId
;
1809 Find out the slot id according to the device's route string.
1811 @param Xhc The XHCI Instance.
1812 @param RouteString The route string described the device location.
1814 @return The slot id used by the device.
1819 XhcRouteStringToSlotId (
1820 IN USB_XHCI_INSTANCE
*Xhc
,
1821 IN USB_DEV_ROUTE RouteString
1826 for (Index
= 0; Index
< 255; Index
++) {
1827 if (Xhc
->UsbDevContext
[Index
+ 1].Enabled
&&
1828 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
!= 0) &&
1829 (Xhc
->UsbDevContext
[Index
+ 1].RouteString
.Dword
== RouteString
.Dword
)) {
1838 return Xhc
->UsbDevContext
[Index
+ 1].SlotId
;
1842 Synchronize the specified event ring to update the enqueue and dequeue pointer.
1844 @param Xhc The XHCI Instance.
1845 @param EvtRing The event ring to sync.
1847 @retval EFI_SUCCESS The event ring is synchronized successfully.
1853 IN USB_XHCI_INSTANCE
*Xhc
,
1854 IN EVENT_RING
*EvtRing
1858 TRB_TEMPLATE
*EvtTrb1
;
1860 ASSERT (EvtRing
!= NULL
);
1863 // Calculate the EventRingEnqueue and EventRingCCS.
1864 // Note: only support single Segment
1866 EvtTrb1
= EvtRing
->EventRingDequeue
;
1868 for (Index
= 0; Index
< EvtRing
->TrbNumber
; Index
++) {
1869 if (EvtTrb1
->CycleBit
!= EvtRing
->EventRingCCS
) {
1875 if ((UINTN
)EvtTrb1
>= ((UINTN
) EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
1876 EvtTrb1
= EvtRing
->EventRingSeg0
;
1877 EvtRing
->EventRingCCS
= (EvtRing
->EventRingCCS
) ? 0 : 1;
1881 if (Index
< EvtRing
->TrbNumber
) {
1882 EvtRing
->EventRingEnqueue
= EvtTrb1
;
1891 Synchronize the specified transfer ring to update the enqueue and dequeue pointer.
1893 @param Xhc The XHCI Instance.
1894 @param TrsRing The transfer ring to sync.
1896 @retval EFI_SUCCESS The transfer ring is synchronized successfully.
1902 IN USB_XHCI_INSTANCE
*Xhc
,
1903 IN TRANSFER_RING
*TrsRing
1907 TRB_TEMPLATE
*TrsTrb
;
1909 ASSERT (TrsRing
!= NULL
);
1911 // Calculate the latest RingEnqueue and RingPCS
1913 TrsTrb
= TrsRing
->RingEnqueue
;
1914 ASSERT (TrsTrb
!= NULL
);
1916 for (Index
= 0; Index
< TrsRing
->TrbNumber
; Index
++) {
1917 if (TrsTrb
->CycleBit
!= (TrsRing
->RingPCS
& BIT0
)) {
1921 if ((UINT8
) TrsTrb
->Type
== TRB_TYPE_LINK
) {
1922 ASSERT (((LINK_TRB
*)TrsTrb
)->TC
!= 0);
1924 // set cycle bit in Link TRB as normal
1926 ((LINK_TRB
*)TrsTrb
)->CycleBit
= TrsRing
->RingPCS
& BIT0
;
1928 // Toggle PCS maintained by software
1930 TrsRing
->RingPCS
= (TrsRing
->RingPCS
& BIT0
) ? 0 : 1;
1931 TrsTrb
= (TRB_TEMPLATE
*) TrsRing
->RingSeg0
; // Use host address
1935 ASSERT (Index
!= TrsRing
->TrbNumber
);
1937 if (TrsTrb
!= TrsRing
->RingEnqueue
) {
1938 TrsRing
->RingEnqueue
= TrsTrb
;
1942 // Clear the Trb context for enqueue, but reserve the PCS bit
1944 TrsTrb
->Parameter1
= 0;
1945 TrsTrb
->Parameter2
= 0;
1949 TrsTrb
->Control
= 0;
1955 Check if there is a new generated event.
1957 @param Xhc The XHCI Instance.
1958 @param EvtRing The event ring to check.
1959 @param NewEvtTrb The new event TRB found.
1961 @retval EFI_SUCCESS Found a new event TRB at the event ring.
1962 @retval EFI_NOT_READY The event ring has no new event.
1968 IN USB_XHCI_INSTANCE
*Xhc
,
1969 IN EVENT_RING
*EvtRing
,
1970 OUT TRB_TEMPLATE
**NewEvtTrb
1973 ASSERT (EvtRing
!= NULL
);
1975 *NewEvtTrb
= EvtRing
->EventRingDequeue
;
1977 if (EvtRing
->EventRingDequeue
== EvtRing
->EventRingEnqueue
) {
1978 return EFI_NOT_READY
;
1981 EvtRing
->EventRingDequeue
++;
1983 // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.
1985 if ((UINTN
)EvtRing
->EventRingDequeue
>= ((UINTN
) EvtRing
->EventRingSeg0
+ sizeof (TRB_TEMPLATE
) * EvtRing
->TrbNumber
)) {
1986 EvtRing
->EventRingDequeue
= EvtRing
->EventRingSeg0
;
1993 Ring the door bell to notify XHCI there is a transaction to be executed.
1995 @param Xhc The XHCI Instance.
1996 @param SlotId The slot id of the target device.
1997 @param Dci The device context index of the target slot or endpoint.
1999 @retval EFI_SUCCESS Successfully ring the door bell.
2005 IN USB_XHCI_INSTANCE
*Xhc
,
2011 XhcWriteDoorBellReg (Xhc
, 0, 0);
2013 XhcWriteDoorBellReg (Xhc
, SlotId
* sizeof (UINT32
), Dci
);
2020 Ring the door bell to notify XHCI there is a transaction to be executed through URB.
2022 @param Xhc The XHCI Instance.
2023 @param Urb The URB to be rung.
2025 @retval EFI_SUCCESS Successfully ring the door bell.
2029 RingIntTransferDoorBell (
2030 IN USB_XHCI_INSTANCE
*Xhc
,
2037 SlotId
= XhcBusDevAddrToSlotId (Xhc
, Urb
->Ep
.BusAddr
);
2038 Dci
= XhcEndpointToDci (Urb
->Ep
.EpAddr
, (UINT8
)(Urb
->Ep
.Direction
));
2039 XhcRingDoorBell (Xhc
, SlotId
, Dci
);
2044 Assign and initialize the device slot for a new device.
2046 @param Xhc The XHCI Instance.
2047 @param ParentRouteChart The route string pointed to the parent device.
2048 @param ParentPort The port at which the device is located.
2049 @param RouteChart The route string pointed to the device.
2050 @param DeviceSpeed The device speed.
2052 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.
2057 XhcInitializeDeviceSlot (
2058 IN USB_XHCI_INSTANCE
*Xhc
,
2059 IN USB_DEV_ROUTE ParentRouteChart
,
2060 IN UINT16 ParentPort
,
2061 IN USB_DEV_ROUTE RouteChart
,
2062 IN UINT8 DeviceSpeed
2066 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
2067 INPUT_CONTEXT
*InputContext
;
2068 DEVICE_CONTEXT
*OutputContext
;
2069 TRANSFER_RING
*EndpointTransferRing
;
2070 CMD_TRB_ADDRESS_DEVICE CmdTrbAddr
;
2071 UINT8 DeviceAddress
;
2072 CMD_TRB_ENABLE_SLOT CmdTrb
;
2075 DEVICE_CONTEXT
*ParentDeviceContext
;
2076 EFI_PHYSICAL_ADDRESS PhyAddr
;
2078 ZeroMem (&CmdTrb
, sizeof (CMD_TRB_ENABLE_SLOT
));
2079 CmdTrb
.CycleBit
= 1;
2080 CmdTrb
.Type
= TRB_TYPE_EN_SLOT
;
2082 Status
= XhcCmdTransfer (
2084 (TRB_TEMPLATE
*) (UINTN
) &CmdTrb
,
2085 XHC_GENERIC_TIMEOUT
,
2086 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2088 if (EFI_ERROR (Status
)) {
2089 DEBUG ((EFI_D_ERROR
, "XhcInitializeDeviceSlot: Enable Slot Failed, Status = %r\n", Status
));
2092 ASSERT (EvtTrb
->SlotId
<= Xhc
->MaxSlotsEn
);
2093 DEBUG ((EFI_D_INFO
, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb
->SlotId
));
2094 SlotId
= (UINT8
)EvtTrb
->SlotId
;
2095 ASSERT (SlotId
!= 0);
2097 ZeroMem (&Xhc
->UsbDevContext
[SlotId
], sizeof (USB_DEV_CONTEXT
));
2098 Xhc
->UsbDevContext
[SlotId
].Enabled
= TRUE
;
2099 Xhc
->UsbDevContext
[SlotId
].SlotId
= SlotId
;
2100 Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
= RouteChart
.Dword
;
2101 Xhc
->UsbDevContext
[SlotId
].ParentRouteString
.Dword
= ParentRouteChart
.Dword
;
2104 // 4.3.3 Device Slot Initialization
2105 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.
2107 InputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (INPUT_CONTEXT
));
2108 ASSERT (InputContext
!= NULL
);
2109 ASSERT (((UINTN
) InputContext
& 0x3F) == 0);
2110 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
2112 Xhc
->UsbDevContext
[SlotId
].InputContext
= (VOID
*) InputContext
;
2115 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1
2116 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input
2117 // Context are affected by the command.
2119 InputContext
->InputControlContext
.Dword2
|= (BIT0
| BIT1
);
2122 // 3) Initialize the Input Slot Context data structure
2124 InputContext
->Slot
.RouteString
= RouteChart
.Route
.RouteString
;
2125 InputContext
->Slot
.Speed
= DeviceSpeed
+ 1;
2126 InputContext
->Slot
.ContextEntries
= 1;
2127 InputContext
->Slot
.RootHubPortNum
= RouteChart
.Route
.RootPortNum
;
2129 if (RouteChart
.Route
.RouteString
) {
2131 // The device is behind of hub device.
2133 ParentSlotId
= XhcRouteStringToSlotId(Xhc
, ParentRouteChart
);
2134 ASSERT (ParentSlotId
!= 0);
2136 //if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context
2138 ParentDeviceContext
= (DEVICE_CONTEXT
*)Xhc
->UsbDevContext
[ParentSlotId
].OutputContext
;
2139 if ((ParentDeviceContext
->Slot
.TTPortNum
== 0) &&
2140 (ParentDeviceContext
->Slot
.TTHubSlotId
== 0)) {
2141 if ((ParentDeviceContext
->Slot
.Speed
== (EFI_USB_SPEED_HIGH
+ 1)) && (DeviceSpeed
< EFI_USB_SPEED_HIGH
)) {
2143 // Full/Low device attached to High speed hub port that isolates the high speed signaling
2144 // environment from Full/Low speed signaling environment for a device
2146 InputContext
->Slot
.TTPortNum
= ParentPort
;
2147 InputContext
->Slot
.TTHubSlotId
= ParentSlotId
;
2151 // Inherit the TT parameters from parent device.
2153 InputContext
->Slot
.TTPortNum
= ParentDeviceContext
->Slot
.TTPortNum
;
2154 InputContext
->Slot
.TTHubSlotId
= ParentDeviceContext
->Slot
.TTHubSlotId
;
2156 // If the device is a High speed device then down the speed to be the same as its parent Hub
2158 if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2159 InputContext
->Slot
.Speed
= ParentDeviceContext
->Slot
.Speed
;
2165 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.
2167 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2168 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0] = EndpointTransferRing
;
2169 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0]);
2171 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).
2173 InputContext
->EP
[0].EPType
= ED_CONTROL_BIDIR
;
2175 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2176 InputContext
->EP
[0].MaxPacketSize
= 512;
2177 } else if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2178 InputContext
->EP
[0].MaxPacketSize
= 64;
2180 InputContext
->EP
[0].MaxPacketSize
= 8;
2183 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints
2184 // 1KB, and Bulk and Isoch endpoints 3KB.
2186 InputContext
->EP
[0].AverageTRBLength
= 8;
2187 InputContext
->EP
[0].MaxBurstSize
= 0;
2188 InputContext
->EP
[0].Interval
= 0;
2189 InputContext
->EP
[0].MaxPStreams
= 0;
2190 InputContext
->EP
[0].Mult
= 0;
2191 InputContext
->EP
[0].CErr
= 3;
2194 // Init the DCS(dequeue cycle state) as the transfer ring's CCS
2196 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2198 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0])->RingSeg0
,
2199 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2201 InputContext
->EP
[0].PtrLo
= XHC_LOW_32BIT (PhyAddr
) | BIT0
;
2202 InputContext
->EP
[0].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2205 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
2207 OutputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (DEVICE_CONTEXT
));
2208 ASSERT (OutputContext
!= NULL
);
2209 ASSERT (((UINTN
) OutputContext
& 0x3F) == 0);
2210 ZeroMem (OutputContext
, sizeof (DEVICE_CONTEXT
));
2212 Xhc
->UsbDevContext
[SlotId
].OutputContext
= OutputContext
;
2214 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with
2215 // a pointer to the Output Device Context data structure (6.2.1).
2217 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, OutputContext
, sizeof (DEVICE_CONTEXT
));
2219 // Fill DCBAA with PCI device address
2221 Xhc
->DCBAA
[SlotId
] = (UINT64
) (UINTN
) PhyAddr
;
2224 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input
2225 // Context data structure described above.
2227 // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request
2230 gBS
->Stall (XHC_RESET_RECOVERY_DELAY
);
2231 ZeroMem (&CmdTrbAddr
, sizeof (CmdTrbAddr
));
2232 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT
));
2233 CmdTrbAddr
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2234 CmdTrbAddr
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2235 CmdTrbAddr
.CycleBit
= 1;
2236 CmdTrbAddr
.Type
= TRB_TYPE_ADDRESS_DEV
;
2237 CmdTrbAddr
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
2238 Status
= XhcCmdTransfer (
2240 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbAddr
,
2241 XHC_GENERIC_TIMEOUT
,
2242 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2244 if (!EFI_ERROR (Status
)) {
2245 DeviceAddress
= (UINT8
) ((DEVICE_CONTEXT
*) OutputContext
)->Slot
.DeviceAddress
;
2246 DEBUG ((EFI_D_INFO
, " Address %d assigned successfully\n", DeviceAddress
));
2247 Xhc
->UsbDevContext
[SlotId
].XhciDevAddr
= DeviceAddress
;
2254 Assign and initialize the device slot for a new device.
2256 @param Xhc The XHCI Instance.
2257 @param ParentRouteChart The route string pointed to the parent device.
2258 @param ParentPort The port at which the device is located.
2259 @param RouteChart The route string pointed to the device.
2260 @param DeviceSpeed The device speed.
2262 @retval EFI_SUCCESS Successfully assign a slot to the device and assign an address to it.
2267 XhcInitializeDeviceSlot64 (
2268 IN USB_XHCI_INSTANCE
*Xhc
,
2269 IN USB_DEV_ROUTE ParentRouteChart
,
2270 IN UINT16 ParentPort
,
2271 IN USB_DEV_ROUTE RouteChart
,
2272 IN UINT8 DeviceSpeed
2276 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
2277 INPUT_CONTEXT_64
*InputContext
;
2278 DEVICE_CONTEXT_64
*OutputContext
;
2279 TRANSFER_RING
*EndpointTransferRing
;
2280 CMD_TRB_ADDRESS_DEVICE CmdTrbAddr
;
2281 UINT8 DeviceAddress
;
2282 CMD_TRB_ENABLE_SLOT CmdTrb
;
2285 DEVICE_CONTEXT_64
*ParentDeviceContext
;
2286 EFI_PHYSICAL_ADDRESS PhyAddr
;
2288 ZeroMem (&CmdTrb
, sizeof (CMD_TRB_ENABLE_SLOT
));
2289 CmdTrb
.CycleBit
= 1;
2290 CmdTrb
.Type
= TRB_TYPE_EN_SLOT
;
2292 Status
= XhcCmdTransfer (
2294 (TRB_TEMPLATE
*) (UINTN
) &CmdTrb
,
2295 XHC_GENERIC_TIMEOUT
,
2296 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2298 if (EFI_ERROR (Status
)) {
2299 DEBUG ((EFI_D_ERROR
, "XhcInitializeDeviceSlot64: Enable Slot Failed, Status = %r\n", Status
));
2302 ASSERT (EvtTrb
->SlotId
<= Xhc
->MaxSlotsEn
);
2303 DEBUG ((EFI_D_INFO
, "Enable Slot Successfully, The Slot ID = 0x%x\n", EvtTrb
->SlotId
));
2304 SlotId
= (UINT8
)EvtTrb
->SlotId
;
2305 ASSERT (SlotId
!= 0);
2307 ZeroMem (&Xhc
->UsbDevContext
[SlotId
], sizeof (USB_DEV_CONTEXT
));
2308 Xhc
->UsbDevContext
[SlotId
].Enabled
= TRUE
;
2309 Xhc
->UsbDevContext
[SlotId
].SlotId
= SlotId
;
2310 Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
= RouteChart
.Dword
;
2311 Xhc
->UsbDevContext
[SlotId
].ParentRouteString
.Dword
= ParentRouteChart
.Dword
;
2314 // 4.3.3 Device Slot Initialization
2315 // 1) Allocate an Input Context data structure (6.2.5) and initialize all fields to '0'.
2317 InputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (INPUT_CONTEXT_64
));
2318 ASSERT (InputContext
!= NULL
);
2319 ASSERT (((UINTN
) InputContext
& 0x3F) == 0);
2320 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
2322 Xhc
->UsbDevContext
[SlotId
].InputContext
= (VOID
*) InputContext
;
2325 // 2) Initialize the Input Control Context (6.2.5.1) of the Input Context by setting the A0 and A1
2326 // flags to '1'. These flags indicate that the Slot Context and the Endpoint 0 Context of the Input
2327 // Context are affected by the command.
2329 InputContext
->InputControlContext
.Dword2
|= (BIT0
| BIT1
);
2332 // 3) Initialize the Input Slot Context data structure
2334 InputContext
->Slot
.RouteString
= RouteChart
.Route
.RouteString
;
2335 InputContext
->Slot
.Speed
= DeviceSpeed
+ 1;
2336 InputContext
->Slot
.ContextEntries
= 1;
2337 InputContext
->Slot
.RootHubPortNum
= RouteChart
.Route
.RootPortNum
;
2339 if (RouteChart
.Route
.RouteString
) {
2341 // The device is behind of hub device.
2343 ParentSlotId
= XhcRouteStringToSlotId(Xhc
, ParentRouteChart
);
2344 ASSERT (ParentSlotId
!= 0);
2346 //if the Full/Low device attached to a High Speed Hub, Init the TTPortNum and TTHubSlotId field of slot context
2348 ParentDeviceContext
= (DEVICE_CONTEXT_64
*)Xhc
->UsbDevContext
[ParentSlotId
].OutputContext
;
2349 if ((ParentDeviceContext
->Slot
.TTPortNum
== 0) &&
2350 (ParentDeviceContext
->Slot
.TTHubSlotId
== 0)) {
2351 if ((ParentDeviceContext
->Slot
.Speed
== (EFI_USB_SPEED_HIGH
+ 1)) && (DeviceSpeed
< EFI_USB_SPEED_HIGH
)) {
2353 // Full/Low device attached to High speed hub port that isolates the high speed signaling
2354 // environment from Full/Low speed signaling environment for a device
2356 InputContext
->Slot
.TTPortNum
= ParentPort
;
2357 InputContext
->Slot
.TTHubSlotId
= ParentSlotId
;
2361 // Inherit the TT parameters from parent device.
2363 InputContext
->Slot
.TTPortNum
= ParentDeviceContext
->Slot
.TTPortNum
;
2364 InputContext
->Slot
.TTHubSlotId
= ParentDeviceContext
->Slot
.TTHubSlotId
;
2366 // If the device is a High speed device then down the speed to be the same as its parent Hub
2368 if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2369 InputContext
->Slot
.Speed
= ParentDeviceContext
->Slot
.Speed
;
2375 // 4) Allocate and initialize the Transfer Ring for the Default Control Endpoint.
2377 EndpointTransferRing
= AllocateZeroPool (sizeof (TRANSFER_RING
));
2378 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0] = EndpointTransferRing
;
2379 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0]);
2381 // 5) Initialize the Input default control Endpoint 0 Context (6.2.3).
2383 InputContext
->EP
[0].EPType
= ED_CONTROL_BIDIR
;
2385 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2386 InputContext
->EP
[0].MaxPacketSize
= 512;
2387 } else if (DeviceSpeed
== EFI_USB_SPEED_HIGH
) {
2388 InputContext
->EP
[0].MaxPacketSize
= 64;
2390 InputContext
->EP
[0].MaxPacketSize
= 8;
2393 // Initial value of Average TRB Length for Control endpoints would be 8B, Interrupt endpoints
2394 // 1KB, and Bulk and Isoch endpoints 3KB.
2396 InputContext
->EP
[0].AverageTRBLength
= 8;
2397 InputContext
->EP
[0].MaxBurstSize
= 0;
2398 InputContext
->EP
[0].Interval
= 0;
2399 InputContext
->EP
[0].MaxPStreams
= 0;
2400 InputContext
->EP
[0].Mult
= 0;
2401 InputContext
->EP
[0].CErr
= 3;
2404 // Init the DCS(dequeue cycle state) as the transfer ring's CCS
2406 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2408 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[0])->RingSeg0
,
2409 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2411 InputContext
->EP
[0].PtrLo
= XHC_LOW_32BIT (PhyAddr
) | BIT0
;
2412 InputContext
->EP
[0].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2415 // 6) Allocate the Output Device Context data structure (6.2.1) and initialize it to '0'.
2417 OutputContext
= UsbHcAllocateMem (Xhc
->MemPool
, sizeof (DEVICE_CONTEXT_64
));
2418 ASSERT (OutputContext
!= NULL
);
2419 ASSERT (((UINTN
) OutputContext
& 0x3F) == 0);
2420 ZeroMem (OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2422 Xhc
->UsbDevContext
[SlotId
].OutputContext
= OutputContext
;
2424 // 7) Load the appropriate (Device Slot ID) entry in the Device Context Base Address Array (5.4.6) with
2425 // a pointer to the Output Device Context data structure (6.2.1).
2427 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2429 // Fill DCBAA with PCI device address
2431 Xhc
->DCBAA
[SlotId
] = (UINT64
) (UINTN
) PhyAddr
;
2434 // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input
2435 // Context data structure described above.
2437 // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request
2440 gBS
->Stall (XHC_RESET_RECOVERY_DELAY
);
2441 ZeroMem (&CmdTrbAddr
, sizeof (CmdTrbAddr
));
2442 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT_64
));
2443 CmdTrbAddr
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2444 CmdTrbAddr
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2445 CmdTrbAddr
.CycleBit
= 1;
2446 CmdTrbAddr
.Type
= TRB_TYPE_ADDRESS_DEV
;
2447 CmdTrbAddr
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
2448 Status
= XhcCmdTransfer (
2450 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbAddr
,
2451 XHC_GENERIC_TIMEOUT
,
2452 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2454 if (!EFI_ERROR (Status
)) {
2455 DeviceAddress
= (UINT8
) ((DEVICE_CONTEXT_64
*) OutputContext
)->Slot
.DeviceAddress
;
2456 DEBUG ((EFI_D_INFO
, " Address %d assigned successfully\n", DeviceAddress
));
2457 Xhc
->UsbDevContext
[SlotId
].XhciDevAddr
= DeviceAddress
;
2464 Disable the specified device slot.
2466 @param Xhc The XHCI Instance.
2467 @param SlotId The slot id to be disabled.
2469 @retval EFI_SUCCESS Successfully disable the device slot.
2475 IN USB_XHCI_INSTANCE
*Xhc
,
2480 TRB_TEMPLATE
*EvtTrb
;
2481 CMD_TRB_DISABLE_SLOT CmdTrbDisSlot
;
2486 // Disable the device slots occupied by these devices on its downstream ports.
2487 // Entry 0 is reserved.
2489 for (Index
= 0; Index
< 255; Index
++) {
2490 if (!Xhc
->UsbDevContext
[Index
+ 1].Enabled
||
2491 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
== 0) ||
2492 (Xhc
->UsbDevContext
[Index
+ 1].ParentRouteString
.Dword
!= Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
)) {
2496 Status
= XhcDisableSlotCmd (Xhc
, Xhc
->UsbDevContext
[Index
+ 1].SlotId
);
2498 if (EFI_ERROR (Status
)) {
2499 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
2500 Xhc
->UsbDevContext
[Index
+ 1].SlotId
= 0;
2505 // Construct the disable slot command
2507 DEBUG ((EFI_D_INFO
, "Disable device slot %d!\n", SlotId
));
2509 ZeroMem (&CmdTrbDisSlot
, sizeof (CmdTrbDisSlot
));
2510 CmdTrbDisSlot
.CycleBit
= 1;
2511 CmdTrbDisSlot
.Type
= TRB_TYPE_DIS_SLOT
;
2512 CmdTrbDisSlot
.SlotId
= SlotId
;
2513 Status
= XhcCmdTransfer (
2515 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbDisSlot
,
2516 XHC_GENERIC_TIMEOUT
,
2517 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2519 if (EFI_ERROR (Status
)) {
2520 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status
));
2524 // Free the slot's device context entry
2526 Xhc
->DCBAA
[SlotId
] = 0;
2529 // Free the slot related data structure
2531 for (Index
= 0; Index
< 31; Index
++) {
2532 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] != NULL
) {
2533 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
])->RingSeg0
;
2534 if (RingSeg
!= NULL
) {
2535 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
2537 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
]);
2538 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] = NULL
;
2542 for (Index
= 0; Index
< Xhc
->UsbDevContext
[SlotId
].DevDesc
.NumConfigurations
; Index
++) {
2543 if (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
] != NULL
) {
2544 FreePool (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
]);
2548 if (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
!= NULL
) {
2549 FreePool (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
);
2552 if (Xhc
->UsbDevContext
[SlotId
].InputContext
!= NULL
) {
2553 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT
));
2556 if (Xhc
->UsbDevContext
[SlotId
].OutputContext
!= NULL
) {
2557 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].OutputContext
, sizeof (DEVICE_CONTEXT
));
2560 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established
2561 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to
2562 // remove urb from XHCI's asynchronous transfer list.
2564 Xhc
->UsbDevContext
[SlotId
].Enabled
= FALSE
;
2565 Xhc
->UsbDevContext
[SlotId
].SlotId
= 0;
2571 Disable the specified device slot.
2573 @param Xhc The XHCI Instance.
2574 @param SlotId The slot id to be disabled.
2576 @retval EFI_SUCCESS Successfully disable the device slot.
2581 XhcDisableSlotCmd64 (
2582 IN USB_XHCI_INSTANCE
*Xhc
,
2587 TRB_TEMPLATE
*EvtTrb
;
2588 CMD_TRB_DISABLE_SLOT CmdTrbDisSlot
;
2593 // Disable the device slots occupied by these devices on its downstream ports.
2594 // Entry 0 is reserved.
2596 for (Index
= 0; Index
< 255; Index
++) {
2597 if (!Xhc
->UsbDevContext
[Index
+ 1].Enabled
||
2598 (Xhc
->UsbDevContext
[Index
+ 1].SlotId
== 0) ||
2599 (Xhc
->UsbDevContext
[Index
+ 1].ParentRouteString
.Dword
!= Xhc
->UsbDevContext
[SlotId
].RouteString
.Dword
)) {
2603 Status
= XhcDisableSlotCmd64 (Xhc
, Xhc
->UsbDevContext
[Index
+ 1].SlotId
);
2605 if (EFI_ERROR (Status
)) {
2606 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: failed to disable child, ignore error\n"));
2607 Xhc
->UsbDevContext
[Index
+ 1].SlotId
= 0;
2612 // Construct the disable slot command
2614 DEBUG ((EFI_D_INFO
, "Disable device slot %d!\n", SlotId
));
2616 ZeroMem (&CmdTrbDisSlot
, sizeof (CmdTrbDisSlot
));
2617 CmdTrbDisSlot
.CycleBit
= 1;
2618 CmdTrbDisSlot
.Type
= TRB_TYPE_DIS_SLOT
;
2619 CmdTrbDisSlot
.SlotId
= SlotId
;
2620 Status
= XhcCmdTransfer (
2622 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbDisSlot
,
2623 XHC_GENERIC_TIMEOUT
,
2624 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
2626 if (EFI_ERROR (Status
)) {
2627 DEBUG ((EFI_D_ERROR
, "XhcDisableSlotCmd: Disable Slot Command Failed, Status = %r\n", Status
));
2631 // Free the slot's device context entry
2633 Xhc
->DCBAA
[SlotId
] = 0;
2636 // Free the slot related data structure
2638 for (Index
= 0; Index
< 31; Index
++) {
2639 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] != NULL
) {
2640 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
])->RingSeg0
;
2641 if (RingSeg
!= NULL
) {
2642 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
2644 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
]);
2645 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Index
] = NULL
;
2649 for (Index
= 0; Index
< Xhc
->UsbDevContext
[SlotId
].DevDesc
.NumConfigurations
; Index
++) {
2650 if (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
] != NULL
) {
2651 FreePool (Xhc
->UsbDevContext
[SlotId
].ConfDesc
[Index
]);
2655 if (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
!= NULL
) {
2656 FreePool (Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
);
2659 if (Xhc
->UsbDevContext
[SlotId
].InputContext
!= NULL
) {
2660 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].InputContext
, sizeof (INPUT_CONTEXT_64
));
2663 if (Xhc
->UsbDevContext
[SlotId
].OutputContext
!= NULL
) {
2664 UsbHcFreeMem (Xhc
->MemPool
, Xhc
->UsbDevContext
[SlotId
].OutputContext
, sizeof (DEVICE_CONTEXT_64
));
2667 // Doesn't zero the entry because XhcAsyncInterruptTransfer() may be invoked to remove the established
2668 // asynchronous interrupt pipe after the device is disabled. It needs the device address mapping info to
2669 // remove urb from XHCI's asynchronous transfer list.
2671 Xhc
->UsbDevContext
[SlotId
].Enabled
= FALSE
;
2672 Xhc
->UsbDevContext
[SlotId
].SlotId
= 0;
2678 Initialize endpoint context in input context.
2680 @param Xhc The XHCI Instance.
2681 @param SlotId The slot id to be configured.
2682 @param DeviceSpeed The device's speed.
2683 @param InputContext The pointer to the input context.
2684 @param IfDesc The pointer to the usb device interface descriptor.
2686 @return The maximum device context index of endpoint.
2691 XhcInitializeEndpointContext (
2692 IN USB_XHCI_INSTANCE
*Xhc
,
2694 IN UINT8 DeviceSpeed
,
2695 IN INPUT_CONTEXT
*InputContext
,
2696 IN USB_INTERFACE_DESCRIPTOR
*IfDesc
2699 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
2706 EFI_PHYSICAL_ADDRESS PhyAddr
;
2708 TRANSFER_RING
*EndpointTransferRing
;
2712 NumEp
= IfDesc
->NumEndpoints
;
2714 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDesc
+ 1);
2715 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
2716 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
2717 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2720 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
2721 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2725 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
2726 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
2728 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
2734 InputContext
->InputControlContext
.Dword2
|= (BIT0
<< Dci
);
2735 InputContext
->EP
[Dci
-1].MaxPacketSize
= EpDesc
->MaxPacketSize
;
2737 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2739 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.
2741 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2743 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2746 switch (EpDesc
->Attributes
& USB_ENDPOINT_TYPE_MASK
) {
2747 case USB_ENDPOINT_BULK
:
2748 if (Direction
== EfiUsbDataIn
) {
2749 InputContext
->EP
[Dci
-1].CErr
= 3;
2750 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_IN
;
2752 InputContext
->EP
[Dci
-1].CErr
= 3;
2753 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_OUT
;
2756 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2757 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2758 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
2759 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
2760 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2761 DEBUG ((DEBUG_INFO
, "Endpoint[%x]: Created BULK ring [%p~%p)\n",
2762 EpDesc
->EndpointAddress
,
2763 EndpointTransferRing
->RingSeg0
,
2764 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2769 case USB_ENDPOINT_ISO
:
2770 if (Direction
== EfiUsbDataIn
) {
2771 InputContext
->EP
[Dci
-1].CErr
= 0;
2772 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_IN
;
2774 InputContext
->EP
[Dci
-1].CErr
= 0;
2775 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_OUT
;
2778 // Get the bInterval from descriptor and init the the interval field of endpoint context.
2779 // Refer to XHCI 1.1 spec section 6.2.3.6.
2781 if (DeviceSpeed
== EFI_USB_SPEED_FULL
) {
2782 Interval
= EpDesc
->Interval
;
2783 ASSERT (Interval
>= 1 && Interval
<= 16);
2784 InputContext
->EP
[Dci
-1].Interval
= Interval
+ 2;
2785 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2786 Interval
= EpDesc
->Interval
;
2787 ASSERT (Interval
>= 1 && Interval
<= 16);
2788 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2792 // Do not support isochronous transfer now.
2794 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
2795 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2797 case USB_ENDPOINT_INTERRUPT
:
2798 if (Direction
== EfiUsbDataIn
) {
2799 InputContext
->EP
[Dci
-1].CErr
= 3;
2800 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_IN
;
2802 InputContext
->EP
[Dci
-1].CErr
= 3;
2803 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_OUT
;
2805 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2806 InputContext
->EP
[Dci
-1].MaxESITPayload
= EpDesc
->MaxPacketSize
;
2808 // Get the bInterval from descriptor and init the the interval field of endpoint context
2810 if ((DeviceSpeed
== EFI_USB_SPEED_FULL
) || (DeviceSpeed
== EFI_USB_SPEED_LOW
)) {
2811 Interval
= EpDesc
->Interval
;
2813 // Calculate through the bInterval field of Endpoint descriptor.
2815 ASSERT (Interval
!= 0);
2816 InputContext
->EP
[Dci
-1].Interval
= (UINT32
)HighBitSet32((UINT32
)Interval
) + 3;
2817 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2818 Interval
= EpDesc
->Interval
;
2819 ASSERT (Interval
>= 1 && Interval
<= 16);
2821 // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
2823 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2824 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2825 InputContext
->EP
[Dci
-1].MaxESITPayload
= 0x0002;
2826 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2827 InputContext
->EP
[Dci
-1].CErr
= 3;
2830 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2831 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
2832 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
2833 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2834 DEBUG ((DEBUG_INFO
, "Endpoint[%x]: Created INT ring [%p~%p)\n",
2835 EpDesc
->EndpointAddress
,
2836 EndpointTransferRing
->RingSeg0
,
2837 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2842 case USB_ENDPOINT_CONTROL
:
2844 // Do not support control transfer now.
2846 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext: Unsupport Control EP found, Transfer ring is not allocated.\n"));
2848 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext: Unknown EP found, Transfer ring is not allocated.\n"));
2849 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2853 PhyAddr
= UsbHcGetPciAddrForHostAddr (
2855 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingSeg0
,
2856 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
2858 PhyAddr
&= ~((EFI_PHYSICAL_ADDRESS
)0x0F);
2859 PhyAddr
|= (EFI_PHYSICAL_ADDRESS
)((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingPCS
;
2860 InputContext
->EP
[Dci
-1].PtrLo
= XHC_LOW_32BIT (PhyAddr
);
2861 InputContext
->EP
[Dci
-1].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
2863 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2870 Initialize endpoint context in input context.
2872 @param Xhc The XHCI Instance.
2873 @param SlotId The slot id to be configured.
2874 @param DeviceSpeed The device's speed.
2875 @param InputContext The pointer to the input context.
2876 @param IfDesc The pointer to the usb device interface descriptor.
2878 @return The maximum device context index of endpoint.
2883 XhcInitializeEndpointContext64 (
2884 IN USB_XHCI_INSTANCE
*Xhc
,
2886 IN UINT8 DeviceSpeed
,
2887 IN INPUT_CONTEXT_64
*InputContext
,
2888 IN USB_INTERFACE_DESCRIPTOR
*IfDesc
2891 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
2898 EFI_PHYSICAL_ADDRESS PhyAddr
;
2900 TRANSFER_RING
*EndpointTransferRing
;
2904 NumEp
= IfDesc
->NumEndpoints
;
2906 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)(IfDesc
+ 1);
2907 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
2908 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
2909 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2912 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
2913 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2917 EpAddr
= (UINT8
)(EpDesc
->EndpointAddress
& 0x0F);
2918 Direction
= (UINT8
)((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
2920 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
2926 InputContext
->InputControlContext
.Dword2
|= (BIT0
<< Dci
);
2927 InputContext
->EP
[Dci
-1].MaxPacketSize
= EpDesc
->MaxPacketSize
;
2929 if (DeviceSpeed
== EFI_USB_SPEED_SUPER
) {
2931 // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor.
2933 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2935 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
2938 switch (EpDesc
->Attributes
& USB_ENDPOINT_TYPE_MASK
) {
2939 case USB_ENDPOINT_BULK
:
2940 if (Direction
== EfiUsbDataIn
) {
2941 InputContext
->EP
[Dci
-1].CErr
= 3;
2942 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_IN
;
2944 InputContext
->EP
[Dci
-1].CErr
= 3;
2945 InputContext
->EP
[Dci
-1].EPType
= ED_BULK_OUT
;
2948 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2949 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
2950 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
2951 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
2952 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
2953 DEBUG ((DEBUG_INFO
, "Endpoint64[%x]: Created BULK ring [%p~%p)\n",
2954 EpDesc
->EndpointAddress
,
2955 EndpointTransferRing
->RingSeg0
,
2956 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
2961 case USB_ENDPOINT_ISO
:
2962 if (Direction
== EfiUsbDataIn
) {
2963 InputContext
->EP
[Dci
-1].CErr
= 0;
2964 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_IN
;
2966 InputContext
->EP
[Dci
-1].CErr
= 0;
2967 InputContext
->EP
[Dci
-1].EPType
= ED_ISOCH_OUT
;
2970 // Get the bInterval from descriptor and init the the interval field of endpoint context.
2971 // Refer to XHCI 1.1 spec section 6.2.3.6.
2973 if (DeviceSpeed
== EFI_USB_SPEED_FULL
) {
2974 Interval
= EpDesc
->Interval
;
2975 ASSERT (Interval
>= 1 && Interval
<= 16);
2976 InputContext
->EP
[Dci
-1].Interval
= Interval
+ 2;
2977 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
2978 Interval
= EpDesc
->Interval
;
2979 ASSERT (Interval
>= 1 && Interval
<= 16);
2980 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
2984 // Do not support isochronous transfer now.
2986 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext64: Unsupport ISO EP found, Transfer ring is not allocated.\n"));
2987 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
2989 case USB_ENDPOINT_INTERRUPT
:
2990 if (Direction
== EfiUsbDataIn
) {
2991 InputContext
->EP
[Dci
-1].CErr
= 3;
2992 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_IN
;
2994 InputContext
->EP
[Dci
-1].CErr
= 3;
2995 InputContext
->EP
[Dci
-1].EPType
= ED_INTERRUPT_OUT
;
2997 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
2998 InputContext
->EP
[Dci
-1].MaxESITPayload
= EpDesc
->MaxPacketSize
;
3000 // Get the bInterval from descriptor and init the the interval field of endpoint context
3002 if ((DeviceSpeed
== EFI_USB_SPEED_FULL
) || (DeviceSpeed
== EFI_USB_SPEED_LOW
)) {
3003 Interval
= EpDesc
->Interval
;
3005 // Calculate through the bInterval field of Endpoint descriptor.
3007 ASSERT (Interval
!= 0);
3008 InputContext
->EP
[Dci
-1].Interval
= (UINT32
)HighBitSet32((UINT32
)Interval
) + 3;
3009 } else if ((DeviceSpeed
== EFI_USB_SPEED_HIGH
) || (DeviceSpeed
== EFI_USB_SPEED_SUPER
)) {
3010 Interval
= EpDesc
->Interval
;
3011 ASSERT (Interval
>= 1 && Interval
<= 16);
3013 // Refer to XHCI 1.0 spec section 6.2.3.6, table 61
3015 InputContext
->EP
[Dci
-1].Interval
= Interval
- 1;
3016 InputContext
->EP
[Dci
-1].AverageTRBLength
= 0x1000;
3017 InputContext
->EP
[Dci
-1].MaxESITPayload
= 0x0002;
3018 InputContext
->EP
[Dci
-1].MaxBurstSize
= 0x0;
3019 InputContext
->EP
[Dci
-1].CErr
= 3;
3022 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] == NULL
) {
3023 EndpointTransferRing
= AllocateZeroPool(sizeof (TRANSFER_RING
));
3024 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1] = (VOID
*) EndpointTransferRing
;
3025 CreateTransferRing(Xhc
, TR_RING_TRB_NUMBER
, (TRANSFER_RING
*)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1]);
3026 DEBUG ((DEBUG_INFO
, "Endpoint64[%x]: Created INT ring [%p~%p)\n",
3027 EpDesc
->EndpointAddress
,
3028 EndpointTransferRing
->RingSeg0
,
3029 (UINTN
) EndpointTransferRing
->RingSeg0
+ TR_RING_TRB_NUMBER
* sizeof (TRB_TEMPLATE
)
3034 case USB_ENDPOINT_CONTROL
:
3036 // Do not support control transfer now.
3038 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext64: Unsupport Control EP found, Transfer ring is not allocated.\n"));
3040 DEBUG ((EFI_D_INFO
, "XhcInitializeEndpointContext64: Unknown EP found, Transfer ring is not allocated.\n"));
3041 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3045 PhyAddr
= UsbHcGetPciAddrForHostAddr (
3047 ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingSeg0
,
3048 sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
3050 PhyAddr
&= ~((EFI_PHYSICAL_ADDRESS
)0x0F);
3051 PhyAddr
|= (EFI_PHYSICAL_ADDRESS
)((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
-1])->RingPCS
;
3052 InputContext
->EP
[Dci
-1].PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3053 InputContext
->EP
[Dci
-1].PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3055 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3062 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
3064 @param Xhc The XHCI Instance.
3065 @param SlotId The slot id to be configured.
3066 @param DeviceSpeed The device's speed.
3067 @param ConfigDesc The pointer to the usb device configuration descriptor.
3069 @retval EFI_SUCCESS Successfully configure all the device endpoints.
3075 IN USB_XHCI_INSTANCE
*Xhc
,
3077 IN UINT8 DeviceSpeed
,
3078 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
3082 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3086 EFI_PHYSICAL_ADDRESS PhyAddr
;
3088 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3089 INPUT_CONTEXT
*InputContext
;
3090 DEVICE_CONTEXT
*OutputContext
;
3091 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3093 // 4.6.6 Configure Endpoint
3095 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3096 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3097 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3098 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT
));
3100 ASSERT (ConfigDesc
!= NULL
);
3104 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3105 for (Index
= 0; Index
< ConfigDesc
->NumInterfaces
; Index
++) {
3106 while ((IfDesc
->DescriptorType
!= USB_DESC_TYPE_INTERFACE
) || (IfDesc
->AlternateSetting
!= 0)) {
3107 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3110 if (IfDesc
->Length
< sizeof (USB_INTERFACE_DESCRIPTOR
)) {
3111 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3115 Dci
= XhcInitializeEndpointContext (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDesc
);
3120 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3123 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3124 InputContext
->Slot
.ContextEntries
= MaxDci
;
3126 // configure endpoint
3128 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3129 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3130 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3131 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3132 CmdTrbCfgEP
.CycleBit
= 1;
3133 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3134 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3135 DEBUG ((EFI_D_INFO
, "Configure Endpoint\n"));
3136 Status
= XhcCmdTransfer (
3138 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3139 XHC_GENERIC_TIMEOUT
,
3140 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3142 if (EFI_ERROR (Status
)) {
3143 DEBUG ((EFI_D_ERROR
, "XhcSetConfigCmd: Config Endpoint Failed, Status = %r\n", Status
));
3145 Xhc
->UsbDevContext
[SlotId
].ActiveConfiguration
= ConfigDesc
->ConfigurationValue
;
3152 Configure all the device endpoints through XHCI's Configure_Endpoint cmd.
3154 @param Xhc The XHCI Instance.
3155 @param SlotId The slot id to be configured.
3156 @param DeviceSpeed The device's speed.
3157 @param ConfigDesc The pointer to the usb device configuration descriptor.
3159 @retval EFI_SUCCESS Successfully configure all the device endpoints.
3165 IN USB_XHCI_INSTANCE
*Xhc
,
3167 IN UINT8 DeviceSpeed
,
3168 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
3172 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3176 EFI_PHYSICAL_ADDRESS PhyAddr
;
3178 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3179 INPUT_CONTEXT_64
*InputContext
;
3180 DEVICE_CONTEXT_64
*OutputContext
;
3181 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3183 // 4.6.6 Configure Endpoint
3185 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3186 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3187 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3188 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT_64
));
3190 ASSERT (ConfigDesc
!= NULL
);
3194 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3195 for (Index
= 0; Index
< ConfigDesc
->NumInterfaces
; Index
++) {
3196 while ((IfDesc
->DescriptorType
!= USB_DESC_TYPE_INTERFACE
) || (IfDesc
->AlternateSetting
!= 0)) {
3197 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3200 if (IfDesc
->Length
< sizeof (USB_INTERFACE_DESCRIPTOR
)) {
3201 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3205 Dci
= XhcInitializeEndpointContext64 (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDesc
);
3210 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3213 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3214 InputContext
->Slot
.ContextEntries
= MaxDci
;
3216 // configure endpoint
3218 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3219 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3220 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3221 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3222 CmdTrbCfgEP
.CycleBit
= 1;
3223 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3224 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3225 DEBUG ((EFI_D_INFO
, "Configure Endpoint\n"));
3226 Status
= XhcCmdTransfer (
3228 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3229 XHC_GENERIC_TIMEOUT
,
3230 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3232 if (EFI_ERROR (Status
)) {
3233 DEBUG ((EFI_D_ERROR
, "XhcSetConfigCmd64: Config Endpoint Failed, Status = %r\n", Status
));
3235 Xhc
->UsbDevContext
[SlotId
].ActiveConfiguration
= ConfigDesc
->ConfigurationValue
;
3242 Stop endpoint through XHCI's Stop_Endpoint cmd.
3244 @param Xhc The XHCI Instance.
3245 @param SlotId The slot id to be configured.
3246 @param Dci The device context index of endpoint.
3247 @param PendingUrb The pending URB to check completion status when stopping the end point.
3249 @retval EFI_SUCCESS Stop endpoint successfully.
3250 @retval Others Failed to stop endpoint.
3256 IN USB_XHCI_INSTANCE
*Xhc
,
3259 IN URB
*PendingUrb OPTIONAL
3263 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3264 CMD_TRB_STOP_ENDPOINT CmdTrbStopED
;
3266 DEBUG ((EFI_D_INFO
, "XhcStopEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId
, Dci
));
3269 // When XhcCheckUrbResult waits for the Stop_Endpoint completion, it also checks
3270 // the PendingUrb completion status, because it's possible that the PendingUrb is
3271 // finished just before stopping the end point, but after the looping check.
3273 // The PendingUrb could be passed to XhcCmdTransfer to XhcExecTransfer to XhcCheckUrbResult
3274 // through function parameter, but That will cause every consumer of XhcCmdTransfer,
3275 // XhcExecTransfer and XhcCheckUrbResult pass a NULL PendingUrb.
3276 // But actually only XhcCheckUrbResult is aware of the PendingUrb.
3277 // So we choose to save the PendingUrb into the USB_XHCI_INSTANCE and use it in XhcCheckUrbResult.
3279 ASSERT (Xhc
->PendingUrb
== NULL
);
3280 Xhc
->PendingUrb
= PendingUrb
;
3282 // Reset the URB result from Timeout to NoError.
3283 // The USB result will be:
3284 // changed to Timeout when Stop/StopInvalidLength Transfer Event is received, or
3285 // remain NoError when Success/ShortPacket Transfer Event is received.
3287 if (PendingUrb
!= NULL
) {
3288 PendingUrb
->Result
= EFI_USB_NOERROR
;
3292 // Send stop endpoint command to transit Endpoint from running to stop state
3294 ZeroMem (&CmdTrbStopED
, sizeof (CmdTrbStopED
));
3295 CmdTrbStopED
.CycleBit
= 1;
3296 CmdTrbStopED
.Type
= TRB_TYPE_STOP_ENDPOINT
;
3297 CmdTrbStopED
.EDID
= Dci
;
3298 CmdTrbStopED
.SlotId
= SlotId
;
3299 Status
= XhcCmdTransfer (
3301 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbStopED
,
3302 XHC_GENERIC_TIMEOUT
,
3303 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3305 if (EFI_ERROR(Status
)) {
3306 DEBUG ((EFI_D_ERROR
, "XhcStopEndpoint: Stop Endpoint Failed, Status = %r\n", Status
));
3309 Xhc
->PendingUrb
= NULL
;
3315 Reset endpoint through XHCI's Reset_Endpoint cmd.
3317 @param Xhc The XHCI Instance.
3318 @param SlotId The slot id to be configured.
3319 @param Dci The device context index of endpoint.
3321 @retval EFI_SUCCESS Reset endpoint successfully.
3322 @retval Others Failed to reset endpoint.
3328 IN USB_XHCI_INSTANCE
*Xhc
,
3334 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3335 CMD_TRB_RESET_ENDPOINT CmdTrbResetED
;
3337 DEBUG ((EFI_D_INFO
, "XhcResetEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId
, Dci
));
3340 // Send stop endpoint command to transit Endpoint from running to stop state
3342 ZeroMem (&CmdTrbResetED
, sizeof (CmdTrbResetED
));
3343 CmdTrbResetED
.CycleBit
= 1;
3344 CmdTrbResetED
.Type
= TRB_TYPE_RESET_ENDPOINT
;
3345 CmdTrbResetED
.EDID
= Dci
;
3346 CmdTrbResetED
.SlotId
= SlotId
;
3347 Status
= XhcCmdTransfer (
3349 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbResetED
,
3350 XHC_GENERIC_TIMEOUT
,
3351 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3353 if (EFI_ERROR(Status
)) {
3354 DEBUG ((EFI_D_ERROR
, "XhcResetEndpoint: Reset Endpoint Failed, Status = %r\n", Status
));
3361 Set transfer ring dequeue pointer through XHCI's Set_Tr_Dequeue_Pointer cmd.
3363 @param Xhc The XHCI Instance.
3364 @param SlotId The slot id to be configured.
3365 @param Dci The device context index of endpoint.
3366 @param Urb The dequeue pointer of the transfer ring specified
3367 by the urb to be updated.
3369 @retval EFI_SUCCESS Set transfer ring dequeue pointer succeeds.
3370 @retval Others Failed to set transfer ring dequeue pointer.
3375 XhcSetTrDequeuePointer (
3376 IN USB_XHCI_INSTANCE
*Xhc
,
3383 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3384 CMD_SET_TR_DEQ_POINTER CmdSetTRDeq
;
3385 EFI_PHYSICAL_ADDRESS PhyAddr
;
3387 DEBUG ((EFI_D_INFO
, "XhcSetTrDequeuePointer: Slot = 0x%x, Dci = 0x%x, Urb = 0x%x\n", SlotId
, Dci
, Urb
));
3390 // Send stop endpoint command to transit Endpoint from running to stop state
3392 ZeroMem (&CmdSetTRDeq
, sizeof (CmdSetTRDeq
));
3393 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, Urb
->Ring
->RingEnqueue
, sizeof (CMD_SET_TR_DEQ_POINTER
));
3394 CmdSetTRDeq
.PtrLo
= XHC_LOW_32BIT (PhyAddr
) | Urb
->Ring
->RingPCS
;
3395 CmdSetTRDeq
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3396 CmdSetTRDeq
.CycleBit
= 1;
3397 CmdSetTRDeq
.Type
= TRB_TYPE_SET_TR_DEQUE
;
3398 CmdSetTRDeq
.Endpoint
= Dci
;
3399 CmdSetTRDeq
.SlotId
= SlotId
;
3400 Status
= XhcCmdTransfer (
3402 (TRB_TEMPLATE
*) (UINTN
) &CmdSetTRDeq
,
3403 XHC_GENERIC_TIMEOUT
,
3404 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3406 if (EFI_ERROR(Status
)) {
3407 DEBUG ((EFI_D_ERROR
, "XhcSetTrDequeuePointer: Set TR Dequeue Pointer Failed, Status = %r\n", Status
));
3414 Set interface through XHCI's Configure_Endpoint cmd.
3416 @param Xhc The XHCI Instance.
3417 @param SlotId The slot id to be configured.
3418 @param DeviceSpeed The device's speed.
3419 @param ConfigDesc The pointer to the usb device configuration descriptor.
3420 @param Request USB device request to send.
3422 @retval EFI_SUCCESS Successfully set interface.
3428 IN USB_XHCI_INSTANCE
*Xhc
,
3430 IN UINT8 DeviceSpeed
,
3431 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
,
3432 IN EFI_USB_DEVICE_REQUEST
*Request
3436 USB_INTERFACE_DESCRIPTOR
*IfDescActive
;
3437 USB_INTERFACE_DESCRIPTOR
*IfDescSet
;
3438 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3439 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3446 EFI_PHYSICAL_ADDRESS PhyAddr
;
3449 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3450 INPUT_CONTEXT
*InputContext
;
3451 DEVICE_CONTEXT
*OutputContext
;
3452 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3454 Status
= EFI_SUCCESS
;
3456 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3457 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3459 // XHCI 4.6.6 Configure Endpoint
3460 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3461 // Context and Add Context flags as follows:
3462 // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
3463 // Context and Add Context flags to '0'.
3465 // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
3466 // So the default Drop Context and Add Context flags can be '0' to cover 1).
3468 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3469 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT
));
3471 ASSERT (ConfigDesc
!= NULL
);
3475 IfDescActive
= NULL
;
3478 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3479 while ((UINTN
) IfDesc
< ((UINTN
) ConfigDesc
+ ConfigDesc
->TotalLength
)) {
3480 if ((IfDesc
->DescriptorType
== USB_DESC_TYPE_INTERFACE
) && (IfDesc
->Length
>= sizeof (USB_INTERFACE_DESCRIPTOR
))) {
3481 if (IfDesc
->InterfaceNumber
== (UINT8
) Request
->Index
) {
3482 if (IfDesc
->AlternateSetting
== Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[IfDesc
->InterfaceNumber
]) {
3484 // Find out the active interface descriptor.
3486 IfDescActive
= IfDesc
;
3487 } else if (IfDesc
->AlternateSetting
== (UINT8
) Request
->Value
) {
3489 // Find out the interface descriptor to set.
3495 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3499 // XHCI 4.6.6 Configure Endpoint
3500 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3501 // Context and Add Context flags as follows:
3502 // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
3503 // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
3504 // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
3505 // the Drop Context flag to '1' and Add Context flag to '0'.
3506 // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
3507 // and Add Context flags shall be set to '1'.
3509 // Below codes are to cover 2), 3) and 4).
3512 if ((IfDescActive
!= NULL
) && (IfDescSet
!= NULL
)) {
3513 NumEp
= IfDescActive
->NumEndpoints
;
3514 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*) (IfDescActive
+ 1);
3515 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3516 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3517 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3520 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3521 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3525 EpAddr
= (UINT8
) (EpDesc
->EndpointAddress
& 0x0F);
3526 Direction
= (UINT8
) ((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3528 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3534 // XHCI 4.3.6 - Setting Alternate Interfaces
3535 // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
3537 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, NULL
);
3538 if (EFI_ERROR (Status
)) {
3542 // XHCI 4.3.6 - Setting Alternate Interfaces
3543 // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
3545 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] != NULL
) {
3546 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1])->RingSeg0
;
3547 if (RingSeg
!= NULL
) {
3548 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
3550 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1]);
3551 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] = NULL
;
3555 // Set the Drop Context flag to '1'.
3557 InputContext
->InputControlContext
.Dword1
|= (BIT0
<< Dci
);
3559 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3563 // XHCI 4.3.6 - Setting Alternate Interfaces
3564 // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
3565 // Interface setting, to '0'.
3567 // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
3571 // XHCI 4.3.6 - Setting Alternate Interfaces
3572 // 4) For each endpoint enabled by the Configure Endpoint Command:
3573 // a. Allocate a Transfer Ring.
3574 // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
3575 // c. Initialize the Endpoint Context data structure.
3577 Dci
= XhcInitializeEndpointContext (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDescSet
);
3582 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3583 InputContext
->Slot
.ContextEntries
= MaxDci
;
3585 // XHCI 4.3.6 - Setting Alternate Interfaces
3586 // 5) Issue and successfully complete a Configure Endpoint Command.
3588 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3589 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3590 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3591 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3592 CmdTrbCfgEP
.CycleBit
= 1;
3593 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3594 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3595 DEBUG ((EFI_D_INFO
, "SetInterface: Configure Endpoint\n"));
3596 Status
= XhcCmdTransfer (
3598 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3599 XHC_GENERIC_TIMEOUT
,
3600 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3602 if (EFI_ERROR (Status
)) {
3603 DEBUG ((EFI_D_ERROR
, "SetInterface: Config Endpoint Failed, Status = %r\n", Status
));
3606 // Update the active AlternateSetting.
3608 Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[(UINT8
) Request
->Index
] = (UINT8
) Request
->Value
;
3616 Set interface through XHCI's Configure_Endpoint cmd.
3618 @param Xhc The XHCI Instance.
3619 @param SlotId The slot id to be configured.
3620 @param DeviceSpeed The device's speed.
3621 @param ConfigDesc The pointer to the usb device configuration descriptor.
3622 @param Request USB device request to send.
3624 @retval EFI_SUCCESS Successfully set interface.
3630 IN USB_XHCI_INSTANCE
*Xhc
,
3632 IN UINT8 DeviceSpeed
,
3633 IN USB_CONFIG_DESCRIPTOR
*ConfigDesc
,
3634 IN EFI_USB_DEVICE_REQUEST
*Request
3638 USB_INTERFACE_DESCRIPTOR
*IfDescActive
;
3639 USB_INTERFACE_DESCRIPTOR
*IfDescSet
;
3640 USB_INTERFACE_DESCRIPTOR
*IfDesc
;
3641 USB_ENDPOINT_DESCRIPTOR
*EpDesc
;
3648 EFI_PHYSICAL_ADDRESS PhyAddr
;
3651 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3652 INPUT_CONTEXT_64
*InputContext
;
3653 DEVICE_CONTEXT_64
*OutputContext
;
3654 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3656 Status
= EFI_SUCCESS
;
3658 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3659 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3661 // XHCI 4.6.6 Configure Endpoint
3662 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3663 // Context and Add Context flags as follows:
3664 // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop
3665 // Context and Add Context flags to '0'.
3667 // Except the interface indicated by Reqeust->Index, no impact to other interfaces.
3668 // So the default Drop Context and Add Context flags can be '0' to cover 1).
3670 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3671 CopyMem (&InputContext
->Slot
, &OutputContext
->Slot
, sizeof (SLOT_CONTEXT_64
));
3673 ASSERT (ConfigDesc
!= NULL
);
3677 IfDescActive
= NULL
;
3680 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)(ConfigDesc
+ 1);
3681 while ((UINTN
) IfDesc
< ((UINTN
) ConfigDesc
+ ConfigDesc
->TotalLength
)) {
3682 if ((IfDesc
->DescriptorType
== USB_DESC_TYPE_INTERFACE
) && (IfDesc
->Length
>= sizeof (USB_INTERFACE_DESCRIPTOR
))) {
3683 if (IfDesc
->InterfaceNumber
== (UINT8
) Request
->Index
) {
3684 if (IfDesc
->AlternateSetting
== Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[IfDesc
->InterfaceNumber
]) {
3686 // Find out the active interface descriptor.
3688 IfDescActive
= IfDesc
;
3689 } else if (IfDesc
->AlternateSetting
== (UINT8
) Request
->Value
) {
3691 // Find out the interface descriptor to set.
3697 IfDesc
= (USB_INTERFACE_DESCRIPTOR
*)((UINTN
)IfDesc
+ IfDesc
->Length
);
3701 // XHCI 4.6.6 Configure Endpoint
3702 // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop
3703 // Context and Add Context flags as follows:
3704 // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set
3705 // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context.
3706 // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set
3707 // the Drop Context flag to '1' and Add Context flag to '0'.
3708 // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context
3709 // and Add Context flags shall be set to '1'.
3711 // Below codes are to cover 2), 3) and 4).
3714 if ((IfDescActive
!= NULL
) && (IfDescSet
!= NULL
)) {
3715 NumEp
= IfDescActive
->NumEndpoints
;
3716 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*) (IfDescActive
+ 1);
3717 for (EpIndex
= 0; EpIndex
< NumEp
; EpIndex
++) {
3718 while (EpDesc
->DescriptorType
!= USB_DESC_TYPE_ENDPOINT
) {
3719 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3722 if (EpDesc
->Length
< sizeof (USB_ENDPOINT_DESCRIPTOR
)) {
3723 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3727 EpAddr
= (UINT8
) (EpDesc
->EndpointAddress
& 0x0F);
3728 Direction
= (UINT8
) ((EpDesc
->EndpointAddress
& 0x80) ? EfiUsbDataIn
: EfiUsbDataOut
);
3730 Dci
= XhcEndpointToDci (EpAddr
, Direction
);
3736 // XHCI 4.3.6 - Setting Alternate Interfaces
3737 // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting.
3739 Status
= XhcStopEndpoint (Xhc
, SlotId
, Dci
, NULL
);
3740 if (EFI_ERROR (Status
)) {
3744 // XHCI 4.3.6 - Setting Alternate Interfaces
3745 // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting.
3747 if (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] != NULL
) {
3748 RingSeg
= ((TRANSFER_RING
*)(UINTN
)Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1])->RingSeg0
;
3749 if (RingSeg
!= NULL
) {
3750 UsbHcFreeMem (Xhc
->MemPool
, RingSeg
, sizeof (TRB_TEMPLATE
) * TR_RING_TRB_NUMBER
);
3752 FreePool (Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1]);
3753 Xhc
->UsbDevContext
[SlotId
].EndpointTransferRing
[Dci
- 1] = NULL
;
3757 // Set the Drop Context flag to '1'.
3759 InputContext
->InputControlContext
.Dword1
|= (BIT0
<< Dci
);
3761 EpDesc
= (USB_ENDPOINT_DESCRIPTOR
*)((UINTN
)EpDesc
+ EpDesc
->Length
);
3765 // XHCI 4.3.6 - Setting Alternate Interfaces
3766 // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate
3767 // Interface setting, to '0'.
3769 // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function.
3773 // XHCI 4.3.6 - Setting Alternate Interfaces
3774 // 4) For each endpoint enabled by the Configure Endpoint Command:
3775 // a. Allocate a Transfer Ring.
3776 // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'.
3777 // c. Initialize the Endpoint Context data structure.
3779 Dci
= XhcInitializeEndpointContext64 (Xhc
, SlotId
, DeviceSpeed
, InputContext
, IfDescSet
);
3784 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3785 InputContext
->Slot
.ContextEntries
= MaxDci
;
3787 // XHCI 4.3.6 - Setting Alternate Interfaces
3788 // 5) Issue and successfully complete a Configure Endpoint Command.
3790 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3791 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3792 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3793 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3794 CmdTrbCfgEP
.CycleBit
= 1;
3795 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3796 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3797 DEBUG ((EFI_D_INFO
, "SetInterface64: Configure Endpoint\n"));
3798 Status
= XhcCmdTransfer (
3800 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3801 XHC_GENERIC_TIMEOUT
,
3802 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3804 if (EFI_ERROR (Status
)) {
3805 DEBUG ((EFI_D_ERROR
, "SetInterface64: Config Endpoint Failed, Status = %r\n", Status
));
3808 // Update the active AlternateSetting.
3810 Xhc
->UsbDevContext
[SlotId
].ActiveAlternateSetting
[(UINT8
) Request
->Index
] = (UINT8
) Request
->Value
;
3818 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
3820 @param Xhc The XHCI Instance.
3821 @param SlotId The slot id to be evaluated.
3822 @param MaxPacketSize The max packet size supported by the device control transfer.
3824 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.
3829 XhcEvaluateContext (
3830 IN USB_XHCI_INSTANCE
*Xhc
,
3832 IN UINT32 MaxPacketSize
3836 CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu
;
3837 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3838 INPUT_CONTEXT
*InputContext
;
3839 EFI_PHYSICAL_ADDRESS PhyAddr
;
3841 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3844 // 4.6.7 Evaluate Context
3846 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3847 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3849 InputContext
->InputControlContext
.Dword2
|= BIT1
;
3850 InputContext
->EP
[0].MaxPacketSize
= MaxPacketSize
;
3852 ZeroMem (&CmdTrbEvalu
, sizeof (CmdTrbEvalu
));
3853 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3854 CmdTrbEvalu
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3855 CmdTrbEvalu
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3856 CmdTrbEvalu
.CycleBit
= 1;
3857 CmdTrbEvalu
.Type
= TRB_TYPE_EVALU_CONTXT
;
3858 CmdTrbEvalu
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3859 DEBUG ((EFI_D_INFO
, "Evaluate context\n"));
3860 Status
= XhcCmdTransfer (
3862 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbEvalu
,
3863 XHC_GENERIC_TIMEOUT
,
3864 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3866 if (EFI_ERROR (Status
)) {
3867 DEBUG ((EFI_D_ERROR
, "XhcEvaluateContext: Evaluate Context Failed, Status = %r\n", Status
));
3873 Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.
3875 @param Xhc The XHCI Instance.
3876 @param SlotId The slot id to be evaluated.
3877 @param MaxPacketSize The max packet size supported by the device control transfer.
3879 @retval EFI_SUCCESS Successfully evaluate the device endpoint 0.
3884 XhcEvaluateContext64 (
3885 IN USB_XHCI_INSTANCE
*Xhc
,
3887 IN UINT32 MaxPacketSize
3891 CMD_TRB_EVALUATE_CONTEXT CmdTrbEvalu
;
3892 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3893 INPUT_CONTEXT_64
*InputContext
;
3894 EFI_PHYSICAL_ADDRESS PhyAddr
;
3896 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3899 // 4.6.7 Evaluate Context
3901 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3902 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
3904 InputContext
->InputControlContext
.Dword2
|= BIT1
;
3905 InputContext
->EP
[0].MaxPacketSize
= MaxPacketSize
;
3907 ZeroMem (&CmdTrbEvalu
, sizeof (CmdTrbEvalu
));
3908 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
3909 CmdTrbEvalu
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3910 CmdTrbEvalu
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3911 CmdTrbEvalu
.CycleBit
= 1;
3912 CmdTrbEvalu
.Type
= TRB_TYPE_EVALU_CONTXT
;
3913 CmdTrbEvalu
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3914 DEBUG ((EFI_D_INFO
, "Evaluate context\n"));
3915 Status
= XhcCmdTransfer (
3917 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbEvalu
,
3918 XHC_GENERIC_TIMEOUT
,
3919 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3921 if (EFI_ERROR (Status
)) {
3922 DEBUG ((EFI_D_ERROR
, "XhcEvaluateContext64: Evaluate Context Failed, Status = %r\n", Status
));
3929 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
3931 @param Xhc The XHCI Instance.
3932 @param SlotId The slot id to be configured.
3933 @param PortNum The total number of downstream port supported by the hub.
3934 @param TTT The TT think time of the hub device.
3935 @param MTT The multi-TT of the hub device.
3937 @retval EFI_SUCCESS Successfully configure the hub device's slot context.
3941 XhcConfigHubContext (
3942 IN USB_XHCI_INSTANCE
*Xhc
,
3950 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
3951 INPUT_CONTEXT
*InputContext
;
3952 DEVICE_CONTEXT
*OutputContext
;
3953 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
3954 EFI_PHYSICAL_ADDRESS PhyAddr
;
3956 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
3957 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
3958 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
3961 // 4.6.7 Evaluate Context
3963 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT
));
3965 InputContext
->InputControlContext
.Dword2
|= BIT0
;
3968 // Copy the slot context from OutputContext to Input context
3970 CopyMem(&(InputContext
->Slot
), &(OutputContext
->Slot
), sizeof (SLOT_CONTEXT
));
3971 InputContext
->Slot
.Hub
= 1;
3972 InputContext
->Slot
.PortNum
= PortNum
;
3973 InputContext
->Slot
.TTT
= TTT
;
3974 InputContext
->Slot
.MTT
= MTT
;
3976 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
3977 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT
));
3978 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
3979 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
3980 CmdTrbCfgEP
.CycleBit
= 1;
3981 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
3982 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
3983 DEBUG ((EFI_D_INFO
, "Configure Hub Slot Context\n"));
3984 Status
= XhcCmdTransfer (
3986 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
3987 XHC_GENERIC_TIMEOUT
,
3988 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
3990 if (EFI_ERROR (Status
)) {
3991 DEBUG ((EFI_D_ERROR
, "XhcConfigHubContext: Config Endpoint Failed, Status = %r\n", Status
));
3997 Evaluate the slot context for hub device through XHCI's Configure_Endpoint cmd.
3999 @param Xhc The XHCI Instance.
4000 @param SlotId The slot id to be configured.
4001 @param PortNum The total number of downstream port supported by the hub.
4002 @param TTT The TT think time of the hub device.
4003 @param MTT The multi-TT of the hub device.
4005 @retval EFI_SUCCESS Successfully configure the hub device's slot context.
4009 XhcConfigHubContext64 (
4010 IN USB_XHCI_INSTANCE
*Xhc
,
4018 EVT_TRB_COMMAND_COMPLETION
*EvtTrb
;
4019 INPUT_CONTEXT_64
*InputContext
;
4020 DEVICE_CONTEXT_64
*OutputContext
;
4021 CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP
;
4022 EFI_PHYSICAL_ADDRESS PhyAddr
;
4024 ASSERT (Xhc
->UsbDevContext
[SlotId
].SlotId
!= 0);
4025 InputContext
= Xhc
->UsbDevContext
[SlotId
].InputContext
;
4026 OutputContext
= Xhc
->UsbDevContext
[SlotId
].OutputContext
;
4029 // 4.6.7 Evaluate Context
4031 ZeroMem (InputContext
, sizeof (INPUT_CONTEXT_64
));
4033 InputContext
->InputControlContext
.Dword2
|= BIT0
;
4036 // Copy the slot context from OutputContext to Input context
4038 CopyMem(&(InputContext
->Slot
), &(OutputContext
->Slot
), sizeof (SLOT_CONTEXT_64
));
4039 InputContext
->Slot
.Hub
= 1;
4040 InputContext
->Slot
.PortNum
= PortNum
;
4041 InputContext
->Slot
.TTT
= TTT
;
4042 InputContext
->Slot
.MTT
= MTT
;
4044 ZeroMem (&CmdTrbCfgEP
, sizeof (CmdTrbCfgEP
));
4045 PhyAddr
= UsbHcGetPciAddrForHostAddr (Xhc
->MemPool
, InputContext
, sizeof (INPUT_CONTEXT_64
));
4046 CmdTrbCfgEP
.PtrLo
= XHC_LOW_32BIT (PhyAddr
);
4047 CmdTrbCfgEP
.PtrHi
= XHC_HIGH_32BIT (PhyAddr
);
4048 CmdTrbCfgEP
.CycleBit
= 1;
4049 CmdTrbCfgEP
.Type
= TRB_TYPE_CON_ENDPOINT
;
4050 CmdTrbCfgEP
.SlotId
= Xhc
->UsbDevContext
[SlotId
].SlotId
;
4051 DEBUG ((EFI_D_INFO
, "Configure Hub Slot Context\n"));
4052 Status
= XhcCmdTransfer (
4054 (TRB_TEMPLATE
*) (UINTN
) &CmdTrbCfgEP
,
4055 XHC_GENERIC_TIMEOUT
,
4056 (TRB_TEMPLATE
**) (UINTN
) &EvtTrb
4058 if (EFI_ERROR (Status
)) {
4059 DEBUG ((EFI_D_ERROR
, "XhcConfigHubContext64: Config Endpoint Failed, Status = %r\n", Status
));