From: Ruiyu Ni Date: Thu, 10 Dec 2015 02:47:16 +0000 (+0000) Subject: MdeModulePkg: Add PciSioSerialDxe driver X-Git-Tag: edk2-stable201903~8327 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=a59e2edebeb2762af458aa63725f5bb0facb7c5e MdeModulePkg: Add PciSioSerialDxe driver PciSioSerialDxe driver can manages UARTs on a SIO chip or a PCI/PCIE card. It manages the SIO instance whose last device path node is a ACPI device path and the HID in the ACPI device path node equals to EISA_PNP_ID (0x501). It also manages the PCI IO instance whose class code is 7/0/2 (16550 UART). But when proper value is set to PcdPciSerialParameters, the driver can also manage non-standard PCI serial cards by matching the Vendor ID and Device ID specified in PcdPciSerialParameters. The PCI BAR index, IO/MMIO offset, register stride, clock rate can also be specified through the same PCD. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ruiyu Ni Reviewed-by: Feng Tian Reviewed-by: Michael Kinney git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19179 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/ComponentName.c b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/ComponentName.c new file mode 100644 index 0000000000..994dc847c9 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/ComponentName.c @@ -0,0 +1,288 @@ +/** @file + UEFI Component Name and Name2 protocol for Isa serial driver. + +Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Serial.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gPciSioSerialComponentName = { + SerialComponentNameGetDriverName, + SerialComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gPciSioSerialComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) SerialComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) SerialComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mSerialDriverNameTable[] = { + { + "eng;en", + L"PCI SIO Serial Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED CHAR16 mSioSerialPortName[] = L"SIO Serial Port #%d"; +GLOBAL_REMOVE_IF_UNREFERENCED CHAR16 mPciSerialPortName[] = L"PCI Serial Port #%d"; + +/** + 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 +SerialComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mSerialDriverNameTable, + DriverName, + (BOOLEAN)(This == &gPciSioSerialComponentName) + ); +} + +/** + 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 +SerialComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + SERIAL_DEV *SerialDevice; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + EFI_GUID *IoProtocolGuid; + + // + // Make sure this driver is currently managing ControllerHandle + // + IoProtocolGuid = &gEfiSioProtocolGuid; + Status = EfiTestManagedDevice ( + ControllerHandle, + gSerialControllerDriver.DriverBindingHandle, + IoProtocolGuid + ); + if (EFI_ERROR (Status)) { + IoProtocolGuid = &gEfiPciIoProtocolGuid; + Status = EfiTestManagedDevice ( + ControllerHandle, + gSerialControllerDriver.DriverBindingHandle, + IoProtocolGuid + ); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + ControllerNameTable = NULL; + if (ChildHandle != NULL) { + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + IoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the Serial I/O Protocol from the child handle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + gSerialControllerDriver.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the Serial Controller's Device structure + // + SerialDevice = SERIAL_DEV_FROM_THIS (SerialIo); + ControllerNameTable = SerialDevice->ControllerNameTable; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gPciSioSerialComponentName) + ); +} + +/** + Add the ISO639-2 and RFC4646 component name both for the Serial IO device + + @param SerialDevice A pointer to the SERIAL_DEV instance. + @param Instance Instance ID for the serial device. +**/ +VOID +AddName ( + IN SERIAL_DEV *SerialDevice, + IN UINT32 Instance + ) +{ + CHAR16 SerialPortName[SERIAL_PORT_NAME_LEN]; + UnicodeSPrint ( + SerialPortName, + sizeof (SerialPortName), + (SerialDevice->PciDeviceInfo != NULL) ? PCI_SERIAL_PORT_NAME : SIO_SERIAL_PORT_NAME, + Instance + ); + AddUnicodeString2 ( + "eng", + gPciSioSerialComponentName.SupportedLanguages, + &SerialDevice->ControllerNameTable, + SerialPortName, + TRUE + ); + AddUnicodeString2 ( + "en", + gPciSioSerialComponentName2.SupportedLanguages, + &SerialDevice->ControllerNameTable, + SerialPortName, + FALSE + ); + +} diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.inf b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.inf new file mode 100644 index 0000000000..03fddfe75e --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.inf @@ -0,0 +1,81 @@ +## @file +# Serial driver for standard UARTS on a SIO chip or PCI/PCIE card. +# +# Produces the Serial I/O protocol for standard UARTS using Super I/O or PCI I/O. +# +# Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PciSioSerialDxe + MODULE_UNI_FILE = PciSioSerialDxe.uni + FILE_GUID = E2775B47-D453-4EE3-ADA7-391A1B05AC17 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializePciSioSerial + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gSerialControllerDriver +# COMPONENT_NAME = gPciSioSerialComponentName +# COMPONENT_NAME2 = gPciSioSerialComponentName2 +# + +[Sources] + ComponentName.c + SerialIo.c + Serial.h + Serial.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PcdLib + ReportStatusCodeLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + DevicePathLib + UefiLib + UefiDriverEntryPoint + DebugLib + IoLib + +[Guids] + gEfiUartDevicePathGuid ## SOMETIMES_CONSUMES ## GUID + +[Protocols] + gEfiSioProtocolGuid ## TO_START + gEfiDevicePathProtocolGuid ## TO_START + gEfiPciIoProtocolGuid ## TO_START + gEfiSerialIoProtocolGuid ## BY_START + gEfiDevicePathProtocolGuid ## BY_START + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseHalfHandshake|FALSE ## CONSUMES + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate|115200 ## CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultDataBits|8 ## CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultParity|1 ## CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultStopBits|1 ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialClockRate|1843200 ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdPciSerialParameters ## CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + PciSioSerialDxeExtra.uni diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.uni b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.uni new file mode 100644 index 0000000000..617f583b35 Binary files /dev/null and b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.uni differ diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxeExtra.uni b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxeExtra.uni new file mode 100644 index 0000000000..935bdba93e Binary files /dev/null and b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxeExtra.uni differ diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c new file mode 100644 index 0000000000..86e75a43f7 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c @@ -0,0 +1,1242 @@ +/** @file + Serial driver for PCI or SIO UARTS. + +Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Serial.h" + +// +// ISA Serial Driver Global Variables +// + +EFI_DRIVER_BINDING_PROTOCOL gSerialControllerDriver = { + SerialControllerDriverSupported, + SerialControllerDriverStart, + SerialControllerDriverStop, + 0xa, + NULL, + NULL +}; + +CONTROLLER_DEVICE_PATH mControllerDevicePathTemplate = { + { + HARDWARE_DEVICE_PATH, + HW_CONTROLLER_DP, + sizeof (CONTROLLER_DEVICE_PATH), + 0 + }, + 0 +}; + +SERIAL_DEV gSerialDevTemplate = { + SERIAL_DEV_SIGNATURE, + NULL, + { + SERIAL_IO_INTERFACE_REVISION, + SerialReset, + SerialSetAttributes, + SerialSetControl, + SerialGetControl, + SerialWrite, + SerialRead, + NULL + }, // SerialIo + { + SERIAL_PORT_SUPPORT_CONTROL_MASK, + SERIAL_PORT_DEFAULT_TIMEOUT, + 0, + 16, + 0, + 0, + 0 + }, // SerialMode + NULL, // DevicePath + NULL, // ParentDevicePath + { + { + MESSAGING_DEVICE_PATH, + MSG_UART_DP, + { + (UINT8) (sizeof (UART_DEVICE_PATH)), + (UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8) + } + }, + 0, 0, 0, 0, 0 + }, // UartDevicePath + 0, // BaseAddress + FALSE, // MmioAccess + 1, // RegisterStride + 0, // ClockRate + 16, // ReceiveFifoDepth + { 0, 0 }, // Receive; + 16, // TransmitFifoDepth + { 0, 0 }, // Transmit; + FALSE, // SoftwareLoopbackEnable; + FALSE, // HardwareFlowControl; + NULL, // *ControllerNameTable; + FALSE, // ContainsControllerNode; + 0, // Instance; + NULL // *PciDeviceInfo; +}; + +/** + Check the device path node whether it's the Flow Control node or not. + + @param[in] FlowControl The device path node to be checked. + + @retval TRUE It's the Flow Control node. + @retval FALSE It's not. + +**/ +BOOLEAN +IsUartFlowControlDevicePathNode ( + IN UART_FLOW_CONTROL_DEVICE_PATH *FlowControl + ) +{ + return (BOOLEAN) ( + (DevicePathType (FlowControl) == MESSAGING_DEVICE_PATH) && + (DevicePathSubType (FlowControl) == MSG_VENDOR_DP) && + (CompareGuid (&FlowControl->Guid, &gEfiUartDevicePathGuid)) + ); +} + +/** + The user Entry Point for module PciSioSerial. The user code starts with this function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializePciSioSerial ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gSerialControllerDriver, + ImageHandle, + &gPciSioSerialComponentName, + &gPciSioSerialComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + // + // Initialize UART default setting in gSerialDevTempate + // + gSerialDevTemplate.SerialMode.BaudRate = PcdGet64 (PcdUartDefaultBaudRate); + gSerialDevTemplate.SerialMode.DataBits = PcdGet8 (PcdUartDefaultDataBits); + gSerialDevTemplate.SerialMode.Parity = PcdGet8 (PcdUartDefaultParity); + gSerialDevTemplate.SerialMode.StopBits = PcdGet8 (PcdUartDefaultStopBits); + gSerialDevTemplate.UartDevicePath.BaudRate = PcdGet64 (PcdUartDefaultBaudRate); + gSerialDevTemplate.UartDevicePath.DataBits = PcdGet8 (PcdUartDefaultDataBits); + gSerialDevTemplate.UartDevicePath.Parity = PcdGet8 (PcdUartDefaultParity); + gSerialDevTemplate.UartDevicePath.StopBits = PcdGet8 (PcdUartDefaultStopBits); + gSerialDevTemplate.ClockRate = PcdGet32 (PcdSerialClockRate); + + return Status; +} + +/** + Return whether the controller is a SIO serial controller. + + @param Controller The controller handle. + + @retval EFI_SUCCESS The controller is a SIO serial controller. + @retval others The controller is not a SIO serial controller. +**/ +EFI_STATUS +IsSioSerialController ( + EFI_HANDLE Controller + ) +{ + EFI_STATUS Status; + EFI_SIO_PROTOCOL *Sio; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + ACPI_HID_DEVICE_PATH *Acpi; + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiSioProtocolGuid, + (VOID **) &Sio, + gSerialControllerDriver.DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (!EFI_ERROR (Status)) { + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + Controller, + &gEfiSioProtocolGuid, + gSerialControllerDriver.DriverBindingHandle, + Controller + ); + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + gSerialControllerDriver.DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + ASSERT (Status != EFI_ALREADY_STARTED); + + if (!EFI_ERROR (Status)) { + do { + Acpi = (ACPI_HID_DEVICE_PATH *) DevicePath; + DevicePath = NextDevicePathNode (DevicePath); + } while (!IsDevicePathEnd (DevicePath)); + + if (DevicePathType (Acpi) != ACPI_DEVICE_PATH || + (DevicePathSubType (Acpi) != ACPI_DP && DevicePathSubType (Acpi) != ACPI_EXTENDED_DP) || + Acpi->HID != EISA_PNP_ID (0x501) + ) { + Status = EFI_UNSUPPORTED; + } + } + + // + // Close protocol, don't use device path protocol in the Support() function + // + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + gSerialControllerDriver.DriverBindingHandle, + Controller + ); + } + return Status; +} + +/** + Return whether the controller is a PCI serial controller. + + @param Controller The controller handle. + + @retval EFI_SUCCESS The controller is a PCI serial controller. + @retval others The controller is not a PCI serial controller. +**/ +EFI_STATUS +IsPciSerialController ( + EFI_HANDLE Controller + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + PCI_TYPE00 Pci; + PCI_SERIAL_PARAMETER *PciSerialParameter; + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + gSerialControllerDriver.DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (!EFI_ERROR (Status)) { + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0, sizeof (Pci), &Pci); + if (!EFI_ERROR (Status)) { + if (!IS_PCI_16550_SERIAL (&Pci)) { + for (PciSerialParameter = (PCI_SERIAL_PARAMETER *) PcdGetPtr (PcdPciSerialParameters) + ; PciSerialParameter->VendorId != 0xFFFF + ; PciSerialParameter++ + ) { + if ((Pci.Hdr.VendorId == PciSerialParameter->VendorId) && + (Pci.Hdr.DeviceId == PciSerialParameter->DeviceId) + ) { + break; + } + } + if (PciSerialParameter->VendorId == 0xFFFF) { + Status = EFI_UNSUPPORTED; + } else { + Status = EFI_SUCCESS; + } + } + } + + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + gSerialControllerDriver.DriverBindingHandle, + Controller + ); + } + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open the EFI Device Path protocol needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + gSerialControllerDriver.DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + ASSERT (Status != EFI_ALREADY_STARTED); + + // + // Close protocol, don't use device path protocol in the Support() function + // + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + gSerialControllerDriver.DriverBindingHandle, + Controller + ); + + return Status; +} + +/** + Check to see if this driver supports the given controller + + @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The handle of the controller to test. + @param RemainingDevicePath A pointer to the remaining portion of a device path. + + @return EFI_SUCCESS This driver can support the given controller + +**/ +EFI_STATUS +EFIAPI +SerialControllerDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) + +{ + EFI_STATUS Status; + UART_DEVICE_PATH *Uart; + UART_FLOW_CONTROL_DEVICE_PATH *FlowControl; + + // + // Test RemainingDevicePath + // + if ((RemainingDevicePath != NULL) && !IsDevicePathEnd (RemainingDevicePath)) { + Status = EFI_UNSUPPORTED; + + Uart = SkipControllerDevicePathNode (RemainingDevicePath, NULL, NULL); + if (DevicePathType (Uart) != MESSAGING_DEVICE_PATH || + DevicePathSubType (Uart) != MSG_UART_DP || + DevicePathNodeLength (Uart) != sizeof (UART_DEVICE_PATH) + ) { + return EFI_UNSUPPORTED; + } + + // + // Do a rough check because Clock Rate is unknown until DriverBindingStart() + // + if (!VerifyUartParameters (0, Uart->BaudRate, Uart->DataBits, Uart->Parity, Uart->StopBits, NULL, NULL)) { + return EFI_UNSUPPORTED; + } + + FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart); + if (IsUartFlowControlDevicePathNode (FlowControl)) { + // + // If the second node is Flow Control Node, + // return error when it request other than hardware flow control. + // + if ((ReadUnaligned32 (&FlowControl->FlowControlMap) & ~UART_FLOW_CONTROL_HARDWARE) != 0) { + return EFI_UNSUPPORTED; + } + } + } + + Status = IsSioSerialController (Controller); + if (EFI_ERROR (Status)) { + Status = IsPciSerialController (Controller); + } + return Status; +} + +/** + Create the child serial device instance. + + @param Controller The parent controller handle. + @param Uart Pointer to the UART device path node in RemainingDevicePath, + or NULL if RemainingDevicePath is NULL. + @param ParentDevicePath Pointer to the parent device path. + @param CreateControllerNode TRUE to create the controller node. + @param Instance Instance number of the serial device. + The value will be set to the controller node + if CreateControllerNode is TRUE. + @param ParentIo A union type pointer to either Sio or PciIo. + @param PciSerialParameter The PCI serial parameter to be used by current serial device. + NULL for SIO serial device. + @param PciDeviceInfo The PCI device info for the current serial device. + NULL for SIO serial device. + + @retval EFI_SUCCESS The serial device was created successfully. + @retval others The serial device wasn't created. +**/ +EFI_STATUS +CreateSerialDevice ( + IN EFI_HANDLE Controller, + IN UART_DEVICE_PATH *Uart, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, + IN BOOLEAN CreateControllerNode, + IN UINT32 Instance, + IN PARENT_IO_PROTOCOL_PTR ParentIo, + IN PCI_SERIAL_PARAMETER *PciSerialParameter, OPTIONAL + IN PCI_DEVICE_INFO *PciDeviceInfo OPTIONAL + ) +{ + EFI_STATUS Status; + SERIAL_DEV *SerialDevice; + UINT8 BarIndex; + UINT64 Offset; + UART_FLOW_CONTROL_DEVICE_PATH *FlowControl; + UINT32 FlowControlMap; + ACPI_RESOURCE_HEADER_PTR Resources; + EFI_ACPI_IO_PORT_DESCRIPTOR *Io; + EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *FixedIo; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *AddressSpace; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + + BarIndex = 0; + Offset = 0; + FlowControl = NULL; + FlowControlMap = 0; + + // + // Initialize the serial device instance + // + SerialDevice = AllocateCopyPool (sizeof (SERIAL_DEV), &gSerialDevTemplate); + ASSERT (SerialDevice != NULL); + + SerialDevice->SerialIo.Mode = &(SerialDevice->SerialMode); + SerialDevice->ParentDevicePath = ParentDevicePath; + SerialDevice->PciDeviceInfo = PciDeviceInfo; + SerialDevice->Instance = Instance; + + if (Uart != NULL) { + CopyMem (&SerialDevice->UartDevicePath, Uart, sizeof (UART_DEVICE_PATH)); + FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart); + if (IsUartFlowControlDevicePathNode (FlowControl)) { + FlowControlMap = ReadUnaligned32 (&FlowControl->FlowControlMap); + } else { + FlowControl = NULL; + } + } + + // + // For PCI serial device, use the information from PCD + // + if (PciSerialParameter != NULL) { + BarIndex = (PciSerialParameter->BarIndex == PCI_BAR_ALL) ? 0 : PciSerialParameter->BarIndex; + Offset = PciSerialParameter->Offset; + if (PciSerialParameter->RegisterStride != 0) { + SerialDevice->RegisterStride = PciSerialParameter->RegisterStride; + } + if (PciSerialParameter->ClockRate != 0) { + SerialDevice->ClockRate = PciSerialParameter->ClockRate; + } + if (PciSerialParameter->ReceiveFifoDepth != 0) { + SerialDevice->ReceiveFifoDepth = PciSerialParameter->ReceiveFifoDepth; + } + if (PciSerialParameter->TransmitFifoDepth != 0) { + SerialDevice->TransmitFifoDepth = PciSerialParameter->TransmitFifoDepth; + } + } + + // + // Pass NULL ActualBaudRate to VerifyUartParameters to disallow baudrate degrade. + // DriverBindingStart() shouldn't create a handle with different UART device path. + // + if (!VerifyUartParameters (SerialDevice->ClockRate, SerialDevice->UartDevicePath.BaudRate, SerialDevice->UartDevicePath.DataBits, + SerialDevice->UartDevicePath.Parity, SerialDevice->UartDevicePath.StopBits, NULL, NULL + )) { + Status = EFI_INVALID_PARAMETER; + goto CreateError; + } + + if (PciSerialParameter == NULL) { + Status = ParentIo.Sio->GetResources (ParentIo.Sio, &Resources); + } else { + Status = ParentIo.PciIo->GetBarAttributes (ParentIo.PciIo, BarIndex, NULL, (VOID **) &Resources); + } + + if (!EFI_ERROR (Status)) { + // + // Get the base address information from ACPI resource descriptor. + // ACPI_IO_PORT_DESCRIPTOR and ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR are returned from Sio; + // ACPI_ADDRESS_SPACE_DESCRIPTOR is returned from PciIo. + // + while ((Resources.SmallHeader->Byte != ACPI_END_TAG_DESCRIPTOR) && (SerialDevice->BaseAddress == 0)) { + switch (Resources.SmallHeader->Byte) { + case ACPI_IO_PORT_DESCRIPTOR: + Io = (EFI_ACPI_IO_PORT_DESCRIPTOR *) Resources.SmallHeader; + if (Io->Length != 0) { + SerialDevice->BaseAddress = Io->BaseAddressMin; + } + break; + + case ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR: + FixedIo = (EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *) Resources.SmallHeader; + if (FixedIo->Length != 0) { + SerialDevice->BaseAddress = FixedIo->BaseAddress; + } + break; + + case ACPI_ADDRESS_SPACE_DESCRIPTOR: + AddressSpace = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Resources.SmallHeader; + if (AddressSpace->AddrLen != 0) { + if (AddressSpace->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) { + SerialDevice->MmioAccess = TRUE; + } + SerialDevice->BaseAddress = AddressSpace->AddrRangeMin + Offset; + } + break; + } + + if (Resources.SmallHeader->Bits.Type == 0) { + Resources.SmallHeader = (ACPI_SMALL_RESOURCE_HEADER *) ((UINT8 *) Resources.SmallHeader + + Resources.SmallHeader->Bits.Length + + sizeof (*Resources.SmallHeader)); + } else { + Resources.LargeHeader = (ACPI_LARGE_RESOURCE_HEADER *) ((UINT8 *) Resources.LargeHeader + + Resources.LargeHeader->Length + + sizeof (*Resources.LargeHeader)); + } + } + } + + if (SerialDevice->BaseAddress == 0) { + Status = EFI_INVALID_PARAMETER; + goto CreateError; + } + + SerialDevice->HardwareFlowControl = (BOOLEAN) (FlowControlMap == UART_FLOW_CONTROL_HARDWARE); + + // + // Report status code the serial present + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_P_PC_PRESENCE_DETECT | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->ParentDevicePath + ); + + if (!SerialPresent (SerialDevice)) { + Status = EFI_DEVICE_ERROR; + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE, + EFI_P_EC_NOT_DETECTED | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->ParentDevicePath + ); + goto CreateError; + } + + // + // 1. Append Controller device path node. + // + if (CreateControllerNode) { + mControllerDevicePathTemplate.ControllerNumber = SerialDevice->Instance; + SerialDevice->DevicePath = AppendDevicePathNode ( + SerialDevice->ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &mControllerDevicePathTemplate + ); + SerialDevice->ContainsControllerNode = TRUE; + } + + // + // 2. Append UART device path node. + // The Uart setings are zero here. + // SetAttribute() will update them to match the default setings. + // + TempDevicePath = SerialDevice->DevicePath; + if (TempDevicePath != NULL) { + SerialDevice->DevicePath = AppendDevicePathNode ( + TempDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &SerialDevice->UartDevicePath + ); + FreePool (TempDevicePath); + } else { + SerialDevice->DevicePath = AppendDevicePathNode ( + SerialDevice->ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &SerialDevice->UartDevicePath + ); + } + // + // 3. Append the Flow Control device path node. + // Only produce the Flow Control node when remaining device path has it + // + if (FlowControl != NULL) { + TempDevicePath = SerialDevice->DevicePath; + if (TempDevicePath != NULL) { + SerialDevice->DevicePath = AppendDevicePathNode ( + TempDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) FlowControl + ); + FreePool (TempDevicePath); + } + } + ASSERT (SerialDevice->DevicePath != NULL); + + // + // Fill in Serial I/O Mode structure based on either the RemainingDevicePath or defaults. + // + SerialDevice->SerialMode.BaudRate = SerialDevice->UartDevicePath.BaudRate; + SerialDevice->SerialMode.DataBits = SerialDevice->UartDevicePath.DataBits; + SerialDevice->SerialMode.Parity = SerialDevice->UartDevicePath.Parity; + SerialDevice->SerialMode.StopBits = SerialDevice->UartDevicePath.StopBits; + + // + // Issue a reset to initialize the COM port + // + Status = SerialDevice->SerialIo.Reset (&SerialDevice->SerialIo); + if (EFI_ERROR (Status)) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE, + EFI_P_EC_CONTROLLER_ERROR | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->DevicePath + ); + goto CreateError; + } + + AddName (SerialDevice, Instance); + // + // Install protocol interfaces for the serial device. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &SerialDevice->Handle, + &gEfiDevicePathProtocolGuid, SerialDevice->DevicePath, + &gEfiSerialIoProtocolGuid, &SerialDevice->SerialIo, + NULL + ); + if (EFI_ERROR (Status)) { + goto CreateError; + } + // + // Open For Child Device + // + Status = gBS->OpenProtocol ( + Controller, + PciSerialParameter != NULL ? &gEfiPciIoProtocolGuid : &gEfiSioProtocolGuid, + (VOID **) &ParentIo, + gSerialControllerDriver.DriverBindingHandle, + SerialDevice->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + &SerialDevice->Handle, + &gEfiDevicePathProtocolGuid, SerialDevice->DevicePath, + &gEfiSerialIoProtocolGuid, &SerialDevice->SerialIo, + NULL + ); + } + +CreateError: + if (EFI_ERROR (Status)) { + if (SerialDevice->DevicePath != NULL) { + FreePool (SerialDevice->DevicePath); + } + if (SerialDevice->ControllerNameTable != NULL) { + FreeUnicodeStringTable (SerialDevice->ControllerNameTable); + } + FreePool (SerialDevice); + } + return Status; +} + +/** + Returns an array of pointers containing all the child serial device pointers. + + @param Controller The parent controller handle. + @param IoProtocolGuid The protocol GUID, either equals to gEfiSioProtocolGuid + or equals to gEfiPciIoProtocolGuid. + @param Count Count of the serial devices. + + @return An array of pointers containing all the child serial device pointers. +**/ +SERIAL_DEV ** +GetChildSerialDevices ( + IN EFI_HANDLE Controller, + IN EFI_GUID *IoProtocolGuid, + OUT UINTN *Count + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + UINTN EntryCount; + SERIAL_DEV **SerialDevices; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + BOOLEAN OpenByDriver; + + *Count = 0; + // + // If the SerialIo instance specified by RemainingDevicePath is already created, + // update the attributes/control. + // + Status = gBS->OpenProtocolInformation ( + Controller, + IoProtocolGuid, + &OpenInfoBuffer, + &EntryCount + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + SerialDevices = AllocatePool (EntryCount * sizeof (SERIAL_DEV *)); + ASSERT (SerialDevices != NULL); + + *Count = 0; + OpenByDriver = FALSE; + for (Index = 0; Index < EntryCount; Index++) { + if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { + Status = gBS->OpenProtocol ( + OpenInfoBuffer[Index].ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + gSerialControllerDriver.DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + SerialDevices[(*Count)++] = SERIAL_DEV_FROM_THIS (SerialIo); + } + } + + + if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) { + ASSERT (OpenInfoBuffer[Index].AgentHandle == gSerialControllerDriver.DriverBindingHandle); + OpenByDriver = TRUE; + } + } + if (OpenInfoBuffer != NULL) { + FreePool (OpenInfoBuffer); + } + + ASSERT ((*Count == 0) || (OpenByDriver)); + + return SerialDevices; +} + +/** + Start to management the controller passed in + + @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The handle of the controller to test. + @param RemainingDevicePath A pointer to the remaining portion of a device path. + + @return EFI_SUCCESS Driver is started successfully +**/ +EFI_STATUS +EFIAPI +SerialControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + UINT32 ControllerNumber; + UART_DEVICE_PATH *Uart; + UART_FLOW_CONTROL_DEVICE_PATH *FlowControl; + UINT32 Control; + PARENT_IO_PROTOCOL_PTR ParentIo; + ACPI_HID_DEVICE_PATH *Acpi; + EFI_GUID *IoProtocolGuid; + PCI_SERIAL_PARAMETER *PciSerialParameter; + PCI_SERIAL_PARAMETER DefaultPciSerialParameter; + PCI_TYPE00 Pci; + UINT32 PciSerialCount; + SERIAL_DEV **SerialDevices; + UINTN SerialDeviceCount; + PCI_DEVICE_INFO *PciDeviceInfo; + UINT64 Supports; + BOOLEAN ContainsControllerNode; + + // + // Get the Parent Device Path + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + return Status; + } + // + // Report status code enable the serial + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_P_PC_ENABLE | EFI_PERIPHERAL_SERIAL_PORT, + ParentDevicePath + ); + + // + // Grab the IO abstraction we need to get any work done + // + IoProtocolGuid = &gEfiSioProtocolGuid; + Status = gBS->OpenProtocol ( + Controller, + IoProtocolGuid, + (VOID **) &ParentIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + IoProtocolGuid = &gEfiPciIoProtocolGuid; + Status = gBS->OpenProtocol ( + Controller, + IoProtocolGuid, + (VOID **) &ParentIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + } + ASSERT (!EFI_ERROR (Status) || Status == EFI_ALREADY_STARTED); + + // + // Do nothing for END device path node + // + if ((RemainingDevicePath != NULL) && IsDevicePathEnd (RemainingDevicePath)) { + return EFI_SUCCESS; + } + + + SerialDevices = GetChildSerialDevices (Controller, IoProtocolGuid, &SerialDeviceCount); + // + // If the SerialIo instance specified by RemainingDevicePath is already created, + // update the attributes/control. + // + if ((SerialDeviceCount != 0) && (RemainingDevicePath != NULL)) { + Uart = (UART_DEVICE_PATH *) SkipControllerDevicePathNode (RemainingDevicePath, &ContainsControllerNode, &ControllerNumber); + for (Index = 0; Index < SerialDeviceCount; Index++) { + if ((!SerialDevices[Index]->ContainsControllerNode && !ContainsControllerNode) || + (SerialDevices[Index]->ContainsControllerNode && ContainsControllerNode && SerialDevices[Index]->Instance == ControllerNumber) + ) { + Status = EFI_INVALID_PARAMETER; + // + // Pass NULL ActualBaudRate to VerifyUartParameters to disallow baudrate degrade. + // DriverBindingStart() shouldn't create a handle with different UART device path. + // + if (VerifyUartParameters (SerialDevices[Index]->ClockRate, Uart->BaudRate, Uart->DataBits, + (EFI_PARITY_TYPE) Uart->Parity, (EFI_STOP_BITS_TYPE) Uart->StopBits, NULL, NULL)) { + SerialIo = &SerialDevices[Index]->SerialIo; + Status = SerialIo->SetAttributes ( + SerialIo, + Uart->BaudRate, + SerialIo->Mode->ReceiveFifoDepth, + SerialIo->Mode->Timeout, + (EFI_PARITY_TYPE) Uart->Parity, + Uart->DataBits, + (EFI_STOP_BITS_TYPE) Uart->StopBits + ); + } + FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart); + if (!EFI_ERROR (Status) && IsUartFlowControlDevicePathNode (FlowControl)) { + Status = SerialIo->GetControl (SerialIo, &Control); + if (!EFI_ERROR (Status)) { + if (ReadUnaligned32 (&FlowControl->FlowControlMap) == UART_FLOW_CONTROL_HARDWARE) { + Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE; + } else { + Control &= ~EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE; + } + // + // Clear the bits that are not allowed to pass to SetControl + // + Control &= (EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY | + EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE | EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE | + EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE); + Status = SerialIo->SetControl (SerialIo, Control); + } + } + break; + } + } + if (Index != SerialDeviceCount) { + // + // Directly return if the SerialIo instance specified by RemainingDevicePath is found and updated. + // Otherwise continue to create the instance specified by RemainingDevicePath. + // + if (SerialDevices != NULL) { + FreePool (SerialDevices); + } + return Status; + } + } + + if (RemainingDevicePath != NULL) { + Uart = (UART_DEVICE_PATH *) SkipControllerDevicePathNode (RemainingDevicePath, &ContainsControllerNode, &ControllerNumber); + } else { + Uart = NULL; + } + + PciDeviceInfo = NULL; + if (IoProtocolGuid == &gEfiSioProtocolGuid) { + Status = EFI_NOT_FOUND; + if (RemainingDevicePath == NULL || !ContainsControllerNode) { + Node = ParentDevicePath; + do { + Acpi = (ACPI_HID_DEVICE_PATH *) Node; + Node = NextDevicePathNode (Node); + } while (!IsDevicePathEnd (Node)); + Status = CreateSerialDevice (Controller, Uart, ParentDevicePath, FALSE, Acpi->UID, ParentIo, NULL, NULL); + DEBUG ((EFI_D_INFO, "PciSioSerial: Create SIO child serial device - %r\n", Status)); + } + } else { + Status = ParentIo.PciIo->Pci.Read (ParentIo.PciIo, EfiPciIoWidthUint8, 0, sizeof (Pci), &Pci); + if (!EFI_ERROR (Status)) { + // + // PcdPciSerialParameters takes the higher priority. + // + PciSerialCount = 0; + for (PciSerialParameter = PcdGetPtr (PcdPciSerialParameters); PciSerialParameter->VendorId != 0xFFFF; PciSerialParameter++) { + if ((PciSerialParameter->VendorId == Pci.Hdr.VendorId) && + (PciSerialParameter->DeviceId == Pci.Hdr.DeviceId) + ) { + PciSerialCount++; + } + } + + if (SerialDeviceCount == 0) { + // + // Enable the IO & MEM decoding when creating the first child. + // Restore the PCI attributes when all children is destroyed (PciDeviceInfo->ChildCount == 0). + // + PciDeviceInfo = AllocatePool (sizeof (PCI_DEVICE_INFO)); + PciDeviceInfo->ChildCount = 0; + PciDeviceInfo->PciIo = ParentIo.PciIo; + Status = ParentIo.PciIo->Attributes ( + ParentIo.PciIo, + EfiPciIoAttributeOperationGet, + 0, + &PciDeviceInfo->PciAttributes + ); + + if (!EFI_ERROR (Status)) { + Status = ParentIo.PciIo->Attributes ( + ParentIo.PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (!EFI_ERROR (Status)) { + Supports &= EFI_PCI_IO_ATTRIBUTE_IO | EFI_PCI_IO_ATTRIBUTE_MEMORY; + Status = ParentIo.PciIo->Attributes ( + ParentIo.PciIo, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } + } + } else { + // + // Re-use the PciDeviceInfo stored in existing children. + // + PciDeviceInfo = SerialDevices[0]->PciDeviceInfo; + ASSERT (PciDeviceInfo != NULL); + } + + Status = EFI_NOT_FOUND; + if (PciSerialCount <= 1) { + // + // PCI serial device contains only one UART + // + if (RemainingDevicePath == NULL || !ContainsControllerNode) { + // + // This PCI serial device is matched by class code in Supported() + // + if (PciSerialCount == 0) { + DefaultPciSerialParameter.VendorId = Pci.Hdr.VendorId; + DefaultPciSerialParameter.DeviceId = Pci.Hdr.DeviceId; + DefaultPciSerialParameter.BarIndex = 0; + DefaultPciSerialParameter.Offset = 0; + DefaultPciSerialParameter.RegisterStride = 0; + DefaultPciSerialParameter.ClockRate = 0; + PciSerialParameter = &DefaultPciSerialParameter; + } else if (PciSerialCount == 1) { + PciSerialParameter = PcdGetPtr (PcdPciSerialParameters); + } + + Status = CreateSerialDevice (Controller, Uart, ParentDevicePath, FALSE, 0, ParentIo, PciSerialParameter, PciDeviceInfo); + DEBUG ((EFI_D_INFO, "PciSioSerial: Create PCI child serial device (single) - %r\n", Status)); + if (!EFI_ERROR (Status)) { + PciDeviceInfo->ChildCount++; + } + } + } else { + // + // PCI serial device contains multiple UARTs + // + if (RemainingDevicePath == NULL || ContainsControllerNode) { + PciSerialCount = 0; + for (PciSerialParameter = PcdGetPtr (PcdPciSerialParameters); PciSerialParameter->VendorId != 0xFFFF; PciSerialParameter++) { + if ((PciSerialParameter->VendorId == Pci.Hdr.VendorId) && + (PciSerialParameter->DeviceId == Pci.Hdr.DeviceId) && + ((RemainingDevicePath == NULL) || (ControllerNumber == PciSerialCount)) + ) { + // + // Create controller node when PCI serial device contains multiple UARTs + // + Status = CreateSerialDevice (Controller, Uart, ParentDevicePath, TRUE, PciSerialCount, ParentIo, PciSerialParameter, PciDeviceInfo); + PciSerialCount++; + DEBUG ((EFI_D_INFO, "PciSioSerial: Create PCI child serial device (multiple) - %r\n", Status)); + if (!EFI_ERROR (Status)) { + PciDeviceInfo->ChildCount++; + } + } + } + } + } + } + } + + if (SerialDevices != NULL) { + FreePool (SerialDevices); + } + + // + // For multiple PCI serial devices, set Status to SUCCESS if one child is created successfully + // + if ((PciDeviceInfo != NULL) && (PciDeviceInfo->ChildCount != 0)) { + Status = EFI_SUCCESS; + } + + if (EFI_ERROR (Status) && (SerialDeviceCount == 0)) { + if (PciDeviceInfo != NULL) { + Status = ParentIo.PciIo->Attributes ( + ParentIo.PciIo, + EfiPciIoAttributeOperationSet, + PciDeviceInfo->PciAttributes, + NULL + ); + ASSERT_EFI_ERROR (Status); + FreePool (PciDeviceInfo); + } + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + gBS->CloseProtocol ( + Controller, + IoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + return Status; +} + +/** + Disconnect this driver with the controller, uninstall related protocol instance + + @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The handle of the controller to test. + @param NumberOfChildren Number of child device. + @param ChildHandleBuffer A pointer to the remaining portion of a device path. + + @retval EFI_SUCCESS Operation successfully + @retval EFI_DEVICE_ERROR Cannot stop the driver successfully + +**/ +EFI_STATUS +EFIAPI +SerialControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) + +{ + EFI_STATUS Status; + UINTN Index; + BOOLEAN AllChildrenStopped; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + SERIAL_DEV *SerialDevice; + VOID *IoProtocol; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + PCI_DEVICE_INFO *PciDeviceInfo; + + PciDeviceInfo = NULL; + + Status = gBS->HandleProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath + ); + + // + // Report the status code disable the serial + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_P_PC_DISABLE | EFI_PERIPHERAL_SERIAL_PORT, + DevicePath + ); + + if (NumberOfChildren == 0) { + // + // Close the bus driver + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + &IoProtocol, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + gBS->CloseProtocol ( + Controller, + !EFI_ERROR (Status) ? &gEfiPciIoProtocolGuid : &gEfiSioProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_SUCCESS; + } + + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren; Index++) { + + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + + SerialDevice = SERIAL_DEV_FROM_THIS (SerialIo); + ASSERT ((PciDeviceInfo == NULL) || (PciDeviceInfo == SerialDevice->PciDeviceInfo)); + PciDeviceInfo = SerialDevice->PciDeviceInfo; + + Status = gBS->CloseProtocol ( + Controller, + PciDeviceInfo != NULL ? &gEfiPciIoProtocolGuid : &gEfiSioProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiDevicePathProtocolGuid, SerialDevice->DevicePath, + &gEfiSerialIoProtocolGuid, &SerialDevice->SerialIo, + NULL + ); + if (EFI_ERROR (Status)) { + gBS->OpenProtocol ( + Controller, + PciDeviceInfo != NULL ? &gEfiPciIoProtocolGuid : &gEfiSioProtocolGuid, + &IoProtocol, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + FreePool (SerialDevice->DevicePath); + FreeUnicodeStringTable (SerialDevice->ControllerNameTable); + FreePool (SerialDevice); + + if (PciDeviceInfo != NULL) { + ASSERT (PciDeviceInfo->ChildCount != 0); + PciDeviceInfo->ChildCount--; + } + } + } + + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } else { + // + // If all children are destroyed, restore the PCI attributes. + // + if ((PciDeviceInfo != NULL) && (PciDeviceInfo->ChildCount == 0)) { + ASSERT (PciDeviceInfo->PciIo != NULL); + Status = PciDeviceInfo->PciIo->Attributes ( + PciDeviceInfo->PciIo, + EfiPciIoAttributeOperationSet, + PciDeviceInfo->PciAttributes, + NULL + ); + ASSERT_EFI_ERROR (Status); + FreePool (PciDeviceInfo); + } + return EFI_SUCCESS; + } +} diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.h b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.h new file mode 100644 index 0000000000..f147e69044 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.h @@ -0,0 +1,789 @@ +/** @file + Header file for PciSioSerial Driver + +Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _SERIAL_H_ +#define _SERIAL_H_ + + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Driver Binding Externs +// +extern EFI_DRIVER_BINDING_PROTOCOL gSerialControllerDriver; +extern EFI_COMPONENT_NAME_PROTOCOL gPciSioSerialComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gPciSioSerialComponentName2; + +#define SIO_SERIAL_PORT_NAME L"SIO Serial Port #%d" +#define PCI_SERIAL_PORT_NAME L"PCI Serial Port #%d" +#define SERIAL_PORT_NAME_LEN (sizeof (SIO_SERIAL_PORT_NAME) / sizeof (CHAR16) + MAXIMUM_VALUE_CHARACTERS) + +// +// Internal Data Structures +// +#define TIMEOUT_STALL_INTERVAL 10 + +#pragma pack(1) +/// +/// PcdPciSerialParameters contains zero or more instances of the below structure. +/// If a PCI device contains multiple UARTs, PcdPciSerialParameters needs to contain +/// two instances of the below structure, with the VendorId and DeviceId equals to the +/// device ID and vendor ID of the device. If the PCI device uses the first two BARs +/// to support multiple UARTs, BarIndex of first instance equals to 0 and BarIndex of +/// second one equals to 1; if the PCI device uses the first BAR to support multiple +/// UARTs, BarIndex of both instance equals to 0 and Offset of first instance equals +/// to 0 while Offset of second one equals to some value bigger or equal to 8. +/// For certain UART whose register needs to be accessed in DWORD aligned address, +/// RegisterStride equals to 4. +/// +typedef struct { + UINT16 VendorId; ///< Vendor ID to match the PCI device. The value 0xFFFF terminates the list of entries. + UINT16 DeviceId; ///< Device ID to match the PCI device + UINT32 ClockRate; ///< UART clock rate. Set to 0 for default clock rate of 1843200 Hz + UINT64 Offset; ///< The byte offset into to the BAR + UINT8 BarIndex; ///< Which BAR to get the UART base address + UINT8 RegisterStride; ///< UART register stride in bytes. Set to 0 for default register stride of 1 byte. + UINT16 ReceiveFifoDepth; ///< UART receive FIFO depth in bytes. Set to 0 for a default FIFO depth of 16 bytes. + UINT16 TransmitFifoDepth; ///< UART transmit FIFO depth in bytes. Set to 0 for a default FIFO depth of 16 bytes. + UINT8 Reserved[2]; +} PCI_SERIAL_PARAMETER; +#pragma pack() + +#define SERIAL_MAX_FIFO_SIZE 17 ///< Actual FIFO size is 16. FIFO based on circular wastes one unit. +typedef struct { + UINT16 Head; ///< Head pointer of the FIFO. Empty when (Head == Tail). + UINT16 Tail; ///< Tail pointer of the FIFO. Full when ((Tail + 1) % SERIAL_MAX_FIFO_SIZE == Head). + UINT8 Data[SERIAL_MAX_FIFO_SIZE]; ///< Store the FIFO data. +} SERIAL_DEV_FIFO; + +typedef union { + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_SIO_PROTOCOL *Sio; +} PARENT_IO_PROTOCOL_PTR; + +typedef struct { + EFI_PCI_IO_PROTOCOL *PciIo; // Pointer to parent PciIo instance. + UINTN ChildCount; // Count of child SerialIo instance. + UINT64 PciAttributes; // Original PCI attributes. +} PCI_DEVICE_INFO; + +typedef struct { + UINT32 Signature; + EFI_HANDLE Handle; + EFI_SERIAL_IO_PROTOCOL SerialIo; + EFI_SERIAL_IO_MODE SerialMode; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + UART_DEVICE_PATH UartDevicePath; + + EFI_PHYSICAL_ADDRESS BaseAddress; ///< UART base address + BOOLEAN MmioAccess; ///< TRUE for MMIO, FALSE for IO + UINT8 RegisterStride; ///< UART Register Stride + UINT32 ClockRate; ///< UART clock rate + + UINT16 ReceiveFifoDepth; ///< UART receive FIFO depth in bytes. + SERIAL_DEV_FIFO Receive; ///< The FIFO used to store received data + + UINT16 TransmitFifoDepth; ///< UART transmit FIFO depth in bytes. + SERIAL_DEV_FIFO Transmit; ///< The FIFO used to store to-transmit data + + BOOLEAN SoftwareLoopbackEnable; + BOOLEAN HardwareFlowControl; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + BOOLEAN ContainsControllerNode; ///< TRUE if the device produced contains Controller node + UINT32 Instance; + PCI_DEVICE_INFO *PciDeviceInfo; +} SERIAL_DEV; + +#define SERIAL_DEV_SIGNATURE SIGNATURE_32 ('s', 'e', 'r', 'd') +#define SERIAL_DEV_FROM_THIS(a) CR (a, SERIAL_DEV, SerialIo, SERIAL_DEV_SIGNATURE) + +// +// Serial Driver Defaults +// +#define SERIAL_PORT_DEFAULT_TIMEOUT 1000000 +#define SERIAL_PORT_SUPPORT_CONTROL_MASK (EFI_SERIAL_CLEAR_TO_SEND | \ + EFI_SERIAL_DATA_SET_READY | \ + EFI_SERIAL_RING_INDICATE | \ + EFI_SERIAL_CARRIER_DETECT | \ + EFI_SERIAL_REQUEST_TO_SEND | \ + EFI_SERIAL_DATA_TERMINAL_READY | \ + EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE | \ + EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE | \ + EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE | \ + EFI_SERIAL_OUTPUT_BUFFER_EMPTY | \ + EFI_SERIAL_INPUT_BUFFER_EMPTY) + +#define SERIAL_PORT_MIN_TIMEOUT 1 // 1 uS +#define SERIAL_PORT_MAX_TIMEOUT 100000000 // 100 seconds +// +// UART Registers +// +#define SERIAL_REGISTER_THR 0 ///< WO Transmit Holding Register +#define SERIAL_REGISTER_RBR 0 ///< RO Receive Buffer Register +#define SERIAL_REGISTER_DLL 0 ///< R/W Divisor Latch LSB +#define SERIAL_REGISTER_DLM 1 ///< R/W Divisor Latch MSB +#define SERIAL_REGISTER_IER 1 ///< R/W Interrupt Enable Register +#define SERIAL_REGISTER_IIR 2 ///< RO Interrupt Identification Register +#define SERIAL_REGISTER_FCR 2 ///< WO FIFO Cotrol Register +#define SERIAL_REGISTER_LCR 3 ///< R/W Line Control Register +#define SERIAL_REGISTER_MCR 4 ///< R/W Modem Control Register +#define SERIAL_REGISTER_LSR 5 ///< R/W Line Status Register +#define SERIAL_REGISTER_MSR 6 ///< R/W Modem Status Register +#define SERIAL_REGISTER_SCR 7 ///< R/W Scratch Pad Register +#pragma pack(1) + +/// +/// Interrupt Enable Register +/// +typedef union { + struct { + UINT8 Ravie : 1; ///< Receiver Data Available Interrupt Enable + UINT8 Theie : 1; ///< Transmistter Holding Register Empty Interrupt Enable + UINT8 Rie : 1; ///< Receiver Interrupt Enable + UINT8 Mie : 1; ///< Modem Interrupt Enable + UINT8 Reserved : 4; + } Bits; + UINT8 Data; +} SERIAL_PORT_IER; + +/// +/// FIFO Control Register +/// +typedef union { + struct { + UINT8 TrFIFOE : 1; ///< Transmit and Receive FIFO Enable + UINT8 ResetRF : 1; ///< Reset Reciever FIFO + UINT8 ResetTF : 1; ///< Reset Transmistter FIFO + UINT8 Dms : 1; ///< DMA Mode Select + UINT8 Reserved : 1; + UINT8 TrFIFO64 : 1; ///< Enable 64 byte FIFO + UINT8 Rtb : 2; ///< Receive Trigger Bits + } Bits; + UINT8 Data; +} SERIAL_PORT_FCR; + +/// +/// Line Control Register +/// +typedef union { + struct { + UINT8 SerialDB : 2; ///< Number of Serial Data Bits + UINT8 StopB : 1; ///< Number of Stop Bits + UINT8 ParEn : 1; ///< Parity Enable + UINT8 EvenPar : 1; ///< Even Parity Select + UINT8 SticPar : 1; ///< Sticky Parity + UINT8 BrCon : 1; ///< Break Control + UINT8 DLab : 1; ///< Divisor Latch Access Bit + } Bits; + UINT8 Data; +} SERIAL_PORT_LCR; + +/// +/// Modem Control Register +/// +typedef union { + struct { + UINT8 DtrC : 1; ///< Data Terminal Ready Control + UINT8 Rts : 1; ///< Request To Send Control + UINT8 Out1 : 1; ///< Output1 + UINT8 Out2 : 1; ///< Output2, used to disable interrupt + UINT8 Lme : 1; ///< Loopback Mode Enable + UINT8 Reserved : 3; + } Bits; + UINT8 Data; +} SERIAL_PORT_MCR; + +/// +/// Line Status Register +/// +typedef union { + struct { + UINT8 Dr : 1; ///< Receiver Data Ready Status + UINT8 Oe : 1; ///< Overrun Error Status + UINT8 Pe : 1; ///< Parity Error Status + UINT8 Fe : 1; ///< Framing Error Status + UINT8 Bi : 1; ///< Break Interrupt Status + UINT8 Thre : 1; ///< Transmistter Holding Register Status + UINT8 Temt : 1; ///< Transmitter Empty Status + UINT8 FIFOe : 1; ///< FIFO Error Status + } Bits; + UINT8 Data; +} SERIAL_PORT_LSR; + +/// +/// Modem Status Register +/// +typedef union { + struct { + UINT8 DeltaCTS : 1; ///< Delta Clear To Send Status + UINT8 DeltaDSR : 1; ///< Delta Data Set Ready Status + UINT8 TrailingEdgeRI : 1; ///< Trailing Edge of Ring Indicator Status + UINT8 DeltaDCD : 1; ///< Delta Data Carrier Detect Status + UINT8 Cts : 1; ///< Clear To Send Status + UINT8 Dsr : 1; ///< Data Set Ready Status + UINT8 Ri : 1; ///< Ring Indicator Status + UINT8 Dcd : 1; ///< Data Carrier Detect Status + } Bits; + UINT8 Data; +} SERIAL_PORT_MSR; + +#pragma pack() +// +// Define serial register I/O macros +// +#define READ_RBR(S) SerialReadRegister (S, SERIAL_REGISTER_RBR) +#define READ_DLL(S) SerialReadRegister (S, SERIAL_REGISTER_DLL) +#define READ_DLM(S) SerialReadRegister (S, SERIAL_REGISTER_DLM) +#define READ_IER(S) SerialReadRegister (S, SERIAL_REGISTER_IER) +#define READ_IIR(S) SerialReadRegister (S, SERIAL_REGISTER_IIR) +#define READ_LCR(S) SerialReadRegister (S, SERIAL_REGISTER_LCR) +#define READ_MCR(S) SerialReadRegister (S, SERIAL_REGISTER_MCR) +#define READ_LSR(S) SerialReadRegister (S, SERIAL_REGISTER_LSR) +#define READ_MSR(S) SerialReadRegister (S, SERIAL_REGISTER_MSR) +#define READ_SCR(S) SerialReadRegister (S, SERIAL_REGISTER_SCR) + +#define WRITE_THR(S, D) SerialWriteRegister (S, SERIAL_REGISTER_THR, D) +#define WRITE_DLL(S, D) SerialWriteRegister (S, SERIAL_REGISTER_DLL, D) +#define WRITE_DLM(S, D) SerialWriteRegister (S, SERIAL_REGISTER_DLM, D) +#define WRITE_IER(S, D) SerialWriteRegister (S, SERIAL_REGISTER_IER, D) +#define WRITE_FCR(S, D) SerialWriteRegister (S, SERIAL_REGISTER_FCR, D) +#define WRITE_LCR(S, D) SerialWriteRegister (S, SERIAL_REGISTER_LCR, D) +#define WRITE_MCR(S, D) SerialWriteRegister (S, SERIAL_REGISTER_MCR, D) +#define WRITE_LSR(S, D) SerialWriteRegister (S, SERIAL_REGISTER_LSR, D) +#define WRITE_MSR(S, D) SerialWriteRegister (S, SERIAL_REGISTER_MSR, D) +#define WRITE_SCR(S, D) SerialWriteRegister (S, SERIAL_REGISTER_SCR, D) + +// +// Prototypes +// Driver model protocol interface +// +/** + Check to see if this driver supports the given controller + + @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The handle of the controller to test. + @param RemainingDevicePath A pointer to the remaining portion of a device path. + + @return EFI_SUCCESS This driver can support the given controller + +**/ +EFI_STATUS +EFIAPI +SerialControllerDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start to management the controller passed in + + @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The handle of the controller to test. + @param RemainingDevicePath A pointer to the remaining portion of a device path. + + @return EFI_SUCCESS Driver is started successfully +**/ +EFI_STATUS +EFIAPI +SerialControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Disconnect this driver with the controller, uninstall related protocol instance + + @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The handle of the controller to test. + @param NumberOfChildren Number of child device. + @param ChildHandleBuffer A pointer to the remaining portion of a device path. + + @retval EFI_SUCCESS Operation successfully + @retval EFI_DEVICE_ERROR Cannot stop the driver successfully + +**/ +EFI_STATUS +EFIAPI +SerialControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// Serial I/O Protocol Interface +// +/** + Reset serial device. + + @param This Pointer to EFI_SERIAL_IO_PROTOCOL + + @retval EFI_SUCCESS Reset successfully + @retval EFI_DEVICE_ERROR Failed to reset + +**/ +EFI_STATUS +EFIAPI +SerialReset ( + IN EFI_SERIAL_IO_PROTOCOL *This + ); + +/** + Set new attributes to a serial device. + + @param This Pointer to EFI_SERIAL_IO_PROTOCOL + @param BaudRate The baudrate of the serial device + @param ReceiveFifoDepth The depth of receive FIFO buffer + @param Timeout The request timeout for a single char + @param Parity The type of parity used in serial device + @param DataBits Number of databits used in serial device + @param StopBits Number of stopbits used in serial device + + @retval EFI_SUCCESS The new attributes were set + @retval EFI_INVALID_PARAMETERS One or more attributes have an unsupported value + @retval EFI_UNSUPPORTED Data Bits can not set to 5 or 6 + @retval EFI_DEVICE_ERROR The serial device is not functioning correctly (no return) + +**/ +EFI_STATUS +EFIAPI +SerialSetAttributes ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT64 BaudRate, + IN UINT32 ReceiveFifoDepth, + IN UINT32 Timeout, + IN EFI_PARITY_TYPE Parity, + IN UINT8 DataBits, + IN EFI_STOP_BITS_TYPE StopBits + ); + +/** + Set Control Bits. + + @param This Pointer to EFI_SERIAL_IO_PROTOCOL + @param Control Control bits that can be settable + + @retval EFI_SUCCESS New Control bits were set successfully + @retval EFI_UNSUPPORTED The Control bits wanted to set are not supported + +**/ +EFI_STATUS +EFIAPI +SerialSetControl ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT32 Control + ); + +/** + Get ControlBits. + + @param This Pointer to EFI_SERIAL_IO_PROTOCOL + @param Control Control signals of the serial device + + @retval EFI_SUCCESS Get Control signals successfully + +**/ +EFI_STATUS +EFIAPI +SerialGetControl ( + IN EFI_SERIAL_IO_PROTOCOL *This, + OUT UINT32 *Control + ); + +/** + Write the specified number of bytes to serial device. + + @param This Pointer to EFI_SERIAL_IO_PROTOCOL + @param BufferSize On input the size of Buffer, on output the amount of + data actually written + @param Buffer The buffer of data to write + + @retval EFI_SUCCESS The data were written successfully + @retval EFI_DEVICE_ERROR The device reported an error + @retval EFI_TIMEOUT The write operation was stopped due to timeout + +**/ +EFI_STATUS +EFIAPI +SerialWrite ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ); + +/** + Read the specified number of bytes from serial device. + + @param This Pointer to EFI_SERIAL_IO_PROTOCOL + @param BufferSize On input the size of Buffer, on output the amount of + data returned in buffer + @param Buffer The buffer to return the data into + + @retval EFI_SUCCESS The data were read successfully + @retval EFI_DEVICE_ERROR The device reported an error + @retval EFI_TIMEOUT The read operation was stopped due to timeout + +**/ +EFI_STATUS +EFIAPI +SerialRead ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +// +// Internal Functions +// +/** + Use scratchpad register to test if this serial port is present. + + @param SerialDevice Pointer to serial device structure + + @return if this serial port is present +**/ +BOOLEAN +SerialPresent ( + IN SERIAL_DEV *SerialDevice + ); + +/** + Detect whether specific FIFO is full or not. + + @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO + + @return whether specific FIFO is full or not + +**/ +BOOLEAN +SerialFifoFull ( + IN SERIAL_DEV_FIFO *Fifo + ); + +/** + Detect whether specific FIFO is empty or not. + + @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO + + @return whether specific FIFO is empty or not + +**/ +BOOLEAN +SerialFifoEmpty ( + IN SERIAL_DEV_FIFO *Fifo + ); + +/** + Add data to specific FIFO. + + @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO + @param Data the data added to FIFO + + @retval EFI_SUCCESS Add data to specific FIFO successfully + @retval EFI_OUT_OF_RESOURCE Failed to add data because FIFO is already full + +**/ +EFI_STATUS +SerialFifoAdd ( + IN SERIAL_DEV_FIFO *Fifo, + IN UINT8 Data + ); + +/** + Remove data from specific FIFO. + + @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO + @param Data the data removed from FIFO + + @retval EFI_SUCCESS Remove data from specific FIFO successfully + @retval EFI_OUT_OF_RESOURCE Failed to remove data because FIFO is empty + +**/ +EFI_STATUS +SerialFifoRemove ( + IN SERIAL_DEV_FIFO *Fifo, + OUT UINT8 *Data + ); + +/** + Reads and writes all avaliable data. + + @param SerialDevice The device to flush + + @retval EFI_SUCCESS Data was read/written successfully. + @retval EFI_OUT_OF_RESOURCE Failed because software receive FIFO is full. Note, when + this happens, pending writes are not done. + +**/ +EFI_STATUS +SerialReceiveTransmit ( + IN SERIAL_DEV *SerialDevice + ); + +/** + Read serial port. + + @param SerialDev Pointer to serial device + @param Offset Offset in register group + + @return Data read from serial port +**/ +UINT8 +SerialReadRegister ( + IN SERIAL_DEV *SerialDev, + IN UINT32 Offset + ); + +/** + Write serial port. + + @param SerialDev Pointer to serial device + @param Offset Offset in register group + @param Data data which is to be written to some serial port register +**/ +VOID +SerialWriteRegister ( + IN SERIAL_DEV *SerialDev, + IN UINT32 Offset, + IN UINT8 Data + ); + + +// +// 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 +SerialComponentNameGetDriverName ( + 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 +SerialComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + Add the component name for the serial io device + + @param SerialDevice A pointer to the SERIAL_DEV instance. + @param Uid Unique ID for the serial device. +**/ +VOID +AddName ( + IN SERIAL_DEV *SerialDevice, + IN UINT32 Uid + ); + +/** + Checks whether the UART parameters are valid and computes the Divisor. + + @param ClockRate The clock rate of the serial device used to verify + the BaudRate. Do not verify the BaudRate if it's 0. + @param BaudRate The requested baudrate of the serial device. + @param DataBits Number of databits used in serial device. + @param Parity The type of parity used in serial device. + @param StopBits Number of stopbits used in serial device. + @param Divisor Return the divisor if ClockRate is not 0. + @param ActualBaudRate Return the actual supported baudrate without + exceeding BaudRate. NULL means baudrate degradation + is not allowed. + If the requested BaudRate is not supported, the routine + returns TRUE and the Actual Baud Rate when ActualBaudRate + is not NULL, returns FALSE when ActualBaudRate is NULL. + + @retval TRUE The UART parameters are valid. + @retval FALSE The UART parameters are not valid. +**/ +BOOLEAN +VerifyUartParameters ( + IN UINT32 ClockRate, + IN UINT64 BaudRate, + IN UINT8 DataBits, + IN EFI_PARITY_TYPE Parity, + IN EFI_STOP_BITS_TYPE StopBits, + OUT UINT64 *Divisor, + OUT UINT64 *ActualBaudRate + ); + +/** + Skip the optional Controller device path node and return the + pointer to the next device path node. + + @param DevicePath Pointer to the device path. + @param ContainsControllerNode Returns TRUE if the Controller device path exists. + @param ControllerNumber Returns the Controller Number if Controller device path exists. + + @return Pointer to the next device path node. +**/ +UART_DEVICE_PATH * +SkipControllerDevicePathNode ( + EFI_DEVICE_PATH_PROTOCOL *DevicePath, + BOOLEAN *ContainsControllerNode, + UINT32 *ControllerNumber + ); + +/** + Check the device path node whether it's the Flow Control node or not. + + @param[in] FlowControl The device path node to be checked. + + @retval TRUE It's the Flow Control node. + @retval FALSE It's not. + +**/ +BOOLEAN +IsUartFlowControlDevicePathNode ( + IN UART_FLOW_CONTROL_DEVICE_PATH *FlowControl + ); +#endif diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/SerialIo.c b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/SerialIo.c new file mode 100644 index 0000000000..f1870f3a1b --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/SerialIo.c @@ -0,0 +1,1320 @@ +/** @file + SerialIo implementation for PCI or SIO UARTs. + +Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Serial.h" + +/** + Skip the optional Controller device path node and return the + pointer to the next device path node. + + @param DevicePath Pointer to the device path. + @param ContainsControllerNode Returns TRUE if the Controller device path exists. + @param ControllerNumber Returns the Controller Number if Controller device path exists. + + @return Pointer to the next device path node. +**/ +UART_DEVICE_PATH * +SkipControllerDevicePathNode ( + EFI_DEVICE_PATH_PROTOCOL *DevicePath, + BOOLEAN *ContainsControllerNode, + UINT32 *ControllerNumber + ) +{ + if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && + (DevicePathSubType (DevicePath) == HW_CONTROLLER_DP) + ) { + if (ContainsControllerNode != NULL) { + *ContainsControllerNode = TRUE; + } + if (ControllerNumber != NULL) { + *ControllerNumber = ((CONTROLLER_DEVICE_PATH *) DevicePath)->ControllerNumber; + } + DevicePath = NextDevicePathNode (DevicePath); + } else { + if (ContainsControllerNode != NULL) { + *ContainsControllerNode = FALSE; + } + } + return (UART_DEVICE_PATH *) DevicePath; +} + +/** + Checks whether the UART parameters are valid and computes the Divisor. + + @param ClockRate The clock rate of the serial device used to verify + the BaudRate. Do not verify the BaudRate if it's 0. + @param BaudRate The requested baudrate of the serial device. + @param DataBits Number of databits used in serial device. + @param Parity The type of parity used in serial device. + @param StopBits Number of stopbits used in serial device. + @param Divisor Return the divisor if ClockRate is not 0. + @param ActualBaudRate Return the actual supported baudrate without + exceeding BaudRate. NULL means baudrate degradation + is not allowed. + If the requested BaudRate is not supported, the routine + returns TRUE and the Actual Baud Rate when ActualBaudRate + is not NULL, returns FALSE when ActualBaudRate is NULL. + + @retval TRUE The UART parameters are valid. + @retval FALSE The UART parameters are not valid. +**/ +BOOLEAN +VerifyUartParameters ( + IN UINT32 ClockRate, + IN UINT64 BaudRate, + IN UINT8 DataBits, + IN EFI_PARITY_TYPE Parity, + IN EFI_STOP_BITS_TYPE StopBits, + OUT UINT64 *Divisor, + OUT UINT64 *ActualBaudRate + ) +{ + UINT64 Remainder; + UINT32 ComputedBaudRate; + UINT64 ComputedDivisor; + UINT64 Percent; + + if ((DataBits < 5) || (DataBits > 8) || + (Parity < NoParity) || (Parity > SpaceParity) || + (StopBits < OneStopBit) || (StopBits > TwoStopBits) || + ((DataBits == 5) && (StopBits == TwoStopBits)) || + ((DataBits >= 6) && (DataBits <= 8) && (StopBits == OneFiveStopBits)) + ) { + return FALSE; + } + + // + // Do not verify the baud rate if clock rate is unknown (0). + // + if (ClockRate == 0) { + return TRUE; + } + + // + // Compute divisor use to program the baud rate using a round determination + // Divisor = ClockRate / 16 / BaudRate = ClockRate / (16 * BaudRate) + // = ClockRate / (BaudRate << 4) + // + ComputedDivisor = DivU64x64Remainder (ClockRate, LShiftU64 (BaudRate, 4), &Remainder); + // + // Round Divisor up by 1 if the Remainder is more than half (16 * BaudRate) + // BaudRate * 16 / 2 = BaudRate * 8 = (BaudRate << 3) + // + if (Remainder >= LShiftU64 (BaudRate, 3)) { + ComputedDivisor++; + } + // + // If the computed divisor is larger than the maximum value that can be programmed + // into the UART, then the requested baud rate can not be supported. + // + if (ComputedDivisor > MAX_UINT16) { + return FALSE; + } + + // + // If the computed divisor is 0, then use a computed divisor of 1, which will select + // the maximum supported baud rate. + // + if (ComputedDivisor == 0) { + ComputedDivisor = 1; + } + + // + // Actual baud rate that the serial port will be programmed for + // should be with in 4% of requested one. + // + ComputedBaudRate = ClockRate / ((UINT16) ComputedDivisor << 4); + if (ComputedBaudRate == 0) { + return FALSE; + } + + Percent = DivU64x32 (MultU64x32 (BaudRate, 100), ComputedBaudRate); + DEBUG ((EFI_D_INFO, "ClockRate = %d\n", ClockRate)); + DEBUG ((EFI_D_INFO, "Divisor = %ld\n", ComputedDivisor)); + DEBUG ((EFI_D_INFO, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate, ComputedBaudRate, Percent)); + + // + // If the requested BaudRate is not supported: + // Returns TRUE and the Actual Baud Rate when ActualBaudRate is not NULL; + // Returns FALSE when ActualBaudRate is NULL. + // + if ((Percent >= 96) && (Percent <= 104)) { + if (ActualBaudRate != NULL) { + *ActualBaudRate = BaudRate; + } + if (Divisor != NULL) { + *Divisor = ComputedDivisor; + } + return TRUE; + } + if (ComputedBaudRate < BaudRate) { + if (ActualBaudRate != NULL) { + *ActualBaudRate = ComputedBaudRate; + } + if (Divisor != NULL) { + *Divisor = ComputedDivisor; + } + return TRUE; + } + + // + // ActualBaudRate is higher than requested baud rate and more than 4% + // higher than the requested value. Increment Divisor if it is less + // than MAX_UINT16 and computed baud rate with new divisor. + // + if (ComputedDivisor == MAX_UINT16) { + return FALSE; + } + ComputedDivisor++; + ComputedBaudRate = ClockRate / ((UINT16) ComputedDivisor << 4); + if (ComputedBaudRate == 0) { + return FALSE; + } + + DEBUG ((EFI_D_INFO, "ClockRate = %d\n", ClockRate)); + DEBUG ((EFI_D_INFO, "Divisor = %ld\n", ComputedDivisor)); + DEBUG ((EFI_D_INFO, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate, ComputedBaudRate, Percent)); + + if (ActualBaudRate != NULL) { + *ActualBaudRate = ComputedBaudRate; + } + if (Divisor != NULL) { + *Divisor = ComputedDivisor; + } + return TRUE; +} + +/** + Detect whether specific FIFO is full or not. + + @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO + + @return whether specific FIFO is full or not +**/ +BOOLEAN +SerialFifoFull ( + IN SERIAL_DEV_FIFO *Fifo + ) +{ + return (BOOLEAN) (((Fifo->Tail + 1) % SERIAL_MAX_FIFO_SIZE) == Fifo->Head); +} + +/** + Detect whether specific FIFO is empty or not. + + @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO + + @return whether specific FIFO is empty or not +**/ +BOOLEAN +SerialFifoEmpty ( + IN SERIAL_DEV_FIFO *Fifo + ) + +{ + return (BOOLEAN) (Fifo->Head == Fifo->Tail); +} + +/** + Add data to specific FIFO. + + @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO + @param Data the data added to FIFO + + @retval EFI_SUCCESS Add data to specific FIFO successfully + @retval EFI_OUT_OF_RESOURCE Failed to add data because FIFO is already full +**/ +EFI_STATUS +SerialFifoAdd ( + IN OUT SERIAL_DEV_FIFO *Fifo, + IN UINT8 Data + ) +{ + // + // if FIFO full can not add data + // + if (SerialFifoFull (Fifo)) { + return EFI_OUT_OF_RESOURCES; + } + // + // FIFO is not full can add data + // + Fifo->Data[Fifo->Tail] = Data; + Fifo->Tail = (Fifo->Tail + 1) % SERIAL_MAX_FIFO_SIZE; + return EFI_SUCCESS; +} + +/** + Remove data from specific FIFO. + + @param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO + @param Data the data removed from FIFO + + @retval EFI_SUCCESS Remove data from specific FIFO successfully + @retval EFI_OUT_OF_RESOURCE Failed to remove data because FIFO is empty + +**/ +EFI_STATUS +SerialFifoRemove ( + IN OUT SERIAL_DEV_FIFO *Fifo, + OUT UINT8 *Data + ) +{ + // + // if FIFO is empty, no data can remove + // + if (SerialFifoEmpty (Fifo)) { + return EFI_OUT_OF_RESOURCES; + } + // + // FIFO is not empty, can remove data + // + *Data = Fifo->Data[Fifo->Head]; + Fifo->Head = (Fifo->Head + 1) % SERIAL_MAX_FIFO_SIZE; + return EFI_SUCCESS; +} + +/** + Reads and writes all avaliable data. + + @param SerialDevice The device to transmit. + + @retval EFI_SUCCESS Data was read/written successfully. + @retval EFI_OUT_OF_RESOURCE Failed because software receive FIFO is full. Note, when + this happens, pending writes are not done. + +**/ +EFI_STATUS +SerialReceiveTransmit ( + IN SERIAL_DEV *SerialDevice + ) + +{ + SERIAL_PORT_LSR Lsr; + UINT8 Data; + BOOLEAN ReceiveFifoFull; + SERIAL_PORT_MSR Msr; + SERIAL_PORT_MCR Mcr; + UINTN TimeOut; + + Data = 0; + + // + // Begin the read or write + // + if (SerialDevice->SoftwareLoopbackEnable) { + do { + ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive); + if (!SerialFifoEmpty (&SerialDevice->Transmit)) { + SerialFifoRemove (&SerialDevice->Transmit, &Data); + if (ReceiveFifoFull) { + return EFI_OUT_OF_RESOURCES; + } + + SerialFifoAdd (&SerialDevice->Receive, Data); + } + } while (!SerialFifoEmpty (&SerialDevice->Transmit)); + } else { + ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive); + // + // For full handshake flow control, tell the peer to send data + // if receive buffer is available. + // + if (SerialDevice->HardwareFlowControl && + !FeaturePcdGet(PcdSerialUseHalfHandshake)&& + !ReceiveFifoFull + ) { + Mcr.Data = READ_MCR (SerialDevice); + Mcr.Bits.Rts = 1; + WRITE_MCR (SerialDevice, Mcr.Data); + } + do { + Lsr.Data = READ_LSR (SerialDevice); + + // + // Flush incomming data to prevent a an overrun during a long write + // + if ((Lsr.Bits.Dr == 1) && !ReceiveFifoFull) { + ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive); + if (!ReceiveFifoFull) { + if (Lsr.Bits.FIFOe == 1 || Lsr.Bits.Oe == 1 || Lsr.Bits.Pe == 1 || Lsr.Bits.Fe == 1 || Lsr.Bits.Bi == 1) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE, + EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->DevicePath + ); + if (Lsr.Bits.FIFOe == 1 || Lsr.Bits.Pe == 1|| Lsr.Bits.Fe == 1 || Lsr.Bits.Bi == 1) { + Data = READ_RBR (SerialDevice); + continue; + } + } + + Data = READ_RBR (SerialDevice); + + SerialFifoAdd (&SerialDevice->Receive, Data); + + // + // For full handshake flow control, if receive buffer full + // tell the peer to stop sending data. + // + if (SerialDevice->HardwareFlowControl && + !FeaturePcdGet(PcdSerialUseHalfHandshake) && + SerialFifoFull (&SerialDevice->Receive) + ) { + Mcr.Data = READ_MCR (SerialDevice); + Mcr.Bits.Rts = 0; + WRITE_MCR (SerialDevice, Mcr.Data); + } + + + continue; + } else { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_P_SERIAL_PORT_PC_CLEAR_BUFFER | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->DevicePath + ); + } + } + // + // Do the write + // + if (Lsr.Bits.Thre == 1 && !SerialFifoEmpty (&SerialDevice->Transmit)) { + // + // Make sure the transmit data will not be missed + // + if (SerialDevice->HardwareFlowControl) { + // + // For half handshake flow control assert RTS before sending. + // + if (FeaturePcdGet(PcdSerialUseHalfHandshake)) { + Mcr.Data = READ_MCR (SerialDevice); + Mcr.Bits.Rts= 0; + WRITE_MCR (SerialDevice, Mcr.Data); + } + // + // Wait for CTS + // + TimeOut = 0; + Msr.Data = READ_MSR (SerialDevice); + while ((Msr.Bits.Dcd == 1) && ((Msr.Bits.Cts == 0) ^ FeaturePcdGet(PcdSerialUseHalfHandshake))) { + gBS->Stall (TIMEOUT_STALL_INTERVAL); + TimeOut++; + if (TimeOut > 5) { + break; + } + + Msr.Data = READ_MSR (SerialDevice); + } + + if ((Msr.Bits.Dcd == 0) || ((Msr.Bits.Cts == 1) ^ FeaturePcdGet(PcdSerialUseHalfHandshake))) { + SerialFifoRemove (&SerialDevice->Transmit, &Data); + WRITE_THR (SerialDevice, Data); + } + + // + // For half handshake flow control, tell DCE we are done. + // + if (FeaturePcdGet(PcdSerialUseHalfHandshake)) { + Mcr.Data = READ_MCR (SerialDevice); + Mcr.Bits.Rts = 1; + WRITE_MCR (SerialDevice, Mcr.Data); + } + } else { + SerialFifoRemove (&SerialDevice->Transmit, &Data); + WRITE_THR (SerialDevice, Data); + } + } + } while (Lsr.Bits.Thre == 1 && !SerialFifoEmpty (&SerialDevice->Transmit)); + } + + return EFI_SUCCESS; +} + +/** + Flush the serial hardware transmit FIFO and shift register. + + @param SerialDevice The device to flush. +**/ +VOID +SerialFlushTransmitFifo ( + SERIAL_DEV *SerialDevice + ) +{ + SERIAL_PORT_LSR Lsr; + + // + // Wait for the serial port to be ready, to make sure both the transmit FIFO + // and shift register empty. + // + do { + Lsr.Data = READ_LSR (SerialDevice); + } while (Lsr.Bits.Temt == 0); +} + +// +// Interface Functions +// +/** + Reset serial device. + + @param This Pointer to EFI_SERIAL_IO_PROTOCOL + + @retval EFI_SUCCESS Reset successfully + @retval EFI_DEVICE_ERROR Failed to reset + +**/ +EFI_STATUS +EFIAPI +SerialReset ( + IN EFI_SERIAL_IO_PROTOCOL *This + ) +{ + EFI_STATUS Status; + SERIAL_DEV *SerialDevice; + SERIAL_PORT_LCR Lcr; + SERIAL_PORT_IER Ier; + SERIAL_PORT_MCR Mcr; + SERIAL_PORT_FCR Fcr; + EFI_TPL Tpl; + UINT32 Control; + + SerialDevice = SERIAL_DEV_FROM_THIS (This); + + // + // Report the status code reset the serial + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_P_PC_RESET | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->DevicePath + ); + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + SerialFlushTransmitFifo (SerialDevice); + + // + // Make sure DLAB is 0. + // + Lcr.Data = READ_LCR (SerialDevice); + Lcr.Bits.DLab = 0; + WRITE_LCR (SerialDevice, Lcr.Data); + + // + // Turn off all interrupts + // + Ier.Data = READ_IER (SerialDevice); + Ier.Bits.Ravie = 0; + Ier.Bits.Theie = 0; + Ier.Bits.Rie = 0; + Ier.Bits.Mie = 0; + WRITE_IER (SerialDevice, Ier.Data); + + // + // Reset the FIFO + // + Fcr.Data = 0; + Fcr.Bits.TrFIFOE = 0; + WRITE_FCR (SerialDevice, Fcr.Data); + + // + // Turn off loopback and disable device interrupt. + // + Mcr.Data = READ_MCR (SerialDevice); + Mcr.Bits.Out1 = 0; + Mcr.Bits.Out2 = 0; + Mcr.Bits.Lme = 0; + WRITE_MCR (SerialDevice, Mcr.Data); + + // + // Clear the scratch pad register + // + WRITE_SCR (SerialDevice, 0); + + // + // Enable FIFO + // + Fcr.Bits.TrFIFOE = 1; + if (SerialDevice->ReceiveFifoDepth > 16) { + Fcr.Bits.TrFIFO64 = 1; + } + Fcr.Bits.ResetRF = 1; + Fcr.Bits.ResetTF = 1; + WRITE_FCR (SerialDevice, Fcr.Data); + + // + // Go set the current attributes + // + Status = This->SetAttributes ( + This, + This->Mode->BaudRate, + This->Mode->ReceiveFifoDepth, + This->Mode->Timeout, + (EFI_PARITY_TYPE) This->Mode->Parity, + (UINT8) This->Mode->DataBits, + (EFI_STOP_BITS_TYPE) This->Mode->StopBits + ); + + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (Tpl); + return EFI_DEVICE_ERROR; + } + // + // Go set the current control bits + // + Control = 0; + if (SerialDevice->HardwareFlowControl) { + Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE; + } + if (SerialDevice->SoftwareLoopbackEnable) { + Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE; + } + Status = This->SetControl ( + This, + Control + ); + + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (Tpl); + return EFI_DEVICE_ERROR; + } + + // + // Reset the software FIFO + // + SerialDevice->Receive.Head = SerialDevice->Receive.Tail = 0; + SerialDevice->Transmit.Head = SerialDevice->Transmit.Tail = 0; + gBS->RestoreTPL (Tpl); + + // + // Device reset is complete + // + return EFI_SUCCESS; +} + +/** + Set new attributes to a serial device. + + @param This Pointer to EFI_SERIAL_IO_PROTOCOL + @param BaudRate The baudrate of the serial device + @param ReceiveFifoDepth The depth of receive FIFO buffer + @param Timeout The request timeout for a single char + @param Parity The type of parity used in serial device + @param DataBits Number of databits used in serial device + @param StopBits Number of stopbits used in serial device + + @retval EFI_SUCCESS The new attributes were set + @retval EFI_INVALID_PARAMETERS One or more attributes have an unsupported value + @retval EFI_UNSUPPORTED Data Bits can not set to 5 or 6 + @retval EFI_DEVICE_ERROR The serial device is not functioning correctly (no return) + +**/ +EFI_STATUS +EFIAPI +SerialSetAttributes ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT64 BaudRate, + IN UINT32 ReceiveFifoDepth, + IN UINT32 Timeout, + IN EFI_PARITY_TYPE Parity, + IN UINT8 DataBits, + IN EFI_STOP_BITS_TYPE StopBits + ) +{ + EFI_STATUS Status; + SERIAL_DEV *SerialDevice; + UINT64 Divisor; + SERIAL_PORT_LCR Lcr; + UART_DEVICE_PATH *Uart; + EFI_TPL Tpl; + + SerialDevice = SERIAL_DEV_FROM_THIS (This); + + // + // Check for default settings and fill in actual values. + // + if (BaudRate == 0) { + BaudRate = PcdGet64 (PcdUartDefaultBaudRate); + } + + if (ReceiveFifoDepth == 0) { + ReceiveFifoDepth = SerialDevice->ReceiveFifoDepth; + } + + if (Timeout == 0) { + Timeout = SERIAL_PORT_DEFAULT_TIMEOUT; + } + + if (Parity == DefaultParity) { + Parity = (EFI_PARITY_TYPE) PcdGet8 (PcdUartDefaultParity); + } + + if (DataBits == 0) { + DataBits = PcdGet8 (PcdUartDefaultDataBits); + } + + if (StopBits == DefaultStopBits) { + StopBits = (EFI_STOP_BITS_TYPE) PcdGet8 (PcdUartDefaultStopBits); + } + + if (!VerifyUartParameters (SerialDevice->ClockRate, BaudRate, DataBits, Parity, StopBits, &Divisor, &BaudRate)) { + return EFI_INVALID_PARAMETER; + } + + if ((ReceiveFifoDepth == 0) || (ReceiveFifoDepth > SerialDevice->ReceiveFifoDepth)) { + return EFI_INVALID_PARAMETER; + } + + if ((Timeout < SERIAL_PORT_MIN_TIMEOUT) || (Timeout > SERIAL_PORT_MAX_TIMEOUT)) { + return EFI_INVALID_PARAMETER; + } + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + SerialFlushTransmitFifo (SerialDevice); + + // + // Put serial port on Divisor Latch Mode + // + Lcr.Data = READ_LCR (SerialDevice); + Lcr.Bits.DLab = 1; + WRITE_LCR (SerialDevice, Lcr.Data); + + // + // Write the divisor to the serial port + // + WRITE_DLL (SerialDevice, (UINT8) Divisor); + WRITE_DLM (SerialDevice, (UINT8) ((UINT16) Divisor >> 8)); + + // + // Put serial port back in normal mode and set remaining attributes. + // + Lcr.Bits.DLab = 0; + + switch (Parity) { + case NoParity: + Lcr.Bits.ParEn = 0; + Lcr.Bits.EvenPar = 0; + Lcr.Bits.SticPar = 0; + break; + + case EvenParity: + Lcr.Bits.ParEn = 1; + Lcr.Bits.EvenPar = 1; + Lcr.Bits.SticPar = 0; + break; + + case OddParity: + Lcr.Bits.ParEn = 1; + Lcr.Bits.EvenPar = 0; + Lcr.Bits.SticPar = 0; + break; + + case SpaceParity: + Lcr.Bits.ParEn = 1; + Lcr.Bits.EvenPar = 1; + Lcr.Bits.SticPar = 1; + break; + + case MarkParity: + Lcr.Bits.ParEn = 1; + Lcr.Bits.EvenPar = 0; + Lcr.Bits.SticPar = 1; + break; + + default: + break; + } + + switch (StopBits) { + case OneStopBit: + Lcr.Bits.StopB = 0; + break; + + case OneFiveStopBits: + case TwoStopBits: + Lcr.Bits.StopB = 1; + break; + + default: + break; + } + // + // DataBits + // + Lcr.Bits.SerialDB = (UINT8) ((DataBits - 5) & 0x03); + WRITE_LCR (SerialDevice, Lcr.Data); + + // + // Set the Serial I/O mode + // + This->Mode->BaudRate = BaudRate; + This->Mode->ReceiveFifoDepth = ReceiveFifoDepth; + This->Mode->Timeout = Timeout; + This->Mode->Parity = Parity; + This->Mode->DataBits = DataBits; + This->Mode->StopBits = StopBits; + + // + // See if Device Path Node has actually changed + // + if (SerialDevice->UartDevicePath.BaudRate == BaudRate && + SerialDevice->UartDevicePath.DataBits == DataBits && + SerialDevice->UartDevicePath.Parity == Parity && + SerialDevice->UartDevicePath.StopBits == StopBits + ) { + gBS->RestoreTPL (Tpl); + return EFI_SUCCESS; + } + // + // Update the device path + // + SerialDevice->UartDevicePath.BaudRate = BaudRate; + SerialDevice->UartDevicePath.DataBits = DataBits; + SerialDevice->UartDevicePath.Parity = (UINT8) Parity; + SerialDevice->UartDevicePath.StopBits = (UINT8) StopBits; + + Status = EFI_SUCCESS; + if (SerialDevice->Handle != NULL) { + + // + // Skip the optional Controller device path node + // + Uart = SkipControllerDevicePathNode ( + (EFI_DEVICE_PATH_PROTOCOL *) ( + (UINT8 *) SerialDevice->DevicePath + GetDevicePathSize (SerialDevice->ParentDevicePath) - END_DEVICE_PATH_LENGTH + ), + NULL, + NULL + ); + CopyMem (Uart, &SerialDevice->UartDevicePath, sizeof (UART_DEVICE_PATH)); + Status = gBS->ReinstallProtocolInterface ( + SerialDevice->Handle, + &gEfiDevicePathProtocolGuid, + SerialDevice->DevicePath, + SerialDevice->DevicePath + ); + } + + gBS->RestoreTPL (Tpl); + + return Status; +} + +/** + Set Control Bits. + + @param This Pointer to EFI_SERIAL_IO_PROTOCOL + @param Control Control bits that can be settable + + @retval EFI_SUCCESS New Control bits were set successfully + @retval EFI_UNSUPPORTED The Control bits wanted to set are not supported + +**/ +EFI_STATUS +EFIAPI +SerialSetControl ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT32 Control + ) +{ + SERIAL_DEV *SerialDevice; + SERIAL_PORT_MCR Mcr; + EFI_TPL Tpl; + UART_FLOW_CONTROL_DEVICE_PATH *FlowControl; + EFI_STATUS Status; + + // + // The control bits that can be set are : + // EFI_SERIAL_DATA_TERMINAL_READY: 0x0001 // WO + // EFI_SERIAL_REQUEST_TO_SEND: 0x0002 // WO + // EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE: 0x1000 // RW + // EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE: 0x2000 // RW + // EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE: 0x4000 // RW + // + SerialDevice = SERIAL_DEV_FROM_THIS (This); + + // + // first determine the parameter is invalid + // + if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY | + EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE | EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE | + EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0) { + return EFI_UNSUPPORTED; + } + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + Mcr.Data = READ_MCR (SerialDevice); + Mcr.Bits.DtrC = 0; + Mcr.Bits.Rts = 0; + Mcr.Bits.Lme = 0; + SerialDevice->SoftwareLoopbackEnable = FALSE; + SerialDevice->HardwareFlowControl = FALSE; + + if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) { + Mcr.Bits.DtrC = 1; + } + + if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) { + Mcr.Bits.Rts = 1; + } + + if ((Control & EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) == EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) { + Mcr.Bits.Lme = 1; + } + + if ((Control & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) { + SerialDevice->HardwareFlowControl = TRUE; + } + + WRITE_MCR (SerialDevice, Mcr.Data); + + if ((Control & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) == EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) { + SerialDevice->SoftwareLoopbackEnable = TRUE; + } + + Status = EFI_SUCCESS; + if (SerialDevice->Handle != NULL) { + FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) ( + (UINTN) SerialDevice->DevicePath + + GetDevicePathSize (SerialDevice->ParentDevicePath) + - END_DEVICE_PATH_LENGTH + + sizeof (UART_DEVICE_PATH) + ); + if (IsUartFlowControlDevicePathNode (FlowControl) && + ((BOOLEAN) (ReadUnaligned32 (&FlowControl->FlowControlMap) == UART_FLOW_CONTROL_HARDWARE) != SerialDevice->HardwareFlowControl)) { + // + // Flow Control setting is changed, need to reinstall device path protocol + // + WriteUnaligned32 (&FlowControl->FlowControlMap, SerialDevice->HardwareFlowControl ? UART_FLOW_CONTROL_HARDWARE : 0); + Status = gBS->ReinstallProtocolInterface ( + SerialDevice->Handle, + &gEfiDevicePathProtocolGuid, + SerialDevice->DevicePath, + SerialDevice->DevicePath + ); + } + } + + gBS->RestoreTPL (Tpl); + + return Status; +} + +/** + Get ControlBits. + + @param This Pointer to EFI_SERIAL_IO_PROTOCOL + @param Control Control signals of the serial device + + @retval EFI_SUCCESS Get Control signals successfully + +**/ +EFI_STATUS +EFIAPI +SerialGetControl ( + IN EFI_SERIAL_IO_PROTOCOL *This, + OUT UINT32 *Control + ) +{ + SERIAL_DEV *SerialDevice; + SERIAL_PORT_MSR Msr; + SERIAL_PORT_MCR Mcr; + EFI_TPL Tpl; + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + SerialDevice = SERIAL_DEV_FROM_THIS (This); + + *Control = 0; + + // + // Read the Modem Status Register + // + Msr.Data = READ_MSR (SerialDevice); + + if (Msr.Bits.Cts == 1) { + *Control |= EFI_SERIAL_CLEAR_TO_SEND; + } + + if (Msr.Bits.Dsr == 1) { + *Control |= EFI_SERIAL_DATA_SET_READY; + } + + if (Msr.Bits.Ri == 1) { + *Control |= EFI_SERIAL_RING_INDICATE; + } + + if (Msr.Bits.Dcd == 1) { + *Control |= EFI_SERIAL_CARRIER_DETECT; + } + // + // Read the Modem Control Register + // + Mcr.Data = READ_MCR (SerialDevice); + + if (Mcr.Bits.DtrC == 1) { + *Control |= EFI_SERIAL_DATA_TERMINAL_READY; + } + + if (Mcr.Bits.Rts == 1) { + *Control |= EFI_SERIAL_REQUEST_TO_SEND; + } + + if (Mcr.Bits.Lme == 1) { + *Control |= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE; + } + + if (SerialDevice->HardwareFlowControl) { + *Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE; + } + // + // Update FIFO status + // + SerialReceiveTransmit (SerialDevice); + + // + // See if the Transmit FIFO is empty + // + if (SerialFifoEmpty (&SerialDevice->Transmit)) { + *Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY; + } + + // + // See if the Receive FIFO is empty. + // + if (SerialFifoEmpty (&SerialDevice->Receive)) { + *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY; + } + + if (SerialDevice->SoftwareLoopbackEnable) { + *Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE; + } + + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +/** + Write the specified number of bytes to serial device. + + @param This Pointer to EFI_SERIAL_IO_PROTOCOL + @param BufferSize On input the size of Buffer, on output the amount of + data actually written + @param Buffer The buffer of data to write + + @retval EFI_SUCCESS The data were written successfully + @retval EFI_DEVICE_ERROR The device reported an error + @retval EFI_TIMEOUT The write operation was stopped due to timeout + +**/ +EFI_STATUS +EFIAPI +SerialWrite ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + SERIAL_DEV *SerialDevice; + UINT8 *CharBuffer; + UINT32 Index; + UINTN Elapsed; + UINTN ActualWrite; + EFI_TPL Tpl; + UINTN Timeout; + UINTN BitsPerCharacter; + + SerialDevice = SERIAL_DEV_FROM_THIS (This); + Elapsed = 0; + ActualWrite = 0; + + if (*BufferSize == 0) { + return EFI_SUCCESS; + } + + if (Buffer == NULL) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE, + EFI_P_EC_OUTPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->DevicePath + ); + + return EFI_DEVICE_ERROR; + } + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + CharBuffer = (UINT8 *) Buffer; + + // + // Compute the number of bits in a single character. This is a start bit, + // followed by the number of data bits, followed by the number of stop bits. + // The number of stop bits is specified by an enumeration that includes + // support for 1.5 stop bits. Treat 1.5 stop bits as 2 stop bits. + // + BitsPerCharacter = + 1 + + This->Mode->DataBits + + ((This->Mode->StopBits == TwoStopBits) ? 2 : This->Mode->StopBits); + + // + // Compute the timeout in microseconds to wait for a single byte to be + // transmitted. The Mode structure contans a Timeout field that is the + // maximum time to transmit or receive a character. However, many UARTs + // have a FIFO for transmits, so the time required to add one new character + // to the transmit FIFO may be the time required to flush a full FIFO. If + // the Timeout in the Mode structure is smaller than the time required to + // flush a full FIFO at the current baud rate, then use a timeout value that + // is required to flush a full transmit FIFO. + // + Timeout = MAX ( + This->Mode->Timeout, + (UINTN)DivU64x64Remainder ( + BitsPerCharacter * (SerialDevice->TransmitFifoDepth + 1) * 1000000, + This->Mode->BaudRate, + NULL + ) + ); + + for (Index = 0; Index < *BufferSize; Index++) { + SerialFifoAdd (&SerialDevice->Transmit, CharBuffer[Index]); + + while (SerialReceiveTransmit (SerialDevice) != EFI_SUCCESS || !SerialFifoEmpty (&SerialDevice->Transmit)) { + // + // Unsuccessful write so check if timeout has expired, if not, + // stall for a bit, increment time elapsed, and try again + // + if (Elapsed >= Timeout) { + *BufferSize = ActualWrite; + gBS->RestoreTPL (Tpl); + return EFI_TIMEOUT; + } + + gBS->Stall (TIMEOUT_STALL_INTERVAL); + + Elapsed += TIMEOUT_STALL_INTERVAL; + } + + ActualWrite++; + // + // Successful write so reset timeout + // + Elapsed = 0; + } + + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +/** + Read the specified number of bytes from serial device. + + @param This Pointer to EFI_SERIAL_IO_PROTOCOL + @param BufferSize On input the size of Buffer, on output the amount of + data returned in buffer + @param Buffer The buffer to return the data into + + @retval EFI_SUCCESS The data were read successfully + @retval EFI_DEVICE_ERROR The device reported an error + @retval EFI_TIMEOUT The read operation was stopped due to timeout + +**/ +EFI_STATUS +EFIAPI +SerialRead ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + SERIAL_DEV *SerialDevice; + UINT32 Index; + UINT8 *CharBuffer; + UINTN Elapsed; + EFI_STATUS Status; + EFI_TPL Tpl; + + SerialDevice = SERIAL_DEV_FROM_THIS (This); + Elapsed = 0; + + if (*BufferSize == 0) { + return EFI_SUCCESS; + } + + if (Buffer == NULL) { + return EFI_DEVICE_ERROR; + } + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + Status = SerialReceiveTransmit (SerialDevice); + + if (EFI_ERROR (Status)) { + *BufferSize = 0; + + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE, + EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->DevicePath + ); + + gBS->RestoreTPL (Tpl); + + return EFI_DEVICE_ERROR; + } + + CharBuffer = (UINT8 *) Buffer; + for (Index = 0; Index < *BufferSize; Index++) { + while (SerialFifoRemove (&SerialDevice->Receive, &(CharBuffer[Index])) != EFI_SUCCESS) { + // + // Unsuccessful read so check if timeout has expired, if not, + // stall for a bit, increment time elapsed, and try again + // Need this time out to get conspliter to work. + // + if (Elapsed >= This->Mode->Timeout) { + *BufferSize = Index; + gBS->RestoreTPL (Tpl); + return EFI_TIMEOUT; + } + + gBS->Stall (TIMEOUT_STALL_INTERVAL); + Elapsed += TIMEOUT_STALL_INTERVAL; + + Status = SerialReceiveTransmit (SerialDevice); + if (Status == EFI_DEVICE_ERROR) { + *BufferSize = Index; + gBS->RestoreTPL (Tpl); + return EFI_DEVICE_ERROR; + } + } + // + // Successful read so reset timeout + // + Elapsed = 0; + } + + SerialReceiveTransmit (SerialDevice); + + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +/** + Use scratchpad register to test if this serial port is present. + + @param SerialDevice Pointer to serial device structure + + @return if this serial port is present +**/ +BOOLEAN +SerialPresent ( + IN SERIAL_DEV *SerialDevice + ) + +{ + UINT8 Temp; + BOOLEAN Status; + + Status = TRUE; + + // + // Save SCR reg + // + Temp = READ_SCR (SerialDevice); + WRITE_SCR (SerialDevice, 0xAA); + + if (READ_SCR (SerialDevice) != 0xAA) { + Status = FALSE; + } + + WRITE_SCR (SerialDevice, 0x55); + + if (READ_SCR (SerialDevice) != 0x55) { + Status = FALSE; + } + // + // Restore SCR + // + WRITE_SCR (SerialDevice, Temp); + return Status; +} + +/** + Read serial port. + + @param SerialDev Pointer to serial device + @param Offset Offset in register group + + @return Data read from serial port + +**/ +UINT8 +SerialReadRegister ( + IN SERIAL_DEV *SerialDev, + IN UINT32 Offset + ) +{ + UINT8 Data; + EFI_STATUS Status; + + if (SerialDev->PciDeviceInfo == NULL) { + return IoRead8 ((UINTN) SerialDev->BaseAddress + Offset * SerialDev->RegisterStride); + } else { + if (SerialDev->MmioAccess) { + Status = SerialDev->PciDeviceInfo->PciIo->Mem.Read (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR, + SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data); + } else { + Status = SerialDev->PciDeviceInfo->PciIo->Io.Read (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR, + SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data); + } + ASSERT_EFI_ERROR (Status); + return Data; + } +} + +/** + Write serial port. + + @param SerialDev Pointer to serial device + @param Offset Offset in register group + @param Data data which is to be written to some serial port register +**/ +VOID +SerialWriteRegister ( + IN SERIAL_DEV *SerialDev, + IN UINT32 Offset, + IN UINT8 Data + ) +{ + EFI_STATUS Status; + + if (SerialDev->PciDeviceInfo == NULL) { + IoWrite8 ((UINTN) SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, Data); + } else { + if (SerialDev->MmioAccess) { + Status = SerialDev->PciDeviceInfo->PciIo->Mem.Write (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR, + SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data); + } else { + Status = SerialDev->PciDeviceInfo->PciIo->Io.Write (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR, + SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data); + } + ASSERT_EFI_ERROR (Status); + } +} diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 270277ee69..5ec37c5a7d 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -686,6 +686,12 @@ # @Prompt Enable S3 performance data support. gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwarePerformanceDataTableS3Support|TRUE|BOOLEAN|0x00010064 + ## Indicates if Serial device uses half hand shake.

+ # TRUE - Serial device uses half hand shake.
+ # FALSE - Serial device doesn't use half hand shake.
+ # @Prompt Enable Serial device Half Hand Shake + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseHalfHandshake|FALSE|BOOLEAN|0x00010073 + [PcdsFeatureFlag.IA32, PcdsFeatureFlag.X64] ## Indicates if DxeIpl should switch to long mode to enter DXE phase. # It is assumed that 64-bit DxeCore is built in firmware if it is true; otherwise 32-bit DxeCore @@ -971,6 +977,38 @@ # @Prompt Pci Serial Device Info gEfiMdeModulePkgTokenSpaceGuid.PcdSerialPciDeviceInfo|{0xFF}|VOID*|0x00010067 + ## PCI Serial Parameters. It is an array of VendorID, DeviceID, ClockRate, Offset, + # BarIndex, RegisterStride, ReceiveFifoDepth, TransmitFifoDepth information that + # describes the parameters of special PCI serial devices. + # Each array entry is 24-byte in length. The array is terminated + # by an array entry with a PCI Vendor ID of 0xFFFF. If a platform only contains a + # standard 16550 PCI serial device whose class code is 7/0/2, the value is 0xFFFF. + # The C style structure is defined as below: + # typedef struct { + # UINT16 VendorId; ///< Vendor ID to match the PCI device. The value 0xFFFF terminates the list of entries. + # UINT16 DeviceId; ///< Device ID to match the PCI device + # UINT32 ClockRate; ///< UART clock rate. Set to 0 for default clock rate of 1843200 Hz + # UINT64 Offset; ///< The byte offset into to the BAR + # UINT8 BarIndex; ///< Which BAR to get the UART base address + # UINT8 RegisterStride; ///< UART register stride in bytes. Set to 0 for default register stride of 1 byte. + # UINT16 ReceiveFifoDepth; ///< UART receive FIFO depth in bytes. Set to 0 for a default FIFO depth of 16 bytes. + # UINT16 TransmitFifoDepth; ///< UART transmit FIFO depth in bytes. Set to 0 for a default FIFO depth of 16 bytes. + # UINT8 Reserved[2]; + # } PCI_SERIAL_PARAMETER; + # It contains zero or more instances of the above structure. + # For example, if a PCI device contains two UARTs, PcdPciSerialParameters needs + # to contain two instances of the above structure, with the VendorId and DeviceId + # equals to the Device ID and Vendor ID of the device; If the PCI device uses the + # first two BARs to support two UARTs, BarIndex of first instance equals to 0 and + # BarIndex of second one equals to 1; If the PCI device uses the first BAR to + # support both UARTs, BarIndex of both instance equals to 0, Offset of first + # instance equals to 0 and Offset of second one equals to a value bigger than or + # equal to 8. + # For certain UART whose register needs to be accessed in DWORD aligned address, + # RegisterStride equals to 4. + # @Prompt Pci Serial Parameters + gEfiMdeModulePkgTokenSpaceGuid.PcdPciSerialParameters|{0xFF, 0xFF}|VOID*|0x00010071 + ## Serial Port Extended Transmit FIFO Size. The default is 64 bytes. # @Prompt Serial Port Extended Transmit FIFO Size in Bytes gEfiMdeModulePkgTokenSpaceGuid.PcdSerialExtendedTxFifoSize|64|UINT32|0x00010068 diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 06e1093d0a..b31c02e19e 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -208,6 +208,7 @@ MdeModulePkg/Application/HelloWorld/HelloWorld.inf MdeModulePkg/Application/MemoryProfileInfo/MemoryProfileInfo.inf + MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.inf MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf