summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Bus
diff options
context:
space:
mode:
Diffstat (limited to 'MdeModulePkg/Bus')
-rw-r--r--MdeModulePkg/Bus/Pci/PciSioSerialDxe/ComponentName.c288
-rw-r--r--MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.inf81
-rw-r--r--MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.unibin0 -> 1936 bytes
-rw-r--r--MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxeExtra.unibin0 -> 1372 bytes
-rw-r--r--MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c1242
-rw-r--r--MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.h789
-rw-r--r--MdeModulePkg/Bus/Pci/PciSioSerialDxe/SerialIo.c1320
7 files changed, 3720 insertions, 0 deletions
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.<BR>
+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.<BR>
+#
+# 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
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.uni
Binary files differ
diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxeExtra.uni b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxeExtra.uni
new file mode 100644
index 0000000000..935bdba93e
--- /dev/null
+++ b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxeExtra.uni
Binary files 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.<BR>
+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.<BR>
+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 <Uefi.h>
+
+#include <IndustryStandard/Pci.h>
+
+#include <Protocol/SuperIo.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/SerialIo.h>
+#include <Protocol/DevicePath.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/PcdLib.h>
+#include <Library/IoLib.h>
+#include <Library/PrintLib.h>
+
+//
+// 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.<BR>
+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);
+ }
+}