Newer
Older
AMI-Aptio-BIOS-Reversed / SerialIo / SerialIo.c
@Ajax Dong Ajax Dong 2 days ago 71 KB Init
/** @file
  SerialIo.c - AMI UEFI Serial I/O Driver Implementation

  Source path (from debug strings): e:\hs\AmiModulePkg\Terminal\SerialIo.c
  Compiler: MSVC (COFF/PE32+ format)
  Binary: SerialIo.efi (index 0133, SHA256: c453bd9c2a85...)

  Implements EFI Serial I/O Protocol on top of NS16550/PC16550D-compatible
  UART hardware. Supports three access backends:
    1. Legacy I/O (in/out instructions) on ISA UARTs (COM1-COM10)
    2. PCI I/O protocol (MMIO or I/O) on PCI UARTs
    3. MMIO-based access via direct memory-mapped register access

  Design:
    - Driver Binding pattern: Supported() probes device path for UART type
    - Start() allocates a SERIAL_IO_PRIVATE context, detects UART capabilities,
      installs EFI_SERIAL_IO_PROTOCOL on a new child handle
    - UART access through UartReadRegister/UartWriteRegister which dispatch
      based on the access method (legacy IO, MMIO, PCI I/O protocol)
    - FIFO detection via the standard 8250/16550 loopback test
    - SW FIFO for buffered writes when hardware flow control is active
**/

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Protocol/SerialIo.h>
#include <Protocol/DevicePath.h>
#include <Protocol/PciIo.h>
#include <Protocol/ComponentName2.h>
#include <Protocol/DriverBinding.h>
#include "SerialIo.h"

//
// ------------------------------- Global Data ------------------------------------
//

//
// COM port name strings (off_3560 at 0x3560)
//
GLOBAL_REMOVE_IF_UNREFERENCED const CHAR16 *mComPortNames[10] = {
  L"COM1",  L"COM2",  L"COM3",  L"COM4",  L"COM5",
  L"COM6",  L"COM7",  L"COM8",  L"COM9",  L"COM10"
};

//
// Baud rate table (dword_35D0 at 0x35D0)
// Supported rates in ascending order, used to clamp requested baud rates
// to the nearest valid value.
//
GLOBAL_REMOVE_IF_UNREFERENCED const UINT32 mBaudRateTable[19] = {
     50,     75,    110,    134,    150,
    300,    600,   1200,   1800,   2000,
   2400,   3600,   4800,   7200,   9600,
  19200,  38400,  57600, 115200
};

//
// PCI UART Device Table (at 0x3AC8)
// Entries: {DeviceId, VendorId, ClockRate, RegisterWidth}
// Terminated by DeviceId = 0xFFFF.
// NOTE: This table may be populated by board-specific code or ACPI table parsing.
// The SerialIoDriverBindingStart code iterates this table to match PCI UART
// device:vendor IDs and set clock frequency and register width accordingly.
//
// Data at 0x3AC8: first entry = {0xFFFF, 0x00FF, ...} (table may be empty/terminator)
//
#define PCI_UART_TABLE_TERMINAL 0xFFFF
#define PCI_UART_ENTRY_SIZE     13

//
// Default serial attributes for initial Setup
// Packed: {StopBits=1, Parity=1(none), DataBits=8, BaudRate=115200}
// xmmword_35B0 at 0x35B0 = 0x0000130E03
//
#define DEFAULT_BAUD_RATE   115200
#define DEFAULT_DATA_BITS   8
#define DEFAULT_PARITY      1
#define DEFAULT_STOP_BITS   1
#define DEFAULT_FIFO_DEPTH  16
#define DEFAULT_TIMEOUT     1000000

//
// Hardware magic constant:
// '$SIO' = 0x4F495324 -- used to verify private context integrity
//
#define SERIAL_IO_MAGIC     SIGNATURE_32('$', 'S', 'I', 'O')

//
// Max consecutive write timeouts before blocking
//
#define MAX_WRITE_RETRIES   10

//
// Software flow control timeout (in 100ns units, 10 seconds)
//
#define TIMER_CANCEL_PERIOD  10000000

//
// Transmit timeout loop limit
//
#define XMIT_LOOP_LIMIT      100000

//
// ---------------------------- GUID Definitions ---------------------------------
//
EFI_GUID gEfiSerialIoProtocolGuid     = EFI_SERIAL_IO_PROTOCOL_GUID;
EFI_GUID gEfiDevicePathProtocolGuid   = EFI_DEVICE_PATH_PROTOCOL_GUID;
EFI_GUID gEfiPciIoProtocolGuid        = EFI_PCI_IO_PROTOCOL_GUID;
EFI_GUID gEfiUartIoProtocolGuid       = EFI_UART_IO_PROTOCOL_GUID;
EFI_GUID gEfiComponentName2ProtocolGuid = EFI_COMPONENT_NAME2_PROTOCOL_GUID;

//
// Device path GUID for UART serial device node
//
EFI_GUID gEfiSerialIoDevicePathGuid   = EFI_SERIAL_IO_DEVICE_PATH_GUID;

//
// ---------------------------- Forward Declarations -----------------------------
//
EFI_STATUS
EFIAPI
SerialIoDriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  );

EFI_STATUS
EFIAPI
SerialIoDriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  );

EFI_STATUS
EFIAPI
SerialIoDriverBindingStop (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                   *ChildHandleBuffer
  );

//
// Component Name 2 Protocol
//
EFI_STATUS
EFIAPI
SerialIoComponentNameGetDriverName (
  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
  IN  CHAR8                         *Language,
  OUT CHAR16                        **DriverName
  );

EFI_STATUS
EFIAPI
SerialIoComponentNameGetControllerName (
  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
  IN  EFI_HANDLE                    ControllerHandle,
  IN  EFI_HANDLE                    ChildHandle,
  IN  CHAR8                         *Language,
  OUT CHAR16                        **ControllerName
  );

//
// Serial I/O Protocol Functions
//
EFI_STATUS
EFIAPI
SerialIoReset (
  IN SERIAL_IO_PRIVATE  *Private
  );

EFI_STATUS
EFIAPI
SerialIoSetAttributes (
  IN SERIAL_IO_PRIVATE  *Private,
  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
EFIAPI
SerialIoSetControlBits (
  IN SERIAL_IO_PRIVATE  *Private,
  IN UINT32             Control
  );

EFI_STATUS
EFIAPI
SerialIoGetControlBits (
  IN SERIAL_IO_PRIVATE  *Private,
  OUT UINT32            *Control
  );

EFI_STATUS
EFIAPI
SerialIoWrite (
  IN SERIAL_IO_PRIVATE  *Private,
  IN OUT UINT64         *BufferSize,
  IN VOID               *Buffer
  );

EFI_STATUS
EFIAPI
SerialIoRead (
  IN SERIAL_IO_PRIVATE  *Private,
  IN OUT UINT64         *BufferSize,
  OUT VOID              *Buffer
  );

//
// UART Hardware Access
//
UINT8
UartReadRegister (
  IN SERIAL_IO_PRIVATE  *Private,
  IN UINT8              Offset
  );

UINT8
UartWriteRegister (
  IN SERIAL_IO_PRIVATE  *Private,
  IN UINT8              Offset,
  IN UINT8              Value
  );

UINT8
UartSetFifoMode (
  IN SERIAL_IO_PRIVATE  *Private,
  IN UINT32             ReceiveFifoDepth,
  IN UINT64             BaudRate
  );

//
// Backend functions (not protocol interface, internal)
//
EFI_STATUS
SerialIoCreateDevicePathNode (
  IN SERIAL_IO_PRIVATE  *Private,
  OUT VOID              **DevicePath  // caller frees
  );

VOID
EFIAPI
SerialIoTransmitReadyTimer (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  );

UINT8
SerialIoDetectUart (
  IN SERIAL_IO_PRIVATE  *Private
  );

//
// ---------------------------- UART Register Access -----------------------------
//

/**
  Read a UART register.

  Dispatches to the appropriate access method:
    - If IsaAccess is set and AccessMethod==0: use legacy in/out (__inbyte)
    - If IsaAccess and AccessMethod==1: use MMIO (direct memory read)
    - If PciIo is set: use PCI I/O protocol (MMIO or I/O depending on width)

  Register stride (1/2/4) and width (byte/word/dword) are applied based on
  RegisterStride and RegisterWidth.

  @param[in]  Private   Serial I/O private context.
  @param[in]  Offset    UART register offset (0-7).

  @return The register value read.

**/
UINT8
UartReadRegister (
  IN SERIAL_IO_PRIVATE  *Private,
  IN UINT8              Offset
  )
{
  if (Private->IsaAccess) {
    //
    // ISA/Legacy UART: MMIO or I/O
    //
    if (Private->AccessMethod) {
      //
      // MMIO access (direct memory read of register)
      //
      switch (Private->RegisterWidth) {
      case 4:
        return *(volatile UINT32 *)(Private->BaseAddress + 4 * Offset);
      case 2:
        return *(volatile UINT16 *)(Private->BaseAddress + 2 * Offset);
      default:
        return *(volatile UINT8  *)(Private->BaseAddress + Offset);
      }
    } else {
      //
      // Legacy I/O port access
      //
      return __inbyte(Offset + (UINT16)Private->BaseAddress);
    }
  }

  if (Private->PciIo) {
    //
    // PCI I/O protocol backed access
    //
    EFI_PCI_IO_PROTOCOL *PciIo;
    UINT8               Result;
    UINT8               Bus;

    PciIo = Private->PciIo;
    Bus   = Private->PciBus;

    if (Private->AccessMethod == 1) {
      //
      // Use PCI MMIO read via PCI I/O protocol
      //
      switch (Private->RegisterWidth) {
      case 4:
        PciIo->Mem.Read (PciIo, EfiPciIoWidthUint32, 0,
                         (UINT16)(4 * Offset), 1, &Result);
        return Result;
      case 2:
        PciIo->Mem.Read (PciIo, EfiPciIoWidthUint16, 0,
                         (UINT16)(2 * Offset), 1, &Result);
        return Result;
      default:
        PciIo->Mem.Read (PciIo, EfiPciIoWidthUint8, 0,
                         (UINT16)Offset, 1, &Result);
        return Result;
      }
    } else {
      //
      // Use PCI I/O port read
      //
      PciIo->Io.Read (PciIo, EfiPciIoWidthUint8, Bus,
                      (UINT16)Offset, 1, &Result);
      return Result;
    }
  }

  //
  // Fallback: legacy I/O
  //
  return __inbyte(Offset + (UINT16)Private->BaseAddress);
}

/**
  Write a UART register.

  Dispatches to the appropriate access method (symmetric with Read).

  @param[in]  Private   Serial I/O private context.
  @param[in]  Offset    UART register offset (0-7).
  @param[in]  Value     Value to write.

  @return The value written (for chaining/convenience).

**/
UINT8
UartWriteRegister (
  IN SERIAL_IO_PRIVATE  *Private,
  IN UINT8              Offset,
  IN UINT8              Value
  )
{
  if (Private->IsaAccess) {
    if (Private->AccessMethod) {
      //
      // MMIO access
      //
      switch (Private->RegisterWidth) {
      case 4:
        *(volatile UINT32 *)(Private->BaseAddress + 4 * Offset) = Value;
        break;
      case 2:
        *(volatile UINT16 *)(Private->BaseAddress + 2 * Offset) = Value;
        break;
      default:
        *(volatile UINT8  *)(Private->BaseAddress + Offset) = Value;
        break;
      }
    } else {
      //
      // Legacy I/O port access
      //
      __outbyte(Offset + (UINT16)Private->BaseAddress, Value);
    }
    return Value;
  }

  if (Private->PciIo) {
    EFI_PCI_IO_PROTOCOL *PciIo;
    UINT8               Bus;

    PciIo = Private->PciIo;
    Bus   = Private->PciBus;

    if (Private->AccessMethod == 1) {
      switch (Private->RegisterWidth) {
      case 4:
        PciIo->Mem.Write (PciIo, EfiPciIoWidthUint32, 0,
                          (UINT16)(4 * Offset), 1, &Value);
        break;
      case 2:
        PciIo->Mem.Write (PciIo, EfiPciIoWidthUint16, 0,
                          (UINT16)(2 * Offset), 1, &Value);
        break;
      default:
        PciIo->Mem.Write (PciIo, EfiPciIoWidthUint8, 0,
                          (UINT16)Offset, 1, &Value);
        break;
      }
    } else {
      PciIo->Io.Write (PciIo, EfiPciIoWidthUint8, Bus,
                       (UINT16)Offset, 1, &Value);
    }
    return Value;
  }

  //
  // Fallback: legacy I/O
  //
  __outbyte(Offset + (UINT16)Private->BaseAddress, Value);
  return Value;
}

//
// ---------------------------- UART FIFO Detection ------------------------------
//

/**
  Detect UART FIFO capabilities and configure FIFO mode.

  Uses the standard 8250/16550 FIFO detection algorithm:
    1. Write FCR with FIFO enable + clear
    2. Read IIR to check if FIFO is supported
    3. Determine FIFO size based on IIR bits

  This function also performs a loopback detection test on certain
  UART implementations (when byte_3FA1 is set).

  @param[in]  Private   Serial I/O private context.

  @return TRUE if the UART is functioning (possibly FIFO-less), FALSE if
          the UART appears to be non-functional (loopback mismatch indicates
          absent hardware).

**/
UINT8
SerialIoDetectUart (
  IN SERIAL_IO_PRIVATE  *Private
  )
{
  UINT8  SavedFcr;
  UINT8  Result;

  //
  // Only run detection for '$SIO' magic UARTs
  //
  if (Private->Magic != SERIAL_IO_MAGIC) {
    return FALSE;
  }

  SavedFcr = UartReadRegister (Private, R_UART_FCR);

  //
  // Enable FIFO, clear RCVR/XMIT FIFOs
  //
  UartWriteRegister (Private, R_UART_FCR, FCR_FIFO_ENABLE |
                                           FCR_CLEAR_RX    |
                                           FCR_CLEAR_TX);

  //
  // Read IIR to check FIFO enable status
  //
  if (UartReadRegister (Private, R_UART_IIR) == 0xFF) {
    //
    // No UART at this address (bus pulls data high)
    //
    UartWriteRegister (Private, R_UART_FCR, SavedFcr);
    Private->DeviceRemoved = TRUE;
    return TRUE;
  }

  //
  // Flush any pending data
  //
  gBS->Stall (2000);
  while ((UartReadRegister (Private, R_UART_LSR) & LSR_DR) != 0) {
    UartReadRegister (Private, R_UART_RBR);
  }

  //
  // Perform 8250/16550 detection by writing 0x80 then checking loopback
  //
  UartWriteRegister (Private, R_UART_THR, 0x80);
  UartWriteRegister (Private, R_UART_THR, 0x08);
  UartWriteRegister (Private, R_UART_THR, 0x20);
  UartWriteRegister (Private, R_UART_THR, 0x08);

  gBS->Stall (50000);

  //
  // Check if 0x80 can be read back correctly
  //
  if ((UartReadRegister (Private, R_UART_LSR) & LSR_DR) != 0 &&
       UartReadRegister (Private, R_UART_RBR) == 0x80) {
    //
    // No FIFO: standard 8250/16450
    //
    UartWriteRegister (Private, R_UART_FCR, SavedFcr);
    Private->DeviceRemoved = TRUE;
    return TRUE;
  }

  //
  // Restore FCR to enabled+cleared state for further probing
  //
  UartWriteRegister (Private, R_UART_FCR, FCR_FIFO_ENABLE |
                                           FCR_CLEAR_RX    |
                                           FCR_CLEAR_TX);

  //
  // Optional: perform DTR/RTS loopback test to verify UART is responsive
  // This is controlled by byte_3FA1 (a global flag, possibly setup-dependent)
  //
  if (mSomeLoopbackEnableFlag) {
    UartWriteRegister (Private, R_UART_FCR, FCR_FIFO_ENABLE |
                                             FCR_CLEAR_RX    |
                                             FCR_CLEAR_TX);
    gBS->Stall (5000);

    //
    // Flush remaining data
    //
    while ((UartReadRegister (Private, R_UART_LSR) & LSR_DR) != 0) {
      UartReadRegister (Private, R_UART_RBR);
    }

    //
    // Perform DTR/RTS loopback test:
    //   - Save MCR, toggle DTR, read MSR
    //   - Toggle DTR again, check CTS/DSR in MSR

    {
      UINT8  SavedMcr = UartReadRegister (Private, R_UART_MCR);
      UINT8  NextMcr  = UartReadRegister (Private, R_UART_MCR);

      gBS->Stall (2000);

      //
      // Toggle DTR bit
      //
      if ((NextMcr & MCR_DTR) != 0) {
        NextMcr &= ~MCR_DTR;
      } else {
        NextMcr |= MCR_DTR;
      }
      UartWriteRegister (Private, R_UART_MCR, NextMcr);

      gBS->Stall (5000);

      Result = UartReadRegister (Private, R_UART_LSR);

      //
      // Restore MCR
      //
      UartWriteRegister (Private, R_UART_FCR, SavedFcr);
      UartWriteRegister (Private, R_UART_MCR, SavedMcr);

      if ((Result & LSR_DR) != 0) {
        Private->DeviceRemoved = TRUE;
        return TRUE;
      }
    }
  }

  return FALSE;
}

//
// ---------------------------- FIFO Mode Configuration ---------------------------
//

/**
  Configure UART FIFO mode based on hardware type and desired FIFO depth.

  @param[in] Private            Serial I/O private context.
  @param[in] ReceiveFifoDepth   Desired FIFO trigger depth (1, 16, 64, or 0=default).

  @return The FCR register value written.

**/
UINT8
UartSetFifoMode (
  IN SERIAL_IO_PRIVATE  *Private,
  IN UINT32             ReceiveFifoDepth,
  IN UINT64             BaudRate  // unused, kept for compatibility
  )
{
  INT32 HwType;

  HwType = Private->HardwareType;

  if (HwType < HW_TYPE_16550A || ReceiveFifoDepth == 1) {
    //
    // 8250/16450 (no FIFO) or FIFO forced off: disable FIFOs
    //
    return UartWriteRegister (Private, R_UART_FCR, 0xFE);
  }

  if (HwType >= HW_TYPE_16750 && ReceiveFifoDepth == 64) {
    //
    // 16750 with 64-byte FIFO: enable FIFO with trigger level 14
    //
    UartWriteRegister (Private, R_UART_FCR, FCR_FIFO_ENABLE |
                                            FCR_CLEAR_RX    |
                                            FCR_CLEAR_TX    |
                                            FCR_TRIGGER_14);
    Private->ReceiveFifoDepth = 64;
  } else {
    //
    // 16550A / standard: 16-byte FIFO with trigger level 1
    //
    UartWriteRegister (Private, R_UART_FCR, FCR_FIFO_ENABLE |
                                            FCR_CLEAR_RX    |
                                            FCR_CLEAR_TX    |
                                            FCR_TRIGGER_1);
    Private->ReceiveFifoDepth = 16;
  }

  return Private->ReceiveFifoDepth;
}

//
// ---------------------------- EFI Serial I/O Protocol ----------------------------
//

/**
  Reset the serial device.

  @param[in] This  Pointer to the EFI_SERIAL_IO_PROTOCOL instance.

  @retval EFI_SUCCESS       The device was reset.
  @retval Others            An error occurred.

**/
EFI_STATUS
EFIAPI
SerialIoReset (
  IN SERIAL_IO_PRIVATE  *Private
  )
{
  UINT8       Lcr;
  UINT8       Mcr;
  UART_CONFIG *UartCfg;

  //
  // Reset UART: clear DLAB, disable break, set LCR to default
  //
  Lcr = UartReadRegister (Private, R_UART_LCR);
  UartWriteRegister (Private, R_UART_LCR, Lcr & 0x7F);

  Mcr = UartReadRegister (Private, R_UART_MCR);
  UartWriteRegister (Private, R_UART_MCR, Mcr & 0xF0);

  //
  // Reset FIFOs
  //
  UartWriteRegister (Private, R_UART_FCR, FCR_FIFO_ENABLE |
                                           FCR_CLEAR_RX    |
                                           FCR_CLEAR_TX);

  //
  // Reconfigure FIFO with default depth (64 if 16750, else 16)
  //
  UartSetFifoMode (Private, 0x40, 0);

  //
  // Clear MCR loopback bits
  //
  Mcr = UartReadRegister (Private, R_UART_MCR);
  UartWriteRegister (Private, R_UART_MCR, Mcr & 0xE3);

  //
  // Reset scratch register
  //
  UartWriteRegister (Private, R_UART_SCR, 0);

  //
  // Restore serial attributes
  //
  UartCfg = Private->UartConfig;

  if (Private->SetAttributes (
        Private,
        UartCfg->BaudRate,
        UartCfg->ReceiveFifoDepth,
        UartCfg->Timeout,
        UartCfg->Parity,
        (UINT8)UartCfg->DataBits,
        UartCfg->StopBits) < 0 ||
      Private->SetControlBits (
        Private,
        *(UINT32 *)Private->UartConfig) < 0) {
    return EFI_DEVICE_ERROR;
  }

  //
  // Reset SW FIFO and state
  //
  Private->TransmitTimeouts = 0;
  Private->FifoReadIndex    = 0;
  Private->FifoWriteIndex   = 0;
  Private->FifoCount        = 0;
  Private->HardwareFlowCtrl = 0;

  ZeroMem (Private->FifoBuffer, sizeof (Private->FifoBuffer));

  //
  // Enable FIFOs
  //
  Mcr = UartReadRegister (Private, R_UART_MCR);
  UartWriteRegister (Private, R_UART_MCR, Mcr | MCR_OUT2);

  //
  // Flush RBR (read to clear)
  //
  UartReadRegister (Private, R_UART_RBR);

  return EFI_SUCCESS;
}

/**
  Set the serial device attributes (baud, parity, data bits, stop bits, etc.).

  @param[in] Private           Serial I/O private context.
  @param[in] BaudRate          Baud rate to set (0=keep current, uses 115200 default).
  @param[in] ReceiveFifoDepth  FIFO depth (0=auto detect HW capability).
  @param[in] Timeout           Timeout in microseconds (0=keep current, uses 1000000 default).
  @param[in] Parity            Parity type.
  @param[in] DataBits          Number of data bits (5-8).
  @param[in] StopBits          Number of stop bits.

  @retval EFI_SUCCESS           The attributes were set.
  @retval EFI_INVALID_PARAMETER An unsupported attribute was requested.

**/
EFI_STATUS
EFIAPI
SerialIoSetAttributes (
  IN SERIAL_IO_PRIVATE    *Private,
  IN UINT64               BaudRate,
  IN UINT32               ReceiveFifoDepth,
  IN INT32                Timeout,
  IN EFI_PARITY_TYPE      Parity,
  IN UINT8                DataBits,
  IN EFI_STOP_BITS_TYPE   StopBits
  )
{
  UINT64      Clock;
  UINT64      Divisor;
  UINT8       Lcr;
  UINT8       NewLcr;
  UINT8       BaudIndex;
  UINT32      FifoDepth;
  INT32       TimeoutVal;
  UINT32      TimeoutClamped;
  EFI_STATUS  Status;

  Clock = Private->ClockFrequency;

  //
  // Apply defaults for zero-valued parameters
  //
  if (BaudRate == 0) {
    BaudRate = DEFAULT_BAUD_RATE;
  }
  if (ReceiveFifoDepth == 0) {
    if (Private->HardwareType >= HW_TYPE_16750) {
      ReceiveFifoDepth = 64;
    } else {
      ReceiveFifoDepth = 16;
    }
  }
  if (Timeout == 0) {
    Timeout = DEFAULT_TIMEOUT;
  }
  if (Parity == 0) {
    Parity = 1;  // DefaultNoParity
  }
  if (DataBits == 0) {
    DataBits = DEFAULT_DATA_BITS;
  }
  if (StopBits == 0) {
    StopBits = 1;  // DefaultStopBits = 1
  }

  //
  // Validate: 16750 with 5 data bits is invalid
  //
  if (Private->HardwareType == HW_TYPE_16750 && DataBits < 7) {
    return EFI_INVALID_PARAMETER;
  }
  if (BaudRate < 50) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Clamp baud rate to nearest valid table entry
  //
  for (BaudIndex = 0; BaudIndex < 19 - 1; BaudIndex++) {
    if (BaudRate < mBaudRateTable[BaudIndex + 1]) {
      BaudRate = mBaudRateTable[BaudIndex];
      break;
    }
  }
  if (BaudRate >= 0x1C200) {
    BaudRate = DEFAULT_BAUD_RATE;
  }

  //
  // Validate all parameters against range limits
  //
  if (ReceiveFifoDepth - 1 > 63 ||
      (UINT32)(Timeout - 1) > 0x5F5E0FF ||
      (UINT32)(Parity - 1) > 4 ||
      (UINT8)(DataBits - 5) > 3 ||
      (UINT32)(StopBits - 1) > 2 ||
      (DataBits >= 6 && StopBits == 2)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check if already configured at these settings
  //
  if (Private->BaudRate         == BaudRate &&
      Private->DataBits         == DataBits &&
      Private->Parity           == Parity &&
      Private->StopBits         == StopBits) {
    UART_CONFIG *Cfg = Private->UartConfig;
    if (Cfg->ReceiveFifoDepth == ReceiveFifoDepth &&
        Cfg->Timeout          == Timeout) {
      return EFI_SUCCESS;
    }
  }

  //
  // Update FIFO depth in hardware if changed
  //
  if (Private->UartConfig->ReceiveFifoDepth != ReceiveFifoDepth) {
    UartSetFifoMode (Private, ReceiveFifoDepth, DEFAULT_BAUD_RATE);
  }

  //
  // Calculate baud rate divisor
  //
  Divisor = Clock / (16 * BaudRate);
  if (Clock % (16 * BaudRate) != 0) {
    Divisor++;
  }
  if (Divisor == 0 || (Divisor & 0xFFFF0000) != 0) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Set DLAB (Divisor Latch Access Bit) and wait for THRE+TSRE
  //
  Lcr = UartReadRegister (Private, R_UART_LCR) | LCR_DLAB;
  while ((UartReadRegister (Private, R_UART_LSR) &
          (LSR_THRE | LSR_TEMT)) != (LSR_THRE | LSR_TEMT));

  //
  // Write divisor
  //
  UartWriteRegister (Private, R_UART_LCR, Lcr);
  UartWriteRegister (Private, R_UART_DLL, Divisor & 0xFF);
  UartWriteRegister (Private, R_UART_DLM, (Divisor >> 8) & 0xFF);
  NewLcr = Lcr & 0x7F;

  //
  // Set line control (parity, stop bits, data bits)
  //
  switch (Parity) {
  case 1:  // NoParity
    NewLcr &= ~(LCR_PEN | LCR_EPS | LCR_SP);
    break;
  case 2:  // EvenParity
    NewLcr = (NewLcr & ~(LCR_PEN | LCR_EPS | LCR_SP)) | (LCR_PEN | LCR_EPS);
    break;
  case 3:  // OddParity
    NewLcr = (NewLcr & ~(LCR_EPS | LCR_SP)) | LCR_PEN;
    break;
  case 4:  // MarkParity / SpaceParity
    NewLcr = (NewLcr & ~LCR_EPS) | LCR_PEN | LCR_SP;
    break;
  case 5:  // SpaceParity (Mark=4, Space=5 in EFI)
    NewLcr = (NewLcr & ~LCR_SP) | LCR_PEN | LCR_EPS;
    break;
  }

  if (StopBits == 1) {
    NewLcr &= ~LCR_STB;
  } else if (StopBits == 2 || StopBits == 3) {
    NewLcr |= LCR_STB;
  }

  //
  // Set word length (5, 6, 7, 8 -> 0, 1, 2, 3)
  //
  NewLcr = (NewLcr & ~(LCR_WLS0 | LCR_WLS1)) | (DataBits - 5);

  UartWriteRegister (Private, R_UART_LCR, NewLcr);

  //
  // Update stored configuration
  //
  UartConfig->BaudRate         = (UINT64)BaudRate;
  UartConfig->ReceiveFifoDepth = ReceiveFifoDepth;
  UartConfig->Timeout          = Timeout;
  UartConfig->Parity           = Parity;
  UartConfig->DataBits         = DataBits;
  UartConfig->StopBits         = StopBits;

  //
  // Notify parent via re-propagated device path if attributes changed
  //
  if (Private->BaudRate   != BaudRate ||
      Private->DataBits   != DataBits ||
      Private->Parity     != Parity ||
      Private->StopBits   != StopBits) {
    //
    // Duplicate device path with new attributes and re-install
    //
    VOID *OldDevPath;
    VOID *NewDevPath;
    VOID *NewUartDevPath;

    OldDevPath = Private->ParentDevicePath;
    Private->BaudRate = BaudRate;
    Private->DataBits = DataBits;
    Private->Parity   = Parity;
    Private->StopBits = StopBits;

    NewDevPath     = HobDuplicateDevicePath (OldDevPath, Private->UartDevicePath);
    NewUartDevPath = HobDuplicateDevicePath (NewDevPath, Private->UartDevicePath + 19);

    if (NewUartDevPath == NULL) {
      return EFI_DEVICE_ERROR;
    }

    if (Private->ChildHandle != NULL) {
      Status = gBS->OpenProtocol (
                      Private->ChildHandle,
                      &gEfiDevicePathProtocolGuid,
                      &NewUartDevPath,
                      Private->ParentHandle,
                      Private->ChildHandle,
                      EFI_OPEN_PROTOCOL_BY_DRIVER
                      );
      if (EFI_ERROR (Status)) {
        return Status;
      }
    }

    //
    // Free old if present
    //
    if (Private->ParentDevicePath != NULL) {
      gBS->FreePool (Private->ParentDevicePath);
    }
    Private->ParentDevicePath = NewUartDevPath;
  }

  return EFI_SUCCESS;
}

/**
  Set control bits on the serial device.

  @param[in] This     Pointer to the SERIAL_IO_PRIVATE instance.
  @param[in] Control  Control bit mask (SERIAL_CTRL_* values).

  @retval EFI_SUCCESS           The control bits were set.
  @retval EFI_INVALID_PARAMETER Invalid control bits were requested.

**/
EFI_STATUS
EFIAPI
SerialIoSetControlBits (
  IN SERIAL_IO_PRIVATE  *Private,
  IN UINT32             Control
  )
{
  UINT8 Mcr;

  //
  // Validate: only DTR (1), RTS (2), and bits 0x1000/0x2000/0x4000 are valid
  //
  if ((Control & 0xFFFF8FFC) != 0) {
    return EFI_INVALID_PARAMETER;
  }

  Mcr = UartReadRegister (Private, R_UART_MCR) & 0xEC;

  //
  // Set DTR (MCR bit 0)
  //
  Mcr = (Mcr & ~MCR_DTR) | ((Control & SERIAL_CTRL_DTR) ? MCR_DTR : 0);

  //
  // Set RTS (MCR bit 1)
  //
  Mcr = (Mcr & ~MCR_RTS) | ((Control & SERIAL_CTRL_RTS) ? MCR_RTS : 0);

  //
  // Set Loopback (MCR bit 4) -> mapped to SERIAL_CTRL_REQUEST_TO_SEND bit 12 (0x1000)
  //
  Mcr = (Mcr & ~MCR_LOOP) | ((Control & 0x1000) ? MCR_LOOP : 0);

  UartWriteRegister (Private, R_UART_MCR, Mcr);

  //
  // Update software control bits
  //
  Private->ControlBits = 0;
  if ((Control & SERIAL_CTRL_HARDWARE_RESET) != 0) {
    Private->ControlBits |= SERIAL_CTRL_HARDWARE_RESET;
  }
  if ((Control & SERIAL_CTRL_REQUEST_TO_SEND) != 0) {
    Private->ControlBits |= SERIAL_CTRL_REQUEST_TO_SEND;
  } else if ((Control & SERIAL_CTRL_DATA_TERMINAL_READY) != 0) {
    Private->ControlBits |= SERIAL_CTRL_DATA_TERMINAL_READY;
  }

  return EFI_SUCCESS;
}

/**
  Get control bits from the serial device.

  @param[in]  Private  Serial I/O private context.
  @param[out] Control  Receives the control bit mask.

  @retval EFI_SUCCESS   The control bits were read.

**/
EFI_STATUS
EFIAPI
SerialIoGetControlBits (
  IN SERIAL_IO_PRIVATE  *Private,
  OUT UINT32            *Control
  )
{
  UINT8 Msr;
  UINT8 Mcr;
  UINT8 Lsr;

  *Control = 0;

  //
  // Read Modem Status Register
  //
  Msr = UartReadRegister (Private, R_UART_MSR);

  if ((Msr & MSR_CTS) != 0) *Control |= SERIAL_CTRL_CLEAR_TO_SEND;
  if ((Msr & MSR_DSR) != 0) *Control |= SERIAL_CTRL_DATA_SET_READY;
  if ((Msr & MSR_RI)  != 0) *Control |= SERIAL_CTRL_RING_INDICATE;
  if ((Msr & MSR_DCD) != 0) *Control |= SERIAL_CTRL_CARRIER_DETECT;

  //
  // Read Modem Control Register
  //
  Mcr = UartReadRegister (Private, R_UART_MCR);

  if ((Mcr & MCR_DTR)  != 0) *Control |= SERIAL_CTRL_DTR;
  if ((Mcr & MCR_RTS)  != 0) *Control |= SERIAL_CTRL_RTS;
  if ((Mcr & MCR_LOOP) != 0) *Control |= 0x1000;

  //
  // Add software-controlled hardware reset bit
  //
  if ((Private->ControlBits & SERIAL_CTRL_HARDWARE_RESET) != 0) {
    *Control |= SERIAL_CTRL_HARDWARE_RESET;
  }

  //
  // Read Line Status Register for error conditions
  //
  Lsr = UartReadRegister (Private, R_UART_LSR);

  if ((Lsr & LSR_OE) != 0) {
    Private->OverrunErrored = TRUE;
  }
  if ((Lsr & LSR_TEMT) == 0) {
    *Control |= SERIAL_CTRL_CLEAR_TO_SEND;  // Not exactly - indicates TX in progress
  }

  if ((Private->ControlBits & SERIAL_CTRL_DATA_TERMINAL_READY) != 0) {
    *Control |= SERIAL_CTRL_DATA_TERMINAL_READY;
  }

  return EFI_SUCCESS;
}

/**
  Write data to the serial device.

  @param[in]      Private    Serial I/O private context.
  @param[in out]  BufferSize On input, size of buffer; on output, bytes written.
  @param[in]      Buffer     Data to write.

  @retval EFI_SUCCESS           Data was written successfully.
  @retval EFI_DEVICE_ERROR      Device error.
  @retval EFI_TIMEOUT           Write timed out.
  @retval EFI_INVALID_PARAMETER BufferSize is NULL.

**/
EFI_STATUS
EFIAPI
SerialIoWrite (
  IN SERIAL_IO_PRIVATE  *Private,
  IN OUT UINT64         *BufferSize,
  IN VOID               *Buffer
  )
{
  EFI_STATUS  Status;
  UINT64      Index;
  UINT64      Written;
  BOOLEAN     SoftwareLoop;
  UINT64      LoopCount;
  BOOLEAN     HardwareFlowControl;
  BOOLEAN     AutoRts;
  UINT8       Lsr;
  UINT8       Mcr;
  UINT64      FifoSize;

  Status            = EFI_SUCCESS;
  Index             = 0;
  Written           = 0;
  LoopCount         = 0;
  SoftwareLoop      = FALSE;
  HardwareFlowControl = FALSE;
  AutoRts           = (Private->ControlBits & SERIAL_CTRL_REQUEST_TO_SEND) != 0;

  if (*BufferSize == 0) {
    return EFI_SUCCESS;
  }

  if (Buffer == NULL) {
    return EFI_DEVICE_ERROR;
  }

  if (Private->DeviceRemoved) {
    *BufferSize = 0;
    return EFI_DEVICE_ERROR;
  }

  if (Private->WriteRetries >= MAX_WRITE_RETRIES ||
      Private->TransmitBlocked) {
    //
    // Too many retries or CTS lost -- arm timer and return error
    //
    *BufferSize = 0;
    if (!Private->TimerArmed) {
      gBS->SetTimer (Private->TimerEvent, TimerPeriodic, TIMER_CANCEL_PERIOD);
      Private->TimerArmed = TRUE;
    }
    return EFI_DEVICE_ERROR;
  }

  if (AutoRts && *BufferSize > 16) {
    //
    // When RTS/CTS is active, limit writes to 16 bytes per call
    //
    *BufferSize = 16;
    Status = EFI_TIMEOUT;
  }

  if (!Private->InsideIo) {
    Private->InsideIo = TRUE;

    if ((Private->ControlBits & SERIAL_CTRL_DATA_TERMINAL_READY) == 0) {
      //
      // No flow control: use SW FIFO or direct write
      //
      if (*BufferSize > 0) {
        UINT32 *FifoDepth;

        FifoDepth = &Private->UartConfig->ReceiveFifoDepth;

        //
        // Fill SW FIFO if space available
        //
        while (Private->FifoCount < *FifoDepth) {
          Private->FifoBuffer[Private->FifoWriteIndex] = *(UINT8 *)(Index + (UINT64)Buffer);
          Private->FifoWriteIndex++;
          Private->FifoCount++;
          Index++;
          if (Private->FifoWriteIndex >= *FifoDepth) {
            Private->FifoWriteIndex = 0;
          }
          if (Index >= *BufferSize) {
            break;
          }
        }

        if (Index < *BufferSize) {
          *BufferSize = Index;
          Status = EFI_TIMEOUT;
        } else {
          Status = EFI_SUCCESS;
        }
      }
    } else if ((Private->ControlBits & SERIAL_CTRL_HARDWARE_RESET) != 0 &&
               Private->HardwareFlowCtrl) {
      //
      // Hardware flow control: poll for CTS before sending
      //
      do {
        Lsr = UartReadRegister (Private, R_UART_LSR);
        if ((Lsr & LSR_OE) != 0) {
          Private->OverrunErrored = TRUE;
        }
      } while ((Lsr & LSR_DR) == 0);

      if (UartReadRegister (Private, R_UART_RBR) == 0x11) {
        //
        // XOFF character received
        //
        Private->HardwareFlowCtrl = FALSE;
      }

      *BufferSize = 0;

    } else {
      //
      // Standard write (no flow control or RTS)
      //
      if ((Private->ControlBits & SERIAL_CTRL_HARDWARE_RESET) != 0 &&
          !Private->TransmitBlocked) {
        //
        // Assert RTS
        //
        SoftwareLoop = TRUE;
        Mcr = UartReadRegister (Private, R_UART_MCR);
        UartWriteRegister (Private, R_UART_MCR, Mcr | MCR_RTS);

        //
        // Wait for CTS (MSR bit 4)
        //
        while ((UartReadRegister (Private, R_UART_MSR) & MSR_CTS) == 0) {
          gBS->Stall (1);
          LoopCount++;
          if (LoopCount == XMIT_LOOP_LIMIT) {
            Private->TransmitTimeouts++;
            if (Private->TransmitTimeouts == 5) {
              Private->TransmitBlocked = TRUE;
              Status = EFI_DEVICE_ERROR;
              goto Done;
            }
            LoopCount = 0;
          }
        }
        LoopCount = 0;
      }

      //
      // Write loop
      //
      if (*BufferSize > 0) {
        while (Index < *BufferSize) {
          //
          // Wait for THR empty (LSR bit 5)
          //
          do {
            gBS->Stall (1);
            LoopCount++;
            if (LoopCount == XMIT_LOOP_LIMIT) {
              //
              // Timeout: return bytes written so far
              //
              *BufferSize = Written;
              Private->WriteRetries++;
              Status = EFI_TIMEOUT;
              goto Done;
            }
            Lsr = UartReadRegister (Private, R_UART_LSR);
            if ((Lsr & LSR_OE) != 0) {
              Private->OverrunErrored = TRUE;
            }
          } while ((Lsr & LSR_THRE) == 0);

          LoopCount = 0;
          Private->WriteRetries = 0;

          //
          // Write byte
          //
          UartWriteRegister (Private, R_UART_THR,
                             *(UINT8 *)((UINT64)Buffer + Index));
          Index++;
          Written++;

          if (AutoRts) {
            break;
          }
        }
      }

      //
      // De-assert RTS
      //
      Mcr = UartReadRegister (Private, R_UART_MCR);
      UartWriteRegister (Private, R_UART_MCR, (Mcr & ~MCR_RTS) | MCR_DTR);
      gBS->Stall (1);

      //
      // Re-assert RTS and DTR for next transfer
      //
      Mcr = UartReadRegister (Private, R_UART_MCR);
      UartWriteRegister (Private, R_UART_MCR, Mcr | (MCR_RTS | MCR_DTR));
    }

    *BufferSize = Written;

  Done:
    //
    // Update status from partial transfer
    //
    if (AutoRts && Status == EFI_TIMEOUT) {
      if (Written > 0) {
        Status = (Status & -(INT64)TRUE);  // preserve EFI_TIMEOUT but with data written
      }
    }

    Private->InsideIo = FALSE;
  }

  return Status;
}

/**
  Read data from the serial device.

  @param[in]      Private    Serial I/O private context.
  @param[in out]  BufferSize On input, size of buffer; on output, bytes read.
  @param[out]     Buffer     Data read.

  @retval EFI_SUCCESS           Data was read successfully.
  @retval EFI_DEVICE_ERROR      Device error.
  @retval EFI_TIMEOUT           No data available.
  @retval EFI_INVALID_PARAMETER BufferSize is NULL.

**/
EFI_STATUS
EFIAPI
SerialIoRead (
  IN SERIAL_IO_PRIVATE  *Private,
  IN OUT UINT64         *BufferSize,
  OUT VOID              *Buffer
  )
{
  UINT8       Lsr;
  UINT8       Data;
  EFI_STATUS  Status;
  UINT64      Index;

  if (*BufferSize == 0) {
    return EFI_SUCCESS;
  }

  if (Buffer == NULL) {
    return EFI_DEVICE_ERROR;
  }

  if (Private->InsideIo) {
    *BufferSize = 0;
    return EFI_TIMEOUT;
  }

  if (Private->WriteRetries >= MAX_WRITE_RETRIES ||
      Private->TransmitBlocked) {
    *BufferSize = 0;
    if (!Private->TimerArmed) {
      gBS->SetTimer (Private->TimerEvent, TimerPeriodic, TIMER_CANCEL_PERIOD);
      Private->TimerArmed = TRUE;
    }
    return EFI_DEVICE_ERROR;
  }

  Private->InsideIo = TRUE;

  if ((Private->ControlBits & SERIAL_CTRL_DATA_TERMINAL_READY) == 0) {
    //
    // SW FIFO mode: read from SW FIFO buffer
    //
    if (*BufferSize > 0) {
      Index = 0;
      while (Private->FifoCount > 0) {
        Data = Private->FifoBuffer[Private->FifoReadIndex];
        Private->FifoReadIndex++;
        Private->FifoCount--;
        if (Private->FifoReadIndex >=
            Private->UartConfig->ReceiveFifoDepth) {
          Private->FifoReadIndex = 0;
        }
        *(UINT8 *)((UINT64)Buffer + Index) = Data;
        Index++;
        if (Index >= *BufferSize) {
          break;
        }
      }
      if (Index > 0) {
        *BufferSize = Index;
        Status = EFI_SUCCESS;
      } else {
        *BufferSize = Index;
        Status = EFI_TIMEOUT;
      }
      goto Done;
    }
    Status = EFI_SUCCESS;
    goto Done;
  }

  if (Private->OverrunErrored) {
    //
    // Flush the FIFO after an overrun
    //
    {
      UINT32 Flush;
      for (Flush = 0; Flush < Private->UartConfig->ReceiveFifoDepth; Flush++) {
        UartReadRegister (Private, R_UART_RBR);
      }
    }
    Private->OverrunErrored = FALSE;
    *BufferSize = 0;
    goto Done;
  }

  //
  // Direct read from UART: poll for data
  //
  {
    UINT64 MaxRead;

    MaxRead = *BufferSize;
    Index   = 0;

    while (Index < MaxRead) {
      Lsr = UartReadRegister (Private, R_UART_LSR);

      if ((Lsr & LSR_OE) != 0) {
        Private->OverrunErrored = TRUE;
        //
        // Drain FIFO on overrun
        //
        for ( ; Index < *BufferSize; Index++) {
          UartReadRegister (Private, R_UART_RBR);
        }
        *BufferSize = 0;
        Status = EFI_DEVICE_ERROR;
        goto Done;
      }

      if ((Lsr & LSR_DR) == 0) {
        //
        // No more data available now
        //
        break;
      }

      Data = UartReadRegister (Private, R_UART_RBR);
      *(UINT8 *)((UINT64)Buffer + Index) = Data;
      Index++;

      if (Index >= *BufferSize) {
        Status = EFI_SUCCESS;
        goto Done;
      }
    }

    *BufferSize = Index;
    Status = (Index != MaxRead) ? EFI_TIMEOUT : EFI_SUCCESS;
  }

Done:
  Private->InsideIo = FALSE;
  return Status;
}

/**
  Timer callback for transmit ready monitoring.

  When the timer fires during a blocked transmit state, this function
  checks the UART status (LSR for XMIT ready, MSR for CTS) and if the
  condition has cleared, cancels the timer to unblock writes/reads.

  @param[in] Event    The timer event.
  @param[in] Context  Serial I/O private context pointer.

**/
VOID
EFIAPI
SerialIoTransmitReadyTimer (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  SERIAL_IO_PRIVATE *Private;

  Private = (SERIAL_IO_PRIVATE *)Context;

  if (Private != NULL) {
    if (Private->TransmitBlocked) {
      //
      // Check for CTS recovery
      //
      if ((UartReadRegister (Private, R_UART_MSR) & MSR_CTS) != 0) {
        Private->TransmitBlocked = FALSE;
        gBS->SetTimer (Private->TimerEvent, TimerCancel, 0);
        Private->TimerArmed = FALSE;
      }
    } else {
      //
      // Check for THRE recovery
      //
      if ((UartReadRegister (Private, R_UART_LSR) & LSR_THRE) != 0) {
        Private->WriteRetries = 0;
        gBS->SetTimer (Private->TimerEvent, TimerCancel, 0);
        Private->TimerArmed = FALSE;
      }
    }
  }
}

//
// ============================= UART Detection (sub_23D0) =============================
//

//
// Global flag controlling extended UART detection (at 0x3FA1)
//
extern UINT8  mExtendedUartDetection;

//
// Perform UART presence and loopback detection.
//
// Returns TRUE if UART appears absent/removed, FALSE if it seems present.
//
UINT8
SerialIoDetectUartEnhanced (
  IN SERIAL_IO_PRIVATE  *Private
  )
{
  UINT8 SavedFcr;
  UINT8 Lsr;
  UINT8 Msr1;
  UINT8 Msr2;
  UINT8 McrToggled;

  if (Private->Magic != SERIAL_IO_MAGIC) {
    return FALSE;
  }

  SavedFcr = UartReadRegister (Private, R_UART_FCR);

  //
  // Enable FIFOs, clear them
  //
  UartWriteRegister (Private, R_UART_FCR, FCR_FIFO_ENABLE |
                                           FCR_CLEAR_RX    |
                                           FCR_CLEAR_TX);

  if (UartReadRegister (Private, R_UART_IIR) == 0xFF) {
    //
    // No UART: return TRUE = removed
    //
    UartWriteRegister (Private, R_UART_FCR, SavedFcr);
    goto DeviceRemoved;
  }

  gBS->Stall (2000);

  //
  // Flush RBR
  //
  while ((UartReadRegister (Private, R_UART_LSR) & LSR_DR) != 0) {
    UartReadRegister (Private, R_UART_RBR);
  }

  //
  // 8250 detection: write 0x80, then 0x08, 0x20, 0x08
  //
  UartWriteRegister (Private, R_UART_THR, 0x80);
  UartWriteRegister (Private, R_UART_THR, 0x08);
  UartWriteRegister (Private, R_UART_THR, 0x20);
  UartWriteRegister (Private, R_UART_THR, 0x08);

  gBS->Stall (50000);

  if ((UartReadRegister (Private, R_UART_LSR) & LSR_DR) != 0 &&
       UartReadRegister (Private, R_UART_RBR) == 0x80) {
    //
    // 8250 without FIFO
    //
    goto DeviceRemoved;
  }

  UartWriteRegister (Private, R_UART_FCR, FCR_FIFO_ENABLE |
                                           FCR_CLEAR_RX    |
                                           FCR_CLEAR_TX);

  //
  // Extended test: toggle DTR and check MSR CTS change
  //
  if (mExtendedUartDetection) {
    UartWriteRegister (Private, R_UART_FCR, FCR_FIFO_ENABLE |
                                             FCR_CLEAR_RX    |
                                             FCR_CLEAR_TX);
    gBS->Stall (5000);

    while ((UartReadRegister (Private, R_UART_LSR) & LSR_DR) != 0) {
      UartReadRegister (Private, R_UART_RBR);
    }

    Msr1 = UartReadRegister (Private, R_UART_MSR);
    Msr2 = UartReadRegister (Private, R_UART_MSR);

    gBS->Stall (2000);

    //
    // Toggle DTR in MCR
    //
    if ((Msr2 & MCR_DTR) != 0) {
      McrToggled = Msr2 & ~MCR_DTR;
    } else {
      McrToggled = Msr2 | MCR_DTR;
    }
    UartWriteRegister (Private, R_UART_MCR, McrToggled);

    gBS->Stall (5000);

    Lsr = UartReadRegister (Private, R_UART_LSR);

    //
    // Restore
    //
    UartWriteRegister (Private, R_UART_FCR, SavedFcr);
    UartWriteRegister (Private, R_UART_MCR, Msr1);

    if ((Lsr & LSR_DR) != 0) {
      goto DeviceRemoved;
    }
  }

  UartWriteRegister (Private, R_UART_FCR, SavedFcr);
  return FALSE;

DeviceRemoved:
  Private->DeviceRemoved = TRUE;
  return TRUE;
}

//
// ============================= Driver Binding Protocol =============================
//

//
// EFI Driver Binding Protocol instance (installed on image handle)
//
EFI_DRIVER_BINDING_PROTOCOL gSerialIoDriverBinding = {
  SERIAL_IO_DRIVER_REVISION,
  SerialIoDriverBindingSupported,
  SerialIoDriverBindingStart,
  SerialIoDriverBindingStop,
  SERIAL_IO_DRIVER_VERSION,
  NULL,
  NULL
};

//
// EFI Component Name 2 Protocol instance
//
EFI_COMPONENT_NAME2_PROTOCOL gSerialIoComponentName2 = {
  SerialIoComponentNameGetDriverName,
  SerialIoComponentNameGetControllerName,
  L"en-US"
};

//
// Supported languages for component name
//
GLOBAL_REMOVE_IF_UNREFERENCED CHAR8  *mSupportedLanguages[] = {
  "en-US"  // Add more languages as needed
};

/**
  Driver entry point.

  Installs the Driver Binding Protocol and Component Name 2 Protocol
  on the driver image handle.

  @param[in] ImageHandle  Driver image handle.
  @param[in] SystemTable  UEFI system table.

  @retval EFI_SUCCESS           Driver initialized.
  @retval EFI_INVALID_PARAMETER ImageHandle or SystemTable was NULL.
  @retval Others                Error from protocol installation.

**/
EFI_STATUS
EFIAPI
SerialIoDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  //
  // Install Driver Binding Protocol and Component Name 2 Protocol
  //
  Status = EfiLibInstallDriverBindingComponentName2 (
             ImageHandle,
             SystemTable,
             &gSerialIoDriverBinding,
             ImageHandle,
             &gSerialIoComponentName2,
             NULL
             );

  return Status;
}

/**
  Test whether the driver supports a given controller.

  @param[in] This                 Driver binding protocol.
  @param[in] ControllerHandle     Controller to test.
  @param[in] RemainingDevicePath  Optional device path.

  @retval EFI_SUCCESS             This driver supports the controller.
  @retval EFI_UNSUPPORTED         Controller not supported (or device path doesn't match).

**/
EFI_STATUS
EFIAPI
SerialIoDriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  EFI_STATUS                     Status;
  EFI_DEVICE_PATH_PROTOCOL       *DevicePath;
  EFI_DEVICE_PATH_PROTOCOL       *AcpiEnd;
  EFI_DEVICE_PATH_PROTOCOL       *ParentPath;
  EFI_PCI_IO_PROTOCOL            *PciIo;
  EFI_UART_IO_PROTOCOL           *UartIo;
  EFI_ISA_IO_PROTOCOL            *IsaIo;
  UINT8                          AcpiHid;
  UINT8                          AcpiUid;
  UINTN                          AcpiStartIndex;
  UINT64                         PciPciReadData;
  UINT32                         PciHwVendor;

  //
  // Case 1: Controller has ISA I/O protocol (legacy ISA/ACPI UART)
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiIsaIoProtocolGuid,
                  &IsaIo,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) {
    if (Status == EFI_SUCCESS) {
      gBS->CloseProtocol (
             ControllerHandle,
             &gEfiIsaIoProtocolGuid,
             This->DriverBindingHandle,
             ControllerHandle
             );
    }

    //
    // Check ACPI device path for UART
    //
    IsaIo->Device.Read (IsaIo, IoWidthUint8, R_UART_IIR, 1, &AcpiHid);
    IsaIo->Device.Read (IsaIo, IoWidthUint8, R_UART_LCR, 1, &AcpiUid);
    if (AcpiHid != 1) {
      return EFI_UNSUPPORTED;
    }
    if (mSomeLoopbackEnableFlag == 1) {
      return EFI_SUCCESS;
    }
    if (RemainingDevicePath != NULL) {
      if (*RemainingDevicePath->Type != 3 ||
          RemainingDevicePath->SubType != 14) {
        return EFI_UNSUPPORTED;
      }
    }
    return EFI_SUCCESS;
  }

  //
  // Case 2: UART IO protocol or ACPI device path
  //
  if (HobGetAcpiDevicePathEnd (This, ControllerHandle, &AcpiEnd, 16) >= 0 &&
      *(UINT32 *)(AcpiEnd + 4) == 0x50141D0) {
    //
    // ACPI device path end found: this is an ACPI UART
    // Check that it has type=3, subtype=14 (Serial I/O device path)
    //
    if (RemainingDevicePath != NULL &&
        (RemainingDevicePath->Type != 3 ||
         RemainingDevicePath->SubType != 14)) {
      return EFI_UNSUPPORTED;
    }
    return EFI_SUCCESS;
  }

  //
  // Case 3: PCI UART - check for PCI I/O protocol
  //
  if (mSomeLoopbackEnableFlag == 1) {
    Status = gBS->OpenProtocol (
                    ControllerHandle,
                    &gEfiPciIoProtocolGuid,
                    &PciIo,
                    This->DriverBindingHandle,
                    ControllerHandle,
                    EFI_OPEN_PROTOCOL_BY_DRIVER
                    );
    if (Status >= EFI_SUCCESS) {
      //
      // Close protocol since we only need it for Supported()
      //
      gBS->CloseProtocol (
             ControllerHandle,
             &gEfiPciIoProtocolGuid,
             This->DriverBindingHandle,
             ControllerHandle
             );
      return EFI_SUCCESS;
    }
  }

  //
  // Case 4: UART IO protocol on child UART device
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiUartIoProtocolGuid,
                  &UartIo,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (Status >= EFI_SUCCESS) {
    //
    // Check for UART mode
    //
    UartIo->GetMode (UartIo, &AcpiHid);
    if (AcpiHid == 1) {
      //
      // Probe for 512 or 256 byte FIFO via UART IO protocol
      // This is used for MMIO UART controllers
      //
      {
        BOOLEAN  ChannelFound;
        UINT8    Channel;

        Channel      = 0;
        ChannelFound = FALSE;

        while (TRUE) {
          UartIo->GetChannel (UartIo, Channel, &ChannelFound, &PciPciReadData);
          AcpiHid = *(UINT8 *)(PciPciReadData + 3);
          if (AcpiHid == 1) {
            break;
          }
          if (AcpiHid == 0) {
            //
            // Found 512-byte UART
            //
            goto Found512;
          }
          gBS->FreePool ((VOID *)PciPciReadData);
          Channel++;
          if (Channel >= MAX_UART_CHANNELS) {
            goto NoUartFound;
          }
        }

        //
        // 256-byte UART found
        //
Found512:
        ;
      }
    }

    gBS->CloseProtocol (
           ControllerHandle,
           &gEfiUartIoProtocolGuid,
           This->DriverBindingHandle,
           ControllerHandle
           );

NoUartFound:
    if (RemainingDevicePath != NULL) {
      if (RemainingDevicePath->Type != 3 ||
          RemainingDevicePath->SubType != 14) {
        return EFI_UNSUPPORTED;
      }
    }

    return EFI_SUCCESS;
  }

  //
  // Fallback: check UART IO protocol via child
  //
  if (mSomeLoopbackEnableFlag == 1) {
    Status = gBS->OpenProtocol (
                    ControllerHandle,
                    &gEfiDevicePathProtocolGuid,
                    &DevicePath,
                    This->DriverBindingHandle,
                    ControllerHandle,
                    EFI_OPEN_PROTOCOL_BY_DRIVER
                    );
    if (Status >= EFI_SUCCESS) {
      //
      // Walk device path looking for ACPI UART node
      //
      ParentPath = DevicePath;
      while (ParentPath->Type != 0x7F) {  // not END
        ParentPath = (EFI_DEVICE_PATH_PROTOCOL *)((UINTN)ParentPath +
                      DevicePathNodeLength (ParentPath));
      }

      if (ParentPath->Type == 2 && ParentPath->SubType == 1) {
        //
        // ACPI device path: check HID/UID
        //
        return EFI_SUCCESS;
      }

      gBS->FreePool (DevicePath);
    }
  }

  //
  // No supported controller found
  //
  if (Status == EFI_ALREADY_STARTED) {
    return Status;
  }
  return (Status == EFI_NOT_FOUND) ? EFI_UNSUPPORTED : Status;
}

/**
  Start the serial I/O driver on a controller.

  Allocates private context, detects UART, initializes registers,
  and installs EFI_SERIAL_IO_PROTOCOL on a new child handle.

  @param[in] This                 Driver binding protocol.
  @param[in] ControllerHandle     Controller to start.
  @param[in] RemainingDevicePath  Optional device path.

  @retval EFI_SUCCESS             Driver started.
  @retval EFI_OUT_OF_RESOURCES    Memory allocation failed.
  @retval EFI_DEVICE_ERROR        Hardware not responding.

**/
EFI_STATUS
EFIAPI
SerialIoDriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  EFI_STATUS                 Status;
  SERIAL_IO_PRIVATE          *Private;
  EFI_DEVICE_PATH_PROTOCOL   *DevicePath;
  EFI_PCI_IO_PROTOCOL        *PciIo;
  EFI_UART_IO_PROTOCOL       *UartIo;
  EFI_ISA_IO_PROTOCOL        *IsaIo;
  UINT8                      HwRevId;
  UINT8                      *UartMmioReg;
  UINT32                     PciVendorDevice;
  PCI_TYPE00                 PciConfig;
  UINT8                      PciBus;
  UINT8                      PciDevice;
  UINT8                      PciFunction;
  VOID                       *ChildDevicePath;
  VOID                       *SerialDevicePathGuid;
  VOID                       *AcpiRootEnd;
  UINT32                     AcpiHid;
  UINT8                      Channel;
  UINT64                     MmioBase;
  UINT16                     LegacyBase;
  UINT8                      IrqNumber;
  UINT8                      RegisterStride;
  INTN                       PciIndex;
  UINT16                     PciDeviceId;
  UINT16                     PciVendorId;

  //
  // GUID for serial I/O device path (0x00180A03-...)
  //
  EFI_GUID  SerialDevicePathGuid = EFI_SERIAL_IO_DEVICE_PATH_GUID;

  //
  // GUID for ACPI UART HID
  //
  EFI_GUID  AcpiUartHid = { 0x01031804, 0x9A9D, 0x3749,
                            { 0x2F, 0x54, 0x89, 0x4C, 0xA0, 0x26, 0x35, 0xDA } };

  Private = AllocateZeroPool (sizeof (SERIAL_IO_PRIVATE));
  if (Private == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Private->Magic = SERIAL_IO_MAGIC;
  Private->HardwareType = HW_TYPE_UNKNOWN;

  //
  // Probe for ISA I/O protocol first (legacy COM ports)
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiIsaIoProtocolGuid,
                  &IsaIo,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (Status >= EFI_SUCCESS) {
    //
    // ISA UART: get resources (I/O base, IRQ)
    //
    IsaIo->Device.Read (IsaIo, IoWidthUint8, R_UART_IIR, 1, &HwRevId);
    IsaIo->Device.Read (IsaIo, IoWidthUint8, R_UART_LCR, 1, &IrqNumber);
    IsaIo->Resource.Read (IsaIo, &IrqNumber, &LegacyBase, &MmioBase);

    Private->IsaAccess       = TRUE;
    Private->ControllerName  = mComPortNames[byte_3FA0++];  // "COM1", "COM2", etc.
    Private->ParentDevicePath = HobCopyDevicePath (&gSerialIoDevicePathGuid);
    Private->ParentDevicePath = HobDuplicateDevicePath (
                                  Private->ParentDevicePath,
                                  &AcpiUartHid
                                  );
    Private->ParentDevicePath = HobDuplicateDevicePath (
                                  Private->ParentDevicePath,
                                  &gSerialIoDevicePathGuid
                                  );
    goto HardwareInit;
  }

  //
  // Probe for device path + ACPI table (legacy UART enumeration)
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiDevicePathProtocolGuid,
                  &DevicePath,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (Status >= EFI_SUCCESS) {
    //
    // Get the UART IO protocol
    //
    Status = gBS->OpenProtocol (
                    ControllerHandle,
                    &gEfiUartIoProtocolGuid,
                    &UartIo,
                    This->DriverBindingHandle,
                    ControllerHandle,
                    EFI_OPEN_PROTOCOL_BY_DRIVER
                    );
    if (Status < EFI_SUCCESS) {
      goto CleanupDevicePath;
    }

    //
    // Query UART capabilities
    //
    UartIo->GetControl (UartIo, &RegisterStride);
    UartIo->GetBaudRate (UartIo, &PciDeviceId);

    Private->UartDevicePath = DevicePath;

    if (RegisterStride == 0) {
      //
      // MMIO UART with ISA I/O
      //
      Private->IsaAccess      = TRUE;
      Private->AccessMethod   = 0;
      Private->RegisterWidth  = 8;
      Private->ClockFrequency = PciDeviceId;

      if (HobGetAcpiDevicePathEnd (This, ControllerHandle, &AcpiRootEnd, 16) >= 0) {
        if (*(UINT32 *)(AcpiRootEnd + 4) == 0x50141D0) {
          //
          // ACPI enumerated UART
          //
          Private->ControllerName  = mComPortNames[byte_3FA0++];
          Private->ParentDevicePath = HobCopyDevicePath (&gSerialIoDevicePathGuid);
        } else {
          Private->ControllerName = L"COM (ACPI)";
          goto HardwareInit;
        }
      }
    }

    //
    // Enumerate PCI UARTs
    //
    if (UartIo->GetChildHandle != NULL) {
      Channel = 0;
      while (TRUE) {
        UartIo->GetChildHandle (UartIo, Channel, &HwRevId, &PciDeviceId);
        if (HwRevId == 1) {
          break;
        }
        if (HwRevId == 0) {
          // Channel found with FIFO type 0 (512 byte)
          break;
        }
        gBS->FreePool (&PciDeviceId);
        Channel++;
        if (Channel >= MAX_UART_CHANNELS) {
          goto NoPciUart;
        }
      }
    }

    {
      UINT8  FoundChannel;

      FoundChannel       = Channel;
      Private->PciBus    = FoundChannel;
      Private->AccessMethod = 1;  // MMIO
      Private->InsideIo  = FALSE;

      //
      // Open parent channel
      //
      UartIo->OpenChannelUart (UartIo, 2, 512, 0);  // 256 or 512 byte FIFO

      //
      // Walk device path to compute device path for child
      //
      DevicePath = DevicePath;
      while (DevicePath != NULL) {
        DevicePath = HobDuplicateDevicePath (DevicePath);
      }

      Private->UartDevicePath = DevicePath;
    }

    Private->ControllerName = SPrint (
                                AllocatePool (75),
                                0,
                                L"COM (Pci Dev%X, Func%X)",
                                Bus,
                                Device
                                );

    goto HardwareInit;

NoPciUart:
    if (HobGetAcpiDevicePathEnd (This, ControllerHandle, &AcpiRootEnd, 2) >= 0) {
      Private->ControllerName = mComPortNames[byte_3FA0++];
    } else {
      gBS->CloseProtocol (
             ControllerHandle,
             &gEfiUartIoProtocolGuid,
             This->DriverBindingHandle,
             ControllerHandle
             );
      goto CleanupDevicePath;
    }
  }

  //
  // Probe for ACPI device path (UART IO protocol from HOB)
  //
  if (HobGetAcpiDevicePathEnd (
        (__int64)&gSerialIoDriverBinding,
        ControllerHandle,
        &AcpiRootEnd,
        16) >= 0) {
    //
    // This may be an ACPI enumerated UART
    //
  }

  //
  // Open protocols
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiUartIoProtocolGuid,
                  &UartIo,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (Status >= EFI_SUCCESS) {
    UartIo->GetMode (UartIo, &HwRevId);
    if (HwRevId == 0) {
      //
      // No UART IO mode: try PCI I/O protocol
      //
      gBS->CloseProtocol (
             ControllerHandle,
             &gEfiUartIoProtocolGuid,
             This->DriverBindingHandle,
             ControllerHandle
             );

      Status = gBS->OpenProtocol (
                      ControllerHandle,
                      &gEfiPciIoProtocolGuid,
                      &PciIo,
                      This->DriverBindingHandle,
                      ControllerHandle,
                      EFI_OPEN_PROTOCOL_BY_DRIVER
                      );
      if (Status >= EFI_SUCCESS) {
        Private->PciIo = PciIo;
        if (PciIo->GetLocation (PciIo, &Private->PciBus, &PciDevice, &PciFunction) >= 0) {
          PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0, sizeof (PciConfig), &PciConfig);

          PciDeviceId = (UINT16)(PciConfig.Hdr.VendorId >> 16);  // actually DeviceId high
          PciVendorId = (UINT16)PciConfig.Hdr.VendorId;

          //
          // Look up clock frequency and register width in PCI UART table
          //
          if (gPciUartTableStart != (UINT16)-1) {
            PciIndex = 0;
            while (gPciUartTable[PciIndex * PCI_UART_ENTRY_SIZE].DeviceId != (UINT16)-1) {
              if (gPciUartTable[PciIndex].DeviceId == PciDeviceId &&
                  gPciUartTable[PciIndex].VendorId == PciVendorId) {
                Private->ClockFrequency = gPciUartTable[PciIndex].ClockRate;
                Private->RegisterWidth  = gPciUartTable[PciIndex].RegisterWidth;
                break;
              }
              PciIndex++;
            }
          }

          Private->ControllerName = SPrint (
                                      AllocatePool (50),
                                      0,
                                      L"COM (Pci Dev%X, Func%X)",
                                      PciDevice,
                                      PciFunction
                                      );
        }
      }
    }

    //
    // UART IO protocol available
    //
    Private->UartDevicePath = AllocateCopyPool (
                                sizeof (EFI_GUID),
                                &SerialDevicePathGuid
                                );
  }

  //
  // If no protocols found, fail
  //
  if (Private->PciIo == NULL && Private->BaseAddress == 0) {
    Status = EFI_UNSUPPORTED;
    goto CleanupAll;
  }

HardwareInit:
  //
  // Initialize UART hardware
  //

  //
  // Test: write scratch register with AA, read back
  //
  UartWriteRegister (Private, R_UART_SCR, 0xAA);
  if (UartReadRegister (Private, R_UART_SCR) != 0xAA ||
      SerialIoDetectUartEnhanced (Private)) {
    //
    // UART not present or not responding
    //
    goto CleanupAll;
  }

  //
  // Probe for FIFO type
  //
  UartWriteRegister (Private, R_UART_FCR, FCR_FIFO_ENABLE |
                                           FCR_CLEAR_RX    |
                                           FCR_CLEAR_TX    |
                                           FCR_TRIGGER_1);
  HwRevId = UartReadRegister (Private, R_UART_IIR) & 0xE0;

  switch (HwRevId) {
  case 0xE0:  // 16750 (64-byte FIFO)
    Private->HardwareType = HW_TYPE_16750;
    break;
  case 0xC0:  // 16550A (16-byte FIFO, rev A)
    Private->HardwareType = HW_TYPE_16550A;
    break;
  case 0x80:  // 16550A (16-byte FIFO, rev B)
    Private->HardwareType = HW_TYPE_16550A;
    break;
  default:    // 8250/16450 (no FIFO)
    Private->HardwareType = HW_TYPE_16550;
    break;
  }

  //
  // Set defaults for clock and register width if not yet configured
  //
  if (Private->ClockFrequency == 0) {
    Private->ClockFrequency = 1843200;  // Standard PC UART clock
  }
  if (Private->RegisterWidth == 0) {
    Private->RegisterWidth = 1;  // Byte access
  }

  //
  // Initialize state
  //
  Private->HardwareFlowCtrl = FALSE;
  Private->TransmitTimeouts = 0;
  Private->FifoReadIndex    = 0;
  Private->FifoWriteIndex   = 0;
  Private->FifoCount        = 0;

  ZeroMem (Private->FifoBuffer, sizeof (Private->FifoBuffer));

  //
  // Set up Serial I/O Protocol function table
  //
  Private->Reset           = SerialIoReset;
  Private->SetAttributes   = SerialIoSetAttributes;
  Private->SetControlBits  = SerialIoSetControlBits;
  Private->GetControlBits  = SerialIoGetControlBits;
  Private->Write           = SerialIoWrite;
  Private->Read            = SerialIoRead;
  Private->UartConfig      = (VOID *)&Private->ControlBits;  // Actually points to self

  Private->Revision = EFI_SERIAL_IO_PROTOCOL_REVISION;

  //
  // Initialize default serial attributes
  //
  Private->BaudRate  = DEFAULT_BAUD_RATE;
  Private->DataBits  = DEFAULT_DATA_BITS;
  Private->Parity    = DEFAULT_PARITY;
  Private->StopBits  = DEFAULT_STOP_BITS;

  //
  // Set default attributes on hardware
  //
  Status = Private->SetAttributes (Private, 0, 0, 0, 0, 0, 0);
  if (EFI_ERROR (Status)) {
    goto CleanupAll;
  }

  //
  // Copy device path attributes if present
  //
  if (RemainingDevicePath != NULL) {
    Private->BaudRate  = *((UINT64 *)RemainingDevicePath->Data + 1);
    Private->DataBits  = *(UINT8 *)(RemainingDevicePath->Data + 16);
    Private->Parity    = *(UINT8 *)(RemainingDevicePath->Data + 17);
    Private->StopBits  = *(UINT8 *)(RemainingDevicePath->Data + 18);
  }

  //
  // Build device path for child
  //
  ChildDevicePath = HobDuplicateDevicePath (
                      Private->ParentDevicePath,
                      &Private->UartDevicePath
                      );

  //
  // Install Serial I/O Protocol on new child handle
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &Private->ChildHandle,
                  &gEfiSerialIoProtocolGuid,
                  Private,
                  &gEfiDevicePathProtocolGuid,
                  Private->UartDevicePath,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    goto CleanupAll;
  }

  //
  // Open protocols (child handle)
  //
  if (Private->IsaAccess) {
    DevicePath = &gEfiIsaIoProtocolGuid;
  } else if (Private->PciIo != NULL) {
    DevicePath = &gEfiPciIoProtocolGuid;
  } else {
    DevicePath = &gEfiUartIoProtocolGuid;
  }

  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  DevicePath,
                  &Private->ParentHandle,
                  This->DriverBindingHandle,
                  Private->ChildHandle,
                  EFI_OPEN_PROTOCOL_BY_CHILD
                  );
  if (EFI_ERROR (Status)) {
    gBS->UninstallMultipleProtocolInterfaces (
           Private->ChildHandle,
           &gEfiSerialIoProtocolGuid,
           Private,
           &gEfiDevicePathProtocolGuid,
           Private->UartDevicePath,
           NULL
           );
    goto CleanupAll;
  }

  //
  *((UINT16 *)Private + 0x94) = 1;   // Flags: initialized

  //
  // Create timer for transmit ready monitoring
  //
  Status = gBS->CreateEvent (
                  EVT_TIMER | EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  SerialIoTransmitReadyTimer,
                  Private,
                  &Private->TimerEvent
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    //
    // Non-fatal: continue without timer
    //
  }

  Private->TimerArmed = FALSE;
  return EFI_SUCCESS;

CleanupAll:
  if (Private != NULL) {
    gBS->FreePool (Private);
  }
  return Status;

CleanupDevicePath:
  gBS->CloseProtocol (
         ControllerHandle,
         &gEfiDevicePathProtocolGuid,
         This->DriverBindingHandle,
         ControllerHandle
         );
  return EFI_UNSUPPORTED;
}

/**
  Stop the serial I/O driver on a controller.

  @param[in] This                 Driver binding protocol.
  @param[in] ControllerHandle     Controller to stop.
  @param[in] NumberOfChildren     Number of child handles.
  @param[in] ChildHandleBuffer    Array of child handles.

  @retval EFI_SUCCESS             Driver stopped.
  @retval EFI_DEVICE_ERROR        Could not stop.

**/
EFI_STATUS
EFIAPI
SerialIoDriverBindingStop (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                   *ChildHandleBuffer
  )
{
  EFI_STATUS            Status;
  SERIAL_IO_PRIVATE     *Private;
  EFI_GUID              *ProtocolGuid;
  EFI_DEVICE_PATH_PROTOCOL *DevicePathInstance;

  Private = NULL;
  Status  = EFI_SUCCESS;

  if (NumberOfChildren == 0) {
    //
    // Close protocols opened by Supported()
    //
    Status = gBS->CloseProtocol (
                    ControllerHandle,
                    &gEfiIsaIoProtocolGuid,
                    This->DriverBindingHandle,
                    ControllerHandle
                    );
    if (EFI_ERROR (Status)) {
      Status = gBS->CloseProtocol (
                      ControllerHandle,
                      &gEfiUartIoProtocolGuid,
                      This->DriverBindingHandle,
                      ControllerHandle
                      );
      if (EFI_ERROR (Status)) {
        Status = gBS->CloseProtocol (
                        ControllerHandle,
                        &gEfiPciIoProtocolGuid,
                        This->DriverBindingHandle,
                        ControllerHandle
                        );
      }
    }
    return Status;
  }

  //
  // Stop the child: get private context from child handle
  //
  Status = gBS->OpenProtocol (
                  ChildHandleBuffer[0],
                  &gEfiSerialIoProtocolGuid,
                  &Private,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  //
  // Determine which protocol was opened on the controller
  //
  if (Private->IsaAccess) {
    ProtocolGuid = &gEfiIsaIoProtocolGuid;
  } else if (Private->PciIo != NULL) {
    ProtocolGuid = &gEfiPciIoProtocolGuid;
  } else {
    ProtocolGuid = &gEfiUartIoProtocolGuid;
  }

  //
  // Close the child protocol
  //
  gBS->CloseProtocol (
         ControllerHandle,
         ProtocolGuid,
         This->DriverBindingHandle,
         Private->ChildHandle
         );

  //
  // Uninstall protocols from child handle
  //
  gBS->UninstallMultipleProtocolInterfaces (
         Private->ChildHandle,
         &gEfiSerialIoProtocolGuid,
         Private,
         &gEfiDevicePathProtocolGuid,
         Private->UartDevicePath,
         NULL
         );

  //
  // Free the private context
  //
  gBS->FreePool (Private);

  return EFI_SUCCESS;
}

//
// ============================= Component Name Protocol =============================
//

/**
  Retrieve the driver name.

  @param[in]  This        Component Name 2 protocol.
  @param[in]  Language    Language code.
  @param[out] DriverName  Driver name string.

  @retval EFI_SUCCESS           Retrieved successfully.
  @retval EFI_UNSUPPORTED       Language not supported.
  @retval EFI_INVALID_PARAMETER DriverName or Language is NULL.

**/
EFI_STATUS
EFIAPI
SerialIoComponentNameGetDriverName (
  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
  IN  CHAR8                         *Language,
  OUT CHAR16                        **DriverName
  )
{
  if (Language == NULL || DriverName == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (StriCmp (Language, (CHAR8 *)This->SupportedLanguages) != 0) {
    return EFI_UNSUPPORTED;
  }

  *DriverName = L"AMI Serial I/O Driver";
  return EFI_SUCCESS;
}

/**
  Retrieve the controller name.

  @param[in]  This              Component Name 2 protocol.
  @param[in]  ControllerHandle  Controller handle.
  @param[in]  ChildHandle       Child handle (optional).
  @param[in]  Language          Language code.
  @param[out] ControllerName    Controller name string.

  @retval EFI_SUCCESS           Retrieved successfully.
  @retval EFI_UNSUPPORTED       Language not supported.

**/
EFI_STATUS
EFIAPI
SerialIoComponentNameGetControllerName (
  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
  IN  EFI_HANDLE                    ControllerHandle,
  IN  EFI_HANDLE                    ChildHandle,
  IN  CHAR8                         *Language,
  OUT CHAR16                        **ControllerName
  )
{
  SERIAL_IO_PRIVATE *Private;
  EFI_STATUS         Status;

  if (Language == NULL || ControllerName == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (StriCmp (Language, (CHAR8 *)This->SupportedLanguages) != 0) {
    return EFI_UNSUPPORTED;
  }

  if (ChildHandle == NULL || ControllerHandle == NULL) {
    return EFI_UNSUPPORTED;
  }

  //
  // Get the private context from the child handle
  //
  Private = NULL;
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiSerialIoProtocolGuid,
                  &Private,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if (Private == NULL) {
    return EFI_UNSUPPORTED;
  }

  *ControllerName = Private->ControllerName;
  return EFI_SUCCESS;
}

//
// ============================= Hob Library Helpers =============================
//

/**
  Walk the ACPI device path to find the end node.

  @param[in]  This        Driver binding protocol.
  @param[in]  Handle      Controller handle.
  @param[out] PathEnd     Receives pointer to end node.
  @param[in]  Depth       Max depth.

  @retval EFI_SUCCESS     ACPI device path end found.
  @retval Others          Not found.

**/
EFI_STATUS
HobGetAcpiDevicePathEnd (
  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN  EFI_HANDLE                   Handle,
  OUT EFI_DEVICE_PATH_PROTOCOL     **PathEnd,
  IN  UINTN                        Depth
  )
{
  EFI_STATUS              Status;
  EFI_DEVICE_PATH_PROTOCOL *DevicePath;
  EFI_DEVICE_PATH_PROTOCOL *PathWalk;
  EFI_DEVICE_PATH_PROTOCOL *LastNode;

  *PathEnd = NULL;
  LastNode = NULL;

  Status = gBS->OpenProtocol (
                  Handle,
                  &gEfiDevicePathProtocolGuid,
                  &DevicePath,
                  This->DriverBindingHandle,
                  Handle,
                  (UINT32)Depth
                  );
  if (EFI_ERROR (Status)) {
    return EFI_UNSUPPORTED;
  }

  PathWalk = DevicePath;
  if (PathWalk != NULL) {
    //
    // Walk to the end of the device path
    //
    while (PathWalk->Type != 0x7F) {  // END
      LastNode = PathWalk;
      PathWalk = (EFI_DEVICE_PATH_PROTOCOL *)((UINTN)PathWalk +
                 256 * (UINTN)(UINT8)PathWalk->SubType +
                 (UINT8)PathWalk->Length[0]);
    }
  }

  if (LastNode == NULL || LastNode->Type != 2 || LastNode->SubType != 1) {
    //
    // Not an ACPI device path
    //
    Status = EFI_UNSUPPORTED;
  } else {
    *PathEnd = LastNode;
  }

  gBS->CloseProtocol (
         Handle,
         &gEfiDevicePathProtocolGuid,
         This->DriverBindingHandle,
         Handle
         );

  return Status;
}