/** @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;
}