]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c
MdeModulePkg: Add PciSioSerialDxe driver
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / PciSioSerialDxe / Serial.c
diff --git a/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c b/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c
new file mode 100644 (file)
index 0000000..86e75a4
--- /dev/null
@@ -0,0 +1,1242 @@
+/** @file\r
+  Serial driver for PCI or SIO UARTS.\r
+\r
+Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Serial.h"\r
+\r
+//\r
+// ISA Serial Driver Global Variables\r
+//\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gSerialControllerDriver = {\r
+  SerialControllerDriverSupported,\r
+  SerialControllerDriverStart,\r
+  SerialControllerDriverStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+CONTROLLER_DEVICE_PATH mControllerDevicePathTemplate = {\r
+  {\r
+    HARDWARE_DEVICE_PATH,\r
+    HW_CONTROLLER_DP,\r
+    sizeof (CONTROLLER_DEVICE_PATH),\r
+    0\r
+  },\r
+  0\r
+};\r
+\r
+SERIAL_DEV  gSerialDevTemplate = {\r
+  SERIAL_DEV_SIGNATURE,\r
+  NULL,\r
+  {\r
+    SERIAL_IO_INTERFACE_REVISION,\r
+    SerialReset,\r
+    SerialSetAttributes,\r
+    SerialSetControl,\r
+    SerialGetControl,\r
+    SerialWrite,\r
+    SerialRead,\r
+    NULL\r
+  },                                       // SerialIo\r
+  {\r
+    SERIAL_PORT_SUPPORT_CONTROL_MASK,\r
+    SERIAL_PORT_DEFAULT_TIMEOUT,\r
+    0,\r
+    16,\r
+    0,\r
+    0,\r
+    0\r
+  },                                       // SerialMode\r
+  NULL,                                    // DevicePath\r
+  NULL,                                    // ParentDevicePath\r
+  {\r
+    {\r
+      MESSAGING_DEVICE_PATH,\r
+      MSG_UART_DP,\r
+      {\r
+        (UINT8) (sizeof (UART_DEVICE_PATH)),\r
+        (UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8)\r
+      }\r
+    },\r
+    0, 0, 0, 0, 0\r
+  },                                       // UartDevicePath\r
+  0,                                       // BaseAddress\r
+  FALSE,                                   // MmioAccess\r
+  1,                                       // RegisterStride\r
+  0,                                       // ClockRate\r
+  16,                                      // ReceiveFifoDepth\r
+  { 0, 0 },                                // Receive;\r
+  16,                                      // TransmitFifoDepth\r
+  { 0, 0 },                                // Transmit;\r
+  FALSE,                                   // SoftwareLoopbackEnable;\r
+  FALSE,                                   // HardwareFlowControl;\r
+  NULL,                                    // *ControllerNameTable;\r
+  FALSE,                                   // ContainsControllerNode;\r
+  0,                                       // Instance;\r
+  NULL                                     // *PciDeviceInfo;\r
+};\r
+\r
+/**\r
+  Check the device path node whether it's the Flow Control node or not.\r
+\r
+  @param[in] FlowControl    The device path node to be checked.\r
+  \r
+  @retval TRUE              It's the Flow Control node.\r
+  @retval FALSE             It's not.\r
+\r
+**/\r
+BOOLEAN\r
+IsUartFlowControlDevicePathNode (\r
+  IN UART_FLOW_CONTROL_DEVICE_PATH *FlowControl\r
+  )\r
+{\r
+  return (BOOLEAN) (\r
+           (DevicePathType (FlowControl) == MESSAGING_DEVICE_PATH) &&\r
+           (DevicePathSubType (FlowControl) == MSG_VENDOR_DP) &&\r
+           (CompareGuid (&FlowControl->Guid, &gEfiUartDevicePathGuid))\r
+           );\r
+}\r
+\r
+/**\r
+  The user Entry Point for module PciSioSerial. The user code starts with this function.\r
+\r
+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.  \r
+  @param[in] SystemTable    A pointer to the EFI System Table.\r
+  \r
+  @retval EFI_SUCCESS       The entry point is executed successfully.\r
+  @retval other             Some error occurs when executing this entry point.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InitializePciSioSerial (\r
+  IN EFI_HANDLE           ImageHandle,\r
+  IN EFI_SYSTEM_TABLE     *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+\r
+  //\r
+  // Install driver model protocol(s).\r
+  //\r
+  Status = EfiLibInstallDriverBindingComponentName2 (\r
+             ImageHandle,\r
+             SystemTable,\r
+             &gSerialControllerDriver,\r
+             ImageHandle,\r
+             &gPciSioSerialComponentName,\r
+             &gPciSioSerialComponentName2\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Initialize UART default setting in gSerialDevTempate\r
+  //\r
+  gSerialDevTemplate.SerialMode.BaudRate = PcdGet64 (PcdUartDefaultBaudRate);\r
+  gSerialDevTemplate.SerialMode.DataBits = PcdGet8 (PcdUartDefaultDataBits);\r
+  gSerialDevTemplate.SerialMode.Parity   = PcdGet8 (PcdUartDefaultParity);\r
+  gSerialDevTemplate.SerialMode.StopBits = PcdGet8 (PcdUartDefaultStopBits);\r
+  gSerialDevTemplate.UartDevicePath.BaudRate = PcdGet64 (PcdUartDefaultBaudRate);\r
+  gSerialDevTemplate.UartDevicePath.DataBits = PcdGet8 (PcdUartDefaultDataBits);\r
+  gSerialDevTemplate.UartDevicePath.Parity   = PcdGet8 (PcdUartDefaultParity);\r
+  gSerialDevTemplate.UartDevicePath.StopBits = PcdGet8 (PcdUartDefaultStopBits);\r
+  gSerialDevTemplate.ClockRate = PcdGet32 (PcdSerialClockRate);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Return whether the controller is a SIO serial controller.\r
+\r
+  @param  Controller   The controller handle.\r
+\r
+  @retval EFI_SUCCESS  The controller is a SIO serial controller.\r
+  @retval others       The controller is not a SIO serial controller.\r
+**/\r
+EFI_STATUS\r
+IsSioSerialController (\r
+  EFI_HANDLE               Controller\r
+  )\r
+{\r
+  EFI_STATUS               Status;\r
+  EFI_SIO_PROTOCOL         *Sio;\r
+  EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+  ACPI_HID_DEVICE_PATH     *Acpi;\r
+\r
+  //\r
+  // Open the IO Abstraction(s) needed to perform the supported test\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiSioProtocolGuid,\r
+                  (VOID **) &Sio,\r
+                  gSerialControllerDriver.DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (Status == EFI_ALREADY_STARTED) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Close the I/O Abstraction(s) used to perform the supported test\r
+    //\r
+    gBS->CloseProtocol (\r
+           Controller,\r
+           &gEfiSioProtocolGuid,\r
+           gSerialControllerDriver.DriverBindingHandle,\r
+           Controller\r
+           );\r
+\r
+    Status = gBS->OpenProtocol (\r
+      Controller,\r
+      &gEfiDevicePathProtocolGuid,\r
+      (VOID **) &DevicePath,\r
+      gSerialControllerDriver.DriverBindingHandle,\r
+      Controller,\r
+      EFI_OPEN_PROTOCOL_BY_DRIVER\r
+      );\r
+    ASSERT (Status != EFI_ALREADY_STARTED);\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+      do {\r
+        Acpi = (ACPI_HID_DEVICE_PATH *) DevicePath;\r
+        DevicePath = NextDevicePathNode (DevicePath);\r
+      } while (!IsDevicePathEnd (DevicePath));\r
+\r
+      if (DevicePathType (Acpi) != ACPI_DEVICE_PATH ||\r
+          (DevicePathSubType (Acpi) != ACPI_DP && DevicePathSubType (Acpi) != ACPI_EXTENDED_DP) ||\r
+          Acpi->HID != EISA_PNP_ID (0x501)\r
+          ) {\r
+        Status = EFI_UNSUPPORTED;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Close protocol, don't use device path protocol in the Support() function\r
+    //\r
+    gBS->CloseProtocol (\r
+      Controller,\r
+      &gEfiDevicePathProtocolGuid,\r
+      gSerialControllerDriver.DriverBindingHandle,\r
+      Controller\r
+      );\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Return whether the controller is a PCI serial controller.\r
+\r
+  @param  Controller   The controller handle.\r
+\r
+  @retval EFI_SUCCESS  The controller is a PCI serial controller.\r
+  @retval others       The controller is not a PCI serial controller.\r
+**/\r
+EFI_STATUS\r
+IsPciSerialController (\r
+  EFI_HANDLE               Controller\r
+  )\r
+{\r
+  EFI_STATUS               Status;\r
+  EFI_PCI_IO_PROTOCOL      *PciIo;\r
+  EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+  PCI_TYPE00               Pci;\r
+  PCI_SERIAL_PARAMETER     *PciSerialParameter;\r
+\r
+  //\r
+  // Open the IO Abstraction(s) needed to perform the supported test\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+    Controller,\r
+    &gEfiPciIoProtocolGuid,\r
+    (VOID **) &PciIo,\r
+    gSerialControllerDriver.DriverBindingHandle,\r
+    Controller,\r
+    EFI_OPEN_PROTOCOL_BY_DRIVER\r
+    );\r
+  if (Status == EFI_ALREADY_STARTED) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0, sizeof (Pci), &Pci);\r
+    if (!EFI_ERROR (Status)) {\r
+      if (!IS_PCI_16550_SERIAL (&Pci)) {\r
+        for (PciSerialParameter = (PCI_SERIAL_PARAMETER *) PcdGetPtr (PcdPciSerialParameters)\r
+             ; PciSerialParameter->VendorId != 0xFFFF\r
+             ; PciSerialParameter++\r
+             ) {\r
+          if ((Pci.Hdr.VendorId == PciSerialParameter->VendorId) &&\r
+              (Pci.Hdr.DeviceId == PciSerialParameter->DeviceId)\r
+              ) {\r
+            break;\r
+          }\r
+        }\r
+        if (PciSerialParameter->VendorId == 0xFFFF) {\r
+          Status = EFI_UNSUPPORTED;\r
+        } else {\r
+          Status = EFI_SUCCESS;\r
+        }\r
+      }\r
+    }\r
+\r
+    //\r
+    // Close the I/O Abstraction(s) used to perform the supported test\r
+    //\r
+    gBS->CloseProtocol (\r
+      Controller,\r
+      &gEfiPciIoProtocolGuid,\r
+      gSerialControllerDriver.DriverBindingHandle,\r
+      Controller\r
+      );\r
+  }\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Open the EFI Device Path protocol needed to perform the supported test\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+    Controller,\r
+    &gEfiDevicePathProtocolGuid,\r
+    (VOID **) &DevicePath,\r
+    gSerialControllerDriver.DriverBindingHandle,\r
+    Controller,\r
+    EFI_OPEN_PROTOCOL_BY_DRIVER\r
+    );\r
+  ASSERT (Status != EFI_ALREADY_STARTED);\r
+\r
+  //\r
+  // Close protocol, don't use device path protocol in the Support() function\r
+  //\r
+  gBS->CloseProtocol (\r
+    Controller,\r
+    &gEfiDevicePathProtocolGuid,\r
+    gSerialControllerDriver.DriverBindingHandle,\r
+    Controller\r
+    );\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Check to see if this driver supports the given controller\r
+\r
+  @param  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param  Controller           The handle of the controller to test.\r
+  @param  RemainingDevicePath  A pointer to the remaining portion of a device path.\r
+\r
+  @return EFI_SUCCESS          This driver can support the given controller\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SerialControllerDriverSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN EFI_HANDLE                     Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
+  )\r
+\r
+{\r
+  EFI_STATUS                                Status;\r
+  UART_DEVICE_PATH                          *Uart;\r
+  UART_FLOW_CONTROL_DEVICE_PATH             *FlowControl;\r
+\r
+  //\r
+  // Test RemainingDevicePath\r
+  //\r
+  if ((RemainingDevicePath != NULL) && !IsDevicePathEnd (RemainingDevicePath)) {\r
+    Status = EFI_UNSUPPORTED;\r
+\r
+    Uart = SkipControllerDevicePathNode (RemainingDevicePath, NULL, NULL);\r
+    if (DevicePathType (Uart) != MESSAGING_DEVICE_PATH ||\r
+        DevicePathSubType (Uart) != MSG_UART_DP ||\r
+        DevicePathNodeLength (Uart) != sizeof (UART_DEVICE_PATH)\r
+        ) {\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+\r
+    //\r
+    // Do a rough check because Clock Rate is unknown until DriverBindingStart()\r
+    //\r
+    if (!VerifyUartParameters (0, Uart->BaudRate, Uart->DataBits, Uart->Parity, Uart->StopBits, NULL, NULL)) {\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+\r
+    FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart);\r
+    if (IsUartFlowControlDevicePathNode (FlowControl)) {\r
+      //\r
+      // If the second node is Flow Control Node,\r
+      //   return error when it request other than hardware flow control.\r
+      //\r
+      if ((ReadUnaligned32 (&FlowControl->FlowControlMap) & ~UART_FLOW_CONTROL_HARDWARE) != 0) {\r
+        return EFI_UNSUPPORTED;\r
+      }\r
+    }\r
+  }\r
+\r
+  Status = IsSioSerialController (Controller);\r
+  if (EFI_ERROR (Status)) {\r
+    Status = IsPciSerialController (Controller);\r
+  }\r
+  return Status;  \r
+}\r
+\r
+/**\r
+  Create the child serial device instance.\r
+\r
+  @param Controller           The parent controller handle.\r
+  @param Uart                 Pointer to the UART device path node in RemainingDevicePath,\r
+                              or NULL if RemainingDevicePath is NULL.\r
+  @param ParentDevicePath     Pointer to the parent device path.\r
+  @param CreateControllerNode TRUE to create the controller node.\r
+  @param Instance             Instance number of the serial device.\r
+                              The value will be set to the controller node\r
+                              if CreateControllerNode is TRUE.\r
+  @param ParentIo             A union type pointer to either Sio or PciIo.\r
+  @param PciSerialParameter   The PCI serial parameter to be used by current serial device.\r
+                              NULL for SIO serial device.\r
+  @param PciDeviceInfo        The PCI device info for the current serial device.\r
+                              NULL for SIO serial device.\r
+\r
+  @retval EFI_SUCCESS         The serial device was created successfully.\r
+  @retval others              The serial device wasn't created.\r
+**/\r
+EFI_STATUS\r
+CreateSerialDevice (\r
+  IN EFI_HANDLE                     Controller,\r
+  IN UART_DEVICE_PATH               *Uart,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *ParentDevicePath,\r
+  IN BOOLEAN                        CreateControllerNode,\r
+  IN UINT32                         Instance,\r
+  IN PARENT_IO_PROTOCOL_PTR         ParentIo,\r
+  IN PCI_SERIAL_PARAMETER           *PciSerialParameter, OPTIONAL\r
+  IN PCI_DEVICE_INFO                *PciDeviceInfo       OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                                 Status;\r
+  SERIAL_DEV                                 *SerialDevice;\r
+  UINT8                                      BarIndex;\r
+  UINT64                                     Offset;\r
+  UART_FLOW_CONTROL_DEVICE_PATH              *FlowControl;\r
+  UINT32                                     FlowControlMap;\r
+  ACPI_RESOURCE_HEADER_PTR                   Resources;\r
+  EFI_ACPI_IO_PORT_DESCRIPTOR                *Io;\r
+  EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *FixedIo;\r
+  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR          *AddressSpace;\r
+  EFI_DEVICE_PATH_PROTOCOL                   *TempDevicePath;\r
+\r
+  BarIndex = 0;\r
+  Offset = 0;\r
+  FlowControl = NULL;\r
+  FlowControlMap = 0;\r
+\r
+  //\r
+  // Initialize the serial device instance\r
+  //\r
+  SerialDevice = AllocateCopyPool (sizeof (SERIAL_DEV), &gSerialDevTemplate);\r
+  ASSERT (SerialDevice != NULL);\r
+\r
+  SerialDevice->SerialIo.Mode    = &(SerialDevice->SerialMode);\r
+  SerialDevice->ParentDevicePath = ParentDevicePath;\r
+  SerialDevice->PciDeviceInfo    = PciDeviceInfo;\r
+  SerialDevice->Instance         = Instance;\r
+\r
+  if (Uart != NULL) {\r
+    CopyMem (&SerialDevice->UartDevicePath, Uart, sizeof (UART_DEVICE_PATH));\r
+    FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart);\r
+    if (IsUartFlowControlDevicePathNode (FlowControl)) {\r
+      FlowControlMap = ReadUnaligned32 (&FlowControl->FlowControlMap);\r
+    } else {\r
+      FlowControl = NULL;\r
+    }\r
+  }\r
+\r
+  //\r
+  // For PCI serial device, use the information from PCD\r
+  //\r
+  if (PciSerialParameter != NULL) {\r
+    BarIndex = (PciSerialParameter->BarIndex == PCI_BAR_ALL) ? 0 : PciSerialParameter->BarIndex;\r
+    Offset = PciSerialParameter->Offset;\r
+    if (PciSerialParameter->RegisterStride != 0) {\r
+      SerialDevice->RegisterStride = PciSerialParameter->RegisterStride;\r
+    }\r
+    if (PciSerialParameter->ClockRate != 0) {\r
+      SerialDevice->ClockRate = PciSerialParameter->ClockRate;\r
+    }\r
+    if (PciSerialParameter->ReceiveFifoDepth != 0) {\r
+      SerialDevice->ReceiveFifoDepth = PciSerialParameter->ReceiveFifoDepth;\r
+    }\r
+    if (PciSerialParameter->TransmitFifoDepth != 0) {\r
+      SerialDevice->TransmitFifoDepth = PciSerialParameter->TransmitFifoDepth;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Pass NULL ActualBaudRate to VerifyUartParameters to disallow baudrate degrade.\r
+  // DriverBindingStart() shouldn't create a handle with different UART device path.\r
+  //\r
+  if (!VerifyUartParameters (SerialDevice->ClockRate, SerialDevice->UartDevicePath.BaudRate, SerialDevice->UartDevicePath.DataBits,\r
+                            SerialDevice->UartDevicePath.Parity, SerialDevice->UartDevicePath.StopBits, NULL, NULL\r
+                            )) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto CreateError;\r
+  }\r
+\r
+  if (PciSerialParameter == NULL) {\r
+    Status = ParentIo.Sio->GetResources (ParentIo.Sio, &Resources);\r
+  } else {\r
+    Status = ParentIo.PciIo->GetBarAttributes (ParentIo.PciIo, BarIndex, NULL, (VOID **) &Resources);\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Get the base address information from ACPI resource descriptor.\r
+    // ACPI_IO_PORT_DESCRIPTOR and ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR are returned from Sio;\r
+    // ACPI_ADDRESS_SPACE_DESCRIPTOR is returned from PciIo.\r
+    //\r
+    while ((Resources.SmallHeader->Byte != ACPI_END_TAG_DESCRIPTOR) && (SerialDevice->BaseAddress == 0)) {\r
+      switch (Resources.SmallHeader->Byte) {\r
+      case ACPI_IO_PORT_DESCRIPTOR:\r
+        Io = (EFI_ACPI_IO_PORT_DESCRIPTOR *) Resources.SmallHeader;\r
+        if (Io->Length != 0) {\r
+          SerialDevice->BaseAddress = Io->BaseAddressMin;\r
+        }\r
+        break;\r
+\r
+      case ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR:\r
+        FixedIo = (EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *) Resources.SmallHeader;\r
+        if (FixedIo->Length != 0) {\r
+          SerialDevice->BaseAddress = FixedIo->BaseAddress;\r
+        }\r
+        break;\r
+\r
+      case ACPI_ADDRESS_SPACE_DESCRIPTOR:\r
+        AddressSpace = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Resources.SmallHeader;\r
+        if (AddressSpace->AddrLen != 0) {\r
+          if (AddressSpace->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) {\r
+            SerialDevice->MmioAccess = TRUE;\r
+          }\r
+          SerialDevice->BaseAddress = AddressSpace->AddrRangeMin + Offset;\r
+        }\r
+        break;\r
+      }\r
+\r
+      if (Resources.SmallHeader->Bits.Type == 0) {\r
+        Resources.SmallHeader = (ACPI_SMALL_RESOURCE_HEADER *) ((UINT8 *) Resources.SmallHeader\r
+                                                                + Resources.SmallHeader->Bits.Length\r
+                                                                + sizeof (*Resources.SmallHeader));\r
+      } else {\r
+        Resources.LargeHeader = (ACPI_LARGE_RESOURCE_HEADER *) ((UINT8 *) Resources.LargeHeader\r
+                                                                + Resources.LargeHeader->Length\r
+                                                                + sizeof (*Resources.LargeHeader));\r
+      }\r
+    }\r
+  }\r
+\r
+  if (SerialDevice->BaseAddress == 0) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto CreateError;\r
+  }\r
+\r
+  SerialDevice->HardwareFlowControl = (BOOLEAN) (FlowControlMap == UART_FLOW_CONTROL_HARDWARE);\r
+\r
+  //\r
+  // Report status code the serial present\r
+  //\r
+  REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
+    EFI_PROGRESS_CODE,\r
+    EFI_P_PC_PRESENCE_DETECT | EFI_PERIPHERAL_SERIAL_PORT,\r
+    SerialDevice->ParentDevicePath\r
+    );\r
+\r
+  if (!SerialPresent (SerialDevice)) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
+      EFI_ERROR_CODE,\r
+      EFI_P_EC_NOT_DETECTED | EFI_PERIPHERAL_SERIAL_PORT,\r
+      SerialDevice->ParentDevicePath\r
+      );\r
+    goto CreateError;\r
+  }\r
+\r
+  //\r
+  // 1. Append Controller device path node.\r
+  //\r
+  if (CreateControllerNode) {\r
+    mControllerDevicePathTemplate.ControllerNumber = SerialDevice->Instance;\r
+    SerialDevice->DevicePath = AppendDevicePathNode (\r
+                                 SerialDevice->ParentDevicePath,\r
+                                 (EFI_DEVICE_PATH_PROTOCOL *) &mControllerDevicePathTemplate\r
+                                 );\r
+    SerialDevice->ContainsControllerNode = TRUE;\r
+  }\r
+\r
+  //\r
+  // 2. Append UART device path node.\r
+  //    The Uart setings are zero here.\r
+  //    SetAttribute() will update them to match the default setings.\r
+  //\r
+  TempDevicePath = SerialDevice->DevicePath;\r
+  if (TempDevicePath != NULL) {\r
+    SerialDevice->DevicePath = AppendDevicePathNode (\r
+                                 TempDevicePath,\r
+                                 (EFI_DEVICE_PATH_PROTOCOL *) &SerialDevice->UartDevicePath\r
+                                 );\r
+    FreePool (TempDevicePath);\r
+  } else {\r
+    SerialDevice->DevicePath = AppendDevicePathNode (\r
+                                 SerialDevice->ParentDevicePath,\r
+                                 (EFI_DEVICE_PATH_PROTOCOL *) &SerialDevice->UartDevicePath\r
+                                 );\r
+  }\r
+  //\r
+  // 3. Append the Flow Control device path node.\r
+  //    Only produce the Flow Control node when remaining device path has it\r
+  //\r
+  if (FlowControl != NULL) {\r
+    TempDevicePath = SerialDevice->DevicePath;\r
+    if (TempDevicePath != NULL) {\r
+      SerialDevice->DevicePath = AppendDevicePathNode (\r
+                                   TempDevicePath,\r
+                                   (EFI_DEVICE_PATH_PROTOCOL *) FlowControl\r
+                                   );\r
+      FreePool (TempDevicePath);\r
+    }\r
+  }\r
+  ASSERT (SerialDevice->DevicePath != NULL);\r
+\r
+  //\r
+  // Fill in Serial I/O Mode structure based on either the RemainingDevicePath or defaults.\r
+  //\r
+  SerialDevice->SerialMode.BaudRate = SerialDevice->UartDevicePath.BaudRate;\r
+  SerialDevice->SerialMode.DataBits = SerialDevice->UartDevicePath.DataBits;\r
+  SerialDevice->SerialMode.Parity   = SerialDevice->UartDevicePath.Parity;\r
+  SerialDevice->SerialMode.StopBits = SerialDevice->UartDevicePath.StopBits;\r
+\r
+  //\r
+  // Issue a reset to initialize the COM port\r
+  //\r
+  Status = SerialDevice->SerialIo.Reset (&SerialDevice->SerialIo);\r
+  if (EFI_ERROR (Status)) {\r
+    REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
+      EFI_ERROR_CODE,\r
+      EFI_P_EC_CONTROLLER_ERROR | EFI_PERIPHERAL_SERIAL_PORT,\r
+      SerialDevice->DevicePath\r
+      );\r
+    goto CreateError;\r
+  }\r
+\r
+  AddName (SerialDevice, Instance);\r
+  //\r
+  // Install protocol interfaces for the serial device.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &SerialDevice->Handle,\r
+                  &gEfiDevicePathProtocolGuid, SerialDevice->DevicePath,\r
+                  &gEfiSerialIoProtocolGuid, &SerialDevice->SerialIo,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto CreateError;\r
+  }\r
+  //\r
+  // Open For Child Device\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  PciSerialParameter != NULL ? &gEfiPciIoProtocolGuid : &gEfiSioProtocolGuid,\r
+                  (VOID **) &ParentIo,\r
+                  gSerialControllerDriver.DriverBindingHandle,\r
+                  SerialDevice->Handle,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           &SerialDevice->Handle,\r
+           &gEfiDevicePathProtocolGuid, SerialDevice->DevicePath,\r
+           &gEfiSerialIoProtocolGuid, &SerialDevice->SerialIo,\r
+           NULL\r
+           );\r
+  }\r
+\r
+CreateError:\r
+  if (EFI_ERROR (Status)) {\r
+    if (SerialDevice->DevicePath != NULL) {\r
+      FreePool (SerialDevice->DevicePath);\r
+    }\r
+    if (SerialDevice->ControllerNameTable != NULL) {\r
+      FreeUnicodeStringTable (SerialDevice->ControllerNameTable);\r
+    }\r
+    FreePool (SerialDevice);\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Returns an array of pointers containing all the child serial device pointers.\r
+\r
+  @param Controller      The parent controller handle.\r
+  @param IoProtocolGuid  The protocol GUID, either equals to gEfiSioProtocolGuid\r
+                         or equals to gEfiPciIoProtocolGuid.\r
+  @param Count           Count of the serial devices.\r
+\r
+  @return  An array of pointers containing all the child serial device pointers.\r
+**/\r
+SERIAL_DEV **\r
+GetChildSerialDevices (\r
+  IN EFI_HANDLE                       Controller,\r
+  IN EFI_GUID                         *IoProtocolGuid,\r
+  OUT UINTN                           *Count\r
+  )\r
+{\r
+  EFI_STATUS                                 Status;\r
+  UINTN                                      Index;\r
+  EFI_OPEN_PROTOCOL_INFORMATION_ENTRY        *OpenInfoBuffer;\r
+  UINTN                                      EntryCount;\r
+  SERIAL_DEV                                 **SerialDevices;\r
+  EFI_SERIAL_IO_PROTOCOL                     *SerialIo;\r
+  BOOLEAN                                    OpenByDriver;\r
+\r
+  *Count = 0;\r
+  //\r
+  // If the SerialIo instance specified by RemainingDevicePath is already created,\r
+  // update the attributes/control.\r
+  //\r
+  Status = gBS->OpenProtocolInformation (\r
+    Controller,\r
+    IoProtocolGuid,\r
+    &OpenInfoBuffer,\r
+    &EntryCount\r
+    );\r
+  if (EFI_ERROR (Status)) {\r
+    return NULL;\r
+  }\r
+\r
+  SerialDevices = AllocatePool (EntryCount * sizeof (SERIAL_DEV *));\r
+  ASSERT (SerialDevices != NULL);\r
+\r
+  *Count = 0;\r
+  OpenByDriver = FALSE;\r
+  for (Index = 0; Index < EntryCount; Index++) {\r
+    if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {\r
+      Status = gBS->OpenProtocol (\r
+        OpenInfoBuffer[Index].ControllerHandle,\r
+        &gEfiSerialIoProtocolGuid,\r
+        (VOID **) &SerialIo,\r
+        gSerialControllerDriver.DriverBindingHandle,\r
+        Controller,\r
+        EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+        );\r
+      if (!EFI_ERROR (Status)) {\r
+        SerialDevices[(*Count)++] = SERIAL_DEV_FROM_THIS (SerialIo);\r
+      }\r
+    }\r
+\r
+\r
+    if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) {\r
+      ASSERT (OpenInfoBuffer[Index].AgentHandle == gSerialControllerDriver.DriverBindingHandle);\r
+      OpenByDriver = TRUE;\r
+    }\r
+  }\r
+  if (OpenInfoBuffer != NULL) {\r
+    FreePool (OpenInfoBuffer);\r
+  }\r
+\r
+  ASSERT ((*Count == 0) || (OpenByDriver));\r
+\r
+  return SerialDevices;\r
+}\r
+\r
+/**\r
+  Start to management the controller passed in\r
+\r
+  @param  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param  Controller           The handle of the controller to test.\r
+  @param  RemainingDevicePath  A pointer to the remaining portion of a device path.\r
+\r
+  @return EFI_SUCCESS   Driver is started successfully\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SerialControllerDriverStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN EFI_HANDLE                     Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
+  )\r
+{\r
+  EFI_STATUS                                 Status;\r
+  UINTN                                      Index;\r
+  EFI_DEVICE_PATH_PROTOCOL                   *ParentDevicePath;\r
+  EFI_DEVICE_PATH_PROTOCOL                   *Node;\r
+  EFI_SERIAL_IO_PROTOCOL                     *SerialIo;\r
+  UINT32                                     ControllerNumber;\r
+  UART_DEVICE_PATH                           *Uart;\r
+  UART_FLOW_CONTROL_DEVICE_PATH              *FlowControl;\r
+  UINT32                                     Control;\r
+  PARENT_IO_PROTOCOL_PTR                     ParentIo;\r
+  ACPI_HID_DEVICE_PATH                       *Acpi;\r
+  EFI_GUID                                   *IoProtocolGuid;\r
+  PCI_SERIAL_PARAMETER                       *PciSerialParameter;\r
+  PCI_SERIAL_PARAMETER                       DefaultPciSerialParameter;\r
+  PCI_TYPE00                                 Pci;\r
+  UINT32                                     PciSerialCount;\r
+  SERIAL_DEV                                 **SerialDevices;\r
+  UINTN                                      SerialDeviceCount;\r
+  PCI_DEVICE_INFO                            *PciDeviceInfo;\r
+  UINT64                                     Supports;\r
+  BOOLEAN                                    ContainsControllerNode;\r
+\r
+  //\r
+  // Get the Parent Device Path\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  (VOID **) &ParentDevicePath,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {\r
+    return Status;\r
+  }\r
+  //\r
+  // Report status code enable the serial\r
+  //\r
+  REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
+    EFI_PROGRESS_CODE,\r
+    EFI_P_PC_ENABLE | EFI_PERIPHERAL_SERIAL_PORT,\r
+    ParentDevicePath\r
+    );\r
+\r
+  //\r
+  // Grab the IO abstraction we need to get any work done\r
+  //\r
+  IoProtocolGuid = &gEfiSioProtocolGuid;\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  IoProtocolGuid,\r
+                  (VOID **) &ParentIo,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {\r
+    IoProtocolGuid = &gEfiPciIoProtocolGuid;\r
+    Status = gBS->OpenProtocol (\r
+                    Controller,\r
+                    IoProtocolGuid,\r
+                    (VOID **) &ParentIo,\r
+                    This->DriverBindingHandle,\r
+                    Controller,\r
+                    EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                    );\r
+  }\r
+  ASSERT (!EFI_ERROR (Status) || Status == EFI_ALREADY_STARTED);\r
+\r
+  //\r
+  // Do nothing for END device path node\r
+  //\r
+  if ((RemainingDevicePath != NULL) && IsDevicePathEnd (RemainingDevicePath)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+\r
+  SerialDevices = GetChildSerialDevices (Controller, IoProtocolGuid, &SerialDeviceCount);\r
+  //\r
+  // If the SerialIo instance specified by RemainingDevicePath is already created,\r
+  // update the attributes/control.\r
+  //\r
+  if ((SerialDeviceCount != 0) && (RemainingDevicePath != NULL)) {\r
+    Uart = (UART_DEVICE_PATH *) SkipControllerDevicePathNode (RemainingDevicePath, &ContainsControllerNode, &ControllerNumber);\r
+    for (Index = 0; Index < SerialDeviceCount; Index++) {\r
+      if ((!SerialDevices[Index]->ContainsControllerNode && !ContainsControllerNode) ||\r
+          (SerialDevices[Index]->ContainsControllerNode && ContainsControllerNode && SerialDevices[Index]->Instance == ControllerNumber)\r
+          ) {\r
+        Status = EFI_INVALID_PARAMETER;\r
+        //\r
+        // Pass NULL ActualBaudRate to VerifyUartParameters to disallow baudrate degrade.\r
+        // DriverBindingStart() shouldn't create a handle with different UART device path.\r
+        //\r
+        if (VerifyUartParameters (SerialDevices[Index]->ClockRate, Uart->BaudRate, Uart->DataBits,\r
+                                  (EFI_PARITY_TYPE) Uart->Parity, (EFI_STOP_BITS_TYPE) Uart->StopBits, NULL, NULL)) {\r
+          SerialIo = &SerialDevices[Index]->SerialIo;\r
+          Status = SerialIo->SetAttributes (\r
+                               SerialIo,\r
+                               Uart->BaudRate,\r
+                               SerialIo->Mode->ReceiveFifoDepth,\r
+                               SerialIo->Mode->Timeout,\r
+                               (EFI_PARITY_TYPE) Uart->Parity,\r
+                               Uart->DataBits,\r
+                               (EFI_STOP_BITS_TYPE) Uart->StopBits\r
+                               );\r
+        }\r
+        FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart);\r
+        if (!EFI_ERROR (Status) && IsUartFlowControlDevicePathNode (FlowControl)) {\r
+          Status = SerialIo->GetControl (SerialIo, &Control);\r
+          if (!EFI_ERROR (Status)) {\r
+            if (ReadUnaligned32 (&FlowControl->FlowControlMap) == UART_FLOW_CONTROL_HARDWARE) {\r
+              Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;\r
+            } else {\r
+              Control &= ~EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;\r
+            }\r
+            //\r
+            // Clear the bits that are not allowed to pass to SetControl\r
+            //\r
+            Control &= (EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY |\r
+                        EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE | EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE |\r
+                        EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE);\r
+            Status = SerialIo->SetControl (SerialIo, Control);\r
+          }\r
+        }\r
+        break;\r
+      }\r
+    }\r
+    if (Index != SerialDeviceCount) {\r
+      //\r
+      // Directly return if the SerialIo instance specified by RemainingDevicePath is found and updated.\r
+      // Otherwise continue to create the instance specified by RemainingDevicePath.\r
+      //\r
+      if (SerialDevices != NULL) {\r
+        FreePool (SerialDevices);\r
+      }\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  if (RemainingDevicePath != NULL) {\r
+    Uart = (UART_DEVICE_PATH *) SkipControllerDevicePathNode (RemainingDevicePath, &ContainsControllerNode, &ControllerNumber);\r
+  } else {\r
+    Uart = NULL;\r
+  }\r
+\r
+  PciDeviceInfo = NULL;\r
+  if (IoProtocolGuid == &gEfiSioProtocolGuid) {\r
+    Status = EFI_NOT_FOUND;\r
+    if (RemainingDevicePath == NULL || !ContainsControllerNode) {\r
+      Node = ParentDevicePath;\r
+      do {\r
+        Acpi = (ACPI_HID_DEVICE_PATH *) Node;\r
+        Node = NextDevicePathNode (Node);\r
+      } while (!IsDevicePathEnd (Node));\r
+      Status = CreateSerialDevice (Controller, Uart, ParentDevicePath, FALSE, Acpi->UID, ParentIo, NULL, NULL);\r
+      DEBUG ((EFI_D_INFO, "PciSioSerial: Create SIO child serial device - %r\n", Status));\r
+    }\r
+  } else {\r
+    Status = ParentIo.PciIo->Pci.Read (ParentIo.PciIo, EfiPciIoWidthUint8, 0, sizeof (Pci), &Pci);\r
+    if (!EFI_ERROR (Status)) {\r
+      //\r
+      // PcdPciSerialParameters takes the higher priority.\r
+      //\r
+      PciSerialCount = 0;\r
+      for (PciSerialParameter = PcdGetPtr (PcdPciSerialParameters); PciSerialParameter->VendorId != 0xFFFF; PciSerialParameter++) {\r
+        if ((PciSerialParameter->VendorId == Pci.Hdr.VendorId) &&\r
+            (PciSerialParameter->DeviceId == Pci.Hdr.DeviceId)\r
+            ) {\r
+          PciSerialCount++;\r
+        }\r
+      }\r
+\r
+      if (SerialDeviceCount == 0) {\r
+        //\r
+        // Enable the IO & MEM decoding when creating the first child.\r
+        // Restore the PCI attributes when all children is destroyed (PciDeviceInfo->ChildCount == 0).\r
+        //\r
+        PciDeviceInfo = AllocatePool (sizeof (PCI_DEVICE_INFO));\r
+        PciDeviceInfo->ChildCount = 0;\r
+        PciDeviceInfo->PciIo = ParentIo.PciIo;\r
+        Status = ParentIo.PciIo->Attributes (\r
+          ParentIo.PciIo,\r
+          EfiPciIoAttributeOperationGet,\r
+          0,\r
+          &PciDeviceInfo->PciAttributes\r
+          );\r
+\r
+        if (!EFI_ERROR (Status)) {\r
+          Status = ParentIo.PciIo->Attributes (\r
+            ParentIo.PciIo,\r
+            EfiPciIoAttributeOperationSupported,\r
+            0,\r
+            &Supports\r
+            );\r
+          if (!EFI_ERROR (Status)) {\r
+            Supports &= EFI_PCI_IO_ATTRIBUTE_IO | EFI_PCI_IO_ATTRIBUTE_MEMORY;\r
+            Status = ParentIo.PciIo->Attributes (\r
+              ParentIo.PciIo,\r
+              EfiPciIoAttributeOperationEnable,\r
+              Supports,\r
+              NULL\r
+              );\r
+          }\r
+        }\r
+      } else {\r
+        //\r
+        // Re-use the PciDeviceInfo stored in existing children.\r
+        //\r
+        PciDeviceInfo = SerialDevices[0]->PciDeviceInfo;\r
+        ASSERT (PciDeviceInfo != NULL);\r
+      }\r
+\r
+      Status = EFI_NOT_FOUND;\r
+      if (PciSerialCount <= 1) {\r
+        //\r
+        // PCI serial device contains only one UART\r
+        //\r
+        if (RemainingDevicePath == NULL || !ContainsControllerNode) {\r
+          //\r
+          // This PCI serial device is matched by class code in Supported()\r
+          //\r
+          if (PciSerialCount == 0) {\r
+            DefaultPciSerialParameter.VendorId = Pci.Hdr.VendorId;\r
+            DefaultPciSerialParameter.DeviceId = Pci.Hdr.DeviceId;\r
+            DefaultPciSerialParameter.BarIndex = 0;\r
+            DefaultPciSerialParameter.Offset = 0;\r
+            DefaultPciSerialParameter.RegisterStride = 0;\r
+            DefaultPciSerialParameter.ClockRate = 0;\r
+            PciSerialParameter = &DefaultPciSerialParameter;\r
+          } else if (PciSerialCount == 1) {\r
+            PciSerialParameter = PcdGetPtr (PcdPciSerialParameters);\r
+          }\r
+\r
+          Status = CreateSerialDevice (Controller, Uart, ParentDevicePath, FALSE, 0, ParentIo, PciSerialParameter, PciDeviceInfo);\r
+          DEBUG ((EFI_D_INFO, "PciSioSerial: Create PCI child serial device (single) - %r\n", Status));\r
+          if (!EFI_ERROR (Status)) {\r
+            PciDeviceInfo->ChildCount++;\r
+          }\r
+        }\r
+      } else {\r
+        //\r
+        // PCI serial device contains multiple UARTs\r
+        //\r
+        if (RemainingDevicePath == NULL || ContainsControllerNode) {\r
+          PciSerialCount = 0;\r
+          for (PciSerialParameter = PcdGetPtr (PcdPciSerialParameters); PciSerialParameter->VendorId != 0xFFFF; PciSerialParameter++) {\r
+            if ((PciSerialParameter->VendorId == Pci.Hdr.VendorId) &&\r
+                (PciSerialParameter->DeviceId == Pci.Hdr.DeviceId) &&\r
+                ((RemainingDevicePath == NULL) || (ControllerNumber == PciSerialCount))\r
+                ) {\r
+              //\r
+              // Create controller node when PCI serial device contains multiple UARTs\r
+              //\r
+              Status = CreateSerialDevice (Controller, Uart, ParentDevicePath, TRUE, PciSerialCount, ParentIo, PciSerialParameter, PciDeviceInfo);\r
+              PciSerialCount++;\r
+              DEBUG ((EFI_D_INFO, "PciSioSerial: Create PCI child serial device (multiple) - %r\n", Status));\r
+              if (!EFI_ERROR (Status)) {\r
+                PciDeviceInfo->ChildCount++;\r
+              }\r
+            }\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  if (SerialDevices != NULL) {\r
+    FreePool (SerialDevices);\r
+  }\r
+\r
+  //\r
+  // For multiple PCI serial devices, set Status to SUCCESS if one child is created successfully\r
+  //\r
+  if ((PciDeviceInfo != NULL) && (PciDeviceInfo->ChildCount != 0)) {\r
+    Status = EFI_SUCCESS;\r
+  }\r
+\r
+  if (EFI_ERROR (Status) && (SerialDeviceCount == 0)) {\r
+    if (PciDeviceInfo != NULL) {\r
+      Status = ParentIo.PciIo->Attributes (\r
+        ParentIo.PciIo,\r
+        EfiPciIoAttributeOperationSet,\r
+        PciDeviceInfo->PciAttributes,\r
+        NULL\r
+        );\r
+      ASSERT_EFI_ERROR (Status);\r
+      FreePool (PciDeviceInfo);\r
+    }\r
+    gBS->CloseProtocol (\r
+           Controller,\r
+           &gEfiDevicePathProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           Controller\r
+           );\r
+    gBS->CloseProtocol (\r
+           Controller,\r
+           IoProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           Controller\r
+           );\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Disconnect this driver with the controller, uninstall related protocol instance\r
+\r
+  @param  This                  A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param  Controller            The handle of the controller to test.\r
+  @param  NumberOfChildren      Number of child device.\r
+  @param  ChildHandleBuffer     A pointer to the remaining portion of a device path.\r
+\r
+  @retval EFI_SUCCESS           Operation successfully\r
+  @retval EFI_DEVICE_ERROR      Cannot stop the driver successfully\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SerialControllerDriverStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN  EFI_HANDLE                     Controller,\r
+  IN  UINTN                          NumberOfChildren,\r
+  IN  EFI_HANDLE                     *ChildHandleBuffer\r
+  )\r
+\r
+{\r
+  EFI_STATUS                          Status;\r
+  UINTN                               Index;\r
+  BOOLEAN                             AllChildrenStopped;\r
+  EFI_SERIAL_IO_PROTOCOL              *SerialIo;\r
+  SERIAL_DEV                          *SerialDevice;\r
+  VOID                                *IoProtocol;\r
+  EFI_DEVICE_PATH_PROTOCOL            *DevicePath;\r
+  PCI_DEVICE_INFO                     *PciDeviceInfo;\r
+\r
+  PciDeviceInfo = NULL;\r
+\r
+  Status = gBS->HandleProtocol (\r
+                  Controller,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  (VOID **) &DevicePath\r
+                  );\r
+\r
+  //\r
+  // Report the status code disable the serial\r
+  //\r
+  REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
+    EFI_PROGRESS_CODE,\r
+    EFI_P_PC_DISABLE | EFI_PERIPHERAL_SERIAL_PORT,\r
+    DevicePath\r
+    );\r
+\r
+  if (NumberOfChildren == 0) {\r
+    //\r
+    // Close the bus driver\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    Controller,\r
+                    &gEfiPciIoProtocolGuid,\r
+                    &IoProtocol,\r
+                    This->DriverBindingHandle,\r
+                    Controller,\r
+                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                    );\r
+    gBS->CloseProtocol (\r
+           Controller,\r
+           !EFI_ERROR (Status) ? &gEfiPciIoProtocolGuid : &gEfiSioProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           Controller\r
+           );\r
+\r
+    gBS->CloseProtocol (\r
+           Controller,\r
+           &gEfiDevicePathProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           Controller\r
+           );\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  AllChildrenStopped = TRUE;\r
+\r
+  for (Index = 0; Index < NumberOfChildren; Index++) {\r
+\r
+    Status = gBS->OpenProtocol (\r
+                    ChildHandleBuffer[Index],\r
+                    &gEfiSerialIoProtocolGuid,\r
+                    (VOID **) &SerialIo,\r
+                    This->DriverBindingHandle,\r
+                    Controller,\r
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                    );\r
+    if (!EFI_ERROR (Status)) {\r
+\r
+      SerialDevice = SERIAL_DEV_FROM_THIS (SerialIo);\r
+      ASSERT ((PciDeviceInfo == NULL) || (PciDeviceInfo == SerialDevice->PciDeviceInfo));\r
+      PciDeviceInfo = SerialDevice->PciDeviceInfo;\r
+\r
+      Status = gBS->CloseProtocol (\r
+                      Controller,\r
+                      PciDeviceInfo != NULL ? &gEfiPciIoProtocolGuid : &gEfiSioProtocolGuid,\r
+                      This->DriverBindingHandle,\r
+                      ChildHandleBuffer[Index]\r
+                      );\r
+\r
+      Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                      ChildHandleBuffer[Index],\r
+                      &gEfiDevicePathProtocolGuid, SerialDevice->DevicePath,\r
+                      &gEfiSerialIoProtocolGuid,   &SerialDevice->SerialIo,\r
+                      NULL\r
+                      );\r
+      if (EFI_ERROR (Status)) {\r
+        gBS->OpenProtocol (\r
+               Controller,\r
+               PciDeviceInfo != NULL ? &gEfiPciIoProtocolGuid : &gEfiSioProtocolGuid,\r
+               &IoProtocol,\r
+               This->DriverBindingHandle,\r
+               ChildHandleBuffer[Index],\r
+               EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+               );\r
+      } else {\r
+        FreePool (SerialDevice->DevicePath);\r
+        FreeUnicodeStringTable (SerialDevice->ControllerNameTable);\r
+        FreePool (SerialDevice);\r
+\r
+        if (PciDeviceInfo != NULL) {\r
+          ASSERT (PciDeviceInfo->ChildCount != 0);\r
+          PciDeviceInfo->ChildCount--;\r
+        }\r
+      }\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      AllChildrenStopped = FALSE;\r
+    }\r
+  }\r
+\r
+  if (!AllChildrenStopped) {\r
+    return EFI_DEVICE_ERROR;\r
+  } else {\r
+    //\r
+    // If all children are destroyed, restore the PCI attributes.\r
+    //\r
+    if ((PciDeviceInfo != NULL) && (PciDeviceInfo->ChildCount == 0)) {\r
+      ASSERT (PciDeviceInfo->PciIo != NULL);\r
+      Status = PciDeviceInfo->PciIo->Attributes (\r
+        PciDeviceInfo->PciIo,\r
+        EfiPciIoAttributeOperationSet,\r
+        PciDeviceInfo->PciAttributes,\r
+        NULL\r
+        );\r
+      ASSERT_EFI_ERROR (Status);\r
+      FreePool (PciDeviceInfo);\r
+    }\r
+    return EFI_SUCCESS;\r
+  }\r
+}\r