From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.c | 225 +++ .../MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.h | 139 ++ roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c | 1883 ++++++++++++++++++++ roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.h | 215 +++ roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.c | 71 + roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.h | 41 + roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf | 80 + roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.uni | 17 + .../MdeModulePkg/Bus/Pci/UhciDxe/UhciDxeExtra.uni | 14 + roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c | 701 ++++++++ roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.h | 266 +++ roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.c | 275 +++ roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.h | 242 +++ roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.c | 1040 +++++++++++ roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.h | 265 +++ roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.c | 558 ++++++ roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.h | 155 ++ 17 files changed, 6187 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.h (limited to 'roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe') diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.c new file mode 100644 index 000000000..572c11c28 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.c @@ -0,0 +1,225 @@ +/** @file + UEFI Component Name(2) protocol implementation for UHCI driver. + +Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Uhci.h" + + +// +// EFI Component Name Protocol +// + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUhciComponentName = { + UhciComponentNameGetDriverName, + UhciComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUhciComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UhciComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UhciComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUhciDriverNameTable[] = { + { "eng;en", L"Usb Uhci Driver" }, + { NULL, NULL } +}; + + +// +// EFI Component Name Functions +// + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UhciComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUhciDriverNameTable, + DriverName, + (BOOLEAN)(This == &gUhciComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UhciComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + USB_HC_DEV *UhciDev; + EFI_USB2_HC_PROTOCOL *Usb2Hc; + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllerHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gUhciDriverBinding.DriverBindingHandle, + &gEfiPciIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the device context + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiUsb2HcProtocolGuid, + (VOID **) &Usb2Hc, + gUhciDriverBinding.DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + UhciDev = UHC_FROM_USB2_HC_PROTO (Usb2Hc); + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + UhciDev->CtrlNameTable, + ControllerName, + (BOOLEAN)(This == &gUhciComponentName) + ); + +} diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.h b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.h new file mode 100644 index 000000000..c3b5674e9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/ComponentName.h @@ -0,0 +1,139 @@ +/** @file + + This file contains the delarations for componet name routines. + +Copyright (c) 2008 - 2011, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _COMPONENT_NAME_H_ +#define _COMPONENT_NAME_H_ + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UhciComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UhciComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c new file mode 100644 index 000000000..3ec5ecf0e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.c @@ -0,0 +1,1883 @@ +/** @file + + The UHCI driver model and HC protocol routines. + +Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Uhci.h" + + +EFI_DRIVER_BINDING_PROTOCOL gUhciDriverBinding = { + UhciDriverBindingSupported, + UhciDriverBindingStart, + UhciDriverBindingStop, + 0x20, + NULL, + NULL +}; + +/** + Provides software reset for the USB host controller according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param Attributes A bit mask of the reset operation to perform. See + below for a list of the supported bit mask values. + + @return EFI_SUCCESS The reset operation succeeded. + @return EFI_INVALID_PARAMETER Attributes is not valid. + @return EFI_UNSUPPORTED This type of reset is not currently supported. + @return EFI_DEVICE_ERROR Other errors. + +**/ +EFI_STATUS +EFIAPI +Uhci2Reset ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT16 Attributes + ) +{ + USB_HC_DEV *Uhc; + EFI_TPL OldTpl; + + if ((Attributes == EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG) || + (Attributes == EFI_USB_HC_RESET_HOST_WITH_DEBUG)) { + return EFI_UNSUPPORTED; + } + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + + if (Uhc->DevicePath != NULL) { + // + // Report Status Code to indicate reset happens + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_IO_BUS_USB | EFI_IOB_PC_RESET), + Uhc->DevicePath + ); + } + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + + switch (Attributes) { + case EFI_USB_HC_RESET_GLOBAL: + // + // Stop schedule and set the Global Reset bit in the command register + // + UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); + UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_GRESET); + + gBS->Stall (UHC_ROOT_PORT_RESET_STALL); + + // + // Clear the Global Reset bit to zero. + // + UhciClearRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_GRESET); + + gBS->Stall (UHC_ROOT_PORT_RECOVERY_STALL); + break; + + case EFI_USB_HC_RESET_HOST_CONTROLLER: + // + // Stop schedule and set Host Controller Reset bit to 1 + // + UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); + UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_HCRESET); + + gBS->Stall (UHC_ROOT_PORT_RECOVERY_STALL); + break; + + default: + goto ON_INVAILD_PARAMETER; + } + + // + // Delete all old transactions on the USB bus, then + // reinitialize the frame list + // + UhciFreeAllAsyncReq (Uhc); + UhciDestoryFrameList (Uhc); + UhciInitFrameList (Uhc); + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +ON_INVAILD_PARAMETER: + + gBS->RestoreTPL (OldTpl); + + return EFI_INVALID_PARAMETER; +} + + +/** + Retrieves current state of the USB host controller according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param State Variable to receive current device state. + + @return EFI_SUCCESS The state is returned. + @return EFI_INVALID_PARAMETER State is not valid. + @return EFI_DEVICE_ERROR Other errors. + +**/ +EFI_STATUS +EFIAPI +Uhci2GetState ( + IN EFI_USB2_HC_PROTOCOL *This, + OUT EFI_USB_HC_STATE *State + ) +{ + USB_HC_DEV *Uhc; + UINT16 UsbSts; + UINT16 UsbCmd; + + if (State == NULL) { + return EFI_INVALID_PARAMETER; + } + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + + UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); + UsbSts = UhciReadReg (Uhc->PciIo, USBSTS_OFFSET); + + if ((UsbCmd & USBCMD_EGSM) !=0 ) { + *State = EfiUsbHcStateSuspend; + + } else if ((UsbSts & USBSTS_HCH) != 0) { + *State = EfiUsbHcStateHalt; + + } else { + *State = EfiUsbHcStateOperational; + } + + return EFI_SUCCESS; +} + + +/** + Sets the USB host controller to a specific state according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param State Indicates the state of the host controller that will + be set. + + @return EFI_SUCCESS Host controller was successfully placed in the state. + @return EFI_INVALID_PARAMETER State is invalid. + @return EFI_DEVICE_ERROR Failed to set the state. + +**/ +EFI_STATUS +EFIAPI +Uhci2SetState ( + IN EFI_USB2_HC_PROTOCOL *This, + IN EFI_USB_HC_STATE State + ) +{ + EFI_USB_HC_STATE CurState; + USB_HC_DEV *Uhc; + EFI_TPL OldTpl; + EFI_STATUS Status; + UINT16 UsbCmd; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + Status = Uhci2GetState (This, &CurState); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + if (CurState == State) { + return EFI_SUCCESS; + } + + Status = EFI_SUCCESS; + OldTpl = gBS->RaiseTPL (UHCI_TPL); + + switch (State) { + case EfiUsbHcStateHalt: + Status = UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); + break; + + case EfiUsbHcStateOperational: + UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); + + if (CurState == EfiUsbHcStateHalt) { + // + // Set Run/Stop bit to 1, also set the bandwidht reclamation + // point to 64 bytes + // + UsbCmd |= USBCMD_RS | USBCMD_MAXP; + UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd); + + } else if (CurState == EfiUsbHcStateSuspend) { + // + // If FGR(Force Global Resume) bit is 0, set it + // + if ((UsbCmd & USBCMD_FGR) == 0) { + UsbCmd |= USBCMD_FGR; + UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd); + } + + // + // wait 20ms to let resume complete (20ms is specified by UHCI spec) + // + gBS->Stall (UHC_FORCE_GLOBAL_RESUME_STALL); + + // + // Write FGR bit to 0 and EGSM(Enter Global Suspend Mode) bit to 0 + // + UsbCmd &= ~USBCMD_FGR; + UsbCmd &= ~USBCMD_EGSM; + UsbCmd |= USBCMD_RS; + UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd); + } + + break; + + case EfiUsbHcStateSuspend: + Status = Uhci2SetState (This, EfiUsbHcStateHalt); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + // + // Set Enter Global Suspend Mode bit to 1. + // + UsbCmd = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); + UsbCmd |= USBCMD_EGSM; + UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, UsbCmd); + break; + + default: + Status = EFI_INVALID_PARAMETER; + break; + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Retrieves capabilities of USB host controller according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param MaxSpeed A pointer to the max speed USB host controller + supports. + @param PortNumber A pointer to the number of root hub ports. + @param Is64BitCapable A pointer to an integer to show whether USB host + controller supports 64-bit memory addressing. + + @return EFI_SUCCESS capabilities were retrieved successfully. + @return EFI_INVALID_PARAMETER MaxSpeed or PortNumber or Is64BitCapable is NULL. + @return EFI_DEVICE_ERROR An error was encountered. + +**/ +EFI_STATUS +EFIAPI +Uhci2GetCapability ( + IN EFI_USB2_HC_PROTOCOL *This, + OUT UINT8 *MaxSpeed, + OUT UINT8 *PortNumber, + OUT UINT8 *Is64BitCapable + ) +{ + USB_HC_DEV *Uhc; + UINT32 Offset; + UINT16 PortSC; + UINT32 Index; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + + if ((NULL == MaxSpeed) || (NULL == PortNumber) || (NULL == Is64BitCapable)) { + return EFI_INVALID_PARAMETER; + } + + *MaxSpeed = EFI_USB_SPEED_FULL; + *Is64BitCapable = (UINT8) FALSE; + + *PortNumber = 0; + + for (Index = 0; Index < USB_MAX_ROOTHUB_PORT; Index++) { + Offset = USBPORTSC_OFFSET + Index * 2; + PortSC = UhciReadReg (Uhc->PciIo, Offset); + + // + // Port status's bit 7 is reserved and always returns 1 if + // the port number is valid. Intel's UHCI (in EHCI controller) + // returns 0 in this bit if port number is invalid. Also, if + // PciIo IoRead returns error, 0xFFFF is returned to caller. + // + if (((PortSC & 0x80) == 0) || (PortSC == 0xFFFF)) { + break; + } + (*PortNumber)++; + } + + Uhc->RootPorts = *PortNumber; + + DEBUG ((EFI_D_INFO, "Uhci2GetCapability: %d ports\n", (UINT32)Uhc->RootPorts)); + return EFI_SUCCESS; +} + + +/** + Retrieves the current status of a USB root hub port according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL. + @param PortNumber The port to get status. + @param PortStatus A pointer to the current port status bits and port + status change bits. + + @return EFI_SUCCESS status of the USB root hub port was returned in PortStatus. + @return EFI_INVALID_PARAMETER PortNumber is invalid. + @return EFI_DEVICE_ERROR Can't read register. + +**/ +EFI_STATUS +EFIAPI +Uhci2GetRootHubPortStatus ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus + ) +{ + USB_HC_DEV *Uhc; + UINT32 Offset; + UINT16 PortSC; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + + if (PortStatus == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (PortNumber >= Uhc->RootPorts) { + return EFI_INVALID_PARAMETER; + } + + Offset = USBPORTSC_OFFSET + PortNumber * 2; + PortStatus->PortStatus = 0; + PortStatus->PortChangeStatus = 0; + + PortSC = UhciReadReg (Uhc->PciIo, Offset); + + if ((PortSC & USBPORTSC_CCS) != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_CONNECTION; + } + + if ((PortSC & USBPORTSC_PED) != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_ENABLE; + } + + if ((PortSC & USBPORTSC_SUSP) != 0) { + DEBUG ((EFI_D_INFO, "Uhci2GetRootHubPortStatus: port %d is suspended\n", PortNumber)); + PortStatus->PortStatus |= USB_PORT_STAT_SUSPEND; + } + + if ((PortSC & USBPORTSC_PR) != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_RESET; + } + + if ((PortSC & USBPORTSC_LSDA) != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED; + } + + // + // CHC will always return one in port owner bit + // + PortStatus->PortStatus |= USB_PORT_STAT_OWNER; + + if ((PortSC & USBPORTSC_CSC) != 0) { + PortStatus->PortChangeStatus |= USB_PORT_STAT_C_CONNECTION; + } + + if ((PortSC & USBPORTSC_PEDC) != 0) { + PortStatus->PortChangeStatus |= USB_PORT_STAT_C_ENABLE; + } + + return EFI_SUCCESS; +} + + +/** + Sets a feature for the specified root hub port according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL. + @param PortNumber Specifies the root hub port whose feature is + requested to be set. + @param PortFeature Indicates the feature selector associated with the + feature set request. + + @return EFI_SUCCESS PortFeature was set for the root port. + @return EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + @return EFI_DEVICE_ERROR Can't read register. + +**/ +EFI_STATUS +EFIAPI +Uhci2SetRootHubPortFeature ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + USB_HC_DEV *Uhc; + EFI_TPL OldTpl; + UINT32 Offset; + UINT16 PortSC; + UINT16 Command; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + + if (PortNumber >= Uhc->RootPorts) { + return EFI_INVALID_PARAMETER; + } + + Offset = USBPORTSC_OFFSET + PortNumber * 2; + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + PortSC = UhciReadReg (Uhc->PciIo, Offset); + + switch (PortFeature) { + case EfiUsbPortSuspend: + Command = UhciReadReg (Uhc->PciIo, USBCMD_OFFSET); + if ((Command & USBCMD_EGSM) == 0) { + // + // if global suspend is not active, can set port suspend + // + PortSC &= 0xfff5; + PortSC |= USBPORTSC_SUSP; + } + break; + + case EfiUsbPortReset: + PortSC &= 0xfff5; + PortSC |= USBPORTSC_PR; + break; + + case EfiUsbPortPower: + // + // No action + // + break; + + case EfiUsbPortEnable: + PortSC &= 0xfff5; + PortSC |= USBPORTSC_PED; + break; + + default: + gBS->RestoreTPL (OldTpl); + return EFI_INVALID_PARAMETER; + } + + UhciWriteReg (Uhc->PciIo, Offset, PortSC); + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Clears a feature for the specified root hub port according to Uefi 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param PortNumber Specifies the root hub port whose feature is + requested to be cleared. + @param PortFeature Indicates the feature selector associated with the + feature clear request. + + @return EFI_SUCCESS PortFeature was cleared for the USB root hub port. + @return EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid. + @return EFI_DEVICE_ERROR Can't read register. + +**/ +EFI_STATUS +EFIAPI +Uhci2ClearRootHubPortFeature ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature + ) +{ + USB_HC_DEV *Uhc; + EFI_TPL OldTpl; + UINT32 Offset; + UINT16 PortSC; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + + if (PortNumber >= Uhc->RootPorts) { + return EFI_INVALID_PARAMETER; + } + + Offset = USBPORTSC_OFFSET + PortNumber * 2; + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + PortSC = UhciReadReg (Uhc->PciIo, Offset); + + switch (PortFeature) { + case EfiUsbPortEnable: + PortSC &= 0xfff5; + PortSC &= ~USBPORTSC_PED; + break; + + case EfiUsbPortSuspend: + // + // Cause a resume on the specified port if in suspend mode. + // + PortSC &= 0xfff5; + PortSC &= ~USBPORTSC_SUSP; + break; + + case EfiUsbPortPower: + // + // No action + // + break; + + case EfiUsbPortReset: + PortSC &= 0xfff5; + PortSC &= ~USBPORTSC_PR; + break; + + case EfiUsbPortConnectChange: + PortSC &= 0xfff5; + PortSC |= USBPORTSC_CSC; + break; + + case EfiUsbPortEnableChange: + PortSC &= 0xfff5; + PortSC |= USBPORTSC_PEDC; + break; + + case EfiUsbPortSuspendChange: + // + // Root hub does not support this + // + break; + + case EfiUsbPortOverCurrentChange: + // + // Root hub does not support this + // + break; + + case EfiUsbPortResetChange: + // + // Root hub does not support this + // + break; + + default: + gBS->RestoreTPL (OldTpl); + return EFI_INVALID_PARAMETER; + } + + UhciWriteReg (Uhc->PciIo, Offset, PortSC); + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Submits control transfer to a target USB device according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param DeviceAddress Target device address. + @param DeviceSpeed Device speed. + @param MaximumPacketLength Maximum packet size of the target endpoint. + @param Request USB device request to send. + @param TransferDirection Data direction of the Data stage in control transfer. + @param Data Data to transmit/receive in data stage. + @param DataLength Length of the data. + @param TimeOut Maximum time, in microseconds, for transfer to complete. + @param Translator Transaction translator to be used by this device. + @param TransferResult Variable to receive the transfer result. + + @return EFI_SUCCESS The control transfer was completed successfully. + @return EFI_OUT_OF_RESOURCES Failed due to lack of resource. + @return EFI_INVALID_PARAMETER Some parameters are invalid. + @return EFI_TIMEOUT Failed due to timeout. + @return EFI_DEVICE_ERROR Failed due to host controller or device error. + +**/ +EFI_STATUS +EFIAPI +Uhci2ControlTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION TransferDirection, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + USB_HC_DEV *Uhc; + UHCI_TD_SW *TDs; + EFI_TPL OldTpl; + EFI_STATUS Status; + UHCI_QH_RESULT QhResult; + UINT8 PktId; + UINT8 *RequestPhy; + VOID *RequestMap; + UINT8 *DataPhy; + VOID *DataMap; + BOOLEAN IsSlowDevice; + UINTN TransferDataLength; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + TDs = NULL; + DataPhy = NULL; + DataMap = NULL; + RequestPhy = NULL; + RequestMap = NULL; + + IsSlowDevice = (BOOLEAN) ((EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE); + + // + // Parameters Checking + // + if (Request == NULL || TransferResult == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IsSlowDevice && (MaximumPacketLength != 8)) { + return EFI_INVALID_PARAMETER; + } + + if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) && + (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) { + + return EFI_INVALID_PARAMETER; + } + + if ((TransferDirection != EfiUsbNoData) && (Data == NULL || DataLength == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (TransferDirection == EfiUsbNoData) { + TransferDataLength = 0; + } else { + TransferDataLength = *DataLength; + } + + *TransferResult = EFI_USB_ERR_SYSTEM; + Status = EFI_DEVICE_ERROR; + + // + // If errors exist that cause host controller halt, + // clear status then return EFI_DEVICE_ERROR. + // + UhciAckAllInterrupt (Uhc); + + if (!UhciIsHcWorking (Uhc->PciIo)) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + + // + // Map the Request and data for bus master access, + // then create a list of TD for this transfer + // + Status = UhciMapUserRequest (Uhc, Request, &RequestPhy, &RequestMap); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = UhciMapUserData (Uhc, TransferDirection, Data, DataLength, &PktId, &DataPhy, &DataMap); + + if (EFI_ERROR (Status)) { + Uhc->PciIo->Unmap (Uhc->PciIo, RequestMap); + goto ON_EXIT; + } + + TDs = UhciCreateCtrlTds ( + Uhc, + DeviceAddress, + PktId, + (UINT8*)Request, + RequestPhy, + (UINT8*)Data, + DataPhy, + TransferDataLength, + (UINT8) MaximumPacketLength, + IsSlowDevice + ); + + if (TDs == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto UNMAP_DATA; + } + + // + // According to the speed of the end point, link + // the TD to corrosponding queue head, then check + // the execution result + // + UhciLinkTdToQh (Uhc, Uhc->CtrlQh, TDs); + Status = UhciExecuteTransfer (Uhc, Uhc->CtrlQh, TDs, TimeOut, IsSlowDevice, &QhResult); + UhciUnlinkTdFromQh (Uhc->CtrlQh, TDs); + + Uhc->PciIo->Flush (Uhc->PciIo); + + *TransferResult = QhResult.Result; + + if (DataLength != NULL) { + *DataLength = QhResult.Complete; + } + + UhciDestoryTds (Uhc, TDs); + +UNMAP_DATA: + Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); + Uhc->PciIo->Unmap (Uhc->PciIo, RequestMap); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Submits bulk transfer to a bulk endpoint of a USB device. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param DeviceAddress Target device address. + @param EndPointAddress Endpoint number and direction. + @param DeviceSpeed Device speed. + @param MaximumPacketLength Maximum packet size of the target endpoint. + @param DataBuffersNumber Number of data buffers prepared for the transfer. + @param Data Array of pointers to the buffers of data. + @param DataLength On input, size of the data buffer, On output, + actually transferred data size. + @param DataToggle On input, data toggle to use; On output, next data toggle. + @param TimeOut Maximum time out, in microseconds. + @param Translator A pointr to the transaction translator data. + @param TransferResult Variable to receive transfer result. + + @return EFI_SUCCESS The bulk transfer was completed successfully. + @return EFI_OUT_OF_RESOURCES Failed due to lack of resource. + @return EFI_INVALID_PARAMETER Some parameters are invalid. + @return EFI_TIMEOUT Failed due to timeout. + @return EFI_DEVICE_ERROR Failed due to host controller or device error. + +**/ +EFI_STATUS +EFIAPI +Uhci2BulkTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN UINT8 DataBuffersNumber, + IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM], + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + EFI_USB_DATA_DIRECTION Direction; + EFI_TPL OldTpl; + USB_HC_DEV *Uhc; + UHCI_TD_SW *TDs; + UHCI_QH_SW *BulkQh; + UHCI_QH_RESULT QhResult; + EFI_STATUS Status; + UINT8 PktId; + UINT8 *DataPhy; + VOID *DataMap; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + DataPhy = NULL; + DataMap = NULL; + + if (DeviceSpeed == EFI_USB_SPEED_LOW) { + return EFI_INVALID_PARAMETER; + } + + if ((DataLength == NULL) || (*DataLength == 0) || (Data == NULL) || (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 1) && (*DataToggle != 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) && + (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) { + return EFI_INVALID_PARAMETER; + } + + *TransferResult = EFI_USB_ERR_SYSTEM; + Status = EFI_OUT_OF_RESOURCES; + + // + // If has errors that cause host controller halt, + // then return EFI_DEVICE_ERROR directly. + // + UhciAckAllInterrupt (Uhc); + + if (!UhciIsHcWorking (Uhc->PciIo)) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + + // + // Map the source data buffer for bus master access, + // then create a list of TDs + // + if ((EndPointAddress & 0x80) != 0) { + Direction = EfiUsbDataIn; + } else { + Direction = EfiUsbDataOut; + } + + Status = UhciMapUserData (Uhc, Direction, *Data, DataLength, &PktId, &DataPhy, &DataMap); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = EFI_OUT_OF_RESOURCES; + TDs = UhciCreateBulkOrIntTds ( + Uhc, + DeviceAddress, + EndPointAddress, + PktId, + (UINT8 *)*Data, + DataPhy, + *DataLength, + DataToggle, + (UINT8) MaximumPacketLength, + FALSE + ); + + if (TDs == NULL) { + Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); + goto ON_EXIT; + } + + + // + // Link the TDs to bulk queue head. According to the platfore + // defintion of UHCI_NO_BW_RECLAMATION, BulkQh is either configured + // to do full speed bandwidth reclamation or not. + // + BulkQh = Uhc->BulkQh; + + UhciLinkTdToQh (Uhc, BulkQh, TDs); + Status = UhciExecuteTransfer (Uhc, BulkQh, TDs, TimeOut, FALSE, &QhResult); + UhciUnlinkTdFromQh (BulkQh, TDs); + + Uhc->PciIo->Flush (Uhc->PciIo); + + *TransferResult = QhResult.Result; + *DataToggle = QhResult.NextToggle; + *DataLength = QhResult.Complete; + + UhciDestoryTds (Uhc, TDs); + Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Submits an asynchronous interrupt transfer to an + interrupt endpoint of a USB device according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param DeviceAddress Target device address. + @param EndPointAddress Endpoint number and direction. + @param DeviceSpeed Device speed. + @param MaximumPacketLength Maximum packet size of the target endpoint. + @param IsNewTransfer If TRUE, submit a new transfer, if FALSE cancel old transfer. + @param DataToggle On input, data toggle to use; On output, next data toggle. + @param PollingInterval Interrupt poll rate in milliseconds. + @param DataLength On input, size of the data buffer, On output, + actually transferred data size. + @param Translator A pointr to the transaction translator data. + @param CallBackFunction Function to call periodically. + @param Context User context. + + @return EFI_SUCCESS Transfer was submitted. + @return EFI_INVALID_PARAMETER Some parameters are invalid. + @return EFI_OUT_OF_RESOURCES Failed due to a lack of resources. + @return EFI_DEVICE_ERROR Can't read register. + +**/ +EFI_STATUS +EFIAPI +Uhci2AsyncInterruptTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN BOOLEAN IsNewTransfer, + IN OUT UINT8 *DataToggle, + IN UINTN PollingInterval, + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction, + IN VOID *Context + ) +{ + USB_HC_DEV *Uhc; + BOOLEAN IsSlowDevice; + UHCI_QH_SW *Qh; + UHCI_TD_SW *IntTds; + EFI_TPL OldTpl; + EFI_STATUS Status; + UINT8 *DataPtr; + UINT8 *DataPhy; + UINT8 PktId; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + Qh = NULL; + IntTds = NULL; + DataPtr = NULL; + DataPhy = NULL; + + IsSlowDevice = (BOOLEAN) ((EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE); + + if ((EndPointAddress & 0x80) == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Delete Async interrupt transfer request + // + if (!IsNewTransfer) { + OldTpl = gBS->RaiseTPL (UHCI_TPL); + Status = UhciRemoveAsyncReq (Uhc, DeviceAddress, EndPointAddress, DataToggle); + + gBS->RestoreTPL (OldTpl); + return Status; + } + + if (PollingInterval < 1 || PollingInterval > 255) { + return EFI_INVALID_PARAMETER; + } + + if (DataLength == 0) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 1) && (*DataToggle != 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // If has errors that cause host controller halt, + // then return EFI_DEVICE_ERROR directly. + // + UhciAckAllInterrupt (Uhc); + + if (!UhciIsHcWorking (Uhc->PciIo)) { + return EFI_DEVICE_ERROR; + } + + if ((EndPointAddress & 0x80) == 0) { + PktId = OUTPUT_PACKET_ID; + } else { + PktId = INPUT_PACKET_ID; + } + + // + // Allocate and map source data buffer for bus master access. + // + DataPtr = UsbHcAllocateMem (Uhc->MemPool, DataLength); + + if (DataPtr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DataPhy = (UINT8 *) (UINTN) UsbHcGetPciAddressForHostMem (Uhc->MemPool, DataPtr, DataLength); + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + + Qh = UhciCreateQh (Uhc, PollingInterval); + + if (Qh == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FREE_DATA; + } + + IntTds = UhciCreateBulkOrIntTds ( + Uhc, + DeviceAddress, + EndPointAddress, + PktId, + DataPtr, + DataPhy, + DataLength, + DataToggle, + (UINT8) MaximumPacketLength, + IsSlowDevice + ); + + if (IntTds == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto DESTORY_QH; + } + + UhciLinkTdToQh (Uhc, Qh, IntTds); + + // + // Save QH-TD structures to async Interrupt transfer list, + // for monitor interrupt transfer execution routine use. + // + Status = UhciCreateAsyncReq ( + Uhc, + Qh, + IntTds, + DeviceAddress, + EndPointAddress, + DataLength, + PollingInterval, + DataPtr, + CallBackFunction, + Context, + IsSlowDevice + ); + + if (EFI_ERROR (Status)) { + goto DESTORY_QH; + } + + UhciLinkQhToFrameList (Uhc, Qh); + + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + +DESTORY_QH: + UsbHcFreeMem (Uhc->MemPool, Qh, sizeof (UHCI_QH_SW)); + +FREE_DATA: + UsbHcFreeMem (Uhc->MemPool, DataPtr, DataLength); + Uhc->PciIo->Flush (Uhc->PciIo); + + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Submits synchronous interrupt transfer to an interrupt endpoint + of a USB device according to UEFI 2.0 spec. + + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param DeviceAddress Target device address. + @param EndPointAddress Endpoint number and direction. + @param DeviceSpeed Device speed. + @param MaximumPacketLength Maximum packet size of the target endpoint. + @param Data Array of pointers to the buffers of data. + @param DataLength On input, size of the data buffer, On output, + actually transferred data size. + @param DataToggle On input, data toggle to use; On output, next data toggle. + @param TimeOut Maximum time out, in microseconds. + @param Translator A pointr to the transaction translator data. + @param TransferResult Variable to receive transfer result. + + @return EFI_SUCCESS The transfer was completed successfully. + @return EFI_OUT_OF_RESOURCES Failed due to lack of resource. + @return EFI_INVALID_PARAMETER Some parameters are invalid. + @return EFI_TIMEOUT Failed due to timeout. + @return EFI_DEVICE_ERROR Failed due to host controller or device error. + +**/ +EFI_STATUS +EFIAPI +Uhci2SyncInterruptTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + EFI_STATUS Status; + USB_HC_DEV *Uhc; + UHCI_TD_SW *TDs; + UHCI_QH_RESULT QhResult; + EFI_TPL OldTpl; + UINT8 *DataPhy; + VOID *DataMap; + UINT8 PktId; + BOOLEAN IsSlowDevice; + + Uhc = UHC_FROM_USB2_HC_PROTO (This); + DataPhy = NULL; + DataMap = NULL; + TDs = NULL; + + if (DeviceSpeed == EFI_USB_SPEED_HIGH) { + return EFI_INVALID_PARAMETER; + } + + IsSlowDevice = (BOOLEAN) ((EFI_USB_SPEED_LOW == DeviceSpeed) ? TRUE : FALSE); + + if ((DataLength == NULL) || (Data == NULL) || (TransferResult == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataToggle != 1) && (*DataToggle != 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataLength == 0) || (MaximumPacketLength > 64)) { + return EFI_INVALID_PARAMETER; + } + + if (IsSlowDevice && (MaximumPacketLength > 8)) { + return EFI_INVALID_PARAMETER; + } + + *TransferResult = EFI_USB_ERR_SYSTEM; + Status = EFI_DEVICE_ERROR; + + + UhciAckAllInterrupt (Uhc); + + if (!UhciIsHcWorking (Uhc->PciIo)) { + return Status; + } + + OldTpl = gBS->RaiseTPL (UHCI_TPL); + + // + // Map the source data buffer for bus master access. + // Create Tds list, then link it to the UHC's interrupt list + // + Status = UhciMapUserData ( + Uhc, + EfiUsbDataIn, + Data, + DataLength, + &PktId, + &DataPhy, + &DataMap + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + TDs = UhciCreateBulkOrIntTds ( + Uhc, + DeviceAddress, + EndPointAddress, + PktId, + (UINT8 *)Data, + DataPhy, + *DataLength, + DataToggle, + (UINT8) MaximumPacketLength, + IsSlowDevice + ); + + if (TDs == NULL) { + Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); + + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + + UhciLinkTdToQh (Uhc, Uhc->SyncIntQh, TDs); + + Status = UhciExecuteTransfer (Uhc, Uhc->SyncIntQh, TDs, TimeOut, IsSlowDevice, &QhResult); + + UhciUnlinkTdFromQh (Uhc->SyncIntQh, TDs); + Uhc->PciIo->Flush (Uhc->PciIo); + + *TransferResult = QhResult.Result; + *DataToggle = QhResult.NextToggle; + *DataLength = QhResult.Complete; + + UhciDestoryTds (Uhc, TDs); + Uhc->PciIo->Unmap (Uhc->PciIo, DataMap); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Submits isochronous transfer to a target USB device according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param DeviceAddress Target device address. + @param EndPointAddress Endpoint number and direction. + @param DeviceSpeed Device speed. + @param MaximumPacketLength Maximum packet size of the target endpoint. + @param DataBuffersNumber Number of data buffers prepared for the transfer. + @param Data Array of pointers to the buffers of data. + @param DataLength On input, size of the data buffer, On output, + actually transferred data size. + @param Translator A pointr to the transaction translator data. + @param TransferResult Variable to receive transfer result. + + @return EFI_UNSUPPORTED + +**/ +EFI_STATUS +EFIAPI +Uhci2IsochronousTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN UINT8 DataBuffersNumber, + IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + OUT UINT32 *TransferResult + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Submits Async isochronous transfer to a target USB device according to UEFI 2.0 spec. + + @param This A pointer to the EFI_USB2_HC_PROTOCOL instance. + @param DeviceAddress Target device address. + @param EndPointAddress Endpoint number and direction. + @param DeviceSpeed Device speed. + @param MaximumPacketLength Maximum packet size of the target endpoint. + @param DataBuffersNumber Number of data buffers prepared for the transfer. + @param Data Array of pointers to the buffers of data. + @param DataLength On input, size of the data buffer, On output, + actually transferred data size. + @param Translator A pointr to the transaction translator data. + @param IsochronousCallBack Function to call when the transfer complete. + @param Context Pass to the call back function as parameter. + + @return EFI_UNSUPPORTED + +**/ +EFI_STATUS +EFIAPI +Uhci2AsyncIsochronousTransfer ( + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN UINT8 DataBuffersNumber, + IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM], + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack, + IN VOID *Context + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Entry point for EFI drivers. + + @param ImageHandle EFI_HANDLE. + @param SystemTable EFI_SYSTEM_TABLE. + + @retval EFI_SUCCESS Driver is successfully loaded. + @return Others Failed. + +**/ +EFI_STATUS +EFIAPI +UhciDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gUhciDriverBinding, + ImageHandle, + &gUhciComponentName, + &gUhciComponentName2 + ); +} + + +/** + Test to see if this driver supports ControllerHandle. Any + ControllerHandle that has UsbHcProtocol installed will be supported. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @return EFI_SUCCESS This driver supports this device. + @return EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +UhciDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS OpenStatus; + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + USB_CLASSC UsbClassCReg; + + // + // Test whether there is PCI IO Protocol attached on the controller handle. + // + OpenStatus = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (OpenStatus)) { + return OpenStatus; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + PCI_CLASSCODE_OFFSET, + sizeof (USB_CLASSC) / sizeof (UINT8), + &UsbClassCReg + ); + + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + + // + // Test whether the controller belongs to UHCI type + // + if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) || + (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) || + (UsbClassCReg.ProgInterface != PCI_IF_UHCI) + ) { + + Status = EFI_UNSUPPORTED; + } + +ON_EXIT: + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; + +} + + +/** + Allocate and initialize the empty UHCI device. + + @param PciIo The PCIIO to use. + @param DevicePath The device path of host controller. + @param OriginalPciAttributes The original PCI attributes. + + @return Allocated UHCI device. If err, return NULL. + +**/ +USB_HC_DEV * +UhciAllocateDev ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN UINT64 OriginalPciAttributes + ) +{ + USB_HC_DEV *Uhc; + EFI_STATUS Status; + + Uhc = AllocateZeroPool (sizeof (USB_HC_DEV)); + + if (Uhc == NULL) { + return NULL; + } + + // + // This driver supports both USB_HC_PROTOCOL and USB2_HC_PROTOCOL. + // USB_HC_PROTOCOL is for EFI 1.1 backward compability. + // + Uhc->Signature = USB_HC_DEV_SIGNATURE; + Uhc->Usb2Hc.GetCapability = Uhci2GetCapability; + Uhc->Usb2Hc.Reset = Uhci2Reset; + Uhc->Usb2Hc.GetState = Uhci2GetState; + Uhc->Usb2Hc.SetState = Uhci2SetState; + Uhc->Usb2Hc.ControlTransfer = Uhci2ControlTransfer; + Uhc->Usb2Hc.BulkTransfer = Uhci2BulkTransfer; + Uhc->Usb2Hc.AsyncInterruptTransfer = Uhci2AsyncInterruptTransfer; + Uhc->Usb2Hc.SyncInterruptTransfer = Uhci2SyncInterruptTransfer; + Uhc->Usb2Hc.IsochronousTransfer = Uhci2IsochronousTransfer; + Uhc->Usb2Hc.AsyncIsochronousTransfer = Uhci2AsyncIsochronousTransfer; + Uhc->Usb2Hc.GetRootHubPortStatus = Uhci2GetRootHubPortStatus; + Uhc->Usb2Hc.SetRootHubPortFeature = Uhci2SetRootHubPortFeature; + Uhc->Usb2Hc.ClearRootHubPortFeature = Uhci2ClearRootHubPortFeature; + Uhc->Usb2Hc.MajorRevision = 0x1; + Uhc->Usb2Hc.MinorRevision = 0x1; + + Uhc->PciIo = PciIo; + Uhc->DevicePath = DevicePath; + Uhc->OriginalPciAttributes = OriginalPciAttributes; + Uhc->MemPool = UsbHcInitMemPool (PciIo, TRUE, 0); + + if (Uhc->MemPool == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + InitializeListHead (&Uhc->AsyncIntList); + + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + UhciMonitorAsyncReqList, + Uhc, + &Uhc->AsyncIntMonitor + ); + + if (EFI_ERROR (Status)) { + UsbHcFreeMemPool (Uhc->MemPool); + goto ON_ERROR; + } + + return Uhc; + +ON_ERROR: + FreePool (Uhc); + return NULL; +} + + +/** + Free the UHCI device and release its associated resources. + + @param Uhc The UHCI device to release. + +**/ +VOID +UhciFreeDev ( + IN USB_HC_DEV *Uhc + ) +{ + if (Uhc->AsyncIntMonitor != NULL) { + gBS->CloseEvent (Uhc->AsyncIntMonitor); + } + + if (Uhc->ExitBootServiceEvent != NULL) { + gBS->CloseEvent (Uhc->ExitBootServiceEvent); + } + + if (Uhc->MemPool != NULL) { + UsbHcFreeMemPool (Uhc->MemPool); + } + + if (Uhc->CtrlNameTable != NULL) { + FreeUnicodeStringTable (Uhc->CtrlNameTable); + } + + FreePool (Uhc); +} + + +/** + Uninstall all Uhci Interface. + + @param Controller Controller handle. + @param This Protocol instance pointer. + +**/ +VOID +UhciCleanDevUp ( + IN EFI_HANDLE Controller, + IN EFI_USB2_HC_PROTOCOL *This + ) +{ + USB_HC_DEV *Uhc; + EFI_STATUS Status; + + // + // Uninstall the USB_HC and USB_HC2 protocol, then disable the controller + // + Uhc = UHC_FROM_USB2_HC_PROTO (This); + + + Status = gBS->UninstallProtocolInterface ( + Controller, + &gEfiUsb2HcProtocolGuid, + &Uhc->Usb2Hc + ); + if (EFI_ERROR (Status)) { + return ; + } + + UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); + UhciFreeAllAsyncReq (Uhc); + UhciDestoryFrameList (Uhc); + + // + // Restore original PCI attributes + // + Uhc->PciIo->Attributes ( + Uhc->PciIo, + EfiPciIoAttributeOperationSet, + Uhc->OriginalPciAttributes, + NULL + ); + + UhciFreeDev (Uhc); +} + +/** + One notified function to stop the Host Controller when gBS->ExitBootServices() called. + + @param Event Pointer to this event + @param Context Event handler private data + +**/ +VOID +EFIAPI +UhcExitBootService ( + EFI_EVENT Event, + VOID *Context + ) +{ + USB_HC_DEV *Uhc; + + Uhc = (USB_HC_DEV *) Context; + + // + // Stop the Host Controller + // + UhciStopHc (Uhc, UHC_GENERIC_TIMEOUT); + + // + // Reset the Host Controller + // + UhciSetRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_HCRESET); + gBS->Stall (UHC_ROOT_PORT_RECOVERY_STALL); +} + +/** + Starting the Usb UHCI Driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + @retval EFI_DEVICE_ERROR This driver cannot be started due to device Error. + EFI_OUT_OF_RESOURCES- Failed due to resource shortage. + +**/ +EFI_STATUS +EFIAPI +UhciDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + USB_HC_DEV *Uhc; + UINT64 Supports; + UINT64 OriginalPciAttributes; + BOOLEAN PciAttributesSaved; + EFI_DEVICE_PATH_PROTOCOL *HcDevicePath; + + // + // Open PCIIO, then enable the EHC device and turn off emulation + // + Uhc = NULL; + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open Device Path Protocol for on USB host controller + // + HcDevicePath = NULL; + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &HcDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + PciAttributesSaved = FALSE; + // + // Save original PCI attributes + // + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationGet, + 0, + &OriginalPciAttributes + ); + + if (EFI_ERROR (Status)) { + goto CLOSE_PCIIO; + } + PciAttributesSaved = TRUE; + + // + // Robustnesss improvement such as for UoL + // Default is not required. + // + if (FeaturePcdGet (PcdTurnOffUsbLegacySupport)) { + UhciTurnOffUsbEmulation (PciIo); + } + + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (!EFI_ERROR (Status)) { + Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } + + if (EFI_ERROR (Status)) { + goto CLOSE_PCIIO; + } + + Uhc = UhciAllocateDev (PciIo, HcDevicePath, OriginalPciAttributes); + + if (Uhc == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto CLOSE_PCIIO; + } + + // + // Allocate and Init Host Controller's Frame List Entry + // + Status = UhciInitFrameList (Uhc); + + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + goto FREE_UHC; + } + + Status = gBS->SetTimer ( + Uhc->AsyncIntMonitor, + TimerPeriodic, + UHC_ASYNC_POLL_INTERVAL + ); + + if (EFI_ERROR (Status)) { + goto FREE_UHC; + } + + // + // Install USB2_HC_PROTOCOL + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiUsb2HcProtocolGuid, + &Uhc->Usb2Hc, + NULL + ); + + if (EFI_ERROR (Status)) { + goto FREE_UHC; + } + + // + // Create event to stop the HC when exit boot service. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + UhcExitBootService, + Uhc, + &gEfiEventExitBootServicesGuid, + &Uhc->ExitBootServiceEvent + ); + if (EFI_ERROR (Status)) { + goto UNINSTALL_USBHC; + } + + // + // Install the component name protocol + // + Uhc->CtrlNameTable = NULL; + + AddUnicodeString2 ( + "eng", + gUhciComponentName.SupportedLanguages, + &Uhc->CtrlNameTable, + L"Usb Universal Host Controller", + TRUE + ); + AddUnicodeString2 ( + "en", + gUhciComponentName2.SupportedLanguages, + &Uhc->CtrlNameTable, + L"Usb Universal Host Controller", + FALSE + ); + + + // + // Start the UHCI hardware, also set its reclamation point to 64 bytes + // + UhciWriteReg (Uhc->PciIo, USBCMD_OFFSET, USBCMD_RS | USBCMD_MAXP); + + return EFI_SUCCESS; + +UNINSTALL_USBHC: + gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiUsb2HcProtocolGuid, + &Uhc->Usb2Hc, + NULL + ); + +FREE_UHC: + UhciFreeDev (Uhc); + +CLOSE_PCIIO: + if (PciAttributesSaved) { + // + // Restore original PCI attributes + // + PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSet, + OriginalPciAttributes, + NULL + ); + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Stop this driver on ControllerHandle. Support stopping any child handles + created by this driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on. + @param NumberOfChildren Number of Children in the ChildHandleBuffer. + @param ChildHandleBuffer List of handles for the children we need to stop. + + @return EFI_SUCCESS + @return others + +**/ +EFI_STATUS +EFIAPI +UhciDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_USB2_HC_PROTOCOL *Usb2Hc; + EFI_STATUS Status; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsb2HcProtocolGuid, + (VOID **) &Usb2Hc, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + // + // Test whether the Controller handler passed in is a valid + // Usb controller handle that should be supported, if not, + // return the error status directly + // + if (EFI_ERROR (Status)) { + return Status; + } + + UhciCleanDevUp (Controller, Usb2Hc); + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_SUCCESS; +} + diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.h b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.h new file mode 100644 index 000000000..882f5e55e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/Uhci.h @@ -0,0 +1,215 @@ +/** @file + + The definition for UHCI driver model and HC protocol routines. + +Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EFI_UHCI_H_ +#define _EFI_UHCI_H_ + + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef struct _USB_HC_DEV USB_HC_DEV; + +#include "UsbHcMem.h" +#include "UhciQueue.h" +#include "UhciReg.h" +#include "UhciSched.h" +#include "UhciDebug.h" +#include "ComponentName.h" + +// +// UHC timeout experience values +// + +#define UHC_1_MICROSECOND 1 +#define UHC_1_MILLISECOND (1000 * UHC_1_MICROSECOND) +#define UHC_1_SECOND (1000 * UHC_1_MILLISECOND) + +// +// UHCI register operation timeout, set by experience +// +#define UHC_GENERIC_TIMEOUT UHC_1_SECOND + +// +// Wait for force global resume(FGR) complete, refers to +// specification[UHCI11-2.1.1] +// +#define UHC_FORCE_GLOBAL_RESUME_STALL (20 * UHC_1_MILLISECOND) + +// +// Wait for roothub port reset and recovery, reset stall +// is set by experience, and recovery stall refers to +// specification[UHCI11-2.1.1] +// +#define UHC_ROOT_PORT_RESET_STALL (50 * UHC_1_MILLISECOND) +#define UHC_ROOT_PORT_RECOVERY_STALL (10 * UHC_1_MILLISECOND) + +// +// Sync and Async transfer polling interval, set by experience, +// and the unit of Async is 100us. +// +#define UHC_SYNC_POLL_INTERVAL (1 * UHC_1_MILLISECOND) +#define UHC_ASYNC_POLL_INTERVAL EFI_TIMER_PERIOD_MILLISECONDS(1) + +// +// UHC raises TPL to TPL_NOTIFY to serialize all its operations +// to protect shared data structures. +// +#define UHCI_TPL TPL_NOTIFY + +#define USB_HC_DEV_SIGNATURE SIGNATURE_32 ('u', 'h', 'c', 'i') + +#pragma pack(1) +typedef struct { + UINT8 ProgInterface; + UINT8 SubClassCode; + UINT8 BaseCode; +} USB_CLASSC; +#pragma pack() + +#define UHC_FROM_USB2_HC_PROTO(This) CR(This, USB_HC_DEV, Usb2Hc, USB_HC_DEV_SIGNATURE) + +// +// USB_HC_DEV support the UHCI hardware controller. It schedules +// the asynchronous interrupt transfer with the same method as +// EHCI: a reversed tree structure. For synchronous interrupt, +// control and bulk transfer, it uses three static queue head to +// schedule them. SyncIntQh is for interrupt transfer. LsCtrlQh is +// for LOW speed control transfer, and FsCtrlBulkQh is for FULL +// speed control or bulk transfer. This is because FULL speed contrl +// or bulk transfer can reclaim the unused bandwidth. Some USB +// device requires this bandwidth reclamation capability. +// +struct _USB_HC_DEV { + UINT32 Signature; + EFI_USB2_HC_PROTOCOL Usb2Hc; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINT64 OriginalPciAttributes; + + // + // Schedule data structures + // + UINT32 *FrameBase; // the buffer pointed by this pointer is used to store pci bus address of the QH descriptor. + UINT32 *FrameBaseHostAddr; // the buffer pointed by this pointer is used to store host memory address of the QH descriptor. + UHCI_QH_SW *SyncIntQh; + UHCI_QH_SW *CtrlQh; + UHCI_QH_SW *BulkQh; + + // + // Structures to maintain asynchronus interrupt transfers. + // When asynchronous interrutp transfer is unlinked from + // the frame list, the hardware may still hold a pointer + // to it. To synchronize with hardware, its resoureces are + // released in two steps using Recycle and RecycleWait. + // Check the asynchronous interrupt management routines. + // + LIST_ENTRY AsyncIntList; + EFI_EVENT AsyncIntMonitor; + UHCI_ASYNC_REQUEST *Recycle; + UHCI_ASYNC_REQUEST *RecycleWait; + + + UINTN RootPorts; + USBHC_MEM_POOL *MemPool; + EFI_UNICODE_STRING_TABLE *CtrlNameTable; + VOID *FrameMapping; + + // + // ExitBootServicesEvent is used to stop the EHC DMA operation + // after exit boot service. + // + EFI_EVENT ExitBootServiceEvent; +}; + +extern EFI_DRIVER_BINDING_PROTOCOL gUhciDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gUhciComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gUhciComponentName2; + +/** + Test to see if this driver supports ControllerHandle. Any + ControllerHandle that has UsbHcProtocol installed will be supported. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @return EFI_SUCCESS This driver supports this device. + @return EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +UhciDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Starting the Usb UHCI Driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + @retval EFI_DEVICE_ERROR This driver cannot be started due to device Error. + EFI_OUT_OF_RESOURCES- Failed due to resource shortage. + +**/ +EFI_STATUS +EFIAPI +UhciDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop this driver on ControllerHandle. Support stopping any child handles + created by this driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on. + @param NumberOfChildren Number of Children in the ChildHandleBuffer. + @param ChildHandleBuffer List of handles for the children we need to stop. + + @return EFI_SUCCESS + @return others + +**/ +EFI_STATUS +EFIAPI +UhciDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.c b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.c new file mode 100644 index 000000000..517780a5f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.c @@ -0,0 +1,71 @@ +/** @file + + This file provides the information dump support for Uhci when in debug mode. + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Uhci.h" + +/** + Dump the content of QH structure. + + @param QhSw Pointer to software QH structure. + +**/ +VOID +UhciDumpQh ( + IN UHCI_QH_SW *QhSw + ) +{ + DEBUG ((EFI_D_VERBOSE, "&QhSw @ 0x%p\n", QhSw)); + DEBUG ((EFI_D_VERBOSE, "QhSw.NextQh - 0x%p\n", QhSw->NextQh)); + DEBUG ((EFI_D_VERBOSE, "QhSw.TDs - 0x%p\n", QhSw->TDs)); + DEBUG ((EFI_D_VERBOSE, "QhSw.QhHw:\n")); + DEBUG ((EFI_D_VERBOSE, " Horizon Link - %x\n", QhSw->QhHw.HorizonLink)); + DEBUG ((EFI_D_VERBOSE, " Vertical Link - %x\n\n", QhSw->QhHw.VerticalLink)); +} + + +/** + Dump the content of TD structure. + + @param TdSw Pointer to software TD structure. + +**/ +VOID +UhciDumpTds ( + IN UHCI_TD_SW *TdSw + ) +{ + UHCI_TD_SW *CurTdSw; + + CurTdSw = TdSw; + + while (CurTdSw != NULL) { + DEBUG ((EFI_D_VERBOSE, "TdSw @ 0x%p\n", CurTdSw)); + DEBUG ((EFI_D_VERBOSE, "TdSw.NextTd - 0x%p\n", CurTdSw->NextTd)); + DEBUG ((EFI_D_VERBOSE, "TdSw.DataLen - %d\n", CurTdSw->DataLen)); + DEBUG ((EFI_D_VERBOSE, "TdSw.Data - 0x%p\n", CurTdSw->Data)); + DEBUG ((EFI_D_VERBOSE, "TdHw:\n")); + DEBUG ((EFI_D_VERBOSE, " NextLink - 0x%x\n", CurTdSw->TdHw.NextLink)); + DEBUG ((EFI_D_VERBOSE, " ActualLen - %d\n", CurTdSw->TdHw.ActualLen)); + DEBUG ((EFI_D_VERBOSE, " Status - 0x%x\n", CurTdSw->TdHw.Status)); + DEBUG ((EFI_D_VERBOSE, " IOC - %d\n", CurTdSw->TdHw.IntOnCpl)); + DEBUG ((EFI_D_VERBOSE, " IsIsoCh - %d\n", CurTdSw->TdHw.IsIsoch)); + DEBUG ((EFI_D_VERBOSE, " LowSpeed - %d\n", CurTdSw->TdHw.LowSpeed)); + DEBUG ((EFI_D_VERBOSE, " ErrorCount - %d\n", CurTdSw->TdHw.ErrorCount)); + DEBUG ((EFI_D_VERBOSE, " ShortPacket - %d\n", CurTdSw->TdHw.ShortPacket)); + DEBUG ((EFI_D_VERBOSE, " PidCode - 0x%x\n", CurTdSw->TdHw.PidCode)); + DEBUG ((EFI_D_VERBOSE, " DevAddr - %d\n", CurTdSw->TdHw.DeviceAddr)); + DEBUG ((EFI_D_VERBOSE, " EndPoint - %d\n", CurTdSw->TdHw.EndPoint)); + DEBUG ((EFI_D_VERBOSE, " DataToggle - %d\n", CurTdSw->TdHw.DataToggle)); + DEBUG ((EFI_D_VERBOSE, " MaxPacketLen - %d\n", CurTdSw->TdHw.MaxPacketLen)); + DEBUG ((EFI_D_VERBOSE, " DataBuffer - 0x%x\n\n",CurTdSw->TdHw.DataBuffer)); + + CurTdSw = CurTdSw->NextTd; + } +} + diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.h b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.h new file mode 100644 index 000000000..34f8ea1ff --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDebug.h @@ -0,0 +1,41 @@ +/** @file + + This file contains the definination for host controller debug support routines + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EFI_UHCI_DEBUG_H_ +#define _EFI_UHCI_DEBUG_H_ + + +/** + Dump the content of QH structure. + + @param QhSw Pointer to software QH structure. + + @return None. + +**/ +VOID +UhciDumpQh ( + IN UHCI_QH_SW *QhSw + ); + + +/** + Dump the content of TD structure. + + @param TdSw Pointer to software TD structure. + + @return None. + +**/ +VOID +UhciDumpTds ( + IN UHCI_TD_SW *TdSw + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf new file mode 100644 index 000000000..a5da1a912 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf @@ -0,0 +1,80 @@ +## @file +# The UhciDxe driver is responsible for managing the behavior of UHCI controller. +# It implements the interfaces of monitoring the status of all ports and transferring +# Control, Bulk, Interrupt and Isochronous requests to Usb1.x device +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UhciDxe + MODULE_UNI_FILE = UhciDxe.uni + FILE_GUID = 2FB92EFA-2EE0-4bae-9EB6-7464125E1EF7 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = UhciDriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC ARM AARCH64 +# +# DRIVER_BINDING = gUhciDriverBinding +# COMPONENT_NAME = gUhciComponentName +# COMPONENT_NAME2 = gUhciComponentName2 +# + +[Sources] + UhciSched.c + UhciDebug.c + UsbHcMem.h + UhciDebug.h + UhciQueue.c + UhciReg.c + UsbHcMem.c + UhciQueue.h + Uhci.c + Uhci.h + UhciReg.h + UhciSched.h + ComponentName.c + ComponentName.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdTurnOffUsbLegacySupport ## CONSUMES + +[LibraryClasses] + MemoryAllocationLib + BaseLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + DebugLib + PcdLib + ReportStatusCodeLib + +[Guids] + gEfiEventExitBootServicesGuid ## SOMETIMES_CONSUMES ## Event + +[Protocols] + gEfiPciIoProtocolGuid ## TO_START + gEfiUsb2HcProtocolGuid ## BY_START + +# [Event] +# EVENT_TYPE_PERIODIC_TIMER ## CONSUMES +# + +[UserExtensions.TianoCore."ExtraFiles"] + UhciDxeExtra.uni diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.uni b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.uni new file mode 100644 index 000000000..e6ee3c304 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.uni @@ -0,0 +1,17 @@ +// /** @file +// The UhciDxe driver is responsible for managing the behavior of UHCI controller. +// +// It implements the interfaces of monitoring the status of all ports and transferring +// Control, Bulk, Interrupt and Isochronous requests to Usb1.x device +// +// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Responsible for managing the behavior of the UHCI controller" + +#string STR_MODULE_DESCRIPTION #language en-US "It implements the interfaces of monitoring the status of all ports and transferring Control, Bulk, Interrupt and Isochronous requests to a Usb1.x device." + diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxeExtra.uni b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxeExtra.uni new file mode 100644 index 000000000..47eaa236d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// UhciDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"UHCI DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c new file mode 100644 index 000000000..fb97326dc --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.c @@ -0,0 +1,701 @@ +/** @file + + The UHCI register operation routines. + +Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Uhci.h" + + +/** + Map address of request structure buffer. + + @param Uhc The UHCI device. + @param Request The user request buffer. + @param MappedAddr Mapped address of request. + @param Map Identificaion of this mapping to return. + + @return EFI_SUCCESS Success. + @return EFI_DEVICE_ERROR Fail to map the user request. + +**/ +EFI_STATUS +UhciMapUserRequest ( + IN USB_HC_DEV *Uhc, + IN OUT VOID *Request, + OUT UINT8 **MappedAddr, + OUT VOID **Map + ) +{ + EFI_STATUS Status; + UINTN Len; + EFI_PHYSICAL_ADDRESS PhyAddr; + + Len = sizeof (EFI_USB_DEVICE_REQUEST); + Status = Uhc->PciIo->Map ( + Uhc->PciIo, + EfiPciIoOperationBusMasterRead, + Request, + &Len, + &PhyAddr, + Map + ); + + if (!EFI_ERROR (Status)) { + *MappedAddr = (UINT8 *) (UINTN) PhyAddr; + } + + return Status; +} + + +/** + Map address of user data buffer. + + @param Uhc The UHCI device. + @param Direction Direction of the data transfer. + @param Data The user data buffer. + @param Len Length of the user data. + @param PktId Packet identificaion. + @param MappedAddr Mapped address to return. + @param Map Identificaion of this mapping to return. + + @return EFI_SUCCESS Success. + @return EFI_DEVICE_ERROR Fail to map the user data. + +**/ +EFI_STATUS +UhciMapUserData ( + IN USB_HC_DEV *Uhc, + IN EFI_USB_DATA_DIRECTION Direction, + IN VOID *Data, + IN OUT UINTN *Len, + OUT UINT8 *PktId, + OUT UINT8 **MappedAddr, + OUT VOID **Map + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS PhyAddr; + + Status = EFI_SUCCESS; + + switch (Direction) { + case EfiUsbDataIn: + // + // BusMasterWrite means cpu read + // + *PktId = INPUT_PACKET_ID; + Status = Uhc->PciIo->Map ( + Uhc->PciIo, + EfiPciIoOperationBusMasterWrite, + Data, + Len, + &PhyAddr, + Map + ); + + if (EFI_ERROR (Status)) { + goto EXIT; + } + + *MappedAddr = (UINT8 *) (UINTN) PhyAddr; + break; + + case EfiUsbDataOut: + *PktId = OUTPUT_PACKET_ID; + Status = Uhc->PciIo->Map ( + Uhc->PciIo, + EfiPciIoOperationBusMasterRead, + Data, + Len, + &PhyAddr, + Map + ); + + if (EFI_ERROR (Status)) { + goto EXIT; + } + + *MappedAddr = (UINT8 *) (UINTN) PhyAddr; + break; + + case EfiUsbNoData: + if ((Len != NULL) && (*Len != 0)) { + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + + *PktId = OUTPUT_PACKET_ID; + *MappedAddr = NULL; + *Map = NULL; + break; + + default: + Status = EFI_INVALID_PARAMETER; + } + +EXIT: + return Status; +} + + +/** + Link the TD To QH. + + @param Uhc The UHCI device. + @param Qh The queue head for the TD to link to. + @param Td The TD to link. + +**/ +VOID +UhciLinkTdToQh ( + IN USB_HC_DEV *Uhc, + IN UHCI_QH_SW *Qh, + IN UHCI_TD_SW *Td + ) +{ + EFI_PHYSICAL_ADDRESS PhyAddr; + + PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Td, sizeof (UHCI_TD_HW)); + + ASSERT ((Qh != NULL) && (Td != NULL)); + + Qh->QhHw.VerticalLink = QH_VLINK (PhyAddr, FALSE); + Qh->TDs = (VOID *) Td; +} + + +/** + Unlink TD from the QH. + + @param Qh The queue head to unlink from. + @param Td The TD to unlink. + +**/ +VOID +UhciUnlinkTdFromQh ( + IN UHCI_QH_SW *Qh, + IN UHCI_TD_SW *Td + ) +{ + ASSERT ((Qh != NULL) && (Td != NULL)); + + Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE); + Qh->TDs = NULL; +} + + +/** + Append a new TD To the previous TD. + + @param Uhc The UHCI device. + @param PrevTd Previous UHCI_TD_SW to be linked to. + @param ThisTd TD to link. + +**/ +VOID +UhciAppendTd ( + IN USB_HC_DEV *Uhc, + IN UHCI_TD_SW *PrevTd, + IN UHCI_TD_SW *ThisTd + ) +{ + EFI_PHYSICAL_ADDRESS PhyAddr; + + PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, ThisTd, sizeof (UHCI_TD_HW)); + + ASSERT ((PrevTd != NULL) && (ThisTd != NULL)); + + PrevTd->TdHw.NextLink = TD_LINK (PhyAddr, TRUE, FALSE); + PrevTd->NextTd = (VOID *) ThisTd; +} + + +/** + Delete a list of TDs. + + @param Uhc The UHCI device. + @param FirstTd TD link list head. + + @return None. + +**/ +VOID +UhciDestoryTds ( + IN USB_HC_DEV *Uhc, + IN UHCI_TD_SW *FirstTd + ) +{ + UHCI_TD_SW *NextTd; + UHCI_TD_SW *ThisTd; + + NextTd = FirstTd; + + while (NextTd != NULL) { + ThisTd = NextTd; + NextTd = ThisTd->NextTd; + UsbHcFreeMem (Uhc->MemPool, ThisTd, sizeof (UHCI_TD_SW)); + } +} + + +/** + Create an initialize a new queue head. + + @param Uhc The UHCI device. + @param Interval The polling interval for the queue. + + @return The newly created queue header. + +**/ +UHCI_QH_SW * +UhciCreateQh ( + IN USB_HC_DEV *Uhc, + IN UINTN Interval + ) +{ + UHCI_QH_SW *Qh; + + Qh = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_QH_SW)); + + if (Qh == NULL) { + return NULL; + } + + Qh->QhHw.HorizonLink = QH_HLINK (NULL, TRUE); + Qh->QhHw.VerticalLink = QH_VLINK (NULL, TRUE); + Qh->Interval = UhciConvertPollRate(Interval); + Qh->TDs = NULL; + Qh->NextQh = NULL; + + return Qh; +} + + +/** + Create and intialize a TD. + + @param Uhc The UHCI device. + + @return The newly allocated and initialized TD. + +**/ +UHCI_TD_SW * +UhciCreateTd ( + IN USB_HC_DEV *Uhc + ) +{ + UHCI_TD_SW *Td; + + Td = UsbHcAllocateMem (Uhc->MemPool, sizeof (UHCI_TD_SW)); + if (Td == NULL) { + return NULL; + } + + Td->TdHw.NextLink = TD_LINK (NULL, FALSE, TRUE); + Td->NextTd = NULL; + Td->Data = NULL; + Td->DataLen = 0; + + return Td; +} + + +/** + Create and initialize a TD for Setup Stage of a control transfer. + + @param Uhc The UHCI device. + @param DevAddr Device address. + @param Request A pointer to cpu memory address of Device request. + @param RequestPhy A pointer to pci memory address of Device request. + @param IsLow Full speed or low speed. + + @return The created setup Td Pointer. + +**/ +UHCI_TD_SW * +UhciCreateSetupTd ( + IN USB_HC_DEV *Uhc, + IN UINT8 DevAddr, + IN UINT8 *Request, + IN UINT8 *RequestPhy, + IN BOOLEAN IsLow + ) +{ + UHCI_TD_SW *Td; + + Td = UhciCreateTd (Uhc); + + if (Td == NULL) { + return NULL; + } + + Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE); + Td->TdHw.ShortPacket = FALSE; + Td->TdHw.IsIsoch = FALSE; + Td->TdHw.IntOnCpl = FALSE; + Td->TdHw.ErrorCount = 0x03; + Td->TdHw.Status |= USBTD_ACTIVE; + Td->TdHw.DataToggle = 0; + Td->TdHw.EndPoint = 0; + Td->TdHw.LowSpeed = IsLow ? 1 : 0; + Td->TdHw.DeviceAddr = DevAddr & 0x7F; + Td->TdHw.MaxPacketLen = (UINT32) (sizeof (EFI_USB_DEVICE_REQUEST) - 1); + Td->TdHw.PidCode = SETUP_PACKET_ID; + Td->TdHw.DataBuffer = (UINT32) (UINTN) RequestPhy; + + Td->Data = Request; + Td->DataLen = (UINT16) sizeof (EFI_USB_DEVICE_REQUEST); + + return Td; +} + + +/** + Create a TD for data. + + @param Uhc The UHCI device. + @param DevAddr Device address. + @param Endpoint Endpoint number. + @param DataPtr A pointer to cpu memory address of Data buffer. + @param DataPhyPtr A pointer to pci memory address of Data buffer. + @param Len Data length. + @param PktId Packet ID. + @param Toggle Data toggle value. + @param IsLow Full speed or low speed. + + @return Data Td pointer if success, otherwise NULL. + +**/ +UHCI_TD_SW * +UhciCreateDataTd ( + IN USB_HC_DEV *Uhc, + IN UINT8 DevAddr, + IN UINT8 Endpoint, + IN UINT8 *DataPtr, + IN UINT8 *DataPhyPtr, + IN UINTN Len, + IN UINT8 PktId, + IN UINT8 Toggle, + IN BOOLEAN IsLow + ) +{ + UHCI_TD_SW *Td; + + // + // Code as length - 1, and the max valid length is 0x500 + // + ASSERT (Len <= 0x500); + + Td = UhciCreateTd (Uhc); + + if (Td == NULL) { + return NULL; + } + + Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE); + Td->TdHw.ShortPacket = FALSE; + Td->TdHw.IsIsoch = FALSE; + Td->TdHw.IntOnCpl = FALSE; + Td->TdHw.ErrorCount = 0x03; + Td->TdHw.Status = USBTD_ACTIVE; + Td->TdHw.LowSpeed = IsLow ? 1 : 0; + Td->TdHw.DataToggle = Toggle & 0x01; + Td->TdHw.EndPoint = Endpoint & 0x0F; + Td->TdHw.DeviceAddr = DevAddr & 0x7F; + Td->TdHw.MaxPacketLen = (UINT32) (Len - 1); + Td->TdHw.PidCode = (UINT8) PktId; + Td->TdHw.DataBuffer = (UINT32) (UINTN) DataPhyPtr; + + Td->Data = DataPtr; + Td->DataLen = (UINT16) Len; + + return Td; +} + + +/** + Create TD for the Status Stage of control transfer. + + @param Uhc The UHCI device. + @param DevAddr Device address. + @param PktId Packet ID. + @param IsLow Full speed or low speed. + + @return Status Td Pointer. + +**/ +UHCI_TD_SW * +UhciCreateStatusTd ( + IN USB_HC_DEV *Uhc, + IN UINT8 DevAddr, + IN UINT8 PktId, + IN BOOLEAN IsLow + ) +{ + UHCI_TD_SW *Td; + + Td = UhciCreateTd (Uhc); + + if (Td == NULL) { + return NULL; + } + + Td->TdHw.NextLink = TD_LINK (NULL, TRUE, TRUE); + Td->TdHw.ShortPacket = FALSE; + Td->TdHw.IsIsoch = FALSE; + Td->TdHw.IntOnCpl = FALSE; + Td->TdHw.ErrorCount = 0x03; + Td->TdHw.Status |= USBTD_ACTIVE; + Td->TdHw.MaxPacketLen = 0x7FF; //0x7FF: there is no data (refer to UHCI spec) + Td->TdHw.DataToggle = 1; + Td->TdHw.EndPoint = 0; + Td->TdHw.LowSpeed = IsLow ? 1 : 0; + Td->TdHw.DeviceAddr = DevAddr & 0x7F; + Td->TdHw.PidCode = (UINT8) PktId; + Td->TdHw.DataBuffer = (UINT32) (UINTN) NULL; + + Td->Data = NULL; + Td->DataLen = 0; + + return Td; +} + + +/** + Create Tds list for Control Transfer. + + @param Uhc The UHCI device. + @param DeviceAddr The device address. + @param DataPktId Packet Identification of Data Tds. + @param Request A pointer to cpu memory address of request structure buffer to transfer. + @param RequestPhy A pointer to pci memory address of request structure buffer to transfer. + @param Data A pointer to cpu memory address of user data buffer to transfer. + @param DataPhy A pointer to pci memory address of user data buffer to transfer. + @param DataLen Length of user data to transfer. + @param MaxPacket Maximum packet size for control transfer. + @param IsLow Full speed or low speed. + + @return The Td list head for the control transfer. + +**/ +UHCI_TD_SW * +UhciCreateCtrlTds ( + IN USB_HC_DEV *Uhc, + IN UINT8 DeviceAddr, + IN UINT8 DataPktId, + IN UINT8 *Request, + IN UINT8 *RequestPhy, + IN UINT8 *Data, + IN UINT8 *DataPhy, + IN UINTN DataLen, + IN UINT8 MaxPacket, + IN BOOLEAN IsLow + ) +{ + UHCI_TD_SW *SetupTd; + UHCI_TD_SW *FirstDataTd; + UHCI_TD_SW *DataTd; + UHCI_TD_SW *PrevDataTd; + UHCI_TD_SW *StatusTd; + UINT8 DataToggle; + UINT8 StatusPktId; + UINTN ThisTdLen; + + + DataTd = NULL; + SetupTd = NULL; + FirstDataTd = NULL; + PrevDataTd = NULL; + StatusTd = NULL; + + // + // Create setup packets for the transfer + // + SetupTd = UhciCreateSetupTd (Uhc, DeviceAddr, Request, RequestPhy, IsLow); + + if (SetupTd == NULL) { + return NULL; + } + + // + // Create data packets for the transfer + // + DataToggle = 1; + + while (DataLen > 0) { + // + // PktSize is the data load size in each Td. + // + ThisTdLen = (DataLen > MaxPacket ? MaxPacket : DataLen); + + DataTd = UhciCreateDataTd ( + Uhc, + DeviceAddr, + 0, + Data, //cpu memory address + DataPhy, //Pci memory address + ThisTdLen, + DataPktId, + DataToggle, + IsLow + ); + + if (DataTd == NULL) { + goto FREE_TD; + } + + if (FirstDataTd == NULL) { + FirstDataTd = DataTd; + FirstDataTd->NextTd = NULL; + } else { + UhciAppendTd (Uhc, PrevDataTd, DataTd); + } + + DataToggle ^= 1; + PrevDataTd = DataTd; + Data += ThisTdLen; + DataPhy += ThisTdLen; + DataLen -= ThisTdLen; + } + + // + // Status packet is on the opposite direction to data packets + // + if (OUTPUT_PACKET_ID == DataPktId) { + StatusPktId = INPUT_PACKET_ID; + } else { + StatusPktId = OUTPUT_PACKET_ID; + } + + StatusTd = UhciCreateStatusTd (Uhc, DeviceAddr, StatusPktId, IsLow); + + if (StatusTd == NULL) { + goto FREE_TD; + } + + // + // Link setup Td -> data Tds -> status Td together + // + if (FirstDataTd != NULL) { + UhciAppendTd (Uhc, SetupTd, FirstDataTd); + UhciAppendTd (Uhc, PrevDataTd, StatusTd); + } else { + UhciAppendTd (Uhc, SetupTd, StatusTd); + } + + return SetupTd; + +FREE_TD: + if (SetupTd != NULL) { + UhciDestoryTds (Uhc, SetupTd); + } + + if (FirstDataTd != NULL) { + UhciDestoryTds (Uhc, FirstDataTd); + } + + return NULL; +} + + +/** + Create Tds list for Bulk/Interrupt Transfer. + + @param Uhc USB_HC_DEV. + @param DevAddr Address of Device. + @param EndPoint Endpoint Number. + @param PktId Packet Identification of Data Tds. + @param Data A pointer to cpu memory address of user data buffer to transfer. + @param DataPhy A pointer to pci memory address of user data buffer to transfer. + @param DataLen Length of user data to transfer. + @param DataToggle Data Toggle Pointer. + @param MaxPacket Maximum packet size for Bulk/Interrupt transfer. + @param IsLow Is Low Speed Device. + + @return The Tds list head for the bulk transfer. + +**/ +UHCI_TD_SW * +UhciCreateBulkOrIntTds ( + IN USB_HC_DEV *Uhc, + IN UINT8 DevAddr, + IN UINT8 EndPoint, + IN UINT8 PktId, + IN UINT8 *Data, + IN UINT8 *DataPhy, + IN UINTN DataLen, + IN OUT UINT8 *DataToggle, + IN UINT8 MaxPacket, + IN BOOLEAN IsLow + ) +{ + UHCI_TD_SW *DataTd; + UHCI_TD_SW *FirstDataTd; + UHCI_TD_SW *PrevDataTd; + UINTN ThisTdLen; + + DataTd = NULL; + FirstDataTd = NULL; + PrevDataTd = NULL; + + // + // Create data packets for the transfer + // + while (DataLen > 0) { + // + // PktSize is the data load size that each Td. + // + ThisTdLen = DataLen; + + if (DataLen > MaxPacket) { + ThisTdLen = MaxPacket; + } + + DataTd = UhciCreateDataTd ( + Uhc, + DevAddr, + EndPoint, + Data, + DataPhy, + ThisTdLen, + PktId, + *DataToggle, + IsLow + ); + + if (DataTd == NULL) { + goto FREE_TD; + } + + if (PktId == INPUT_PACKET_ID) { + DataTd->TdHw.ShortPacket = TRUE; + } + + if (FirstDataTd == NULL) { + FirstDataTd = DataTd; + FirstDataTd->NextTd = NULL; + } else { + UhciAppendTd (Uhc, PrevDataTd, DataTd); + } + + *DataToggle ^= 1; + PrevDataTd = DataTd; + Data += ThisTdLen; + DataPhy += ThisTdLen; + DataLen -= ThisTdLen; + } + + return FirstDataTd; + +FREE_TD: + if (FirstDataTd != NULL) { + UhciDestoryTds (Uhc, FirstDataTd); + } + + return NULL; +} diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.h b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.h new file mode 100644 index 000000000..594ea28ee --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciQueue.h @@ -0,0 +1,266 @@ +/** @file + + The definition for UHCI register operation routines. + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EFI_UHCI_QUEUE_H_ +#define _EFI_UHCI_QUEUE_H_ + +// +// Macroes used to set various links in UHCI's driver. +// In this UHCI driver, QH's horizontal link always pointers to other QH, +// and its vertical link always pointers to TD. TD's next pointer always +// pointers to other sibling TD. Frame link always pointers to QH because +// ISO transfer isn't supported. +// +// We should use UINT32 to access these pointers to void race conditions +// with hardware. +// +#define QH_HLINK(Pointer, Terminate) \ + (((UINT32) ((UINTN) (Pointer)) & 0xFFFFFFF0) | 0x02 | ((Terminate) ? 0x01 : 0)) + +#define QH_VLINK(Pointer, Terminate) \ + (((UINT32) ((UINTN) (Pointer)) & 0xFFFFFFF0) | ((Terminate) ? 0x01 : 0)) + +#define TD_LINK(Pointer, VertFirst, Terminate) \ + (((UINT32) ((UINTN) (Pointer)) & 0xFFFFFFF0) | \ + ((VertFirst) ? 0x04 : 0) | ((Terminate) ? 0x01 : 0)) + +#define LINK_TERMINATED(Link) (((Link) & 0x01) != 0) + +#define UHCI_ADDR(QhOrTd) ((VOID *) (UINTN) ((QhOrTd) & 0xFFFFFFF0)) + +#pragma pack(1) +// +// Both links in QH has this internal structure: +// Next pointer: 28, Reserved: 2, NextIsQh: 1, Terminate: 1 +// This is the same as frame list entry. +// +typedef struct { + UINT32 HorizonLink; + UINT32 VerticalLink; +} UHCI_QH_HW; + +// +// Next link in TD has this internal structure: +// Next pointer: 28, Reserved: 1, Vertical First: 1, NextIsQh: 1, Terminate: 1 +// +typedef struct { + UINT32 NextLink; + UINT32 ActualLen : 11; + UINT32 Reserved1 : 5; + UINT32 Status : 8; + UINT32 IntOnCpl : 1; + UINT32 IsIsoch : 1; + UINT32 LowSpeed : 1; + UINT32 ErrorCount : 2; + UINT32 ShortPacket : 1; + UINT32 Reserved2 : 2; + UINT32 PidCode : 8; + UINT32 DeviceAddr : 7; + UINT32 EndPoint : 4; + UINT32 DataToggle : 1; + UINT32 Reserved3 : 1; + UINT32 MaxPacketLen: 11; + UINT32 DataBuffer; +} UHCI_TD_HW; +#pragma pack() + +typedef struct _UHCI_TD_SW UHCI_TD_SW; +typedef struct _UHCI_QH_SW UHCI_QH_SW; + +struct _UHCI_QH_SW { + UHCI_QH_HW QhHw; + UHCI_QH_SW *NextQh; + UHCI_TD_SW *TDs; + UINTN Interval; +}; + +struct _UHCI_TD_SW { + UHCI_TD_HW TdHw; + UHCI_TD_SW *NextTd; + UINT8 *Data; + UINT16 DataLen; +}; + + +/** + Link the TD To QH. + + @param Uhc The UHCI device. + @param Qh The queue head for the TD to link to. + @param Td The TD to link. + +**/ +VOID +UhciLinkTdToQh ( + IN USB_HC_DEV *Uhc, + IN UHCI_QH_SW *Qh, + IN UHCI_TD_SW *Td + ); + + +/** + Unlink TD from the QH. + + @param Qh The queue head to unlink from. + @param Td The TD to unlink. + + @return None. + +**/ +VOID +UhciUnlinkTdFromQh ( + IN UHCI_QH_SW *Qh, + IN UHCI_TD_SW *Td + ); + + +/** + Map address of request structure buffer. + + @param Uhc The UHCI device. + @param Request The user request buffer. + @param MappedAddr Mapped address of request. + @param Map Identificaion of this mapping to return. + + @return EFI_SUCCESS Success. + @return EFI_DEVICE_ERROR Fail to map the user request. + +**/ +EFI_STATUS +UhciMapUserRequest ( + IN USB_HC_DEV *Uhc, + IN OUT VOID *Request, + OUT UINT8 **MappedAddr, + OUT VOID **Map + ); + + +/** + Map address of user data buffer. + + @param Uhc The UHCI device. + @param Direction Direction of the data transfer. + @param Data The user data buffer. + @param Len Length of the user data. + @param PktId Packet identificaion. + @param MappedAddr Mapped address to return. + @param Map Identificaion of this mapping to return. + + @return EFI_SUCCESS Success. + @return EFI_DEVICE_ERROR Fail to map the user data. + +**/ +EFI_STATUS +UhciMapUserData ( + IN USB_HC_DEV *Uhc, + IN EFI_USB_DATA_DIRECTION Direction, + IN VOID *Data, + IN OUT UINTN *Len, + OUT UINT8 *PktId, + OUT UINT8 **MappedAddr, + OUT VOID **Map + ); + + +/** + Delete a list of TDs. + + @param Uhc The UHCI device. + @param FirstTd TD link list head. + + @return None. + +**/ +VOID +UhciDestoryTds ( + IN USB_HC_DEV *Uhc, + IN UHCI_TD_SW *FirstTd + ); + + +/** + Create an initialize a new queue head. + + @param Uhc The UHCI device. + @param Interval The polling interval for the queue. + + @return The newly created queue header. + +**/ +UHCI_QH_SW * +UhciCreateQh ( + IN USB_HC_DEV *Uhc, + IN UINTN Interval + ); + + +/** + Create Tds list for Control Transfer. + + @param Uhc The UHCI device. + @param DeviceAddr The device address. + @param DataPktId Packet Identification of Data Tds. + @param Request A pointer to cpu memory address of request structure buffer to transfer. + @param RequestPhy A pointer to pci memory address of request structure buffer to transfer. + @param Data A pointer to cpu memory address of user data buffer to transfer. + @param DataPhy A pointer to pci memory address of user data buffer to transfer. + @param DataLen Length of user data to transfer. + @param MaxPacket Maximum packet size for control transfer. + @param IsLow Full speed or low speed. + + @return The Td list head for the control transfer. + +**/ +UHCI_TD_SW * +UhciCreateCtrlTds ( + IN USB_HC_DEV *Uhc, + IN UINT8 DeviceAddr, + IN UINT8 DataPktId, + IN UINT8 *Request, + IN UINT8 *RequestPhy, + IN UINT8 *Data, + IN UINT8 *DataPhy, + IN UINTN DataLen, + IN UINT8 MaxPacket, + IN BOOLEAN IsLow + ); + + +/** + Create Tds list for Bulk/Interrupt Transfer. + + @param Uhc USB_HC_DEV. + @param DevAddr Address of Device. + @param EndPoint Endpoint Number. + @param PktId Packet Identification of Data Tds. + @param Data A pointer to cpu memory address of user data buffer to transfer. + @param DataPhy A pointer to pci memory address of user data buffer to transfer. + @param DataLen Length of user data to transfer. + @param DataToggle Data Toggle Pointer. + @param MaxPacket Maximum packet size for Bulk/Interrupt transfer. + @param IsLow Is Low Speed Device. + + @return The Tds list head for the bulk transfer. + +**/ +UHCI_TD_SW * +UhciCreateBulkOrIntTds ( + IN USB_HC_DEV *Uhc, + IN UINT8 DevAddr, + IN UINT8 EndPoint, + IN UINT8 PktId, + IN UINT8 *Data, + IN UINT8 *DataPhy, + IN UINTN DataLen, + IN OUT UINT8 *DataToggle, + IN UINT8 MaxPacket, + IN BOOLEAN IsLow + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.c b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.c new file mode 100644 index 000000000..582c25e78 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.c @@ -0,0 +1,275 @@ +/** @file + + The UHCI register operation routines. + +Copyright (c) 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Uhci.h" + + +/** + Read a UHCI register. + + @param PciIo The EFI_PCI_IO_PROTOCOL to use. + @param Offset Register offset to USB_BAR_INDEX. + + @return Content of register. + +**/ +UINT16 +UhciReadReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 Offset + ) +{ + UINT16 Data; + EFI_STATUS Status; + + Status = PciIo->Io.Read ( + PciIo, + EfiPciIoWidthUint16, + USB_BAR_INDEX, + Offset, + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UhciReadReg: PciIo Io.Read error: %r at offset %d\n", Status, Offset)); + + Data = 0xFFFF; + } + + return Data; +} + + +/** + Write data to UHCI register. + + @param PciIo The EFI_PCI_IO_PROTOCOL to use. + @param Offset Register offset to USB_BAR_INDEX. + @param Data Data to write. + +**/ +VOID +UhciWriteReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 Offset, + IN UINT16 Data + ) +{ + EFI_STATUS Status; + + Status = PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint16, + USB_BAR_INDEX, + Offset, + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UhciWriteReg: PciIo Io.Write error: %r at offset %d\n", Status, Offset)); + } +} + + +/** + Set a bit of the UHCI Register. + + @param PciIo The EFI_PCI_IO_PROTOCOL to use. + @param Offset Register offset to USB_BAR_INDEX. + @param Bit The bit to set. + +**/ +VOID +UhciSetRegBit ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 Offset, + IN UINT16 Bit + ) +{ + UINT16 Data; + + Data = UhciReadReg (PciIo, Offset); + Data = (UINT16) (Data |Bit); + UhciWriteReg (PciIo, Offset, Data); +} + + +/** + Clear a bit of the UHCI Register. + + @param PciIo The PCI_IO protocol to access the PCI. + @param Offset Register offset to USB_BAR_INDEX. + @param Bit The bit to clear. + +**/ +VOID +UhciClearRegBit ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 Offset, + IN UINT16 Bit + ) +{ + UINT16 Data; + + Data = UhciReadReg (PciIo, Offset); + Data = (UINT16) (Data & ~Bit); + UhciWriteReg (PciIo, Offset, Data); +} + + +/** + Clear all the interrutp status bits, these bits + are Write-Clean. + + @param Uhc The UHCI device. + +**/ +VOID +UhciAckAllInterrupt ( + IN USB_HC_DEV *Uhc + ) +{ + UhciWriteReg (Uhc->PciIo, USBSTS_OFFSET, 0x3F); + + // + // If current HC is halted, re-enable it. Host Controller Process Error + // is a temporary error status. + // + if (!UhciIsHcWorking (Uhc->PciIo)) { + DEBUG ((EFI_D_ERROR, "UhciAckAllInterrupt: re-enable the UHCI from system error\n")); + Uhc->Usb2Hc.SetState (&Uhc->Usb2Hc, EfiUsbHcStateOperational); + } +} + + +/** + Stop the host controller. + + @param Uhc The UHCI device. + @param Timeout Max time allowed. + + @retval EFI_SUCCESS The host controller is stopped. + @retval EFI_TIMEOUT Failed to stop the host controller. + +**/ +EFI_STATUS +UhciStopHc ( + IN USB_HC_DEV *Uhc, + IN UINTN Timeout + ) +{ + UINT16 UsbSts; + UINTN Index; + + UhciClearRegBit (Uhc->PciIo, USBCMD_OFFSET, USBCMD_RS); + + // + // ensure the HC is in halt status after send the stop command + // Timeout is in us unit. + // + for (Index = 0; Index < (Timeout / 50) + 1; Index++) { + UsbSts = UhciReadReg (Uhc->PciIo, USBSTS_OFFSET); + + if ((UsbSts & USBSTS_HCH) == USBSTS_HCH) { + return EFI_SUCCESS; + } + + gBS->Stall (50); + } + + return EFI_TIMEOUT; +} + + +/** + Check whether the host controller operates well. + + @param PciIo The PCI_IO protocol to use. + + @retval TRUE Host controller is working. + @retval FALSE Host controller is halted or system error. + +**/ +BOOLEAN +UhciIsHcWorking ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + UINT16 UsbSts; + + UsbSts = UhciReadReg (PciIo, USBSTS_OFFSET); + + if ((UsbSts & (USBSTS_HCPE | USBSTS_HSE | USBSTS_HCH)) != 0) { + DEBUG ((EFI_D_ERROR, "UhciIsHcWorking: current USB state is %x\n", UsbSts)); + return FALSE; + } + + return TRUE; +} + + +/** + Set the UHCI frame list base address. It can't use + UhciWriteReg which access memory in UINT16. + + @param PciIo The EFI_PCI_IO_PROTOCOL to use. + @param Addr Address to set. + +**/ +VOID +UhciSetFrameListBaseAddr ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN VOID *Addr + ) +{ + EFI_STATUS Status; + UINT32 Data; + + Data = (UINT32) ((UINTN) Addr & 0xFFFFF000); + + Status = PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint32, + USB_BAR_INDEX, + (UINT64) USB_FRAME_BASE_OFFSET, + 1, + &Data + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UhciSetFrameListBaseAddr: PciIo Io.Write error: %r\n", Status)); + } +} + + +/** + Disable USB Emulation. + + @param PciIo The EFI_PCI_IO_PROTOCOL protocol to use. + +**/ +VOID +UhciTurnOffUsbEmulation ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + UINT16 Command; + + Command = 0; + + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + USB_EMULATION_OFFSET, + 1, + &Command + ); +} diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.h b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.h new file mode 100644 index 000000000..b39dcbbbe --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciReg.h @@ -0,0 +1,242 @@ +/** @file + + The definition for UHCI register operation routines. + +Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EFI_UHCI_REG_H_ +#define _EFI_UHCI_REG_H_ + +// +// UHCI register offset +// + +#define UHCI_FRAME_NUM 1024 + +// +// Register offset and PCI related staff +// +#define USB_BAR_INDEX 4 + +#define USBCMD_OFFSET 0 +#define USBSTS_OFFSET 2 +#define USBINTR_OFFSET 4 +#define USBPORTSC_OFFSET 0x10 +#define USB_FRAME_NO_OFFSET 6 +#define USB_FRAME_BASE_OFFSET 8 +#define USB_EMULATION_OFFSET 0xC0 + +// +// Packet IDs +// +#define SETUP_PACKET_ID 0x2D +#define INPUT_PACKET_ID 0x69 +#define OUTPUT_PACKET_ID 0xE1 +#define ERROR_PACKET_ID 0x55 + +// +// USB port status and control bit definition. +// +#define USBPORTSC_CCS BIT0 // Current Connect Status +#define USBPORTSC_CSC BIT1 // Connect Status Change +#define USBPORTSC_PED BIT2 // Port Enable / Disable +#define USBPORTSC_PEDC BIT3 // Port Enable / Disable Change +#define USBPORTSC_LSL BIT4 // Line Status Low BIT +#define USBPORTSC_LSH BIT5 // Line Status High BIT +#define USBPORTSC_RD BIT6 // Resume Detect +#define USBPORTSC_LSDA BIT8 // Low Speed Device Attached +#define USBPORTSC_PR BIT9 // Port Reset +#define USBPORTSC_SUSP BIT12 // Suspend + +// +// UHCI Spec said it must implement 2 ports each host at least, +// and if more, check whether the bit7 of PORTSC is always 1. +// So here assume the max of port number each host is 16. +// +#define USB_MAX_ROOTHUB_PORT 0x0F + +// +// Command register bit definitions +// +#define USBCMD_RS BIT0 // Run/Stop +#define USBCMD_HCRESET BIT1 // Host reset +#define USBCMD_GRESET BIT2 // Global reset +#define USBCMD_EGSM BIT3 // Global Suspend Mode +#define USBCMD_FGR BIT4 // Force Global Resume +#define USBCMD_SWDBG BIT5 // SW Debug mode +#define USBCMD_CF BIT6 // Config Flag (sw only) +#define USBCMD_MAXP BIT7 // Max Packet (0 = 32, 1 = 64) + +// +// USB Status register bit definitions +// +#define USBSTS_USBINT BIT0 // Interrupt due to IOC +#define USBSTS_ERROR BIT1 // Interrupt due to error +#define USBSTS_RD BIT2 // Resume Detect +#define USBSTS_HSE BIT3 // Host System Error +#define USBSTS_HCPE BIT4 // Host Controller Process Error +#define USBSTS_HCH BIT5 // HC Halted + +#define USBTD_ACTIVE BIT7 // TD is still active +#define USBTD_STALLED BIT6 // TD is stalled +#define USBTD_BUFFERR BIT5 // Buffer underflow or overflow +#define USBTD_BABBLE BIT4 // Babble condition +#define USBTD_NAK BIT3 // NAK is received +#define USBTD_CRC BIT2 // CRC/Time out error +#define USBTD_BITSTUFF BIT1 // Bit stuff error + + +/** + Read a UHCI register. + + @param PciIo The EFI_PCI_IO_PROTOCOL to use. + @param Offset Register offset to USB_BAR_INDEX. + + @return Content of register. + +**/ +UINT16 +UhciReadReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 Offset + ); + + + +/** + Write data to UHCI register. + + @param PciIo The EFI_PCI_IO_PROTOCOL to use. + @param Offset Register offset to USB_BAR_INDEX. + @param Data Data to write. + + @return None. + +**/ +VOID +UhciWriteReg ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 Offset, + IN UINT16 Data + ); + + + +/** + Set a bit of the UHCI Register. + + @param PciIo The EFI_PCI_IO_PROTOCOL to use. + @param Offset Register offset to USB_BAR_INDEX. + @param Bit The bit to set. + + @return None. + +**/ +VOID +UhciSetRegBit ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 Offset, + IN UINT16 Bit + ); + + + +/** + Clear a bit of the UHCI Register. + + @param PciIo The PCI_IO protocol to access the PCI. + @param Offset Register offset to USB_BAR_INDEX. + @param Bit The bit to clear. + + @return None. + +**/ +VOID +UhciClearRegBit ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT32 Offset, + IN UINT16 Bit + ); + + +/** + Clear all the interrutp status bits, these bits + are Write-Clean. + + @param Uhc The UHCI device. + + @return None. + +**/ +VOID +UhciAckAllInterrupt ( + IN USB_HC_DEV *Uhc + ); + + +/** + Stop the host controller. + + @param Uhc The UHCI device. + @param Timeout Max time allowed. + + @retval EFI_SUCCESS The host controller is stopped. + @retval EFI_TIMEOUT Failed to stop the host controller. + +**/ +EFI_STATUS +UhciStopHc ( + IN USB_HC_DEV *Uhc, + IN UINTN Timeout + ); + + + +/** + Check whether the host controller operates well. + + @param PciIo The PCI_IO protocol to use. + + @retval TRUE Host controller is working. + @retval FALSE Host controller is halted or system error. + +**/ +BOOLEAN +UhciIsHcWorking ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ); + + +/** + Set the UHCI frame list base address. It can't use + UhciWriteReg which access memory in UINT16. + + @param PciIo The EFI_PCI_IO_PROTOCOL to use. + @param Addr Address to set. + + @return None. + +**/ +VOID +UhciSetFrameListBaseAddr ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN VOID *Addr + ); + + +/** + Disable USB Emulation. + + @param PciIo The EFI_PCI_IO_PROTOCOL protocol to use. + + @return None. + +**/ +VOID +UhciTurnOffUsbEmulation ( + IN EFI_PCI_IO_PROTOCOL *PciIo + ); +#endif diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.c b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.c new file mode 100644 index 000000000..e21641884 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.c @@ -0,0 +1,1040 @@ +/** @file + + The EHCI register operation routines. + +Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Uhci.h" + + +/** + Create Frame List Structure. + + @param Uhc UHCI device. + + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_UNSUPPORTED Map memory fail. + @retval EFI_SUCCESS Success. + +**/ +EFI_STATUS +UhciInitFrameList ( + IN USB_HC_DEV *Uhc + ) +{ + EFI_PHYSICAL_ADDRESS MappedAddr; + EFI_STATUS Status; + VOID *Buffer; + VOID *Mapping; + UINTN Pages; + UINTN Bytes; + UINTN Index; + EFI_PHYSICAL_ADDRESS PhyAddr; + + // + // The Frame List is a common buffer that will be + // accessed by both the cpu and the usb bus master + // at the same time. The Frame List ocupies 4K bytes, + // and must be aligned on 4-Kbyte boundaries. + // + Bytes = 4096; + Pages = EFI_SIZE_TO_PAGES (Bytes); + + Status = Uhc->PciIo->AllocateBuffer ( + Uhc->PciIo, + AllocateAnyPages, + EfiBootServicesData, + Pages, + &Buffer, + 0 + ); + + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Uhc->PciIo->Map ( + Uhc->PciIo, + EfiPciIoOperationBusMasterCommonBuffer, + Buffer, + &Bytes, + &MappedAddr, + &Mapping + ); + + if (EFI_ERROR (Status) || (Bytes != 4096)) { + Status = EFI_UNSUPPORTED; + goto ON_ERROR; + } + + Uhc->FrameBase = (UINT32 *) (UINTN) Buffer; + Uhc->FrameMapping = Mapping; + + // + // Tell the Host Controller where the Frame List lies, + // by set the Frame List Base Address Register. + // + UhciSetFrameListBaseAddr (Uhc->PciIo, (VOID *) (UINTN) MappedAddr); + + // + // Allocate the QH used by sync interrupt/control/bulk transfer. + // FS ctrl/bulk queue head is set to loopback so additional BW + // can be reclaimed. Notice, LS don't support bulk transfer and + // also doesn't support BW reclamation. + // + Uhc->SyncIntQh = UhciCreateQh (Uhc, 1); + Uhc->CtrlQh = UhciCreateQh (Uhc, 1); + Uhc->BulkQh = UhciCreateQh (Uhc, 1); + + if ((Uhc->SyncIntQh == NULL) || (Uhc->CtrlQh == NULL) || (Uhc->BulkQh == NULL)) { + Uhc->PciIo->Unmap (Uhc->PciIo, Mapping); + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // +-------------+ + // | | + // Link the three together: SyncIntQh->CtrlQh->BulkQh <---------+ + // Each frame entry is linked to this sequence of QH. These QH + // will remain on the schedul, never got removed + // + PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_HW)); + Uhc->SyncIntQh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE); + Uhc->SyncIntQh->NextQh = Uhc->CtrlQh; + + PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_HW)); + Uhc->CtrlQh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE); + Uhc->CtrlQh->NextQh = Uhc->BulkQh; + + // + // Some old platform such as Intel's Tiger 4 has a difficult time + // in supporting the full speed bandwidth reclamation in the previous + // mentioned form. Most new platforms don't suffer it. + // + Uhc->BulkQh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE); + + Uhc->BulkQh->NextQh = NULL; + + Uhc->FrameBaseHostAddr = AllocateZeroPool (4096); + if (Uhc->FrameBaseHostAddr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_HW)); + for (Index = 0; Index < UHCI_FRAME_NUM; Index++) { + Uhc->FrameBase[Index] = QH_HLINK (PhyAddr, FALSE); + Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Uhc->SyncIntQh; + } + + return EFI_SUCCESS; + +ON_ERROR: + if (Uhc->SyncIntQh != NULL) { + UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW)); + } + + if (Uhc->CtrlQh != NULL) { + UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW)); + } + + if (Uhc->BulkQh != NULL) { + UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW)); + } + + Uhc->PciIo->FreeBuffer (Uhc->PciIo, Pages, Buffer); + return Status; +} + + +/** + Destory FrameList buffer. + + @param Uhc The UHCI device. + +**/ +VOID +UhciDestoryFrameList ( + IN USB_HC_DEV *Uhc + ) +{ + // + // Unmap the common buffer for framelist entry, + // and free the common buffer. + // Uhci's frame list occupy 4k memory. + // + Uhc->PciIo->Unmap (Uhc->PciIo, Uhc->FrameMapping); + + Uhc->PciIo->FreeBuffer ( + Uhc->PciIo, + EFI_SIZE_TO_PAGES (4096), + (VOID *) Uhc->FrameBase + ); + + if (Uhc->FrameBaseHostAddr != NULL) { + FreePool (Uhc->FrameBaseHostAddr); + } + + if (Uhc->SyncIntQh != NULL) { + UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW)); + } + + if (Uhc->CtrlQh != NULL) { + UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW)); + } + + if (Uhc->BulkQh != NULL) { + UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW)); + } + + Uhc->FrameBase = NULL; + Uhc->FrameBaseHostAddr = NULL; + Uhc->SyncIntQh = NULL; + Uhc->CtrlQh = NULL; + Uhc->BulkQh = NULL; +} + + +/** + Convert the poll rate to the maxium 2^n that is smaller + than Interval. + + @param Interval The poll rate to convert. + + @return The converted poll rate. + +**/ +UINTN +UhciConvertPollRate ( + IN UINTN Interval + ) +{ + UINTN BitCount; + + ASSERT (Interval != 0); + + // + // Find the index (1 based) of the highest non-zero bit + // + BitCount = 0; + + while (Interval != 0) { + Interval >>= 1; + BitCount++; + } + + return (UINTN)1 << (BitCount - 1); +} + + +/** + Link a queue head (for asynchronous interrupt transfer) to + the frame list. + + @param Uhc The UHCI device. + @param Qh The queue head to link into. + +**/ +VOID +UhciLinkQhToFrameList ( + USB_HC_DEV *Uhc, + UHCI_QH_SW *Qh + ) +{ + UINTN Index; + UHCI_QH_SW *Prev; + UHCI_QH_SW *Next; + EFI_PHYSICAL_ADDRESS PhyAddr; + EFI_PHYSICAL_ADDRESS QhPciAddr; + + ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL)); + + QhPciAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Qh, sizeof (UHCI_QH_HW)); + + for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) { + // + // First QH can't be NULL because we always keep static queue + // heads on the frame list + // + ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index])); + Next = (UHCI_QH_SW*)(UINTN)Uhc->FrameBaseHostAddr[Index]; + Prev = NULL; + + // + // Now, insert the queue head (Qh) into this frame: + // 1. Find a queue head with the same poll interval, just insert + // Qh after this queue head, then we are done. + // + // 2. Find the position to insert the queue head into: + // Previous head's interval is bigger than Qh's + // Next head's interval is less than Qh's + // Then, insert the Qh between then + // + // This method is very much the same as that used by EHCI. + // Because each QH's interval is round down to 2^n, poll + // rate is correct. + // + while (Next->Interval > Qh->Interval) { + Prev = Next; + Next = Next->NextQh; + ASSERT (Next != NULL); + } + + // + // The entry may have been linked into the frame by early insertation. + // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh + // with Qh.Interval == 8 on the frame. If so, we are done with this frame. + // It isn't necessary to compare all the QH with the same interval to + // Qh. This is because if there is other QH with the same interval, Qh + // should has been inserted after that at FrameBase[0] and at FrameBase[0] it is + // impossible (Next == Qh) + // + if (Next == Qh) { + continue; + } + + if (Next->Interval == Qh->Interval) { + // + // If there is a QH with the same interval, it locates at + // FrameBase[0], and we can simply insert it after this QH. We + // are all done. + // + ASSERT ((Index == 0) && (Qh->NextQh == NULL)); + + Prev = Next; + Next = Next->NextQh; + + Qh->NextQh = Next; + Prev->NextQh = Qh; + + Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink; + + Prev->QhHw.HorizonLink = QH_HLINK (QhPciAddr, FALSE); + break; + } + + // + // OK, find the right position, insert it in. If Qh's next + // link has already been set, it is in position. This is + // guarranted by 2^n polling interval. + // + if (Qh->NextQh == NULL) { + Qh->NextQh = Next; + PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Next, sizeof (UHCI_QH_HW)); + Qh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE); + } + + if (Prev == NULL) { + Uhc->FrameBase[Index] = QH_HLINK (QhPciAddr, FALSE); + Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Qh; + } else { + Prev->NextQh = Qh; + Prev->QhHw.HorizonLink = QH_HLINK (QhPciAddr, FALSE); + } + } +} + + +/** + Unlink QH from the frame list is easier: find all + the precedence node, and pointer there next to QhSw's + next. + + @param Uhc The UHCI device. + @param Qh The queue head to unlink. + +**/ +VOID +UhciUnlinkQhFromFrameList ( + USB_HC_DEV *Uhc, + UHCI_QH_SW *Qh + ) +{ + UINTN Index; + UHCI_QH_SW *Prev; + UHCI_QH_SW *This; + + ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL)); + + for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) { + // + // Frame link can't be NULL because we always keep static + // queue heads on the frame list + // + ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index])); + This = (UHCI_QH_SW*)(UINTN)Uhc->FrameBaseHostAddr[Index]; + Prev = NULL; + + // + // Walk through the frame's QH list to find the + // queue head to remove + // + while ((This != NULL) && (This != Qh)) { + Prev = This; + This = This->NextQh; + } + + // + // Qh may have already been unlinked from this frame + // by early action. + // + if (This == NULL) { + continue; + } + + if (Prev == NULL) { + // + // Qh is the first entry in the frame + // + Uhc->FrameBase[Index] = Qh->QhHw.HorizonLink; + Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Qh->NextQh; + } else { + Prev->NextQh = Qh->NextQh; + Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink; + } + } +} + + +/** + Check TDs Results. + + @param Uhc This UHCI device. + @param Td UHCI_TD_SW to check. + @param IsLow Is Low Speed Device. + @param QhResult Return the result of this TD list. + + @return Whether the TD's result is finialized. + +**/ +BOOLEAN +UhciCheckTdStatus ( + IN USB_HC_DEV *Uhc, + IN UHCI_TD_SW *Td, + IN BOOLEAN IsLow, + OUT UHCI_QH_RESULT *QhResult + ) +{ + UINTN Len; + UINT8 State; + UHCI_TD_HW *TdHw; + BOOLEAN Finished; + + Finished = TRUE; + + // + // Initialize the data toggle to that of the first + // TD. The next toggle to use is either: + // 1. first TD's toggle if no TD is executed OK + // 2. the next toggle of last executed-OK TD + // + QhResult->Result = EFI_USB_NOERROR; + QhResult->NextToggle = (UINT8)Td->TdHw.DataToggle; + QhResult->Complete = 0; + + while (Td != NULL) { + TdHw = &Td->TdHw; + State = (UINT8)TdHw->Status; + + // + // UHCI will set STALLED bit when it abort the execution + // of TD list. There are several reasons: + // 1. BABBLE error happened + // 2. Received a STALL response + // 3. Error count decreased to zero. + // + // It also set CRC/Timeout/NAK/Buffer Error/BitStuff Error + // bits when corresponding conditions happen. But these + // conditions are not deadly, that is a TD can successfully + // completes even these bits are set. But it is likely that + // upper layer won't distinguish these condtions. So, only + // set these bits when TD is actually halted. + // + if ((State & USBTD_STALLED) != 0) { + if ((State & USBTD_BABBLE) != 0) { + QhResult->Result |= EFI_USB_ERR_BABBLE; + + } else if (TdHw->ErrorCount != 0) { + QhResult->Result |= EFI_USB_ERR_STALL; + } + + if ((State & USBTD_CRC) != 0) { + QhResult->Result |= EFI_USB_ERR_CRC; + } + + if ((State & USBTD_BUFFERR) != 0) { + QhResult->Result |= EFI_USB_ERR_BUFFER; + } + + if ((Td->TdHw.Status & USBTD_BITSTUFF) != 0) { + QhResult->Result |= EFI_USB_ERR_BITSTUFF; + } + + if (TdHw->ErrorCount == 0) { + QhResult->Result |= EFI_USB_ERR_TIMEOUT; + } + + Finished = TRUE; + goto ON_EXIT; + + } else if ((State & USBTD_ACTIVE) != 0) { + // + // The TD is still active, no need to check further. + // + QhResult->Result |= EFI_USB_ERR_NOTEXECUTE; + + Finished = FALSE; + goto ON_EXIT; + + } else { + // + // Update the next data toggle, it is always the + // next to the last known-good TD's data toggle if + // any TD is executed OK + // + QhResult->NextToggle = (UINT8) (1 - (UINT8)TdHw->DataToggle); + + // + // This TD is finished OK or met short packet read. Update the + // transfer length if it isn't a SETUP. + // + Len = (TdHw->ActualLen + 1) & 0x7FF; + + if (TdHw->PidCode != SETUP_PACKET_ID) { + QhResult->Complete += Len; + } + + // + // Short packet condition for full speed input TD, also + // terminate the transfer + // + if (!IsLow && (TdHw->ShortPacket == 1) && (Len < Td->DataLen)) { + DEBUG ((DEBUG_VERBOSE, "UhciCheckTdStatus: short packet read occurred\n")); + + Finished = TRUE; + goto ON_EXIT; + } + } + + Td = Td->NextTd; + } + +ON_EXIT: + // + // Check whether HC is halted. Don't move this up. It must be + // called after data toggle is successfully updated. + // + if (!UhciIsHcWorking (Uhc->PciIo)) { + QhResult->Result |= EFI_USB_ERR_SYSTEM; + Finished = TRUE; + } + + if (Finished) { + Uhc->PciIo->Flush (Uhc->PciIo); + } + + UhciAckAllInterrupt (Uhc); + return Finished; +} + + +/** + Check the result of the transfer. + + @param Uhc The UHCI device. + @param Qh The queue head of the transfer. + @param Td The first TDs of the transfer. + @param TimeOut TimeOut value in milliseconds. + @param IsLow Is Low Speed Device. + @param QhResult The variable to return result. + + @retval EFI_SUCCESS The transfer finished with success. + @retval EFI_DEVICE_ERROR Transfer failed. + +**/ +EFI_STATUS +UhciExecuteTransfer ( + IN USB_HC_DEV *Uhc, + IN UHCI_QH_SW *Qh, + IN UHCI_TD_SW *Td, + IN UINTN TimeOut, + IN BOOLEAN IsLow, + OUT UHCI_QH_RESULT *QhResult + ) +{ + UINTN Index; + UINTN Delay; + BOOLEAN Finished; + EFI_STATUS Status; + BOOLEAN InfiniteLoop; + + Finished = FALSE; + Status = EFI_SUCCESS; + Delay = TimeOut * UHC_1_MILLISECOND; + InfiniteLoop = FALSE; + + // + // According to UEFI spec section 16.2.4, If Timeout is 0, then the caller + // must wait for the function to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR + // is returned. + // + if (TimeOut == 0) { + InfiniteLoop = TRUE; + } + + for (Index = 0; InfiniteLoop || (Index < Delay); Index++) { + Finished = UhciCheckTdStatus (Uhc, Td, IsLow, QhResult); + + // + // Transfer is OK or some error occurred (TD inactive) + // + if (Finished) { + break; + } + + gBS->Stall (UHC_1_MICROSECOND); + } + + if (!Finished) { + DEBUG ((EFI_D_ERROR, "UhciExecuteTransfer: execution not finished for %dms\n", (UINT32)TimeOut)); + UhciDumpQh (Qh); + UhciDumpTds (Td); + + Status = EFI_TIMEOUT; + + } else if (QhResult->Result != EFI_USB_NOERROR) { + DEBUG ((EFI_D_ERROR, "UhciExecuteTransfer: execution failed with result %x\n", QhResult->Result)); + UhciDumpQh (Qh); + UhciDumpTds (Td); + + Status = EFI_DEVICE_ERROR; + } + + return Status; +} + + +/** + Update Async Request, QH and TDs. + + @param Uhc The UHCI device. + @param AsyncReq The UHCI asynchronous transfer to update. + @param Result Transfer reslut. + @param NextToggle The toggle of next data. + +**/ +VOID +UhciUpdateAsyncReq ( + IN USB_HC_DEV *Uhc, + IN UHCI_ASYNC_REQUEST *AsyncReq, + IN UINT32 Result, + IN UINT32 NextToggle + ) +{ + UHCI_QH_SW *Qh; + UHCI_TD_SW *FirstTd; + UHCI_TD_SW *Td; + + Qh = AsyncReq->QhSw; + FirstTd = AsyncReq->FirstTd; + + if (Result == EFI_USB_NOERROR) { + // + // The last transfer succeeds. Then we need to update + // the Qh and Td for next round of transfer. + // 1. Update the TD's data toggle + // 2. Activate all the TDs + // 3. Link the TD to the queue head again since during + // execution, queue head's TD pointer is changed by + // hardware. + // + for (Td = FirstTd; Td != NULL; Td = Td->NextTd) { + Td->TdHw.DataToggle = NextToggle; + NextToggle ^= 1; + Td->TdHw.Status |= USBTD_ACTIVE; + } + + UhciLinkTdToQh (Uhc, Qh, FirstTd); + return ; + } +} + + +/** + Create Async Request node, and Link to List. + + @param Uhc The UHCI device. + @param Qh The queue head of the transfer. + @param FirstTd First TD of the transfer. + @param DevAddr Device Address. + @param EndPoint EndPoint Address. + @param DataLen Data length. + @param Interval Polling Interval when inserted to frame list. + @param Data Data buffer, unmapped. + @param Callback Callback after interrupt transfeer. + @param Context Callback Context passed as function parameter. + @param IsLow Is Low Speed. + + @retval EFI_SUCCESS An asynchronous transfer is created. + @retval EFI_INVALID_PARAMETER Paremeter is error. + @retval EFI_OUT_OF_RESOURCES Failed because of resource shortage. + +**/ +EFI_STATUS +UhciCreateAsyncReq ( + IN USB_HC_DEV *Uhc, + IN UHCI_QH_SW *Qh, + IN UHCI_TD_SW *FirstTd, + IN UINT8 DevAddr, + IN UINT8 EndPoint, + IN UINTN DataLen, + IN UINTN Interval, + IN UINT8 *Data, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context, + IN BOOLEAN IsLow + ) +{ + UHCI_ASYNC_REQUEST *AsyncReq; + + AsyncReq = AllocatePool (sizeof (UHCI_ASYNC_REQUEST)); + + if (AsyncReq == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Fill Request field. Data is allocated host memory, not mapped + // + AsyncReq->Signature = UHCI_ASYNC_INT_SIGNATURE; + AsyncReq->DevAddr = DevAddr; + AsyncReq->EndPoint = EndPoint; + AsyncReq->DataLen = DataLen; + AsyncReq->Interval = UhciConvertPollRate(Interval); + AsyncReq->Data = Data; + AsyncReq->Callback = Callback; + AsyncReq->Context = Context; + AsyncReq->QhSw = Qh; + AsyncReq->FirstTd = FirstTd; + AsyncReq->IsLow = IsLow; + + // + // Insert the new interrupt transfer to the head of the list. + // The interrupt transfer's monitor function scans the whole + // list from head to tail. The new interrupt transfer MUST be + // added to the head of the list. + // + InsertHeadList (&(Uhc->AsyncIntList), &(AsyncReq->Link)); + + return EFI_SUCCESS; +} + + +/** + Free an asynchronous request's resource such as memory. + + @param Uhc The UHCI device. + @param AsyncReq The asynchronous request to free. + +**/ +VOID +UhciFreeAsyncReq ( + IN USB_HC_DEV *Uhc, + IN UHCI_ASYNC_REQUEST *AsyncReq + ) +{ + ASSERT ((Uhc != NULL) && (AsyncReq != NULL)); + + UhciDestoryTds (Uhc, AsyncReq->FirstTd); + UsbHcFreeMem (Uhc->MemPool, AsyncReq->QhSw, sizeof (UHCI_QH_SW)); + + if (AsyncReq->Data != NULL) { + UsbHcFreeMem (Uhc->MemPool, AsyncReq->Data, AsyncReq->DataLen); + } + + gBS->FreePool (AsyncReq); +} + + +/** + Unlink an asynchronous request's from UHC's asynchronus list. + also remove the queue head from the frame list. If FreeNow, + release its resource also. Otherwise, add the request to the + UHC's recycle list to wait for a while before release the memory. + Until then, hardware won't hold point to the request. + + @param Uhc The UHCI device. + @param AsyncReq The asynchronous request to free. + @param FreeNow If TRUE, free the resource immediately, otherwise + add the request to recycle wait list. + +**/ +VOID +UhciUnlinkAsyncReq ( + IN USB_HC_DEV *Uhc, + IN UHCI_ASYNC_REQUEST *AsyncReq, + IN BOOLEAN FreeNow + ) +{ + ASSERT ((Uhc != NULL) && (AsyncReq != NULL)); + + RemoveEntryList (&(AsyncReq->Link)); + UhciUnlinkQhFromFrameList (Uhc, AsyncReq->QhSw); + + if (FreeNow) { + UhciFreeAsyncReq (Uhc, AsyncReq); + } else { + // + // To sychronize with hardware, mark the queue head as inactive + // then add AsyncReq to UHC's recycle list + // + AsyncReq->QhSw->QhHw.VerticalLink = QH_VLINK (NULL, TRUE); + AsyncReq->Recycle = Uhc->RecycleWait; + Uhc->RecycleWait = AsyncReq; + } +} + + +/** + Delete Async Interrupt QH and TDs. + + @param Uhc The UHCI device. + @param DevAddr Device Address. + @param EndPoint EndPoint Address. + @param Toggle The next data toggle to use. + + @retval EFI_SUCCESS The request is deleted. + @retval EFI_INVALID_PARAMETER Paremeter is error. + @retval EFI_NOT_FOUND The asynchronous isn't found. + +**/ +EFI_STATUS +UhciRemoveAsyncReq ( + IN USB_HC_DEV *Uhc, + IN UINT8 DevAddr, + IN UINT8 EndPoint, + OUT UINT8 *Toggle + ) +{ + EFI_STATUS Status; + UHCI_ASYNC_REQUEST *AsyncReq; + UHCI_QH_RESULT QhResult; + LIST_ENTRY *Link; + BOOLEAN Found; + + Status = EFI_SUCCESS; + + // + // If no asynchronous interrupt transaction exists + // + if (IsListEmpty (&(Uhc->AsyncIntList))) { + return EFI_SUCCESS; + } + + // + // Find the asynchronous transfer to this device/endpoint pair + // + Found = FALSE; + Link = Uhc->AsyncIntList.ForwardLink; + + do { + AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Link); + Link = Link->ForwardLink; + + if ((AsyncReq->DevAddr == DevAddr) && (AsyncReq->EndPoint == EndPoint)) { + Found = TRUE; + break; + } + + } while (Link != &(Uhc->AsyncIntList)); + + if (!Found) { + return EFI_NOT_FOUND; + } + + // + // Check the result of the async transfer then update it + // to get the next data toggle to use. + // + UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult); + *Toggle = QhResult.NextToggle; + + // + // Don't release the request now, keep it to synchronize with hardware. + // + UhciUnlinkAsyncReq (Uhc, AsyncReq, FALSE); + return Status; +} + + +/** + Recycle the asynchronouse request. When a queue head + is unlinked from frame list, host controller hardware + may still hold a cached pointer to it. To synchronize + with hardware, the request is released in two steps: + first it is linked to the UHC's RecycleWait list. At + the next time UhciMonitorAsyncReqList is fired, it is + moved to UHC's Recylelist. Then, at another timer + activation, all the requests on Recycle list is freed. + This guarrantes that each unlink queue head keeps + existing for at least 50ms, far enough for the hardware + to clear its cache. + + @param Uhc The UHCI device. + +**/ +VOID +UhciRecycleAsyncReq ( + IN USB_HC_DEV *Uhc + ) +{ + UHCI_ASYNC_REQUEST *Req; + UHCI_ASYNC_REQUEST *Next; + + Req = Uhc->Recycle; + + while (Req != NULL) { + Next = Req->Recycle; + UhciFreeAsyncReq (Uhc, Req); + Req = Next; + } + + Uhc->Recycle = Uhc->RecycleWait; + Uhc->RecycleWait = NULL; +} + + + +/** + Release all the asynchronous transfers on the lsit. + + @param Uhc The UHCI device. + +**/ +VOID +UhciFreeAllAsyncReq ( + IN USB_HC_DEV *Uhc + ) +{ + LIST_ENTRY *Head; + UHCI_ASYNC_REQUEST *AsyncReq; + + // + // Call UhciRecycleAsyncReq twice. The requests on Recycle + // will be released at the first call; The requests on + // RecycleWait will be released at the second call. + // + UhciRecycleAsyncReq (Uhc); + UhciRecycleAsyncReq (Uhc); + + Head = &(Uhc->AsyncIntList); + + if (IsListEmpty (Head)) { + return; + } + + while (!IsListEmpty (Head)) { + AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Head->ForwardLink); + UhciUnlinkAsyncReq (Uhc, AsyncReq, TRUE); + } +} + + +/** + Interrupt transfer periodic check handler. + + @param Event The event of the time. + @param Context Context of the event, pointer to USB_HC_DEV. + +**/ +VOID +EFIAPI +UhciMonitorAsyncReqList ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UHCI_ASYNC_REQUEST *AsyncReq; + LIST_ENTRY *Link; + USB_HC_DEV *Uhc; + VOID *Data; + BOOLEAN Finished; + UHCI_QH_RESULT QhResult; + + Uhc = (USB_HC_DEV *) Context; + + // + // Recycle the asynchronous requests expired, and promote + // requests waiting to be recycled the next time when this + // timer expires + // + UhciRecycleAsyncReq (Uhc); + + if (IsListEmpty (&(Uhc->AsyncIntList))) { + return ; + } + + // + // This loop must be delete safe + // + Link = Uhc->AsyncIntList.ForwardLink; + + do { + AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Link); + Link = Link->ForwardLink; + + Finished = UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult); + + if (!Finished) { + continue; + } + + // + // Copy the data to temporary buffer if there are some + // data transferred. We may have zero-length packet. + // Make sure the data received from HW is no more than expected. + // + Data = NULL; + + if ((QhResult.Complete != 0) && (QhResult.Complete <= AsyncReq->DataLen)) { + Data = AllocatePool (QhResult.Complete); + + if (Data == NULL) { + return ; + } + + CopyMem (Data, AsyncReq->FirstTd->Data, QhResult.Complete); + } + + UhciUpdateAsyncReq (Uhc, AsyncReq, QhResult.Result, QhResult.NextToggle); + + // + // Now, either transfer is SUCCESS or met errors since + // we have skipped to next transfer earlier if current + // transfer is still active. + // + if (QhResult.Result == EFI_USB_NOERROR) { + AsyncReq->Callback (Data, QhResult.Complete, AsyncReq->Context, QhResult.Result); + } else { + // + // Leave error recovery to its related device driver. + // A common case of the error recovery is to re-submit + // the interrupt transfer. When an interrupt transfer + // is re-submitted, its position in the linked list is + // changed. It is inserted to the head of the linked + // list, while this function scans the whole list from + // head to tail. Thus, the re-submitted interrupt transfer's + // callback function will not be called again in this round. + // + AsyncReq->Callback (NULL, 0, AsyncReq->Context, QhResult.Result); + } + + if (Data != NULL) { + gBS->FreePool (Data); + } + } while (Link != &(Uhc->AsyncIntList)); +} diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.h b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.h new file mode 100644 index 000000000..5bcfad5c6 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.h @@ -0,0 +1,265 @@ +/** @file + + The definition for EHCI register operation routines. + +Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EFI_UHCI_SCHED_H_ +#define _EFI_UHCI_SCHED_H_ + + +#define UHCI_ASYNC_INT_SIGNATURE SIGNATURE_32 ('u', 'h', 'c', 'a') +// +// The failure mask for USB transfer return status. If any of +// these bit is set, the transfer failed. EFI_USB_ERR_NOEXECUTE +// and EFI_USB_ERR_NAK are not considered as error condition: +// the transfer is still going on. +// +#define USB_ERR_FAIL_MASK (EFI_USB_ERR_STALL | EFI_USB_ERR_BUFFER | \ + EFI_USB_ERR_BABBLE | EFI_USB_ERR_CRC | \ + EFI_USB_ERR_TIMEOUT | EFI_USB_ERR_BITSTUFF | \ + EFI_USB_ERR_SYSTEM) + + +// +// Structure to return the result of UHCI QH execution. +// Result is the final result of the QH's QTD. NextToggle +// is the next data toggle to use. Complete is the actual +// length of data transferred. +// +typedef struct { + UINT32 Result; + UINT8 NextToggle; + UINTN Complete; +} UHCI_QH_RESULT; + +typedef struct _UHCI_ASYNC_REQUEST UHCI_ASYNC_REQUEST; + +// +// Structure used to manager the asynchronous interrupt transfers. +// +struct _UHCI_ASYNC_REQUEST{ + UINTN Signature; + LIST_ENTRY Link; + UHCI_ASYNC_REQUEST *Recycle; + + // + // Endpoint attributes + // + UINT8 DevAddr; + UINT8 EndPoint; + BOOLEAN IsLow; + UINTN Interval; + + // + // Data and UHC structures + // + UHCI_QH_SW *QhSw; + UHCI_TD_SW *FirstTd; + UINT8 *Data; // Allocated host memory, not mapped memory + UINTN DataLen; + VOID *Mapping; + + // + // User callback and its context + // + EFI_ASYNC_USB_TRANSFER_CALLBACK Callback; + VOID *Context; +}; + +#define UHCI_ASYNC_INT_FROM_LINK(a) \ + CR (a, UHCI_ASYNC_REQUEST, Link, UHCI_ASYNC_INT_SIGNATURE) + + +/** + Create Frame List Structure. + + @param Uhc The UHCI device. + + @return EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @return EFI_UNSUPPORTED Map memory fail. + @return EFI_SUCCESS Success. + +**/ +EFI_STATUS +UhciInitFrameList ( + IN USB_HC_DEV *Uhc + ); + +/** + Destory FrameList buffer. + + @param Uhc The UHCI device. + + @return None. + +**/ +VOID +UhciDestoryFrameList ( + IN USB_HC_DEV *Uhc + ); + + +/** + Convert the poll rate to the maxium 2^n that is smaller + than Interval. + + @param Interval The poll rate to convert. + + @return The converted poll rate. + +**/ +UINTN +UhciConvertPollRate ( + IN UINTN Interval + ); + + +/** + Link a queue head (for asynchronous interrupt transfer) to + the frame list. + + @param Uhc The UHCI device. + @param Qh The queue head to link into. + +**/ +VOID +UhciLinkQhToFrameList ( + USB_HC_DEV *Uhc, + UHCI_QH_SW *Qh + ); + + +/** + Unlink QH from the frame list is easier: find all + the precedence node, and pointer there next to QhSw's + next. + + @param Uhc The UHCI device. + @param Qh The queue head to unlink. + +**/ +VOID +UhciUnlinkQhFromFrameList ( + USB_HC_DEV *Uhc, + UHCI_QH_SW *Qh + ); + + +/** + Check the result of the transfer. + + @param Uhc The UHCI device. + @param Qh The queue head of the transfer. + @param Td The first TDs of the transfer. + @param TimeOut TimeOut value in milliseconds. + @param IsLow Is Low Speed Device. + @param QhResult The variable to return result. + + @retval EFI_SUCCESS The transfer finished with success. + @retval EFI_DEVICE_ERROR Transfer failed. + +**/ +EFI_STATUS +UhciExecuteTransfer ( + IN USB_HC_DEV *Uhc, + IN UHCI_QH_SW *Qh, + IN UHCI_TD_SW *Td, + IN UINTN TimeOut, + IN BOOLEAN IsLow, + OUT UHCI_QH_RESULT *QhResult + ); + + +/** + Create Async Request node, and Link to List. + + @param Uhc The UHCI device. + @param Qh The queue head of the transfer. + @param FirstTd First TD of the transfer. + @param DevAddr Device Address. + @param EndPoint EndPoint Address. + @param DataLen Data length. + @param Interval Polling Interval when inserted to frame list. + @param Data Data buffer, unmapped. + @param Callback Callback after interrupt transfeer. + @param Context Callback Context passed as function parameter. + @param IsLow Is Low Speed. + + @retval EFI_SUCCESS An asynchronous transfer is created. + @retval EFI_INVALID_PARAMETER Paremeter is error. + @retval EFI_OUT_OF_RESOURCES Failed because of resource shortage. + +**/ +EFI_STATUS +UhciCreateAsyncReq ( + IN USB_HC_DEV *Uhc, + IN UHCI_QH_SW *Qh, + IN UHCI_TD_SW *FirstTd, + IN UINT8 DevAddr, + IN UINT8 EndPoint, + IN UINTN DataLen, + IN UINTN Interval, + IN UINT8 *Data, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, + IN VOID *Context, + IN BOOLEAN IsLow + ); + + +/** + Delete Async Interrupt QH and TDs. + + @param Uhc The UHCI device. + @param DevAddr Device Address. + @param EndPoint EndPoint Address. + @param Toggle The next data toggle to use. + + @retval EFI_SUCCESS The request is deleted. + @retval EFI_INVALID_PARAMETER Paremeter is error. + @retval EFI_NOT_FOUND The asynchronous isn't found. + +**/ +EFI_STATUS +UhciRemoveAsyncReq ( + IN USB_HC_DEV *Uhc, + IN UINT8 DevAddr, + IN UINT8 EndPoint, + OUT UINT8 *Toggle + ); + + +/** + Release all the asynchronous transfers on the lsit. + + @param Uhc The UHCI device. + + @return None. + +**/ +VOID +UhciFreeAllAsyncReq ( + IN USB_HC_DEV *Uhc + ); + + +/** + Interrupt transfer periodic check handler. + + @param Event The event of the time. + @param Context Context of the event, pointer to USB_HC_DEV. + + @return None. + +**/ +VOID +EFIAPI +UhciMonitorAsyncReqList ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.c b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.c new file mode 100644 index 000000000..9aade19f8 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.c @@ -0,0 +1,558 @@ +/** @file + + The routine procedure for uhci memory allocate/free. + +Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Uhci.h" + + +/** + Allocate a block of memory to be used by the buffer pool. + + @param Pool The buffer pool to allocate memory for. + @param Pages How many pages to allocate. + + @return The allocated memory block or NULL if failed. + +**/ +USBHC_MEM_BLOCK * +UsbHcAllocMemBlock ( + IN USBHC_MEM_POOL *Pool, + IN UINTN Pages + ) +{ + USBHC_MEM_BLOCK *Block; + EFI_PCI_IO_PROTOCOL *PciIo; + VOID *BufHost; + VOID *Mapping; + EFI_PHYSICAL_ADDRESS MappedAddr; + UINTN Bytes; + EFI_STATUS Status; + + PciIo = Pool->PciIo; + + Block = AllocateZeroPool (sizeof (USBHC_MEM_BLOCK)); + if (Block == NULL) { + return NULL; + } + + // + // each bit in the bit array represents USBHC_MEM_UNIT + // bytes of memory in the memory block. + // + ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE); + + Block->BufLen = EFI_PAGES_TO_SIZE (Pages); + Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8); + Block->Bits = AllocateZeroPool (Block->BitsLen); + + if (Block->Bits == NULL) { + gBS->FreePool (Block); + return NULL; + } + + // + // Allocate the number of Pages of memory, then map it for + // bus master read and write. + // + Status = PciIo->AllocateBuffer ( + PciIo, + AllocateAnyPages, + EfiBootServicesData, + Pages, + &BufHost, + 0 + ); + + if (EFI_ERROR (Status)) { + goto FREE_BITARRAY; + } + + Bytes = EFI_PAGES_TO_SIZE (Pages); + Status = PciIo->Map ( + PciIo, + EfiPciIoOperationBusMasterCommonBuffer, + BufHost, + &Bytes, + &MappedAddr, + &Mapping + ); + + if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (Pages))) { + goto FREE_BUFFER; + } + + // + // Check whether the data structure used by the host controller + // should be restricted into the same 4G + // + if (Pool->Check4G && (Pool->Which4G != USB_HC_HIGH_32BIT (MappedAddr))) { + PciIo->Unmap (PciIo, Mapping); + goto FREE_BUFFER; + } + + Block->BufHost = BufHost; + Block->Buf = (UINT8 *) ((UINTN) MappedAddr); + Block->Mapping = Mapping; + + return Block; + +FREE_BUFFER: + PciIo->FreeBuffer (PciIo, Pages, BufHost); + +FREE_BITARRAY: + gBS->FreePool (Block->Bits); + gBS->FreePool (Block); + return NULL; +} + + +/** + Free the memory block from the memory pool. + + @param Pool The memory pool to free the block from. + @param Block The memory block to free. + +**/ +VOID +UsbHcFreeMemBlock ( + IN USBHC_MEM_POOL *Pool, + IN USBHC_MEM_BLOCK *Block + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + + ASSERT ((Pool != NULL) && (Block != NULL)); + + PciIo = Pool->PciIo; + + // + // Unmap the common buffer then free the structures + // + PciIo->Unmap (PciIo, Block->Mapping); + PciIo->FreeBuffer (PciIo, EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost); + + gBS->FreePool (Block->Bits); + gBS->FreePool (Block); +} + + +/** + Alloc some memory from the block. + + @param Block The memory block to allocate memory from. + @param Units Number of memory units to allocate. + + @return EFI_SUCCESS The needed memory is allocated. + @return EFI_NOT_FOUND Can't find the free memory. + +**/ +VOID * +UsbHcAllocMemFromBlock ( + IN USBHC_MEM_BLOCK *Block, + IN UINTN Units + ) +{ + UINTN Byte; + UINT8 Bit; + UINTN StartByte; + UINT8 StartBit; + UINTN Available; + UINTN Count; + + ASSERT ((Block != 0) && (Units != 0)); + + StartByte = 0; + StartBit = 0; + Available = 0; + + for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) { + // + // If current bit is zero, the corresponding memory unit is + // available, otherwise we need to restart our searching. + // Available counts the consective number of zero bit. + // + if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) { + Available++; + + if (Available >= Units) { + break; + } + + NEXT_BIT (Byte, Bit); + + } else { + NEXT_BIT (Byte, Bit); + + Available = 0; + StartByte = Byte; + StartBit = Bit; + } + } + + if (Available < Units) { + return NULL; + } + + // + // Mark the memory as allocated + // + Byte = StartByte; + Bit = StartBit; + + for (Count = 0; Count < Units; Count++) { + ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) USB_HC_BIT (Bit)); + NEXT_BIT (Byte, Bit); + } + + return Block->Buf + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT; +} + +/** + Calculate the corresponding pci bus address according to the Mem parameter. + + @param Pool The memory pool of the host controller. + @param Mem The pointer to host memory. + @param Size The size of the memory region. + + @return the pci memory address +**/ +EFI_PHYSICAL_ADDRESS +UsbHcGetPciAddressForHostMem ( + IN USBHC_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ) +{ + USBHC_MEM_BLOCK *Head; + USBHC_MEM_BLOCK *Block; + UINTN AllocSize; + EFI_PHYSICAL_ADDRESS PhyAddr; + UINTN Offset; + + Head = Pool->Head; + AllocSize = USBHC_MEM_ROUND (Size); + + if (Mem == NULL) { + return 0; + } + + for (Block = Head; Block != NULL; Block = Block->Next) { + // + // scan the memory block list for the memory block that + // completely contains the allocated memory. + // + if ((Block->BufHost <= (UINT8 *) Mem) && (((UINT8 *) Mem + AllocSize) <= (Block->BufHost + Block->BufLen))) { + break; + } + } + + ASSERT ((Block != NULL)); + // + // calculate the pci memory address for host memory address. + // + Offset = (UINT8 *)Mem - Block->BufHost; + PhyAddr = (EFI_PHYSICAL_ADDRESS)(UINTN) (Block->Buf + Offset); + return PhyAddr; +} + +/** + Insert the memory block to the pool's list of the blocks. + + @param Head The head of the memory pool's block list. + @param Block The memory block to insert. + +**/ +VOID +UsbHcInsertMemBlockToPool ( + IN USBHC_MEM_BLOCK *Head, + IN USBHC_MEM_BLOCK *Block + ) +{ + ASSERT ((Head != NULL) && (Block != NULL)); + Block->Next = Head->Next; + Head->Next = Block; +} + + +/** + Is the memory block empty? + + @param Block The memory block to check. + + @return TRUE The memory block is empty. + @return FALSE The memory block isn't empty. + +**/ +BOOLEAN +UsbHcIsMemBlockEmpty ( + IN USBHC_MEM_BLOCK *Block + ) +{ + UINTN Index; + + for (Index = 0; Index < Block->BitsLen; Index++) { + if (Block->Bits[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + + +/** + Unlink the memory block from the pool's list. + + @param Head The block list head of the memory's pool. + @param BlockToUnlink The memory block to unlink. + +**/ +VOID +UsbHcUnlinkMemBlock ( + IN USBHC_MEM_BLOCK *Head, + IN USBHC_MEM_BLOCK *BlockToUnlink + ) +{ + USBHC_MEM_BLOCK *Block; + + ASSERT ((Head != NULL) && (BlockToUnlink != NULL)); + + for (Block = Head; Block != NULL; Block = Block->Next) { + if (Block->Next == BlockToUnlink) { + Block->Next = BlockToUnlink->Next; + BlockToUnlink->Next = NULL; + break; + } + } +} + + +/** + Initialize the memory management pool for the host controller. + + @param PciIo The PciIo that can be used to access the host controller. + @param Check4G Whether the host controller requires allocated memory + from one 4G address space. + @param Which4G The 4G memory area each memory allocated should be from. + + @return EFI_SUCCESS The memory pool is initialized. + @return EFI_OUT_OF_RESOURCE Fail to init the memory pool. + +**/ +USBHC_MEM_POOL * +UsbHcInitMemPool ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN BOOLEAN Check4G, + IN UINT32 Which4G + ) +{ + USBHC_MEM_POOL *Pool; + + Pool = AllocatePool (sizeof (USBHC_MEM_POOL)); + + if (Pool == NULL) { + return Pool; + } + + Pool->PciIo = PciIo; + Pool->Check4G = Check4G; + Pool->Which4G = Which4G; + Pool->Head = UsbHcAllocMemBlock (Pool, USBHC_MEM_DEFAULT_PAGES); + + if (Pool->Head == NULL) { + gBS->FreePool (Pool); + Pool = NULL; + } + + return Pool; +} + + +/** + Release the memory management pool. + + @param Pool The USB memory pool to free. + + @return EFI_SUCCESS The memory pool is freed. + @return EFI_DEVICE_ERROR Failed to free the memory pool. + +**/ +EFI_STATUS +UsbHcFreeMemPool ( + IN USBHC_MEM_POOL *Pool + ) +{ + USBHC_MEM_BLOCK *Block; + + ASSERT (Pool->Head != NULL); + + // + // Unlink all the memory blocks from the pool, then free them. + // UsbHcUnlinkMemBlock can't be used to unlink and free the + // first block. + // + for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) { + UsbHcUnlinkMemBlock (Pool->Head, Block); + UsbHcFreeMemBlock (Pool, Block); + } + + UsbHcFreeMemBlock (Pool, Pool->Head); + gBS->FreePool (Pool); + return EFI_SUCCESS; +} + + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +UsbHcAllocateMem ( + IN USBHC_MEM_POOL *Pool, + IN UINTN Size + ) +{ + USBHC_MEM_BLOCK *Head; + USBHC_MEM_BLOCK *Block; + USBHC_MEM_BLOCK *NewBlock; + VOID *Mem; + UINTN AllocSize; + UINTN Pages; + + Mem = NULL; + AllocSize = USBHC_MEM_ROUND (Size); + Head = Pool->Head; + ASSERT (Head != NULL); + + // + // First check whether current memory blocks can satisfy the allocation. + // + for (Block = Head; Block != NULL; Block = Block->Next) { + Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT); + + if (Mem != NULL) { + ZeroMem (Mem, Size); + break; + } + } + + if (Mem != NULL) { + return Mem; + } + + // + // Create a new memory block if there is not enough memory + // in the pool. If the allocation size is larger than the + // default page number, just allocate a large enough memory + // block. Otherwise allocate default pages. + // + if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) { + Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1; + } else { + Pages = USBHC_MEM_DEFAULT_PAGES; + } + + NewBlock = UsbHcAllocMemBlock (Pool, Pages); + + if (NewBlock == NULL) { + DEBUG ((EFI_D_INFO, "UsbHcAllocateMem: failed to allocate block\n")); + return NULL; + } + + // + // Add the new memory block to the pool, then allocate memory from it + // + UsbHcInsertMemBlockToPool (Head, NewBlock); + Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT); + + if (Mem != NULL) { + ZeroMem (Mem, Size); + } + + return Mem; +} + + +/** + Free the allocated memory back to the memory pool. + + @param Pool The memory pool of the host controller. + @param Mem The memory to free. + @param Size The size of the memory to free. + +**/ +VOID +UsbHcFreeMem ( + IN USBHC_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ) +{ + USBHC_MEM_BLOCK *Head; + USBHC_MEM_BLOCK *Block; + UINT8 *ToFree; + UINTN AllocSize; + UINTN Byte; + UINTN Bit; + UINTN Count; + + Head = Pool->Head; + AllocSize = USBHC_MEM_ROUND (Size); + ToFree = (UINT8 *) Mem; + + for (Block = Head; Block != NULL; Block = Block->Next) { + // + // scan the memory block list for the memory block that + // completely contains the memory to free. + // + if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) { + // + // compute the start byte and bit in the bit array + // + Byte = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) / 8; + Bit = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) % 8; + + // + // reset associated bits in bit array + // + for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) { + ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ USB_HC_BIT (Bit)); + NEXT_BIT (Byte, Bit); + } + + break; + } + } + + // + // If Block == NULL, it means that the current memory isn't + // in the host controller's pool. This is critical because + // the caller has passed in a wrong memory point + // + ASSERT (Block != NULL); + + // + // Release the current memory block if it is empty and not the head + // + if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) { + UsbHcUnlinkMemBlock (Head, Block); + UsbHcFreeMemBlock (Pool, Block); + } + + return ; +} diff --git a/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.h b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.h new file mode 100644 index 000000000..d202669c1 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Pci/UhciDxe/UsbHcMem.h @@ -0,0 +1,155 @@ +/** @file + + This file contains the definination for host controller memory management routines + +Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EFI_EHCI_MEM_H_ +#define _EFI_EHCI_MEM_H_ + +#define USB_HC_BIT(a) ((UINTN)(1 << (a))) + +#define USB_HC_BIT_IS_SET(Data, Bit) \ + ((BOOLEAN)(((Data) & USB_HC_BIT(Bit)) == USB_HC_BIT(Bit))) + +#define USB_HC_HIGH_32BIT(Addr64) \ + ((UINT32)(RShiftU64((UINTN)(Addr64), 32) & 0XFFFFFFFF)) + + +typedef struct _USBHC_MEM_BLOCK USBHC_MEM_BLOCK; +struct _USBHC_MEM_BLOCK { + UINT8 *Bits; // Bit array to record which unit is allocated + UINTN BitsLen; + UINT8 *Buf; + UINT8 *BufHost; + UINTN BufLen; // Memory size in bytes + VOID *Mapping; + USBHC_MEM_BLOCK *Next; +}; + +// +// USBHC_MEM_POOL is used to manage the memory used by USB +// host controller. EHCI requires the control memory and transfer +// data to be on the same 4G memory. +// +typedef struct _USBHC_MEM_POOL { + EFI_PCI_IO_PROTOCOL *PciIo; + BOOLEAN Check4G; + UINT32 Which4G; + USBHC_MEM_BLOCK *Head; +} USBHC_MEM_POOL; + +// +// Memory allocation unit, must be 2^n, n>4 +// +#define USBHC_MEM_UNIT 64 + +#define USBHC_MEM_UNIT_MASK (USBHC_MEM_UNIT - 1) +#define USBHC_MEM_DEFAULT_PAGES 16 + +#define USBHC_MEM_ROUND(Len) (((Len) + USBHC_MEM_UNIT_MASK) & (~USBHC_MEM_UNIT_MASK)) + +// +// Advance the byte and bit to the next bit, adjust byte accordingly. +// +#define NEXT_BIT(Byte, Bit) \ + do { \ + (Bit)++; \ + if ((Bit) > 7) { \ + (Byte)++; \ + (Bit) = 0; \ + } \ + } while (0) + + +/** + Initialize the memory management pool for the host controller. + + @param PciIo The PciIo that can be used to access the host controller. + @param Check4G Whether the host controller requires allocated memory + from one 4G address space. + @param Which4G The 4G memory area each memory allocated should be from. + + @retval EFI_SUCCESS The memory pool is initialized. + @retval EFI_OUT_OF_RESOURCE Fail to init the memory pool. + +**/ +USBHC_MEM_POOL * +UsbHcInitMemPool ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN BOOLEAN Check4G, + IN UINT32 Which4G + ); + + +/** + Release the memory management pool. + + @param Pool The USB memory pool to free. + + @return EFI_SUCCESS The memory pool is freed. + @return EFI_DEVICE_ERROR Failed to free the memory pool. + +**/ +EFI_STATUS +UsbHcFreeMemPool ( + IN USBHC_MEM_POOL *Pool + ); + + + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +UsbHcAllocateMem ( + IN USBHC_MEM_POOL *Pool, + IN UINTN Size + ); + + + +/** + Free the allocated memory back to the memory pool. + + @param Pool The memory pool of the host controller. + @param Mem The memory to free. + @param Size The size of the memory to free. + + @return None. + +**/ +VOID +UsbHcFreeMem ( + IN USBHC_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ); + +/** + Calculate the corresponding pci bus address according to the Mem parameter. + + @param Pool The memory pool of the host controller. + @param Mem The pointer to host memory. + @param Size The size of the memory region. + + @return the pci memory address +**/ +EFI_PHYSICAL_ADDRESS +UsbHcGetPciAddressForHostMem ( + IN USBHC_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ); + +#endif -- cgit 1.2.3-korg