X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdeModulePkg%2FBus%2FPci%2FXhciDxe%2FXhciSched.c;h=58a2f984a9450a127e065f95fa8ec35580ef15c7;hp=dea5b1af1884b8426b3bc888c44dd4d790205cf1;hb=3719c2aa879bcbcf3f0ab1f710e81fe2dbc19d9a;hpb=260fbf5340dc330ddd84c1358ed2b6bff74f61c0 diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c index dea5b1af18..58a2f984a9 100644 --- a/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c +++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c @@ -2,7 +2,7 @@ XHCI transfer scheduling routines. -Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.
+Copyright (c) 2011 - 2017, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -295,7 +295,7 @@ XhcCreateTransferTrb ( TrbStart->TrbCtrSetup.wValue = Urb->Request->Value; TrbStart->TrbCtrSetup.wIndex = Urb->Request->Index; TrbStart->TrbCtrSetup.wLength = Urb->Request->Length; - TrbStart->TrbCtrSetup.Lenth = 8; + TrbStart->TrbCtrSetup.Length = 8; TrbStart->TrbCtrSetup.IntTarget = 0; TrbStart->TrbCtrSetup.IOC = 1; TrbStart->TrbCtrSetup.IDT = 1; @@ -321,7 +321,7 @@ XhcCreateTransferTrb ( TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue; TrbStart->TrbCtrData.TRBPtrLo = XHC_LOW_32BIT(Urb->DataPhy); TrbStart->TrbCtrData.TRBPtrHi = XHC_HIGH_32BIT(Urb->DataPhy); - TrbStart->TrbCtrData.Lenth = (UINT32) Urb->DataLen; + TrbStart->TrbCtrData.Length = (UINT32) Urb->DataLen; TrbStart->TrbCtrData.TDSize = 0; TrbStart->TrbCtrData.IntTarget = 0; TrbStart->TrbCtrData.ISP = 1; @@ -387,7 +387,7 @@ XhcCreateTransferTrb ( TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue; TrbStart->TrbNormal.TRBPtrLo = XHC_LOW_32BIT((UINT8 *) Urb->DataPhy + TotalLen); TrbStart->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT((UINT8 *) Urb->DataPhy + TotalLen); - TrbStart->TrbNormal.Lenth = (UINT32) Len; + TrbStart->TrbNormal.Length = (UINT32) Len; TrbStart->TrbNormal.TDSize = 0; TrbStart->TrbNormal.IntTarget = 0; TrbStart->TrbNormal.ISP = 1; @@ -422,7 +422,7 @@ XhcCreateTransferTrb ( TrbStart = (TRB *)(UINTN)EPRing->RingEnqueue; TrbStart->TrbNormal.TRBPtrLo = XHC_LOW_32BIT((UINT8 *) Urb->DataPhy + TotalLen); TrbStart->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT((UINT8 *) Urb->DataPhy + TotalLen); - TrbStart->TrbNormal.Lenth = (UINT32) Len; + TrbStart->TrbNormal.Length = (UINT32) Len; TrbStart->TrbNormal.TDSize = 0; TrbStart->TrbNormal.IntTarget = 0; TrbStart->TrbNormal.ISP = 1; @@ -525,6 +525,7 @@ XhcInitSched ( ASSERT (ScratchEntry != NULL); Xhc->ScratchEntry = ScratchEntry; + ScratchPhy = 0; Status = UsbHcAllocateAlignedPages ( Xhc->PciIo, EFI_SIZE_TO_PAGES (MaxScratchpadBufs * sizeof (UINT64)), @@ -542,6 +543,7 @@ XhcInitSched ( // Allocate each scratch buffer // for (Index = 0; Index < MaxScratchpadBufs; Index++) { + ScratchEntryPhy = 0; Status = UsbHcAllocateAlignedPages ( Xhc->PciIo, EFI_SIZE_TO_PAGES (Xhc->PageSize), @@ -643,12 +645,8 @@ XhcRecoverHaltedEndpoint ( ) { EFI_STATUS Status; - EVT_TRB_COMMAND_COMPLETION *EvtTrb; - CMD_TRB_RESET_ENDPOINT CmdTrbResetED; - CMD_SET_TR_DEQ_POINTER CmdSetTRDeq; UINT8 Dci; UINT8 SlotId; - EFI_PHYSICAL_ADDRESS PhyAddr; Status = EFI_SUCCESS; SlotId = XhcBusDevAddrToSlotId (Xhc, Urb->Ep.BusAddr); @@ -663,17 +661,7 @@ XhcRecoverHaltedEndpoint ( // // 1) Send Reset endpoint command to transit from halt to stop state // - ZeroMem (&CmdTrbResetED, sizeof (CmdTrbResetED)); - CmdTrbResetED.CycleBit = 1; - CmdTrbResetED.Type = TRB_TYPE_RESET_ENDPOINT; - CmdTrbResetED.EDID = Dci; - CmdTrbResetED.SlotId = SlotId; - Status = XhcCmdTransfer ( - Xhc, - (TRB_TEMPLATE *) (UINTN) &CmdTrbResetED, - XHC_GENERIC_TIMEOUT, - (TRB_TEMPLATE **) (UINTN) &EvtTrb - ); + Status = XhcResetEndpoint(Xhc, SlotId, Dci); if (EFI_ERROR(Status)) { DEBUG ((EFI_D_ERROR, "XhcRecoverHaltedEndpoint: Reset Endpoint Failed, Status = %r\n", Status)); goto Done; @@ -682,22 +670,70 @@ XhcRecoverHaltedEndpoint ( // // 2)Set dequeue pointer // - ZeroMem (&CmdSetTRDeq, sizeof (CmdSetTRDeq)); - PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Urb->Ring->RingEnqueue, sizeof (CMD_SET_TR_DEQ_POINTER)); - CmdSetTRDeq.PtrLo = XHC_LOW_32BIT (PhyAddr) | Urb->Ring->RingPCS; - CmdSetTRDeq.PtrHi = XHC_HIGH_32BIT (PhyAddr); - CmdSetTRDeq.CycleBit = 1; - CmdSetTRDeq.Type = TRB_TYPE_SET_TR_DEQUE; - CmdSetTRDeq.Endpoint = Dci; - CmdSetTRDeq.SlotId = SlotId; - Status = XhcCmdTransfer ( - Xhc, - (TRB_TEMPLATE *) (UINTN) &CmdSetTRDeq, - XHC_GENERIC_TIMEOUT, - (TRB_TEMPLATE **) (UINTN) &EvtTrb - ); + Status = XhcSetTrDequeuePointer(Xhc, SlotId, Dci, Urb); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "XhcRecoverHaltedEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status)); + goto Done; + } + + // + // 3)Ring the doorbell to transit from stop to active + // + XhcRingDoorBell (Xhc, SlotId, Dci); + +Done: + return Status; +} + +/** + System software shall use a Stop Endpoint Command (section 4.6.9) and the Set TR Dequeue Pointer + Command (section 4.6.10) to remove the timed-out TDs from the xHC transfer ring. The next write to + the Doorbell of the Endpoint will transition the Endpoint Context from the Stopped to the Running + state. + + @param Xhc The XHCI Instance. + @param Urb The urb which doesn't get completed in a specified timeout range. + + @retval EFI_SUCCESS The dequeuing of the TDs is successful. + @retval Others Failed to stop the endpoint and dequeue the TDs. + +**/ +EFI_STATUS +EFIAPI +XhcDequeueTrbFromEndpoint ( + IN USB_XHCI_INSTANCE *Xhc, + IN URB *Urb + ) +{ + EFI_STATUS Status; + UINT8 Dci; + UINT8 SlotId; + + Status = EFI_SUCCESS; + SlotId = XhcBusDevAddrToSlotId (Xhc, Urb->Ep.BusAddr); + if (SlotId == 0) { + return EFI_DEVICE_ERROR; + } + Dci = XhcEndpointToDci (Urb->Ep.EpAddr, (UINT8)(Urb->Ep.Direction)); + ASSERT (Dci < 32); + + DEBUG ((EFI_D_INFO, "Stop Slot = %x,Dci = %x\n", SlotId, Dci)); + + // + // 1) Send Stop endpoint command to stop xHC from executing of the TDs on the endpoint + // + Status = XhcStopEndpoint(Xhc, SlotId, Dci); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "XhcDequeueTrbFromEndpoint: Stop Endpoint Failed, Status = %r\n", Status)); + goto Done; + } + + // + // 2)Set dequeue pointer + // + Status = XhcSetTrDequeuePointer(Xhc, SlotId, Dci, Urb); if (EFI_ERROR(Status)) { - DEBUG ((EFI_D_ERROR, "XhcRecoverHaltedEndpoint: Set Dequeue Pointer Failed, Status = %r\n", Status)); + DEBUG ((EFI_D_ERROR, "XhcDequeueTrbFromEndpoint: Set Transfer Ring Dequeue Pointer Failed, Status = %r\n", Status)); goto Done; } @@ -750,7 +786,7 @@ CreateEventRing ( // EventRing->EventRingCCS = 1; - Size = EFI_SIZE_TO_PAGES (sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER); + Size = sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER; Buf = UsbHcAllocateMem (Xhc->MemPool, Size); ASSERT (Buf != NULL); ASSERT (((UINTN) Buf & 0x3F) == 0); @@ -973,7 +1009,11 @@ IsAsyncIntTrb ( return TRUE; } CheckedTrb++; - if ((UINTN)CheckedTrb >= ((UINTN) CheckedUrb->Ring->RingSeg0 + sizeof (TRB_TEMPLATE) * CheckedUrb->Ring->TrbNumber)) { + // + // If the checked TRB is the link TRB at the end of the transfer ring, + // recircle it to the head of the ring. + // + if (CheckedTrb->Type == TRB_TYPE_LINK) { CheckedTrb = (TRB_TEMPLATE*) CheckedUrb->Ring->RingSeg0; } } @@ -1025,7 +1065,7 @@ IsTransferRingTrb ( @return Whether the result of URB transfer is finialized. **/ -EFI_STATUS +BOOLEAN XhcCheckUrbResult ( IN USB_XHCI_INSTANCE *Xhc, IN URB *Urb @@ -1056,7 +1096,6 @@ XhcCheckUrbResult ( if (XhcIsHalt (Xhc) || XhcIsSysError (Xhc)) { Urb->Result |= EFI_USB_ERR_SYSTEM; - Status = EFI_DEVICE_ERROR; goto EXIT; } @@ -1105,37 +1144,37 @@ XhcCheckUrbResult ( CheckedUrb->Result |= EFI_USB_ERR_STALL; CheckedUrb->Finished = TRUE; DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: STALL_ERROR! Completecode = %x\n",EvtTrb->Completecode)); - break; + goto EXIT; case TRB_COMPLETION_BABBLE_ERROR: CheckedUrb->Result |= EFI_USB_ERR_BABBLE; CheckedUrb->Finished = TRUE; DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: BABBLE_ERROR! Completecode = %x\n",EvtTrb->Completecode)); - break; + goto EXIT; case TRB_COMPLETION_DATA_BUFFER_ERROR: CheckedUrb->Result |= EFI_USB_ERR_BUFFER; CheckedUrb->Finished = TRUE; DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: ERR_BUFFER! Completecode = %x\n",EvtTrb->Completecode)); - break; + goto EXIT; case TRB_COMPLETION_USB_TRANSACTION_ERROR: CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT; CheckedUrb->Finished = TRUE; DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: TRANSACTION_ERROR! Completecode = %x\n",EvtTrb->Completecode)); - break; + goto EXIT; case TRB_COMPLETION_SHORT_PACKET: case TRB_COMPLETION_SUCCESS: if (EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) { - DEBUG ((EFI_D_ERROR, "XhcCheckUrbResult: short packet happens!\n")); + DEBUG ((EFI_D_VERBOSE, "XhcCheckUrbResult: short packet happens!\n")); } TRBType = (UINT8) (TRBPtr->Type); if ((TRBType == TRB_TYPE_DATA_STAGE) || (TRBType == TRB_TYPE_NORMAL) || (TRBType == TRB_TYPE_ISOCH)) { - CheckedUrb->Completed += (CheckedUrb->DataLen - EvtTrb->Lenth); + CheckedUrb->Completed += (((TRANSFER_TRB_NORMAL*)TRBPtr)->Length - EvtTrb->Length); } break; @@ -1144,7 +1183,7 @@ XhcCheckUrbResult ( DEBUG ((EFI_D_ERROR, "Transfer Default Error Occur! Completecode = 0x%x!\n",EvtTrb->Completecode)); CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT; CheckedUrb->Finished = TRUE; - break; + goto EXIT; } // @@ -1187,7 +1226,7 @@ EXIT: XhcWriteRuntimeReg (Xhc, XHC_ERDP_OFFSET + 4, XHC_HIGH_32BIT (PhyAddr)); } - return Status; + return Urb->Finished; } @@ -1214,9 +1253,10 @@ XhcExecTransfer ( { EFI_STATUS Status; UINTN Index; - UINTN Loop; + UINT64 Loop; UINT8 SlotId; UINT8 Dci; + BOOLEAN Finished; if (CmdTransfer) { SlotId = 0; @@ -1239,8 +1279,8 @@ XhcExecTransfer ( XhcRingDoorBell (Xhc, SlotId, Dci); for (Index = 0; Index < Loop; Index++) { - Status = XhcCheckUrbResult (Xhc, Urb); - if (Urb->Finished) { + Finished = XhcCheckUrbResult (Xhc, Urb); + if (Finished) { break; } gBS->Stall (XHC_1_MICROSECOND); @@ -1248,6 +1288,9 @@ XhcExecTransfer ( if (Index == Loop) { Urb->Result = EFI_USB_ERR_TIMEOUT; + Status = EFI_TIMEOUT; + } else if (Urb->Result != EFI_USB_NOERROR) { + Status = EFI_DEVICE_ERROR; } return Status; @@ -1535,6 +1578,10 @@ XhcPollPortStatusChange ( Status = EFI_SUCCESS; + if ((PortState->PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { + return EFI_SUCCESS; + } + if (ParentRouteChart.Dword == 0) { RouteChart.Route.RouteString = 0; RouteChart.Route.RootPortNum = Port + 1; @@ -1549,6 +1596,15 @@ XhcPollPortStatusChange ( RouteChart.Route.TierNum = ParentRouteChart.Route.TierNum + 1; } + SlotId = XhcRouteStringToSlotId (Xhc, RouteChart); + if (SlotId != 0) { + if (Xhc->HcCParams.Data.Csz == 0) { + Status = XhcDisableSlotCmd (Xhc, SlotId); + } else { + Status = XhcDisableSlotCmd64 (Xhc, SlotId); + } + } + if (((PortState->PortStatus & USB_PORT_STAT_ENABLE) != 0) && ((PortState->PortStatus & USB_PORT_STAT_CONNECTION) != 0)) { // @@ -1566,26 +1622,15 @@ XhcPollPortStatusChange ( // Execute Enable_Slot cmd for attached device, initialize device context and assign device address. // SlotId = XhcRouteStringToSlotId (Xhc, RouteChart); - if (SlotId == 0) { + if ((SlotId == 0) && ((PortState->PortChangeStatus & USB_PORT_STAT_C_RESET) != 0)) { if (Xhc->HcCParams.Data.Csz == 0) { Status = XhcInitializeDeviceSlot (Xhc, ParentRouteChart, Port, RouteChart, Speed); } else { Status = XhcInitializeDeviceSlot64 (Xhc, ParentRouteChart, Port, RouteChart, Speed); } } - } else if ((PortState->PortStatus & USB_PORT_STAT_CONNECTION) == 0) { - // - // Device is detached. Disable the allocated device slot and release resource. - // - SlotId = XhcRouteStringToSlotId (Xhc, RouteChart); - if (SlotId != 0) { - if (Xhc->HcCParams.Data.Csz == 0) { - Status = XhcDisableSlotCmd (Xhc, SlotId); - } else { - Status = XhcDisableSlotCmd64 (Xhc, SlotId); - } - } - } + } + return Status; } @@ -2070,6 +2115,10 @@ XhcInitializeDeviceSlot ( // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input // Context data structure described above. // + // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request + // to device. + // + gBS->Stall (XHC_RESET_RECOVERY_DELAY); ZeroMem (&CmdTrbAddr, sizeof (CmdTrbAddr)); PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Xhc->UsbDevContext[SlotId].InputContext, sizeof (INPUT_CONTEXT)); CmdTrbAddr.PtrLo = XHC_LOW_32BIT (PhyAddr); @@ -2276,6 +2325,10 @@ XhcInitializeDeviceSlot64 ( // 8) Issue an Address Device Command for the Device Slot, where the command points to the Input // Context data structure described above. // + // Delay 10ms to meet TRSTRCY delay requirement in usb 2.0 spec chapter 7.1.7.5 before sending SetAddress() request + // to device. + // + gBS->Stall (XHC_RESET_RECOVERY_DELAY); ZeroMem (&CmdTrbAddr, sizeof (CmdTrbAddr)); PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Xhc->UsbDevContext[SlotId].InputContext, sizeof (INPUT_CONTEXT_64)); CmdTrbAddr.PtrLo = XHC_LOW_32BIT (PhyAddr); @@ -2383,6 +2436,10 @@ XhcDisableSlotCmd ( } } + if (Xhc->UsbDevContext[SlotId].ActiveAlternateSetting != NULL) { + FreePool (Xhc->UsbDevContext[SlotId].ActiveAlternateSetting); + } + if (Xhc->UsbDevContext[SlotId].InputContext != NULL) { UsbHcFreeMem (Xhc->MemPool, Xhc->UsbDevContext[SlotId].InputContext, sizeof (INPUT_CONTEXT)); } @@ -2486,6 +2543,10 @@ XhcDisableSlotCmd64 ( } } + if (Xhc->UsbDevContext[SlotId].ActiveAlternateSetting != NULL) { + FreePool (Xhc->UsbDevContext[SlotId].ActiveAlternateSetting); + } + if (Xhc->UsbDevContext[SlotId].InputContext != NULL) { UsbHcFreeMem (Xhc->MemPool, Xhc->UsbDevContext[SlotId].InputContext, sizeof (INPUT_CONTEXT_64)); } @@ -2504,249 +2565,408 @@ XhcDisableSlotCmd64 ( return Status; } - /** - Configure all the device endpoints through XHCI's Configure_Endpoint cmd. + Initialize endpoint context in input context. - @param Xhc The XHCI Instance. - @param SlotId The slot id to be configured. - @param DeviceSpeed The device's speed. - @param ConfigDesc The pointer to the usb device configuration descriptor. + @param Xhc The XHCI Instance. + @param SlotId The slot id to be configured. + @param DeviceSpeed The device's speed. + @param InputContext The pointer to the input context. + @param IfDesc The pointer to the usb device interface descriptor. - @retval EFI_SUCCESS Successfully configure all the device endpoints. + @return The maximum device context index of endpoint. **/ -EFI_STATUS +UINT8 EFIAPI -XhcSetConfigCmd ( - IN USB_XHCI_INSTANCE *Xhc, - IN UINT8 SlotId, - IN UINT8 DeviceSpeed, - IN USB_CONFIG_DESCRIPTOR *ConfigDesc +XhcInitializeEndpointContext ( + IN USB_XHCI_INSTANCE *Xhc, + IN UINT8 SlotId, + IN UINT8 DeviceSpeed, + IN INPUT_CONTEXT *InputContext, + IN USB_INTERFACE_DESCRIPTOR *IfDesc ) { - EFI_STATUS Status; - USB_INTERFACE_DESCRIPTOR *IfDesc; - USB_ENDPOINT_DESCRIPTOR *EpDesc; - UINT8 Index; - UINTN NumEp; - UINTN EpIndex; - UINT8 EpAddr; - UINT8 Direction; - UINT8 Dci; - UINT8 MaxDci; - EFI_PHYSICAL_ADDRESS PhyAddr; - UINT8 Interval; + USB_ENDPOINT_DESCRIPTOR *EpDesc; + UINTN NumEp; + UINTN EpIndex; + UINT8 EpAddr; + UINT8 Direction; + UINT8 Dci; + UINT8 MaxDci; + EFI_PHYSICAL_ADDRESS PhyAddr; + UINT8 Interval; + TRANSFER_RING *EndpointTransferRing; - TRANSFER_RING *EndpointTransferRing; - CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP; - INPUT_CONTEXT *InputContext; - DEVICE_CONTEXT *OutputContext; - EVT_TRB_COMMAND_COMPLETION *EvtTrb; - // - // 4.6.6 Configure Endpoint - // - InputContext = Xhc->UsbDevContext[SlotId].InputContext; - OutputContext = Xhc->UsbDevContext[SlotId].OutputContext; - ZeroMem (InputContext, sizeof (INPUT_CONTEXT)); - CopyMem (&InputContext->Slot, &OutputContext->Slot, sizeof (SLOT_CONTEXT)); + MaxDci = 0; - ASSERT (ConfigDesc != NULL); + NumEp = IfDesc->NumEndpoints; - MaxDci = 0; + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)(IfDesc + 1); + for (EpIndex = 0; EpIndex < NumEp; EpIndex++) { + while (EpDesc->DescriptorType != USB_DESC_TYPE_ENDPOINT) { + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + } - IfDesc = (USB_INTERFACE_DESCRIPTOR *)(ConfigDesc + 1); - for (Index = 0; Index < ConfigDesc->NumInterfaces; Index++) { - while (IfDesc->DescriptorType != USB_DESC_TYPE_INTERFACE) { - IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length); + if (EpDesc->Length < sizeof (USB_ENDPOINT_DESCRIPTOR)) { + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + continue; } - NumEp = IfDesc->NumEndpoints; + EpAddr = (UINT8)(EpDesc->EndpointAddress & 0x0F); + Direction = (UINT8)((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut); - EpDesc = (USB_ENDPOINT_DESCRIPTOR *)(IfDesc + 1); - for (EpIndex = 0; EpIndex < NumEp; EpIndex++) { - while (EpDesc->DescriptorType != USB_DESC_TYPE_ENDPOINT) { - EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); - } + Dci = XhcEndpointToDci (EpAddr, Direction); + ASSERT (Dci < 32); + if (Dci > MaxDci) { + MaxDci = Dci; + } - EpAddr = (UINT8)(EpDesc->EndpointAddress & 0x0F); - Direction = (UINT8)((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut); + InputContext->InputControlContext.Dword2 |= (BIT0 << Dci); + InputContext->EP[Dci-1].MaxPacketSize = EpDesc->MaxPacketSize; - Dci = XhcEndpointToDci (EpAddr, Direction); - ASSERT (Dci < 32); - if (Dci > MaxDci) { - MaxDci = Dci; - } + if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + // + // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor. + // + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + } else { + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + } + + switch (EpDesc->Attributes & USB_ENDPOINT_TYPE_MASK) { + case USB_ENDPOINT_BULK: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_BULK_IN; + } else { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_BULK_OUT; + } - InputContext->InputControlContext.Dword2 |= (BIT0 << Dci); - InputContext->EP[Dci-1].MaxPacketSize = EpDesc->MaxPacketSize; + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { + EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING)); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; + CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + } - if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + break; + case USB_ENDPOINT_ISO: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 0; + InputContext->EP[Dci-1].EPType = ED_ISOCH_IN; + } else { + InputContext->EP[Dci-1].CErr = 0; + InputContext->EP[Dci-1].EPType = ED_ISOCH_OUT; + } // - // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor. + // Get the bInterval from descriptor and init the the interval field of endpoint context. + // Refer to XHCI 1.1 spec section 6.2.3.6. // - InputContext->EP[Dci-1].MaxBurstSize = 0x0; - } else { - InputContext->EP[Dci-1].MaxBurstSize = 0x0; - } - - switch (EpDesc->Attributes & USB_ENDPOINT_TYPE_MASK) { - case USB_ENDPOINT_BULK: - if (Direction == EfiUsbDataIn) { - InputContext->EP[Dci-1].CErr = 3; - InputContext->EP[Dci-1].EPType = ED_BULK_IN; - } else { - InputContext->EP[Dci-1].CErr = 3; - InputContext->EP[Dci-1].EPType = ED_BULK_OUT; - } + if (DeviceSpeed == EFI_USB_SPEED_FULL) { + Interval = EpDesc->Interval; + ASSERT (Interval >= 1 && Interval <= 16); + InputContext->EP[Dci-1].Interval = Interval + 2; + } else if ((DeviceSpeed == EFI_USB_SPEED_HIGH) || (DeviceSpeed == EFI_USB_SPEED_SUPER)) { + Interval = EpDesc->Interval; + ASSERT (Interval >= 1 && Interval <= 16); + InputContext->EP[Dci-1].Interval = Interval - 1; + } - InputContext->EP[Dci-1].AverageTRBLength = 0x1000; - if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { - EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING)); - Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; - CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); - } - - break; - case USB_ENDPOINT_ISO: - if (Direction == EfiUsbDataIn) { - InputContext->EP[Dci-1].CErr = 0; - InputContext->EP[Dci-1].EPType = ED_ISOCH_IN; - } else { - InputContext->EP[Dci-1].CErr = 0; - InputContext->EP[Dci-1].EPType = ED_ISOCH_OUT; - } - break; - case USB_ENDPOINT_INTERRUPT: - if (Direction == EfiUsbDataIn) { - InputContext->EP[Dci-1].CErr = 3; - InputContext->EP[Dci-1].EPType = ED_INTERRUPT_IN; - } else { - InputContext->EP[Dci-1].CErr = 3; - InputContext->EP[Dci-1].EPType = ED_INTERRUPT_OUT; - } - InputContext->EP[Dci-1].AverageTRBLength = 0x1000; - InputContext->EP[Dci-1].MaxESITPayload = EpDesc->MaxPacketSize; + // + // Do not support isochronous transfer now. + // + DEBUG ((EFI_D_INFO, "XhcInitializeEndpointContext: Unsupport ISO EP found, Transfer ring is not allocated.\n")); + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + continue; + case USB_ENDPOINT_INTERRUPT: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_INTERRUPT_IN; + } else { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_INTERRUPT_OUT; + } + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + InputContext->EP[Dci-1].MaxESITPayload = EpDesc->MaxPacketSize; + // + // Get the bInterval from descriptor and init the the interval field of endpoint context + // + if ((DeviceSpeed == EFI_USB_SPEED_FULL) || (DeviceSpeed == EFI_USB_SPEED_LOW)) { + Interval = EpDesc->Interval; // - // Get the bInterval from descriptor and init the the interval field of endpoint context + // Calculate through the bInterval field of Endpoint descriptor. // - if ((DeviceSpeed == EFI_USB_SPEED_FULL) || (DeviceSpeed == EFI_USB_SPEED_LOW)) { - Interval = EpDesc->Interval; - // - // Hard code the interval to MAX first, need calculate through the bInterval field of Endpoint descriptor. - // - InputContext->EP[Dci-1].Interval = 6; - } else if ((DeviceSpeed == EFI_USB_SPEED_HIGH) || (DeviceSpeed == EFI_USB_SPEED_SUPER)) { - Interval = EpDesc->Interval; - ASSERT (Interval >= 1 && Interval <= 16); - // - // Refer to XHCI 1.0 spec section 6.2.3.6, table 61 - // - InputContext->EP[Dci-1].Interval = Interval - 1; - InputContext->EP[Dci-1].AverageTRBLength = 0x1000; - InputContext->EP[Dci-1].MaxESITPayload = 0x0002; - InputContext->EP[Dci-1].MaxBurstSize = 0x0; - InputContext->EP[Dci-1].CErr = 3; - } - - if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { - EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING)); - Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; - CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); - } - break; - - case USB_ENDPOINT_CONTROL: - default: - ASSERT (0); - break; - } + ASSERT (Interval != 0); + InputContext->EP[Dci-1].Interval = (UINT32)HighBitSet32((UINT32)Interval) + 3; + } else if ((DeviceSpeed == EFI_USB_SPEED_HIGH) || (DeviceSpeed == EFI_USB_SPEED_SUPER)) { + Interval = EpDesc->Interval; + ASSERT (Interval >= 1 && Interval <= 16); + // + // Refer to XHCI 1.0 spec section 6.2.3.6, table 61 + // + InputContext->EP[Dci-1].Interval = Interval - 1; + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + InputContext->EP[Dci-1].MaxESITPayload = 0x0002; + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + InputContext->EP[Dci-1].CErr = 3; + } - PhyAddr = UsbHcGetPciAddrForHostAddr ( - Xhc->MemPool, - ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingSeg0, - sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER - ); - PhyAddr &= ~(0x0F); - PhyAddr |= ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingPCS; - InputContext->EP[Dci-1].PtrLo = XHC_LOW_32BIT (PhyAddr); - InputContext->EP[Dci-1].PtrHi = XHC_HIGH_32BIT (PhyAddr); + if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { + EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING)); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; + CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + } + break; - EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + case USB_ENDPOINT_CONTROL: + // + // Do not support control transfer now. + // + DEBUG ((EFI_D_INFO, "XhcInitializeEndpointContext: Unsupport Control EP found, Transfer ring is not allocated.\n")); + default: + DEBUG ((EFI_D_INFO, "XhcInitializeEndpointContext: Unknown EP found, Transfer ring is not allocated.\n")); + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + continue; } - IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length); - } - InputContext->InputControlContext.Dword2 |= BIT0; - InputContext->Slot.ContextEntries = MaxDci; - // - // configure endpoint - // - ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP)); - PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT)); - CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr); - CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr); - CmdTrbCfgEP.CycleBit = 1; - CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT; - CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId; - DEBUG ((EFI_D_INFO, "Configure Endpoint\n")); - Status = XhcCmdTransfer ( - Xhc, - (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP, - XHC_GENERIC_TIMEOUT, - (TRB_TEMPLATE **) (UINTN) &EvtTrb - ); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "XhcSetConfigCmd: Config Endpoint Failed, Status = %r\n", Status)); + PhyAddr = UsbHcGetPciAddrForHostAddr ( + Xhc->MemPool, + ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingSeg0, + sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER + ); + PhyAddr &= ~((EFI_PHYSICAL_ADDRESS)0x0F); + PhyAddr |= (EFI_PHYSICAL_ADDRESS)((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingPCS; + InputContext->EP[Dci-1].PtrLo = XHC_LOW_32BIT (PhyAddr); + InputContext->EP[Dci-1].PtrHi = XHC_HIGH_32BIT (PhyAddr); + + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); } - return Status; + + return MaxDci; } /** - Configure all the device endpoints through XHCI's Configure_Endpoint cmd. + Initialize endpoint context in input context. - @param Xhc The XHCI Instance. - @param SlotId The slot id to be configured. - @param DeviceSpeed The device's speed. - @param ConfigDesc The pointer to the usb device configuration descriptor. + @param Xhc The XHCI Instance. + @param SlotId The slot id to be configured. + @param DeviceSpeed The device's speed. + @param InputContext The pointer to the input context. + @param IfDesc The pointer to the usb device interface descriptor. - @retval EFI_SUCCESS Successfully configure all the device endpoints. + @return The maximum device context index of endpoint. **/ -EFI_STATUS +UINT8 EFIAPI -XhcSetConfigCmd64 ( - IN USB_XHCI_INSTANCE *Xhc, - IN UINT8 SlotId, - IN UINT8 DeviceSpeed, - IN USB_CONFIG_DESCRIPTOR *ConfigDesc +XhcInitializeEndpointContext64 ( + IN USB_XHCI_INSTANCE *Xhc, + IN UINT8 SlotId, + IN UINT8 DeviceSpeed, + IN INPUT_CONTEXT_64 *InputContext, + IN USB_INTERFACE_DESCRIPTOR *IfDesc ) { - EFI_STATUS Status; - USB_INTERFACE_DESCRIPTOR *IfDesc; - USB_ENDPOINT_DESCRIPTOR *EpDesc; - UINT8 Index; - UINTN NumEp; - UINTN EpIndex; - UINT8 EpAddr; - UINT8 Direction; + USB_ENDPOINT_DESCRIPTOR *EpDesc; + UINTN NumEp; + UINTN EpIndex; + UINT8 EpAddr; + UINT8 Direction; + UINT8 Dci; + UINT8 MaxDci; + EFI_PHYSICAL_ADDRESS PhyAddr; + UINT8 Interval; + TRANSFER_RING *EndpointTransferRing; + + MaxDci = 0; + + NumEp = IfDesc->NumEndpoints; + + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)(IfDesc + 1); + for (EpIndex = 0; EpIndex < NumEp; EpIndex++) { + while (EpDesc->DescriptorType != USB_DESC_TYPE_ENDPOINT) { + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + } + + if (EpDesc->Length < sizeof (USB_ENDPOINT_DESCRIPTOR)) { + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + continue; + } + + EpAddr = (UINT8)(EpDesc->EndpointAddress & 0x0F); + Direction = (UINT8)((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut); + + Dci = XhcEndpointToDci (EpAddr, Direction); + ASSERT (Dci < 32); + if (Dci > MaxDci) { + MaxDci = Dci; + } + + InputContext->InputControlContext.Dword2 |= (BIT0 << Dci); + InputContext->EP[Dci-1].MaxPacketSize = EpDesc->MaxPacketSize; + + if (DeviceSpeed == EFI_USB_SPEED_SUPER) { + // + // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor. + // + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + } else { + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + } + + switch (EpDesc->Attributes & USB_ENDPOINT_TYPE_MASK) { + case USB_ENDPOINT_BULK: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_BULK_IN; + } else { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_BULK_OUT; + } + + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { + EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING)); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; + CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + } + + break; + case USB_ENDPOINT_ISO: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 0; + InputContext->EP[Dci-1].EPType = ED_ISOCH_IN; + } else { + InputContext->EP[Dci-1].CErr = 0; + InputContext->EP[Dci-1].EPType = ED_ISOCH_OUT; + } + // + // Get the bInterval from descriptor and init the the interval field of endpoint context. + // Refer to XHCI 1.1 spec section 6.2.3.6. + // + if (DeviceSpeed == EFI_USB_SPEED_FULL) { + Interval = EpDesc->Interval; + ASSERT (Interval >= 1 && Interval <= 16); + InputContext->EP[Dci-1].Interval = Interval + 2; + } else if ((DeviceSpeed == EFI_USB_SPEED_HIGH) || (DeviceSpeed == EFI_USB_SPEED_SUPER)) { + Interval = EpDesc->Interval; + ASSERT (Interval >= 1 && Interval <= 16); + InputContext->EP[Dci-1].Interval = Interval - 1; + } + + // + // Do not support isochronous transfer now. + // + DEBUG ((EFI_D_INFO, "XhcInitializeEndpointContext64: Unsupport ISO EP found, Transfer ring is not allocated.\n")); + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + continue; + case USB_ENDPOINT_INTERRUPT: + if (Direction == EfiUsbDataIn) { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_INTERRUPT_IN; + } else { + InputContext->EP[Dci-1].CErr = 3; + InputContext->EP[Dci-1].EPType = ED_INTERRUPT_OUT; + } + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + InputContext->EP[Dci-1].MaxESITPayload = EpDesc->MaxPacketSize; + // + // Get the bInterval from descriptor and init the the interval field of endpoint context + // + if ((DeviceSpeed == EFI_USB_SPEED_FULL) || (DeviceSpeed == EFI_USB_SPEED_LOW)) { + Interval = EpDesc->Interval; + // + // Calculate through the bInterval field of Endpoint descriptor. + // + ASSERT (Interval != 0); + InputContext->EP[Dci-1].Interval = (UINT32)HighBitSet32((UINT32)Interval) + 3; + } else if ((DeviceSpeed == EFI_USB_SPEED_HIGH) || (DeviceSpeed == EFI_USB_SPEED_SUPER)) { + Interval = EpDesc->Interval; + ASSERT (Interval >= 1 && Interval <= 16); + // + // Refer to XHCI 1.0 spec section 6.2.3.6, table 61 + // + InputContext->EP[Dci-1].Interval = Interval - 1; + InputContext->EP[Dci-1].AverageTRBLength = 0x1000; + InputContext->EP[Dci-1].MaxESITPayload = 0x0002; + InputContext->EP[Dci-1].MaxBurstSize = 0x0; + InputContext->EP[Dci-1].CErr = 3; + } + + if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { + EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING)); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; + CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); + } + break; + + case USB_ENDPOINT_CONTROL: + // + // Do not support control transfer now. + // + DEBUG ((EFI_D_INFO, "XhcInitializeEndpointContext64: Unsupport Control EP found, Transfer ring is not allocated.\n")); + default: + DEBUG ((EFI_D_INFO, "XhcInitializeEndpointContext64: Unknown EP found, Transfer ring is not allocated.\n")); + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + continue; + } + + PhyAddr = UsbHcGetPciAddrForHostAddr ( + Xhc->MemPool, + ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingSeg0, + sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER + ); + PhyAddr &= ~((EFI_PHYSICAL_ADDRESS)0x0F); + PhyAddr |= (EFI_PHYSICAL_ADDRESS)((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingPCS; + InputContext->EP[Dci-1].PtrLo = XHC_LOW_32BIT (PhyAddr); + InputContext->EP[Dci-1].PtrHi = XHC_HIGH_32BIT (PhyAddr); + + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + } + + return MaxDci; +} + +/** + Configure all the device endpoints through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI Instance. + @param SlotId The slot id to be configured. + @param DeviceSpeed The device's speed. + @param ConfigDesc The pointer to the usb device configuration descriptor. + + @retval EFI_SUCCESS Successfully configure all the device endpoints. + +**/ +EFI_STATUS +EFIAPI +XhcSetConfigCmd ( + IN USB_XHCI_INSTANCE *Xhc, + IN UINT8 SlotId, + IN UINT8 DeviceSpeed, + IN USB_CONFIG_DESCRIPTOR *ConfigDesc + ) +{ + EFI_STATUS Status; + USB_INTERFACE_DESCRIPTOR *IfDesc; + UINT8 Index; UINT8 Dci; UINT8 MaxDci; EFI_PHYSICAL_ADDRESS PhyAddr; - UINT8 Interval; - TRANSFER_RING *EndpointTransferRing; CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP; - INPUT_CONTEXT_64 *InputContext; - DEVICE_CONTEXT_64 *OutputContext; + INPUT_CONTEXT *InputContext; + DEVICE_CONTEXT *OutputContext; EVT_TRB_COMMAND_COMPLETION *EvtTrb; // // 4.6.6 Configure Endpoint // InputContext = Xhc->UsbDevContext[SlotId].InputContext; OutputContext = Xhc->UsbDevContext[SlotId].OutputContext; - ZeroMem (InputContext, sizeof (INPUT_CONTEXT_64)); - CopyMem (&InputContext->Slot, &OutputContext->Slot, sizeof (SLOT_CONTEXT_64)); + ZeroMem (InputContext, sizeof (INPUT_CONTEXT)); + CopyMem (&InputContext->Slot, &OutputContext->Slot, sizeof (SLOT_CONTEXT)); ASSERT (ConfigDesc != NULL); @@ -2754,125 +2974,110 @@ XhcSetConfigCmd64 ( IfDesc = (USB_INTERFACE_DESCRIPTOR *)(ConfigDesc + 1); for (Index = 0; Index < ConfigDesc->NumInterfaces; Index++) { - while (IfDesc->DescriptorType != USB_DESC_TYPE_INTERFACE) { + while ((IfDesc->DescriptorType != USB_DESC_TYPE_INTERFACE) || (IfDesc->AlternateSetting != 0)) { IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length); } - NumEp = IfDesc->NumEndpoints; + if (IfDesc->Length < sizeof (USB_INTERFACE_DESCRIPTOR)) { + IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length); + continue; + } - EpDesc = (USB_ENDPOINT_DESCRIPTOR *)(IfDesc + 1); - for (EpIndex = 0; EpIndex < NumEp; EpIndex++) { - while (EpDesc->DescriptorType != USB_DESC_TYPE_ENDPOINT) { - EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); - } + Dci = XhcInitializeEndpointContext (Xhc, SlotId, DeviceSpeed, InputContext, IfDesc); + if (Dci > MaxDci) { + MaxDci = Dci; + } - EpAddr = (UINT8)(EpDesc->EndpointAddress & 0x0F); - Direction = (UINT8)((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut); + IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length); + } - Dci = XhcEndpointToDci (EpAddr, Direction); - ASSERT (Dci < 32); - if (Dci > MaxDci) { - MaxDci = Dci; - } + InputContext->InputControlContext.Dword2 |= BIT0; + InputContext->Slot.ContextEntries = MaxDci; + // + // configure endpoint + // + ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT)); + CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr); + CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdTrbCfgEP.CycleBit = 1; + CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT; + CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId; + DEBUG ((EFI_D_INFO, "Configure Endpoint\n")); + Status = XhcCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XhcSetConfigCmd: Config Endpoint Failed, Status = %r\n", Status)); + } else { + Xhc->UsbDevContext[SlotId].ActiveConfiguration = ConfigDesc->ConfigurationValue; + } + + return Status; +} - InputContext->InputControlContext.Dword2 |= (BIT0 << Dci); - InputContext->EP[Dci-1].MaxPacketSize = EpDesc->MaxPacketSize; +/** + Configure all the device endpoints through XHCI's Configure_Endpoint cmd. - if (DeviceSpeed == EFI_USB_SPEED_SUPER) { - // - // 6.2.3.4, shall be set to the value defined in the bMaxBurst field of the SuperSpeed Endpoint Companion Descriptor. - // - InputContext->EP[Dci-1].MaxBurstSize = 0x0; - } else { - InputContext->EP[Dci-1].MaxBurstSize = 0x0; - } + @param Xhc The XHCI Instance. + @param SlotId The slot id to be configured. + @param DeviceSpeed The device's speed. + @param ConfigDesc The pointer to the usb device configuration descriptor. - switch (EpDesc->Attributes & USB_ENDPOINT_TYPE_MASK) { - case USB_ENDPOINT_BULK: - if (Direction == EfiUsbDataIn) { - InputContext->EP[Dci-1].CErr = 3; - InputContext->EP[Dci-1].EPType = ED_BULK_IN; - } else { - InputContext->EP[Dci-1].CErr = 3; - InputContext->EP[Dci-1].EPType = ED_BULK_OUT; - } + @retval EFI_SUCCESS Successfully configure all the device endpoints. - InputContext->EP[Dci-1].AverageTRBLength = 0x1000; - if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { - EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING)); - Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; - CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); - } - - break; - case USB_ENDPOINT_ISO: - if (Direction == EfiUsbDataIn) { - InputContext->EP[Dci-1].CErr = 0; - InputContext->EP[Dci-1].EPType = ED_ISOCH_IN; - } else { - InputContext->EP[Dci-1].CErr = 0; - InputContext->EP[Dci-1].EPType = ED_ISOCH_OUT; - } - break; - case USB_ENDPOINT_INTERRUPT: - if (Direction == EfiUsbDataIn) { - InputContext->EP[Dci-1].CErr = 3; - InputContext->EP[Dci-1].EPType = ED_INTERRUPT_IN; - } else { - InputContext->EP[Dci-1].CErr = 3; - InputContext->EP[Dci-1].EPType = ED_INTERRUPT_OUT; - } - InputContext->EP[Dci-1].AverageTRBLength = 0x1000; - InputContext->EP[Dci-1].MaxESITPayload = EpDesc->MaxPacketSize; - // - // Get the bInterval from descriptor and init the the interval field of endpoint context - // - if ((DeviceSpeed == EFI_USB_SPEED_FULL) || (DeviceSpeed == EFI_USB_SPEED_LOW)) { - Interval = EpDesc->Interval; - // - // Hard code the interval to MAX first, need calculate through the bInterval field of Endpoint descriptor. - // - InputContext->EP[Dci-1].Interval = 6; - } else if ((DeviceSpeed == EFI_USB_SPEED_HIGH) || (DeviceSpeed == EFI_USB_SPEED_SUPER)) { - Interval = EpDesc->Interval; - ASSERT (Interval >= 1 && Interval <= 16); - // - // Refer to XHCI 1.0 spec section 6.2.3.6, table 61 - // - InputContext->EP[Dci-1].Interval = Interval - 1; - InputContext->EP[Dci-1].AverageTRBLength = 0x1000; - InputContext->EP[Dci-1].MaxESITPayload = 0x0002; - InputContext->EP[Dci-1].MaxBurstSize = 0x0; - InputContext->EP[Dci-1].CErr = 3; - } - - if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] == NULL) { - EndpointTransferRing = AllocateZeroPool(sizeof (TRANSFER_RING)); - Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1] = (VOID *) EndpointTransferRing; - CreateTransferRing(Xhc, TR_RING_TRB_NUMBER, (TRANSFER_RING *)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1]); - } - break; - - case USB_ENDPOINT_CONTROL: - default: - ASSERT (0); - break; - } +**/ +EFI_STATUS +EFIAPI +XhcSetConfigCmd64 ( + IN USB_XHCI_INSTANCE *Xhc, + IN UINT8 SlotId, + IN UINT8 DeviceSpeed, + IN USB_CONFIG_DESCRIPTOR *ConfigDesc + ) +{ + EFI_STATUS Status; + USB_INTERFACE_DESCRIPTOR *IfDesc; + UINT8 Index; + UINT8 Dci; + UINT8 MaxDci; + EFI_PHYSICAL_ADDRESS PhyAddr; + + CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP; + INPUT_CONTEXT_64 *InputContext; + DEVICE_CONTEXT_64 *OutputContext; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + // + // 4.6.6 Configure Endpoint + // + InputContext = Xhc->UsbDevContext[SlotId].InputContext; + OutputContext = Xhc->UsbDevContext[SlotId].OutputContext; + ZeroMem (InputContext, sizeof (INPUT_CONTEXT_64)); + CopyMem (&InputContext->Slot, &OutputContext->Slot, sizeof (SLOT_CONTEXT_64)); - PhyAddr = UsbHcGetPciAddrForHostAddr ( - Xhc->MemPool, - ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingSeg0, - sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER - ); + ASSERT (ConfigDesc != NULL); - PhyAddr &= ~(0x0F); - PhyAddr |= ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci-1])->RingPCS; + MaxDci = 0; - InputContext->EP[Dci-1].PtrLo = XHC_LOW_32BIT (PhyAddr); - InputContext->EP[Dci-1].PtrHi = XHC_HIGH_32BIT (PhyAddr); + IfDesc = (USB_INTERFACE_DESCRIPTOR *)(ConfigDesc + 1); + for (Index = 0; Index < ConfigDesc->NumInterfaces; Index++) { + while ((IfDesc->DescriptorType != USB_DESC_TYPE_INTERFACE) || (IfDesc->AlternateSetting != 0)) { + IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length); + } - EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + if (IfDesc->Length < sizeof (USB_INTERFACE_DESCRIPTOR)) { + IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length); + continue; + } + + Dci = XhcInitializeEndpointContext64 (Xhc, SlotId, DeviceSpeed, InputContext, IfDesc); + if (Dci > MaxDci) { + MaxDci = Dci; } + IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length); } @@ -2897,11 +3102,561 @@ XhcSetConfigCmd64 ( ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "XhcSetConfigCmd64: Config Endpoint Failed, Status = %r\n", Status)); + } else { + Xhc->UsbDevContext[SlotId].ActiveConfiguration = ConfigDesc->ConfigurationValue; + } + + return Status; +} + +/** + Stop endpoint through XHCI's Stop_Endpoint cmd. + + @param Xhc The XHCI Instance. + @param SlotId The slot id to be configured. + @param Dci The device context index of endpoint. + + @retval EFI_SUCCESS Stop endpoint successfully. + @retval Others Failed to stop endpoint. + +**/ +EFI_STATUS +EFIAPI +XhcStopEndpoint ( + IN USB_XHCI_INSTANCE *Xhc, + IN UINT8 SlotId, + IN UINT8 Dci + ) +{ + EFI_STATUS Status; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + CMD_TRB_STOP_ENDPOINT CmdTrbStopED; + + DEBUG ((EFI_D_INFO, "XhcStopEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId, Dci)); + + // + // Send stop endpoint command to transit Endpoint from running to stop state + // + ZeroMem (&CmdTrbStopED, sizeof (CmdTrbStopED)); + CmdTrbStopED.CycleBit = 1; + CmdTrbStopED.Type = TRB_TYPE_STOP_ENDPOINT; + CmdTrbStopED.EDID = Dci; + CmdTrbStopED.SlotId = SlotId; + Status = XhcCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbStopED, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "XhcStopEndpoint: Stop Endpoint Failed, Status = %r\n", Status)); + } + + return Status; +} + +/** + Reset endpoint through XHCI's Reset_Endpoint cmd. + + @param Xhc The XHCI Instance. + @param SlotId The slot id to be configured. + @param Dci The device context index of endpoint. + + @retval EFI_SUCCESS Reset endpoint successfully. + @retval Others Failed to reset endpoint. + +**/ +EFI_STATUS +EFIAPI +XhcResetEndpoint ( + IN USB_XHCI_INSTANCE *Xhc, + IN UINT8 SlotId, + IN UINT8 Dci + ) +{ + EFI_STATUS Status; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + CMD_TRB_RESET_ENDPOINT CmdTrbResetED; + + DEBUG ((EFI_D_INFO, "XhcResetEndpoint: Slot = 0x%x, Dci = 0x%x\n", SlotId, Dci)); + + // + // Send stop endpoint command to transit Endpoint from running to stop state + // + ZeroMem (&CmdTrbResetED, sizeof (CmdTrbResetED)); + CmdTrbResetED.CycleBit = 1; + CmdTrbResetED.Type = TRB_TYPE_RESET_ENDPOINT; + CmdTrbResetED.EDID = Dci; + CmdTrbResetED.SlotId = SlotId; + Status = XhcCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbResetED, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "XhcResetEndpoint: Reset Endpoint Failed, Status = %r\n", Status)); } return Status; } +/** + Set transfer ring dequeue pointer through XHCI's Set_Tr_Dequeue_Pointer cmd. + + @param Xhc The XHCI Instance. + @param SlotId The slot id to be configured. + @param Dci The device context index of endpoint. + @param Urb The dequeue pointer of the transfer ring specified + by the urb to be updated. + + @retval EFI_SUCCESS Set transfer ring dequeue pointer succeeds. + @retval Others Failed to set transfer ring dequeue pointer. + +**/ +EFI_STATUS +EFIAPI +XhcSetTrDequeuePointer ( + IN USB_XHCI_INSTANCE *Xhc, + IN UINT8 SlotId, + IN UINT8 Dci, + IN URB *Urb + ) +{ + EFI_STATUS Status; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + CMD_SET_TR_DEQ_POINTER CmdSetTRDeq; + EFI_PHYSICAL_ADDRESS PhyAddr; + + DEBUG ((EFI_D_INFO, "XhcSetTrDequeuePointer: Slot = 0x%x, Dci = 0x%x, Urb = 0x%x\n", SlotId, Dci, Urb)); + + // + // Send stop endpoint command to transit Endpoint from running to stop state + // + ZeroMem (&CmdSetTRDeq, sizeof (CmdSetTRDeq)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, Urb->Ring->RingEnqueue, sizeof (CMD_SET_TR_DEQ_POINTER)); + CmdSetTRDeq.PtrLo = XHC_LOW_32BIT (PhyAddr) | Urb->Ring->RingPCS; + CmdSetTRDeq.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdSetTRDeq.CycleBit = 1; + CmdSetTRDeq.Type = TRB_TYPE_SET_TR_DEQUE; + CmdSetTRDeq.Endpoint = Dci; + CmdSetTRDeq.SlotId = SlotId; + Status = XhcCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdSetTRDeq, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "XhcSetTrDequeuePointer: Set TR Dequeue Pointer Failed, Status = %r\n", Status)); + } + + return Status; +} + +/** + Set interface through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI Instance. + @param SlotId The slot id to be configured. + @param DeviceSpeed The device's speed. + @param ConfigDesc The pointer to the usb device configuration descriptor. + @param Request USB device request to send. + + @retval EFI_SUCCESS Successfully set interface. + +**/ +EFI_STATUS +EFIAPI +XhcSetInterface ( + IN USB_XHCI_INSTANCE *Xhc, + IN UINT8 SlotId, + IN UINT8 DeviceSpeed, + IN USB_CONFIG_DESCRIPTOR *ConfigDesc, + IN EFI_USB_DEVICE_REQUEST *Request + ) +{ + EFI_STATUS Status; + USB_INTERFACE_DESCRIPTOR *IfDescActive; + USB_INTERFACE_DESCRIPTOR *IfDescSet; + USB_INTERFACE_DESCRIPTOR *IfDesc; + USB_ENDPOINT_DESCRIPTOR *EpDesc; + UINTN NumEp; + UINTN EpIndex; + UINT8 EpAddr; + UINT8 Direction; + UINT8 Dci; + UINT8 MaxDci; + EFI_PHYSICAL_ADDRESS PhyAddr; + VOID *RingSeg; + + CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP; + INPUT_CONTEXT *InputContext; + DEVICE_CONTEXT *OutputContext; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + + Status = EFI_SUCCESS; + + InputContext = Xhc->UsbDevContext[SlotId].InputContext; + OutputContext = Xhc->UsbDevContext[SlotId].OutputContext; + // + // XHCI 4.6.6 Configure Endpoint + // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop + // Context and Add Context flags as follows: + // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop + // Context and Add Context flags to '0'. + // + // Except the interface indicated by Reqeust->Index, no impact to other interfaces. + // So the default Drop Context and Add Context flags can be '0' to cover 1). + // + ZeroMem (InputContext, sizeof (INPUT_CONTEXT)); + CopyMem (&InputContext->Slot, &OutputContext->Slot, sizeof (SLOT_CONTEXT)); + + ASSERT (ConfigDesc != NULL); + + MaxDci = 0; + + IfDescActive = NULL; + IfDescSet = NULL; + + IfDesc = (USB_INTERFACE_DESCRIPTOR *)(ConfigDesc + 1); + while ((UINTN) IfDesc < ((UINTN) ConfigDesc + ConfigDesc->TotalLength)) { + if ((IfDesc->DescriptorType == USB_DESC_TYPE_INTERFACE) && (IfDesc->Length >= sizeof (USB_INTERFACE_DESCRIPTOR))) { + if (IfDesc->InterfaceNumber == (UINT8) Request->Index) { + if (IfDesc->AlternateSetting == Xhc->UsbDevContext[SlotId].ActiveAlternateSetting[IfDesc->InterfaceNumber]) { + // + // Find out the active interface descriptor. + // + IfDescActive = IfDesc; + } else if (IfDesc->AlternateSetting == (UINT8) Request->Value) { + // + // Find out the interface descriptor to set. + // + IfDescSet = IfDesc; + } + } + } + IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length); + } + + // + // XHCI 4.6.6 Configure Endpoint + // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop + // Context and Add Context flags as follows: + // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set + // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context. + // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set + // the Drop Context flag to '1' and Add Context flag to '0'. + // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context + // and Add Context flags shall be set to '1'. + // + // Below codes are to cover 2), 3) and 4). + // + + if ((IfDescActive != NULL) && (IfDescSet != NULL)) { + NumEp = IfDescActive->NumEndpoints; + EpDesc = (USB_ENDPOINT_DESCRIPTOR *) (IfDescActive + 1); + for (EpIndex = 0; EpIndex < NumEp; EpIndex++) { + while (EpDesc->DescriptorType != USB_DESC_TYPE_ENDPOINT) { + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + } + + if (EpDesc->Length < sizeof (USB_ENDPOINT_DESCRIPTOR)) { + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + continue; + } + + EpAddr = (UINT8) (EpDesc->EndpointAddress & 0x0F); + Direction = (UINT8) ((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut); + + Dci = XhcEndpointToDci (EpAddr, Direction); + ASSERT (Dci < 32); + if (Dci > MaxDci) { + MaxDci = Dci; + } + // + // XHCI 4.3.6 - Setting Alternate Interfaces + // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting. + // + Status = XhcStopEndpoint (Xhc, SlotId, Dci); + if (EFI_ERROR (Status)) { + return Status; + } + // + // XHCI 4.3.6 - Setting Alternate Interfaces + // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting. + // + if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1] != NULL) { + RingSeg = ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1])->RingSeg0; + if (RingSeg != NULL) { + UsbHcFreeMem (Xhc->MemPool, RingSeg, sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER); + } + FreePool (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1]); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1] = NULL; + } + + // + // Set the Drop Context flag to '1'. + // + InputContext->InputControlContext.Dword1 |= (BIT0 << Dci); + + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + } + + // + // XHCI 4.3.6 - Setting Alternate Interfaces + // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate + // Interface setting, to '0'. + // + // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function. + // + + // + // XHCI 4.3.6 - Setting Alternate Interfaces + // 4) For each endpoint enabled by the Configure Endpoint Command: + // a. Allocate a Transfer Ring. + // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'. + // c. Initialize the Endpoint Context data structure. + // + Dci = XhcInitializeEndpointContext (Xhc, SlotId, DeviceSpeed, InputContext, IfDescSet); + if (Dci > MaxDci) { + MaxDci = Dci; + } + + InputContext->InputControlContext.Dword2 |= BIT0; + InputContext->Slot.ContextEntries = MaxDci; + // + // XHCI 4.3.6 - Setting Alternate Interfaces + // 5) Issue and successfully complete a Configure Endpoint Command. + // + ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT)); + CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr); + CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdTrbCfgEP.CycleBit = 1; + CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT; + CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId; + DEBUG ((EFI_D_INFO, "SetInterface: Configure Endpoint\n")); + Status = XhcCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SetInterface: Config Endpoint Failed, Status = %r\n", Status)); + } else { + // + // Update the active AlternateSetting. + // + Xhc->UsbDevContext[SlotId].ActiveAlternateSetting[(UINT8) Request->Index] = (UINT8) Request->Value; + } + } + + return Status; +} + +/** + Set interface through XHCI's Configure_Endpoint cmd. + + @param Xhc The XHCI Instance. + @param SlotId The slot id to be configured. + @param DeviceSpeed The device's speed. + @param ConfigDesc The pointer to the usb device configuration descriptor. + @param Request USB device request to send. + + @retval EFI_SUCCESS Successfully set interface. + +**/ +EFI_STATUS +EFIAPI +XhcSetInterface64 ( + IN USB_XHCI_INSTANCE *Xhc, + IN UINT8 SlotId, + IN UINT8 DeviceSpeed, + IN USB_CONFIG_DESCRIPTOR *ConfigDesc, + IN EFI_USB_DEVICE_REQUEST *Request + ) +{ + EFI_STATUS Status; + USB_INTERFACE_DESCRIPTOR *IfDescActive; + USB_INTERFACE_DESCRIPTOR *IfDescSet; + USB_INTERFACE_DESCRIPTOR *IfDesc; + USB_ENDPOINT_DESCRIPTOR *EpDesc; + UINTN NumEp; + UINTN EpIndex; + UINT8 EpAddr; + UINT8 Direction; + UINT8 Dci; + UINT8 MaxDci; + EFI_PHYSICAL_ADDRESS PhyAddr; + VOID *RingSeg; + + CMD_TRB_CONFIG_ENDPOINT CmdTrbCfgEP; + INPUT_CONTEXT_64 *InputContext; + DEVICE_CONTEXT_64 *OutputContext; + EVT_TRB_COMMAND_COMPLETION *EvtTrb; + + Status = EFI_SUCCESS; + + InputContext = Xhc->UsbDevContext[SlotId].InputContext; + OutputContext = Xhc->UsbDevContext[SlotId].OutputContext; + // + // XHCI 4.6.6 Configure Endpoint + // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop + // Context and Add Context flags as follows: + // 1) If an endpoint is not modified by the Alternate Interface setting, then software shall set the Drop + // Context and Add Context flags to '0'. + // + // Except the interface indicated by Reqeust->Index, no impact to other interfaces. + // So the default Drop Context and Add Context flags can be '0' to cover 1). + // + ZeroMem (InputContext, sizeof (INPUT_CONTEXT_64)); + CopyMem (&InputContext->Slot, &OutputContext->Slot, sizeof (SLOT_CONTEXT_64)); + + ASSERT (ConfigDesc != NULL); + + MaxDci = 0; + + IfDescActive = NULL; + IfDescSet = NULL; + + IfDesc = (USB_INTERFACE_DESCRIPTOR *)(ConfigDesc + 1); + while ((UINTN) IfDesc < ((UINTN) ConfigDesc + ConfigDesc->TotalLength)) { + if ((IfDesc->DescriptorType == USB_DESC_TYPE_INTERFACE) && (IfDesc->Length >= sizeof (USB_INTERFACE_DESCRIPTOR))) { + if (IfDesc->InterfaceNumber == (UINT8) Request->Index) { + if (IfDesc->AlternateSetting == Xhc->UsbDevContext[SlotId].ActiveAlternateSetting[IfDesc->InterfaceNumber]) { + // + // Find out the active interface descriptor. + // + IfDescActive = IfDesc; + } else if (IfDesc->AlternateSetting == (UINT8) Request->Value) { + // + // Find out the interface descriptor to set. + // + IfDescSet = IfDesc; + } + } + } + IfDesc = (USB_INTERFACE_DESCRIPTOR *)((UINTN)IfDesc + IfDesc->Length); + } + + // + // XHCI 4.6.6 Configure Endpoint + // When this command is used to "Set an Alternate Interface on a device", software shall set the Drop + // Context and Add Context flags as follows: + // 2) If an endpoint previously disabled, is enabled by the Alternate Interface setting, then software shall set + // the Drop Context flag to '0' and Add Context flag to '1', and initialize the Input Endpoint Context. + // 3) If an endpoint previously enabled, is disabled by the Alternate Interface setting, then software shall set + // the Drop Context flag to '1' and Add Context flag to '0'. + // 4) If a parameter of an enabled endpoint is modified by an Alternate Interface setting, the Drop Context + // and Add Context flags shall be set to '1'. + // + // Below codes are to cover 2), 3) and 4). + // + + if ((IfDescActive != NULL) && (IfDescSet != NULL)) { + NumEp = IfDescActive->NumEndpoints; + EpDesc = (USB_ENDPOINT_DESCRIPTOR *) (IfDescActive + 1); + for (EpIndex = 0; EpIndex < NumEp; EpIndex++) { + while (EpDesc->DescriptorType != USB_DESC_TYPE_ENDPOINT) { + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + } + + if (EpDesc->Length < sizeof (USB_ENDPOINT_DESCRIPTOR)) { + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + continue; + } + + EpAddr = (UINT8) (EpDesc->EndpointAddress & 0x0F); + Direction = (UINT8) ((EpDesc->EndpointAddress & 0x80) ? EfiUsbDataIn : EfiUsbDataOut); + + Dci = XhcEndpointToDci (EpAddr, Direction); + ASSERT (Dci < 32); + if (Dci > MaxDci) { + MaxDci = Dci; + } + // + // XHCI 4.3.6 - Setting Alternate Interfaces + // 1) Stop any Running Transfer Rings affected by the Alternate Interface setting. + // + Status = XhcStopEndpoint (Xhc, SlotId, Dci); + if (EFI_ERROR (Status)) { + return Status; + } + // + // XHCI 4.3.6 - Setting Alternate Interfaces + // 2) Free Transfer Rings of all endpoints that will be affected by the Alternate Interface setting. + // + if (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1] != NULL) { + RingSeg = ((TRANSFER_RING *)(UINTN)Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1])->RingSeg0; + if (RingSeg != NULL) { + UsbHcFreeMem (Xhc->MemPool, RingSeg, sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER); + } + FreePool (Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1]); + Xhc->UsbDevContext[SlotId].EndpointTransferRing[Dci - 1] = NULL; + } + + // + // Set the Drop Context flag to '1'. + // + InputContext->InputControlContext.Dword1 |= (BIT0 << Dci); + + EpDesc = (USB_ENDPOINT_DESCRIPTOR *)((UINTN)EpDesc + EpDesc->Length); + } + + // + // XHCI 4.3.6 - Setting Alternate Interfaces + // 3) Clear all the Endpoint Context fields of each endpoint that will be disabled by the Alternate + // Interface setting, to '0'. + // + // The step 3) has been covered by the ZeroMem () to InputContext at the start of the function. + // + + // + // XHCI 4.3.6 - Setting Alternate Interfaces + // 4) For each endpoint enabled by the Configure Endpoint Command: + // a. Allocate a Transfer Ring. + // b. Initialize the Transfer Ring Segment(s) by clearing all fields of all TRBs to '0'. + // c. Initialize the Endpoint Context data structure. + // + Dci = XhcInitializeEndpointContext64 (Xhc, SlotId, DeviceSpeed, InputContext, IfDescSet); + if (Dci > MaxDci) { + MaxDci = Dci; + } + + InputContext->InputControlContext.Dword2 |= BIT0; + InputContext->Slot.ContextEntries = MaxDci; + // + // XHCI 4.3.6 - Setting Alternate Interfaces + // 5) Issue and successfully complete a Configure Endpoint Command. + // + ZeroMem (&CmdTrbCfgEP, sizeof (CmdTrbCfgEP)); + PhyAddr = UsbHcGetPciAddrForHostAddr (Xhc->MemPool, InputContext, sizeof (INPUT_CONTEXT_64)); + CmdTrbCfgEP.PtrLo = XHC_LOW_32BIT (PhyAddr); + CmdTrbCfgEP.PtrHi = XHC_HIGH_32BIT (PhyAddr); + CmdTrbCfgEP.CycleBit = 1; + CmdTrbCfgEP.Type = TRB_TYPE_CON_ENDPOINT; + CmdTrbCfgEP.SlotId = Xhc->UsbDevContext[SlotId].SlotId; + DEBUG ((EFI_D_INFO, "SetInterface64: Configure Endpoint\n")); + Status = XhcCmdTransfer ( + Xhc, + (TRB_TEMPLATE *) (UINTN) &CmdTrbCfgEP, + XHC_GENERIC_TIMEOUT, + (TRB_TEMPLATE **) (UINTN) &EvtTrb + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SetInterface64: Config Endpoint Failed, Status = %r\n", Status)); + } else { + // + // Update the active AlternateSetting. + // + Xhc->UsbDevContext[SlotId].ActiveAlternateSetting[(UINT8) Request->Index] = (UINT8) Request->Value; + } + } + + return Status; +} /** Evaluate the endpoint 0 context through XHCI's Evaluate_Context cmd.