Ray,

This looks good.  I tested the baud rate check and calculations.

Reviewed-by: Michael Kinney <[email protected]>

Mike

> -----Original Message-----
> From: Ni, Ruiyu
> Sent: Wednesday, December 9, 2015 6:21 PM
> To: [email protected]
> Cc: Ni, Ruiyu <[email protected]>; Tian, Feng <[email protected]>; Kinney, 
> Michael D <[email protected]>
> Subject: [Patch] MdeModulePkg: Add PciSioSerialDxe driver
> 
> PciSioSerialDxe driver can manages UARTs on a SIO chip or a PCI/PCIE
> card.
> It manages the SIO instance whose last device path node is a ACPI
> device path and the HID in the ACPI device path node equals to
> EISA_PNP_ID (0x501).
> It also manages the PCI IO instance whose class code is 7/0/2 (16550
> UART). But when proper value is set to PcdPciSerialParameters, the
> driver can also manage non-standard PCI serial cards by matching
> the Vendor ID and Device ID specified in PcdPciSerialParameters.
> The PCI BAR index, IO/MMIO offset, register stride, clock rate can
> also be specified through the same PCD.
> 
> Contributed-under: TianoCore Contribution Agreement 1.0
> Signed-off-by: Ruiyu Ni <[email protected]>
> Cc: Feng Tian <[email protected]>
> Cc: Michael Kinney <[email protected]>
> ---
>  .../Bus/Pci/PciSioSerialDxe/ComponentName.c        |  288 +++++
>  .../Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.inf    |   81 ++
>  .../Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.uni    |  Bin 0 -> 1940 bytes
>  .../Pci/PciSioSerialDxe/PciSioSerialDxeExtra.uni   |  Bin 0 -> 1380 bytes
>  MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c      | 1243 ++++++++++++++++++
>  MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.h      |  789 ++++++++++++
>  MdeModulePkg/Bus/Pci/PciSioSerialDxe/SerialIo.c    | 1321 
> ++++++++++++++++++++
>  MdeModulePkg/MdeModulePkg.dec                      |   38 +
>  MdeModulePkg/MdeModulePkg.dsc                      |    1 +
>  9 files changed, 3761 insertions(+)
>  create mode 100644 MdeModulePkg/Bus/Pci/PciSioSerialDxe/ComponentName.c
>  create mode 100644 MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.inf
>  create mode 100644 MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.uni
>  create mode 100644 
> MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxeExtra.uni
>  create mode 100644 MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c
>  create mode 100644 MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.h
>  create mode 100644 MdeModulePkg/Bus/Pci/PciSioSerialDxe/SerialIo.c
> 
> diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/ComponentName.c
> b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/ComponentName.c
> new file mode 100644
> index 0000000..994dc84
> --- /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 0000000..af92e02
> --- /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 
> 0000000000000000000000000000000000000000..44ff6ef58285c0430c97e863d0daf9bbf39a9e66
> GIT binary patch
> literal 1940
> zcmd6o-D?w35XI+N@PD|ZFDh-Kf(RmF+OE2&-H;EePqJ;=W}$7$CaLzHSAS<VyEh3D
> z`=*5ZHFxfrbI;7({QB9kjs@Bie!<?_xfOP4XZFw@VAbA$Rf#pn9&GIMTek(gr>uj`
> zY|j2QyE*GImXUR>Z$nN8e6Hzp+Q3rF7*A~i;*Ia=e9sQp=`6>@Ju0Mj!z4255o1b}
> z1vqEK4n&_Y+vF-lMZ0nTIcH93y2br?FTgI~e&NUqaHK?Vroyf6UU9Fj+xQ;fd1H5+
> zEx?+<HrOM$JZ5}uPx-xMwzph8vlq6@JSFm+h~g`R5)Ab@)`Ho!wb|*iFOP^VJxbR_
> zY?Y}op0916e7xDB=FoGYQ<Y8^9^;C2mC>kf4reKNg{SuJ8g;ImRjiX)6Gtdw6JS|z
> z`iyfk=M?dEdh9+qd~^IR=}+i&ZovudO8Gj^(xrrlSl2AYOrAxfk9MczI-c970uNb#
> zTH_Kc>$_{A>J;w!Z3|)wQ5*`NDV8c<+MmF4H7`<C?9Z`R84~9o_-t}o*ClYP9AyKD
> z)%odC0Wq4d=&CxJlZjXUPE}!*w(>7s={C17skqLD_A@LeFdtKM>(SewOlVP&I2+mr
> zOW@U`jxu{s-?h)IQ-9-!J`S;_RN9D_pSaQ^J7P?*2W%BW%tA_KCwL4TIb)`ZR^MVA
> zEYVwx`@UAJ-4%AnL=$S}5rZA$)puq`*i%r2k$Ln!D{+@iF^BldM^P)Uclav$SH_R*
> zh`C;3vW!yQBC#(GPDD@9qDNgR<{|H`)~z0ce_=armrZS_sh;{9BmKUClz}k<En(H=
> z?gN-g?*qp0$KMXw)N8n)0&;rQk7{BMEHQ1-$2a;fzU8gv{`b4PDrdm0RaLzvTiX=)
> M``!QVe~qZjZ@CO8UH||9
> 
> literal 0
> HcmV?d00001
> 
> diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxeExtra.uni
> b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxeExtra.uni
> new file mode 100644
> index 
> 0000000000000000000000000000000000000000..1de340ac907d59af1d83c86f8a04f239742af182
> GIT binary patch
> literal 1380
> zcmZXUTW`}q5QXO%iT|()FM!&Fi+Di@ku(M@N*(1Q;3;x0O)ce8ISDlUc;GwZ*lvm<
> z@9fUboH=`THh=%LtYv}kIlo{Z?AkJ0+T0%7BfQ${*%aS^Z^JmZoLNdt*X~#ayTm_r
> zPiDXI%<v6t=W%OW*p_u_YxiFeyJm0hV_BoL!L)yC2hUyD4Uxf~g8mHq%3jz@d(G^;
> zTD`F|=J648$y^9I7#dUZ#d>W`cG_@Og!mWOVwS^Y%a_|v=ian;*0H|5uN0`+4Plh|
> zd2Wf{fT9{JP|EY|h>#{JoTo&rV4}SPdD0@_5vy9oZB<8EViRDQku8nHOnR31%AR>l
> z3g48tCF6uqd3!Rnn=>z&6tz|-w;*PU%qo4P#D=@C^E%vNm-Xcdms*QouEmt`|I=o0
> z5TZB~da2fywX~nW^Zva^Rk6RuU)+#5-x2eW)2S|nmItCH$NbGv0WsR$(^Y-6<0jsE
> zN2|jMZ55xp(oIgEb3d&I+HdhpU_PVgPOCRTx#24nDQ&Q!eYOZzJ-R5dPmFE*$~s2T
> zfTprTyfJ+?Vivj1BfG*z_-lmv4r2N`B4Xgk2{ZMx#s*fnLTz9>zSioj;uh>1`{I>&
> zMXlRZ60<A(F{r{wyn3INxXY)SL!uO;szuaCVwL#=`-L4b*E@`kRf<zY_PxTX(POme
> z(N(J1<z1G#l{WZ4QOo`EJ*oPpct3@9<D<GgQ2Nyg#$YF)j=+y>3@<6KyC{(KR)n(0
> yF1%;d2j|3#pJE!|UcgQNU^;PNPf88lQ@>j2y!RgOa!OU`dT;ALBnL%U<@XPs4%3VP
> 
> literal 0
> HcmV?d00001
> 
> diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c 
> b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c
> new file mode 100644
> index 0000000..3219cb4
> --- /dev/null
> +++ b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c
> @@ -0,0 +1,1243 @@
> +/** @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 0000000..f147e69
> --- /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 0000000..6adf7e3
> --- /dev/null
> +++ b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/SerialIo.c
> @@ -0,0 +1,1321 @@
> +/** @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);
> +  }
> +}
> +
> diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
> index 270277e..5ec37c5 100644
> --- a/MdeModulePkg/MdeModulePkg.dec
> +++ b/MdeModulePkg/MdeModulePkg.dec
> @@ -686,6 +686,12 @@
>    # @Prompt Enable S3 performance data support.
>    
> gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwarePerformanceDataTableS3Support|TRUE|BOOLEAN|0x00010064
> 
> +  ## Indicates if Serial device uses half hand shake.<BR><BR>
> +  #   TRUE  - Serial device uses half hand shake.<BR>
> +  #   FALSE - Serial device doesn't use half hand shake.<BR>
> +  # @Prompt Enable Serial device Half Hand Shake
> +  
> gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseHalfHandshake|FALSE|BOOLEAN|0x00010073
> +
>  [PcdsFeatureFlag.IA32, PcdsFeatureFlag.X64]
>    ## Indicates if DxeIpl should switch to long mode to enter DXE phase.
>    #  It is assumed that 64-bit DxeCore is built in firmware if it is true; 
> otherwise 32-bit DxeCore
> @@ -971,6 +977,38 @@
>    # @Prompt Pci Serial Device Info
>    
> gEfiMdeModulePkgTokenSpaceGuid.PcdSerialPciDeviceInfo|{0xFF}|VOID*|0x00010067
> 
> +  ## PCI Serial Parameters. It is an array of VendorID, DeviceID, ClockRate, 
> Offset,
> +  #  BarIndex, RegisterStride, ReceiveFifoDepth, TransmitFifoDepth 
> information that
> +  #  describes the parameters of special PCI serial devices.
> +  #  Each array entry is 24-byte in length. The array is terminated
> +  #  by an array entry with a PCI Vendor ID of 0xFFFF. If a platform only 
> contains a
> +  #  standard 16550 PCI serial device whose class code is 7/0/2, the value 
> is 0xFFFF.
> +  #  The C style structure is defined as below:
> +  #  typedef struct {
> +  #    UINT16  VendorId;          ///< Vendor ID to match the PCI device.  
> The value 0xFFFF terminates the list of entries.
> +  #    UINT16  DeviceId;          ///< Device ID to match the PCI device
> +  #    UINT32  ClockRate;         ///< UART clock rate.  Set to 0 for 
> default clock rate of 1843200 Hz
> +  #    UINT64  Offset;            ///< The byte offset into to the BAR
> +  #    UINT8   BarIndex;          ///< Which BAR to get the UART base address
> +  #    UINT8   RegisterStride;    ///< UART register stride in bytes.  Set 
> to 0 for default register stride of 1 byte.
> +  #    UINT16  ReceiveFifoDepth;  ///< UART receive FIFO depth in bytes. Set 
> to 0 for a default FIFO depth of 16 bytes.
> +  #    UINT16  TransmitFifoDepth; ///< UART transmit FIFO depth in bytes. 
> Set to 0 for a default FIFO depth of 16 bytes.
> +  #    UINT8   Reserved[2];
> +  #  } PCI_SERIAL_PARAMETER;
> +  #  It contains zero or more instances of the above structure.
> +  #  For example, if a PCI device contains two UARTs, PcdPciSerialParameters 
> needs
> +  #  to contain  two instances of the above structure, with the VendorId and 
> DeviceId
> +  #  equals to the Device ID and Vendor ID of the device; If the PCI device 
> uses the
> +  #  first two BARs to support two UARTs, BarIndex of first instance equals 
> to 0 and
> +  #  BarIndex of second one equals to 1; If the PCI device uses the first 
> BAR to
> +  #  support both UARTs, BarIndex of both instance equals to 0, Offset of 
> first
> +  #  instance equals to 0 and Offset of second one equals to a value bigger 
> than or
> +  #  equal to 8.
> +  #  For certain UART whose register needs to be accessed in DWORD aligned 
> address,
> +  #  RegisterStride equals to 4.
> +  # @Prompt Pci Serial Parameters
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdPciSerialParameters|{0xFF, 
> 0xFF}|VOID*|0x00010071
> +
>    ## Serial Port Extended Transmit FIFO Size.  The default is 64 bytes.
>    # @Prompt Serial Port Extended Transmit FIFO Size in Bytes
>    
> gEfiMdeModulePkgTokenSpaceGuid.PcdSerialExtendedTxFifoSize|64|UINT32|0x00010068
> diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
> index 06e1093..b31c02e 100644
> --- a/MdeModulePkg/MdeModulePkg.dsc
> +++ b/MdeModulePkg/MdeModulePkg.dsc
> @@ -208,6 +208,7 @@
>    MdeModulePkg/Application/HelloWorld/HelloWorld.inf
>    MdeModulePkg/Application/MemoryProfileInfo/MemoryProfileInfo.inf
> 
> +  MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.inf
>    MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
>    
> MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf
>    MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
> --
> 1.9.5.msysgit.1

_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to