/**
* @file ConSplitter.c
* @brief AMI Console Splitter Driver Implementation
*
* Source: AmiModulePkg\Console\ConSplitter\ConSplit.c
* Binary: ConSplitter.efi (from HR650X BIOS)
*
* This driver implements a console multiplexer. It registers three
* UEFI driver binding protocols to aggregate multiple physical console
* devices into single logical console handles for ConOut, ConIn, and
* StdErr.
*/
#include "ConSplitter.h"
//
// ======================================================================
// SECTION 1: Global Data (.data section)
// ======================================================================
//
// The following globals are defined in the .data section of
// ConSplitter.efi (0x5C20 - 0x6220 range).
//
//
// Driver binding protocol instances (installed via InstallMultipleProtocolInterfaces)
//
// Text Out driver binding:
// off_5E98 = protocol GUID array
// off_5E80 = driver binding protocol structure
// Text In driver binding:
// off_5D68 = protocol GUID array
// off_5E68 = driver binding protocol structure
// Pointer driver binding:
// off_5DB0 = protocol GUID array
// off_5E50 = driver binding protocol structure
//
//
// Protocol GUIDs referenced from .rdata
//
// unk_5C20 = EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL GUID
// unk_5C70 = EFI_GRAPHICS_OUTPUT_PROTOCOL GUID
// unk_5CA0 = EFI_DEVICE_PATH_PROTOCOL GUID
// unk_5CC0 = EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL GUID
// unk_5CB0 = (secondary text output mode protocol)
// unk_5CD0 = EFI_UGA_DRAW_PROTOCOL GUID (or GOP mode protocol)
// unk_5C90 = EFI_SIMPLE_POINTER_PROTOCOL GUID
// unk_5D00 = (text output mode protocol alt)
// unk_5D50 = EFI_ABSOLUTE_POINTER_PROTOCOL GUID
// unk_5D40 = gEfiConsoleOutDeviceGuid
// unk_5CF0 = EFI_HII_DATABASE_PROTOCOL GUID
// unk_5C40 = EFI_SERIAL_IO_PROTOCOL GUID (or debug port)
// unk_5C30 = gEfiGlobalVariableGuid (for "ConOutDev"/"ConInDev" UEFI vars)
// unk_5D20 = gEfiConsoleControlGuid or gEfiSimpleTextOutProtocolGuid for HOB matching
// unk_6010 / unk_6020 = DebugLib protocol GUIDs
//
//
// System table and boot services cached globally
//
UINT64 gImageHandle = 0; // 0x6118
UINT64 gSystemTable = 0; // 0x6108
UINT64 gBootServices = 0; // 0x6110
UINT64 gRuntimeServices = 0; // 0x6120
//
// Module-level cached pointers
//
UINT64 gBootServicesLocal = 0; // 0x6138
UINT64 gRuntimeServicesLocal = 0; // 0x6140
UINT64 gSystemTableLocal = 0; // 0x6148
//
// Cached image handles for each driver binding
//
UINT64 gImageHandleTextOut = 0; // 0x5EB8 (ImageHandle_0)
UINT64 gImageHandleTextIn = 0; // 0x5D88 (ImageHandle_1)
UINT64 gImageHandlePointer = 0; // 0x5DD0 (ImageHandle_2)
//
// Text Out state
//
VOID *gConSplitterTextOutPrivate = NULL; // 0x6070 (qword_6070)
UINT64 gConSplitterTextOutActiveCount = 0; // 0x61E0 (p_n0xA = active count)
UINT64 gConSplitterTextOutModeData = 0; // 0x5EC0
//
// Text In state
//
VOID *gConSplitterTextInPrivate = NULL; // 0x5D90
UINT64 gConSplitterTextInActiveCount = 0; // 0x61A0 (n0xA = active count)
UINT64 gConSplitterTextInCountVar2 = 0; // 0x61C0 (n0xA_2)
//
// Pointer state
//
UINT64 gConSplitterPointerActiveCount = 0; // 0x6200 (n0xA_0)
UINT64 gConSplitterPointerCountVar2 = 0; // 0x61E8 (i count)
UINT64 gConSplitterPointerCountVar3 = 0; // 0x6208 (i_0 count)
UINT64 gConSplitterAbsPointerActiveCount = 0; // 0x61A8 (j count)
UINT64 gConSplitterAbsPointerCountVar2 = 0; // 0x6188 (j_0 count)
//
// Linked list anchors for child devices
//
// Text Out children: anchored at gConSplitterTextOutChildren
UINT64 gTextOutChildList[2]; // 0x61E8 (i, i_1)
UINT64 gTextOutChildList2[2]; // 0x6208 (i_0, i_1_2)
// Text In children
UINT64 gTextInChildList[2]; // 0x61A0 (j, j_1)
UINT64 gTextInChildList2[2]; // 0x61C0 / 0x61A8 / 0x6188
//
// Mode management
//
UINT32 gConSplitterModeCount = 0; // 0x605C (dword_605C)
UINT32 gConSplitterCurrentMode = 0; // 0x6100 (dword_6100)
UINT32 gConSplitterGlobalMode = 0; // 0x5FF4 (dword_5FF4)
UINT32 gConSplitterModeTableSize = 0; // 0x5FF0 (dword_5FF0)
//
// Mode table (9 bytes per entry: columns(4) + rows(4) + valid(1))
//
VOID *gConSplitterModeTable = NULL; // 0x60D0 (qword_60D0)
//
// Text buffer pointers (double-buffered for scrollback)
//
VOID *gConSplitterTextBuffer = NULL; // 0x60C8 (src / qword_60C8)
VOID *gConSplitterTextAttrib = NULL; // 0x60D8 (src_0 / qword_60D8)
VOID *gConSplitterTextBufEnd = NULL; // 0x60B8 (src_1 / qword_60B8)
VOID *gConSplitterTextBufDst = NULL; // 0x60F8 (dst / qword_60F8)
VOID *gConSplitterTextBufDstAttr = NULL; // 0x60F0 (dst_0 / qword_60F0)
//
// Cursor and display state
//
UINT32 gConSplitterCursorColumn = 0; // low part of n15
UINT32 gConSplitterCursorRow = 0; // high part of n15
UINT32 gConSplitterRows = 0; // n0x19
UINT32 gConSplitterColumns = 0; // n80
UINT32 gConSplitterDefaultAttr = 0; // qword_5FF8 (n15)
//
// Reentrancy guards (nested call protection)
//
UINT8 gConSplitterTextOutRecursionCount = 0; // 0x6040 (byte_6040)
UINT8 gConSplitterTextOutBlockedFlag = 0; // 0x6041 (byte_6041)
UINT8 gConSplitterTextOutRecursionFlag = 0; // 0x6042 (byte_6042)
UINT8 gConSplitterPointerRecursionCount = 0; // 0x6058 (byte_6058)
UINT8 gConSplitterPointerBlockedFlag = 0; // 0x606C (byte_606C)
//
// Text In blocked state
//
UINT8 gConSplitterTextInBlocked = 0; // 0x6059 (byte_6059)
UINT8 gConSplitterTextInBlocked2 = 0; // 0x6179 (byte_6179)
UINT8 gConSplitterKeyboardLayoutValid = 0; // 0x607E (byte_607E)
//
// Attribute / cursor visibility
//
UINT8 gConSplitterCursorVisible = 0; // 0x6004 (byte_6004)
UINT8 gConSplitterBufferClean = 0; // 0x6008 (byte_6008)
//
// Keyboard LED state (low 2 bits = Scroll Lock state)
//
UINT8 gConSplitterLedState = 0; // 0x5D60 (byte_5D60)
UINT8 gConSplitterLedStateMask = 0; // 0x5D61 (byte_5D61)
//
// Debug output mask (determines which debug level messages print)
//
UINT32 gConSplitterDebugMask = 0; // 0x5FFC
//
// HII protocol handle (keyboard layout database)
//
UINT64 gConSplitterHiiHandle = 0; // 0x6048 (qword_6048)
UINT64 gConSplitterKeyboardLayout = 0; // 0x6060 (qword_6060)
//
// HOB list handle
//
UINT64 gConSplitterHobListHandle = 0; // 0x6130 (qword_6130)
//
// DebugLib protocol handles
//
UINT64 gConSplitterDebugProtocol = 0; // 0x6128 (qword_6128)
UINT64 gConSplitterDebugProtocol2 = 0; // 0x6140 / 0x6168 / 0x6158
//
// Key notification state
//
UINT16 gConSplitterLastKeyScan = 0; // 0x6078 (word_6078)
UINT16 gConSplitterSavedKeyIndex = 0xFFFF; // 0x5E40 (word_5E40)
//
// Key filter table (8 entries, 12 bytes each)
//
// Each entry has: ScanCode(2) + UnicodeChar(2) + ShiftState(4) + Reserved(4)
// These define which key combinations the splitter intercepts for
// special handling (e.g. hotkeys for console switching).
//
UINT8 gConSplitterKeyFilter[8 * 12]; // 0x5DE0 (word_5DE0)
//
// Key filter override flags (1 byte per filter entry)
// Set to 1 when a default notification handler is overridden.
//
UINT8 gConSplitterKeyFilterOverride[8]; // 0x6050 (byte_6050)
//
// Text In keyboard self-test state
//
UINT8 gConSplitterKeyboardTestPassed = 0; // 0x6068 (dword_6068, but used as byte)
UINT8 gConSplitterTestModeActive = 0; // 0x6041 (byte_6041) - shared with blocked flag
//
// Timer event for keyboard polling
//
UINT64 gConSplitterTimerEvent = 0; // 0x5EC0 area
//
// Resolution scaling factors
//
UINT32 gConSplitterScaleX = 0; // 0x5F80 (dword_5F80)
UINT32 gConSplitterScaleY = 0; // 0x5F88 (dword_5F88)
UINT32 gConSplitterScaleZ = 0; // 0x5F90 (dword_5F90)
//
// Resolution mode tracking data (off_5F10)
//
UINT64 gConSplitterResolutionModeData[6] = {0}; // 0x5F10
//
// Notification events
//
VOID *gConSplitterConOutNotifyEvent = NULL; // 0x5EE8
VOID *gConSplitterConInNotifyEvent = NULL;
VOID *gConSplitterStdErrNotifyEvent = NULL;
VOID *gConSplitterConOutNotifyReg = NULL; // 0x5F18
VOID *gConSplitterConInNotifyReg = NULL; // 0x5F30
VOID *gConSplitterStdErrNotifyReg = NULL; // 0x5F60
//
// Protocol interface pointers (installed on system table)
//
VOID *gConSplitterConOutInterface = NULL; // off_5FA0
VOID *gConSplitterConInInterface = NULL; // off_5F18
VOID *gConSplitterStdErrInterface = NULL; // off_5F30
//
// Resolution change notification structures
//
UINT64 gConSplitterResolutionEvent = 0; // 0x5F70 (off_5F70)
//
// ======================================================================
// SECTION 2: Library Helpers
// ======================================================================
//
/**
* Initialize HII database protocol handle for keyboard layout access.
* Locates gEfiHiiDatabaseProtocolGuid via the boot services table.
*/
STATIC
EFI_STATUS
ConSplitterInitializeHiiProtocol (
VOID
)
{
if (gConSplitterHiiHandle == 0) {
return gBS->LocateProtocol (
&gEfiHiiDatabaseProtocolGuid,
NULL,
&gConSplitterHiiHandle
);
}
return EFI_SUCCESS;
}
//
// ======================================================================
// SECTION 3: Text Out Protocol Implementation
// ======================================================================
//
/**
* ConSplitterTextOutReset
* Address: 0x43A4
*
* Resets all active text output child devices.
* Iterates the linked list of text output devices, calling Reset on each.
* Then clears the text buffer and resets mode tracking.
*/
EFI_STATUS
EFIAPI
ConSplitterTextOutReset (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
TEXT_OUT_NODE *Node;
EFI_STATUS Status;
EFI_STATUS ReturnStatus;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL **ChildProtocol;
Node = gTextOutChildren;
ReturnStatus = EFI_SUCCESS;
if (Node != NULL) {
do {
if (Node->Active) {
Status = Node->PrivateData->Reset (
Node->PrivateData,
ExtendedVerification
);
if (EFI_ERROR(Status)) {
ReturnStatus = Status;
}
}
Node = Node->ForwardLink;
} while (Node != NULL);
// Update mode state from current mode table
ConSplitterUpdateModeState ();
} else {
// No children - reset to defaults
gConSplitterCursorColumn = 0;
gConSplitterCursorRow = 0;
gConSplitterColumns = 80;
gConSplitterRows = 25;
}
// Clear internal text buffer
ConSplitterClearTextBuffer ();
return ReturnStatus;
}
/**
* ConSplitterTextOutOutputString
* Address: 0x4434
*
* Outputs a Unicode string to all active text output child devices.
* Also writes the string into the internal scrollback buffer.
* Handles backspace (8), newline (10), carriage return (13) specially.
*/
EFI_STATUS
EFIAPI
ConSplitterTextOutOutputString (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN CHAR16 *String
)
{
TEXT_OUT_NODE *Node;
EFI_STATUS Status;
EFI_STATUS ReturnStatus;
UINTN CursorCol;
UINTN CursorRow;
UINTN RowCount;
CHAR16 *BufferPos;
UINT32 *AttribPos;
UINTN Cols;
UINTN VOffset;
CHAR16 Char;
Cols = gConSplitterColumns;
RowCount = gConSplitterRows;
CursorCol = gConSplitterCursorColumn;
CursorRow = gConSplitterCursorRow;
ReturnStatus = EFI_SUCCESS;
// Mark buffer as dirty (needs re-initialization)
gConSplitterBufferClean = FALSE;
// Calculate starting position in the scrollback buffer
VOffset = (CursorRow * Cols) + CursorCol;
if ((CursorRow * Cols) + CursorCol > 0) {
VOffset = 0;
}
BufferPos = &gConSplitterTextBuffer[2 * VOffset];
AttribPos = &gConSplitterTextAttrib[4 * VOffset];
Char = *String;
if (Char == 0) {
return EFI_SUCCESS;
}
while (Char != 0) {
if (Char == L'\b') {
// Backspace
if (CursorCol > 0) {
BufferPos -= 2;
AttribPos -= 4;
CursorCol--;
}
} else if (Char == L'\n') {
// Newline
if ((UINTN)(BufferPos + 2) >= (UINTN)gConSplitterTextBufEnd) {
ConSplitterScrollTextBufferOneLine ();
// Recalculate position after scroll
CursorCol = 0;
} else {
CursorRow++;
CursorCol = 0;
}
} else if (Char == L'\r') {
// Carriage return
CursorCol = 0;
} else {
// Normal character
if (BufferPos + 2 >= gConSplitterTextBufEnd) {
ConSplitterScrollTextBufferOneLine ();
// Recalculate position
CursorCol = 0;
} else {
// Write character to buffer
*(CHAR16 *)BufferPos = Char;
BufferPos += 2;
*(UINT32 *)AttribPos = gConSplitterDefaultAttr;
AttribPos += 4;
CursorCol++;
if (CursorCol >= Cols) {
CursorRow++;
CursorCol = 0;
}
}
}
String++;
Char = *String;
}
// Update global cursor position
gConSplitterCursorColumn = (UINT32)CursorCol;
gConSplitterCursorRow = (UINT32)CursorRow + RowCount;
gConSplitterBufferClean = FALSE; // will be recalculated
// Now dispatch to all active child devices
Node = gConOutTextChildren;
while (Node != NULL) {
if (Node->Active) {
// Re-output to this device
// (In the original code this processes each string
// by resetting cursor position and dispatching)
Status = Node->PrivateData->OutputString (
Node->PrivateData,
String
);
if (EFI_ERROR(Status)) {
ReturnStatus = Status;
}
}
Node = Node->ForwardLink;
}
// Update mode state
ConSplitterUpdateModeState ();
return ReturnStatus;
}
/**
* ConSplitterTextOutTestString
* Address: 0x45E8
*
* Tests whether a string can be displayed on all active text out devices.
*/
EFI_STATUS
EFIAPI
ConSplitterTextOutTestString (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN CHAR16 *String
)
{
TEXT_OUT_NODE *Node;
EFI_STATUS Status;
EFI_STATUS ReturnStatus;
Node = gConOutTextChildren;
ReturnStatus = EFI_SUCCESS;
if (Node == NULL) {
return EFI_SUCCESS;
}
do {
if (Node->Active) {
Status = Node->PrivateData->TestString (
Node->PrivateData,
String
);
if (EFI_ERROR(Status)) {
ReturnStatus = Status;
}
}
Node = Node->ForwardLink;
} while (Node != NULL);
ConSplitterUpdateModeState ();
return ReturnStatus;
}
/**
* ConSplitterTextOutQueryMode
* Address: 0x4888
*
* Returns the dimensions of a given text mode.
* If no children are connected, returns default values (80x25).
*/
EFI_STATUS
EFIAPI
ConSplitterTextOutQueryMode (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN UINTN ModeNumber,
OUT UINTN *Columns,
OUT UINTN *Rows
)
{
TEXT_OUT_NODE *Node;
EFI_STATUS Status;
EFI_STATUS ReturnStatus;
Node = gConOutTextChildren;
ReturnStatus = EFI_SUCCESS;
if (Node != NULL) {
do {
if (Node->Active) {
Status = Node->PrivateData->QueryMode (
Node->PrivateData,
ModeNumber,
Columns,
Rows
);
if (EFI_ERROR(Status)) {
ReturnStatus = Status;
}
}
Node = Node->ForwardLink;
} while (Node != NULL);
ConSplitterUpdateModeState ();
return ReturnStatus;
}
// No children - return defaults
if (ModeNumber < 80 && ModeNumber < 25) {
*Columns = 80;
*Rows = 25;
return EFI_SUCCESS;
}
*Columns = 0;
*Rows = 0;
return EFI_UNSUPPORTED;
}
/**
* ConSplitterTextOutSetMode
* Address: 0x46B4
*
* Sets the display mode for all active text out children.
* Selects a new mode entry from the mode table and reconfigures
* all child devices.
*/
EFI_STATUS
EFIAPI
ConSplitterTextOutSetMode (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN UINTN ModeNumber
)
{
TEXT_OUT_NODE *Node;
EFI_STATUS Status;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOut;
UINT32 ModeIndex;
Node = gConOutTextChildren;
if (ModeNumber >= gConSplitterModeTableSize ||
!gConSplitterModeTable[Modes].Valid) {
return EFI_UNSUPPORTED;
}
if (ModeNumber == gConSplitterGlobalMode) {
// Already in this mode
ConSplitterUpdateCurrentTextOut (Node);
return EFI_SUCCESS;
}
if (Node == NULL) {
return EFI_SUCCESS;
}
// Re-allocate and fill the mode table from this mode
Status = ConSplitterReconstructModeTable (ModeNumber);
if (EFI_ERROR(Status)) {
return Status;
}
gConSplitterGlobalMode = (UINT32)ModeNumber;
// Update each child's mode
do {
ModeIndex = ConSplitterFindMatchingMode (Node->PrivateData, ModeNumber);
if (ModeIndex != (UINT32)-1) {
Status = Node->PrivateData->SetMode (
Node->PrivateData,
ModeIndex
);
if (!EFI_ERROR(Status)) {
Node->Active = TRUE;
} else {
Node->Active = FALSE;
}
} else {
Node->Active = FALSE;
}
Node = Node->ForwardLink;
} while (Node != NULL);
ConSplitterUpdateModeState ();
ConSplitterSetResolutionData ();
return Status;
}
/**
* ConSplitterTextOutSetAttribute
* Address: 0x479C
*
* Sets the text attribute on all active child devices.
*/
EFI_STATUS
EFIAPI
ConSplitterTextOutSetAttribute (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN UINTN Attribute
)
{
TEXT_OUT_NODE *Node;
EFI_STATUS Status;
EFI_STATUS ReturnStatus;
Node = gConOutTextChildren;
ReturnStatus = EFI_SUCCESS;
if (Node == NULL) {
return EFI_SUCCESS;
}
gConSplitterDefaultAttr = (UINT32)Attribute;
do {
if (Node->Active) {
Status = Node->PrivateData->SetAttribute (
Node->PrivateData,
Attribute
);
if (EFI_ERROR(Status)) {
ReturnStatus = Status;
}
}
Node = Node->ForwardLink;
} while (Node != NULL);
ConSplitterUpdateModeState ();
return ReturnStatus;
}
/**
* ConSplitterTextOutClearScreen
* Address: 0x4810
*
* Clears the screen on all active text out children.
*/
EFI_STATUS
EFIAPI
ConSplitterTextOutClearScreen (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
)
{
TEXT_OUT_NODE *Node;
EFI_STATUS Status;
EFI_STATUS ReturnStatus;
Node = gConOutTextChildren;
ReturnStatus = EFI_SUCCESS;
if (Node != NULL) {
do {
if (Node->Active) {
Status = Node->PrivateData->ClearScreen (
Node->PrivateData
);
if (EFI_ERROR(Status)) {
ReturnStatus = Status;
}
}
Node = Node->ForwardLink;
} while (Node != NULL);
ConSplitterUpdateModeState ();
return ReturnStatus;
}
// No children - reset cursor to origin
gConSplitterCursorColumn = 0;
gConSplitterCursorRow = 0;
ConSplitterClearTextBuffer ();
return ReturnStatus;
}
/**
* ConSplitterTextOutSetCursorPosition
* Address: 0x4888
*
* Sets cursor position on all active text out children.
*/
EFI_STATUS
EFIAPI
ConSplitterTextOutSetCursorPosition (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN UINTN Column,
IN UINTN Row
)
{
TEXT_OUT_NODE *Node;
EFI_STATUS Status;
EFI_STATUS ReturnStatus;
Node = gConOutTextChildren;
ReturnStatus = EFI_SUCCESS;
if (Node != NULL) {
do {
if (Node->Active) {
Status = Node->PrivateData->SetCursorPosition (
Node->PrivateData,
Column,
Row
);
if (EFI_ERROR(Status)) {
ReturnStatus = Status;
}
}
Node = Node->ForwardLink;
} while (Node != NULL);
ConSplitterUpdateModeState ();
return ReturnStatus;
}
// No children - update local state
if (Column < 80 && Row < 25) {
gConSplitterCursorColumn = (UINT32)Column;
gConSplitterCursorRow = (UINT32)Row;
}
return EFI_SUCCESS;
}
/**
* ConSplitterTextOutEnableCursor
* Address: 0x4920
*
* Enables or disables the cursor on all active text out children.
*/
EFI_STATUS
EFIAPI
ConSplitterTextOutEnableCursor (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN BOOLEAN Visible
)
{
TEXT_OUT_NODE *Node;
EFI_STATUS Status;
EFI_STATUS ReturnStatus;
Node = gConOutTextChildren;
ReturnStatus = EFI_SUCCESS;
if (Node != NULL) {
do {
if (Node->Active) {
Status = Node->PrivateData->EnableCursor (
Node->PrivateData,
Visible
);
if (EFI_ERROR(Status)) {
ReturnStatus = Status;
}
}
Node = Node->ForwardLink;
} while (Node != NULL);
ReturnStatus = ReturnStatus;
}
gConSplitterCursorVisible = (UINT8)Visible;
return ReturnStatus;
}
//
// ======================================================================
// SECTION 4: Text In (Keyboard) Protocol Implementation
// ======================================================================
//
/**
* ConSplitterTextInReset
* Address: 0x3710
*
* Resets all active text input child devices.
*/
EFI_STATUS
EFIAPI
ConSplitterTextInReset (
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
EFI_STATUS Status;
if (gConSplitterTextOutBlockedFlag) {
return EFI_SPLITTER_PROTOCOL_ERROR;
}
Status = ConSplitterPointerResetDispatch (ExtendedVerification);
return Status;
}
/**
* ConSplitterTextInReadKeyStroke
* Address: 0x3514
*
* Reads a keystroke from any active text input child device.
* Iterates through all children and returns the first available key.
*/
EFI_STATUS
EFIAPI
ConSplitterTextInReadKeyStroke (
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
OUT EFI_KEY_DATA *KeyData
)
{
TEXT_IN_NODE *Node;
EFI_STATUS Status;
EFI_STATUS ReturnStatus;
UINT8 SavedRecursion;
ReturnStatus = EFI_SUCCESS;
SavedRecursion = gConSplitterPointerRecursionCount;
gConSplitterPointerRecursionCount++;
if (gConSplitterTextOutBlockedFlag) {
gConSplitterPointerRecursionCount = SavedRecursion;
return EFI_SPLITTER_PROTOCOL_ERROR;
}
Node = gConTextInChildren;
if (Node != NULL) {
do {
Status = Node->TextOutInterface->ReadKeyStroke (
Node->TextOutInterface,
KeyData
);
if (!EFI_ERROR(Status)) {
gConSplitterPointerRecursionCount = SavedRecursion;
return EFI_SUCCESS;
}
if (Status != EFI_NOT_READY) {
ReturnStatus = Status;
}
Node = Node->ForwardLink;
} while (Node != NULL);
}
gConSplitterPointerRecursionCount = SavedRecursion;
return ReturnStatus;
}
/**
* ConSplitterTextInWaitForKey
* Address: 0x2E38
*
* Waits for a keystroke from any child device.
* Iterates through all children checking for available input.
*/
EFI_STATUS
EFIAPI
ConSplitterTextInWaitForKey (
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This
)
{
TEXT_IN_NODE *Node;
EFI_STATUS Status;
EFI_STATUS ReturnStatus;
UINT8 SavedRecursion;
ReturnStatus = EFI_SUCCESS;
SavedRecursion = gConSplitterPointerRecursionCount;
gConSplitterPointerRecursionCount++;
if (gConSplitterTextOutBlockedFlag) {
gConSplitterPointerRecursionCount = SavedRecursion;
return EFI_SPLITTER_PROTOCOL_ERROR;
}
Node = gConTextInChildren;
if (Node != NULL) {
do {
Status = Node->TextOutInterface->WaitForKey (Node->TextOutInterface);
if (!EFI_ERROR(Status)) {
gConSplitterPointerRecursionCount = SavedRecursion;
return EFI_SUCCESS;
}
if (Status != EFI_NOT_READY) {
ReturnStatus = Status;
}
Node = Node->ForwardLink;
} while (Node != NULL);
}
gConSplitterPointerRecursionCount = SavedRecursion;
return ReturnStatus;
}
//
// ======================================================================
// SECTION 6: Simple Pointer Protocol Implementation
// ======================================================================
//
/**
* ConSplitterPointerReset
* Address: 0x35A8
*
* Resets the pointer state by querying the first active child.
*/
EFI_STATUS
EFIAPI
ConSplitterPointerReset (
IN EFI_SIMPLE_POINTER_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
EFI_STATUS Status;
EFI_SIMPLE_POINTER_STATE State;
if (gConSplitterTextOutBlockedFlag) {
return EFI_SPLITTER_NOT_READY;
}
Status = ConSplitterPointerGetStateInternal (&State);
if (EFI_ERROR(Status) && Status != EFI_SPLITTER_NOT_READY) {
return Status;
}
return EFI_SUCCESS;
}
/**
* ConSplitterPointerGetState
* Address: 0x3764
*
* Gets the pointer state from all active pointer children.
* Aggregates relative motion across devices.
*/
EFI_STATUS
EFIAPI
ConSplitterPointerGetState (
IN EFI_SIMPLE_POINTER_PROTOCOL *This,
OUT EFI_SIMPLE_POINTER_STATE *State
)
{
POINTER_NODE *Node;
EFI_STATUS Status;
EFI_SIMPLE_POINTER_STATE ChildState;
EFI_STATUS ReturnStatus;
gConSplitterPointerRecursionCount++;
if (gConSplitterTextOutBlockedFlag) {
gConSplitterPointerRecursionCount--;
return EFI_SPLITTER_NOT_READY;
}
if (State == NULL) {
gConSplitterPointerRecursionCount--;
return EFI_INVALID_PARAMETER;
}
Status = ConSplitterPointerGetStateInternal (&ChildState);
if (!EFI_ERROR(Status)) {
State->RelativeMovementX = ChildState.RelativeMovementX;
State->RelativeMovementY = ChildState.RelativeMovementY;
State->RelativeMovementZ = ChildState.RelativeMovementZ;
State->LeftButton = ChildState.LeftButton;
State->RightButton = ChildState.RightButton;
} else {
ReturnStatus = Status;
}
gConSplitterPointerRecursionCount--;
return ReturnStatus;
}
//
// ======================================================================
// SECTION 7: Driver Binding Protocol Implementation
// ======================================================================
//
/**
* ConSplitterTextOutDriverBindingSupported
* Address: 0x44C for Text Out; 0x4A8 for generic
*
* Tests whether the driver supports a given controller.
* Checks for EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on the controller.
*/
EFI_STATUS
EFIAPI
ConSplitterTextOutDriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
VOID *TextOutProtocol;
if (ControllerHandle == NULL ||
This == NULL ||
RemainingDevicePath == NULL) {
return EFI_INVALID_PARAMETER;
}
// Check if controller supports text output protocol
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiSimpleTextOutProtocolGuid,
&TextOutProtocol,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR(Status)) {
return Status;
}
gBS->CloseProtocol (
ControllerHandle,
&gEfiSimpleTextOutProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return EFI_SUCCESS;
}
/**
* ConSplitterDriverBindingStart
* Address: 0x1054 for Text Out
* 0x10E0 for Text Out (w/ mode setup)
* 0x1398 for Text In
* 0x14F8 for Text In (w/ extended setup)
* 0x18A4 for Simple Pointer
* 0x19AC for Absolute Pointer
*
* Starts the driver on a controller, creating a child device node
* and adding it to the internal linked list.
*/
EFI_STATUS
EFIAPI
ConSplitterTextOutDriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOutProtocol;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
TEXT_OUT_NODE *Node;
// Validate the controller supports text output
Status = ConSplitterTextOutDriverBindingSupported (
This,
ControllerHandle,
RemainingDevicePath
);
if (EFI_ERROR(Status)) {
return Status;
}
// Check if already connected
Status = ConSplitterTextOutIsDeviceAttached (ControllerHandle);
if (Status == EFI_ALREADY_STARTED) {
return EFI_SUCCESS;
}
// Open protocol to get the text output interface
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiSimpleTextOutProtocolGuid,
&TextOutProtocol,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR(Status)) {
return Status;
}
// Check for duplicate device path
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiDevicePathProtocolGuid,
&DevicePath,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR(Status)) {
DevicePath = NULL;
}
// Create child device node
Node = AllocateZeroPool (sizeof (TEXT_OUT_NODE));
if (Node == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Node->PrivateData = TextOutProtocol;
Node->DeviceHandle = ControllerHandle;
Node->Active = TRUE;
// Insert into linked list of text out children
ConSplitterTextOutInsertChild (Node);
// If this is the first child, set up the mode table
if (gConSplitterTextOutActiveCount == 1) {
ConSplitterTextOutReconstructModeList (TextOutProtocol);
}
// Set up graphics output protocol notification
ConSplitterSetUpGraphicsOutput (ControllerHandle);
return EFI_SUCCESS;
}
/**
* ConSplitterDriverBindingStop
* Address: 0x11F4 for Text Out
* 0x1750 for Text In
* 0x1BB0 for Pointer
*
* Stops the driver on a controller, removing the child device node
* from the internal linked list.
*/
EFI_STATUS
EFIAPI
ConSplitterTextOutDriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
TEXT_OUT_NODE *Node;
TEXT_OUT_NODE *FoundNode;
EFI_STATUS Status;
Status = EFI_NOT_FOUND;
FoundNode = NULL;
// Find the child node for this controller
Node = gConTextOutChildren;
while (Node != NULL) {
if (Node->DeviceHandle == ControllerHandle) {
// Close protocols on this handle
gBS->CloseProtocol (
ControllerHandle,
&gEfiSimpleTextOutProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
gBS->CloseProtocol (
ControllerHandle,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
// Remove from linked list
ConSplitterTextOutRemoveChild (Node);
// Free the node
gBS->FreePool (Node);
Status = EFI_SUCCESS;
break;
}
Node = Node->ForwardLink;
}
// Check if we need to update mode state
if (gConSplitterTextOutActiveCount > 0 ||
gConSplitterConOutInterface != NULL) {
if (Status == EFI_SUCCESS && gConSplitterTextOutActiveCount > 0) {
ConSplitterTextOutReconstructModeList (NULL);
}
} else {
// All children removed - clean up resources
if (gConSplitterModeTable != NULL) {
gBS->FreePool (gConSplitterModeTable);
gConSplitterModeTable = NULL;
}
if (gConSplitterTextBuffer != NULL) {
gBS->FreePool (gConSplitterTextBuffer);
gConSplitterTextBuffer = NULL;
}
if (gConSplitterTextAttrib != NULL) {
gBS->FreePool (gConSplitterTextAttrib);
gConSplitterTextAttrib = NULL;
}
gConSplitterTextOutBlockedFlag = FALSE;
}
return Status;
}
//
// ======================================================================
// SECTION 8: Driver Entry Point
// ======================================================================
//
/**
* ConSplitterDriverEntryPoint
* Address: 0xAE4
*
* Main entry point for the AMI Console Splitter Driver.
*
* Performs the following:
* 1. Caches the system table, boot services, and runtime services
* 2. Installs three driver binding protocols:
* - Text Out Driver Binding
* - Text In Driver Binding
* - Pointer Driver Binding
* 3. Allocates notification event structures for:
* - ConOut device arrival notification
* - ConIn device arrival notification
* - StdErr device arrival notification
* - Graphics output mode change notification
* - Text input key notification
* 4. Registers notification handler for Graphics Output Protocol
* mode changes
* 5. Registers protocol notifications for console device arrival
* 6. Sets up setup variable support
* 7. Installs the console splitter protocol interfaces into the
* system table (ConOut, ConIn, StdErr)
* 8. Creates a periodic timer for keyboard polling
*
* Called from ModuleEntryPoint() at 0x390 which initializes the
* standard UEFI globals (gImageHandle, gST, gBS, gRT) before
* delegating to this function.
*/
EFI_STATUS
EFIAPI
ConSplitterDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// 1. Cache system table pointers
//
if (gSystemTableLocal == 0) {
gSystemTableLocal = (UINT64)SystemTable;
gBootServicesLocal = (UINT64)SystemTable->BootServices;
gRuntimeServicesLocal = (UINT64)SystemTable->RuntimeServices;
}
//
// 2. Install Text Out Driver Binding
//
gConSplitterTextOutStartPrivate = 0;
gImageHandleTextOut = (UINT64)ImageHandle;
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gConSplitterTextOutDriverBinding,
ImageHandle,
&gConSplitterTextOutComponentName,
NULL
);
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
//
// 3. Install Text In Driver Binding
//
gConSplitterTextInStartPrivate = 0;
gImageHandleTextIn = (UINT64)ImageHandle;
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gConSplitterTextInDriverBinding,
ImageHandle,
&gConSplitterTextInComponentName,
NULL
);
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
//
// 4. Install Pointer Driver Binding
//
gConSplitterPointerStartPrivate = 0;
gImageHandlePointer = (UINT64)ImageHandle;
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gConSplitterPointerDriverBinding,
ImageHandle,
&gConSplitterPointerComponentName,
NULL
);
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
//
// 5. Allocate ConOut notification event and register protocol notify
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
ConSplitterConOutNotifyHandler,
NULL,
&gConSplitterConOutNotifyEvent
);
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
Status = gBS->RegisterProtocolNotify (
&gEfiSimpleTextOutProtocolGuid,
gConSplitterConOutNotifyEvent,
&gConSplitterConOutNotifyReg
);
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
//
// 6. Allocate ConIn notification event
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
ConSplitterConInNotifyHandler,
NULL,
&gConSplitterConInNotifyEvent
);
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
Status = gBS->RegisterProtocolNotify (
&gEfiSimpleTextInProtocolGuid,
gConSplitterConInNotifyEvent,
&gConSplitterConInNotifyReg
);
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
//
// 7. Allocate StdErr notification event
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
ConSplitterStdErrNotifyHandler,
NULL,
&gConSplitterStdErrNotifyEvent
);
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
Status = gBS->RegisterProtocolNotify (
&gEfiSimpleTextOutProtocolGuid,
gConSplitterStdErrNotifyEvent,
&gConSplitterStdErrNotifyReg
);
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
//
// 8. Allocate GOP mode change notification event
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
ConSplitterGraphicsOutputNotify,
NULL,
&gConSplitterResolutionEvent
);
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
Status = gBS->RegisterProtocolNotify (
&gEfiGraphicsOutputProtocolGuid,
gConSplitterResolutionEvent,
&gConSplitterResolutionNotifyReg
);
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
//
// 9. Initialize internal state tracking variables
//
gConSplitterTextOutActiveCount = 0;
gConSplitterTextInActiveCount = 0;
gConSplitterPointerActiveCount = 0;
gConSplitterAbsPointerActiveCount = 0;
gConSplitterTextInCountVar2 = 0;
gConSplitterPointerCountVar2 = 0;
gConSplitterPointerCountVar3 = 0;
gConSplitterAbsPointerCountVar2 = 0;
gConTextOutChildList[0] = 0;
gConTextOutChildList[1] = 0;
gConTextOutChildList2[0] = 0;
gConTextOutChildList2[1] = 0;
gConTextInChildList[0] = 0;
gConTextInChildList[1] = 0;
//
// 10. Register notification handlers for ConOut device arrival
//
Status = gBS->RegisterProtocolNotify (
&gEfiConsoleOutDeviceGuid,
gConSplitterConOutNotifyEvent,
&gConSplitterConOutNotifyReg
);
//
// 11. Signal the events to catch any already-present devices
//
gBS->SignalEvent (gConSplitterConOutNotifyEvent);
gBS->SignalEvent (gConSplitterConInNotifyEvent);
gBS->SignalEvent (gConSplitterStdErrNotifyEvent);
//
// 12. Set up console mode
//
{
UINT32 DefaultColumns = 80;
UINT32 DefaultRows = 25;
gConSplitterGlobalMode = 0xFF; // invalid/not set
Status = ConSplitterInitializeConsoleMode (0);
// Read Setup variable for default mode configuration
if (gConSplitterGlobalMode == 0) {
Status = gRT->GetVariable (
L"Setup",
&gEfiGlobalVariableGuid,
NULL,
NULL,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
// Setup variable exists - check its contents
VOID *SetupBuffer;
UINTN SetupSize;
SetupBuffer = AllocatePool (Status);
if (SetupBuffer != NULL) {
Status = gRT->GetVariable (
L"Setup",
&gEfiGlobalVariableGuid,
NULL,
&SetupSize,
SetupBuffer
);
if (!EFI_ERROR(Status)) {
// Check console mode bit (offset depends on
// setup variable layout - bit 0 in first byte)
UINT8 *SetupData = (UINT8 *)SetupBuffer;
if (*SetupData & 2) {
gConSplitterLedState |= 2;
}
}
gBS->FreePool (SetupBuffer);
}
}
}
gConSplitterKeyboardLayoutValid = TRUE;
}
//
// 13. Install console protocol interfaces into the system table
// and configure the default mode
//
if (gConSplitterModeTable != NULL) {
gConSplitterModeTable->Columns = 80;
gConSplitterModeTable->Rows = 25;
gConSplitterModeTable->Valid = TRUE;
}
SystemTable->ConsoleOutHandle = gConSplitterConOutInterface;
SystemTable->ConOut = gConSplitterConOutProtocol;
SystemTable->ConsoleInHandle = gConSplitterConInInterface;
SystemTable->ConIn = gConSplitterConInProtocol;
SystemTable->StdErrHandle = gConSplitterStdErrInterface;
SystemTable->StdErr = gConSplitterStdErrProtocol;
// Set the mode and cursor
gSystemTableLocal->Mode->Mode = 0;
gBS->SetWatchdogTimer (120, 0, 0, NULL);
{
UINT32 TestMode = 0;
gBS->CalculateCrc32 (gSystemTableLocal, 120, &TestMode);
gSystemTableLocal->Mode->Mode = TestMode;
}
//
// 14. Create periodic timer for keyboard polling
//
{
EFI_EVENT TimerEvent;
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
ConSplitterKeyboardTimerHandler,
NULL,
&TimerEvent
);
if (!EFI_ERROR(Status)) {
gBS->SetTimer (
TimerEvent,
TimerPeriodic,
50000 // 50ms = 20Hz keyboard poll
);
}
}
return EFI_SUCCESS;
}
//
// ======================================================================
// SECTION 9: Key Notification Support
// ======================================================================
//
/**
* ConSplitterRegisterKeyNotify
* Address: 0x3A10
*
* Registers a notification function for a particular key on all
* input child devices. If the console splitter is blocked (e.g.,
* during keyboard self-test), the notification is still recorded
* in a global list for later activation.
*/
EFI_STATUS
ConSplitterRegisterKeyNotify (
IN VOID *This,
IN EFI_KEY_DATA *KeyData,
IN VOID *NotificationFunction,
OUT VOID **NotifyHandle
)
{
KEY_NOTIFY_NODE *NotifyNode;
TEXT_IN_NODE *Node;
UINT32 ChildIndex;
if (KeyData == NULL || NotificationFunction == NULL || NotifyHandle == NULL) {
return EFI_INVALID_PARAMETER;
}
if (gConSplitterTextOutBlockedFlag) {
// Console is blocked (self-test mode) - register in the global
// notification list for later activation
DEBUG ((EFI_D_INFO, "ConIn Devices are blocked. Registering the key notifications in the global list\n"));
Status = gBS->AllocatePool (
EfiBootServicesData,
sizeof (KEY_NOTIFY_NODE),
&NotifyNode
);
if (EFI_ERROR(Status)) {
*NotifyHandle = NULL;
return Status;
}
NotifyNode->KeyData = *KeyData;
NotifyNode->NotificationFunction = NotificationFunction;
NotifyNode->NotifyHandle = 0;
NotifyNode->PerDeviceBadTags[0] = 0x00BADBAD;
// Insert into notification linked list (i_1 list)
NotifyNode->ForwardLink = NULL;
NotifyNode->BackLink = gConSplitterTextInNotifyList;
if (gConSplitterTextInNotifyList != NULL) {
gConSplitterTextInNotifyList->ForwardLink = NotifyNode;
}
gConSplitterTextInNotifyList = NotifyNode;
*NotifyHandle = NotifyNode;
return EFI_SPLITTER_PROTOCOL_ERROR;
}
//
// Normal path - register on each child device
//
Status = gBS->AllocatePool (
EfiBootServicesData,
sizeof (KEY_NOTIFY_NODE),
&NotifyNode
);
if (EFI_ERROR(Status)) {
return Status;
}
NotifyNode->KeyData = *KeyData;
NotifyNode->NotificationFunction = NotificationFunction;
// Register on each Text In child device
Node = gConTextInChildren;
ChildIndex = 0;
while (Node != NULL && ChildIndex < MAX_CON_IN_CHILDREN) {
// Store the child pointer
NotifyNode->PerDeviceHandles[ChildIndex] = (UINT32)(UINTN)Node;
// Register on this child (if it supports KeyNotify)
if (Node->TextOutInterface != NULL) {
Status = ConSplitterRegisterChildKeyNotify (
Node->TextOutInterface,
KeyData,
NotificationFunction,
&NotifyNode->PerDeviceBadTags[ChildIndex]
);
if (EFI_ERROR(Status)) {
NotifyNode->PerDeviceBadTags[ChildIndex] = 0x00BADBAD;
}
} else {
NotifyNode->PerDeviceBadTags[ChildIndex] = 0x00BADBAD;
}
Node = Node->ForwardLink;
ChildIndex++;
}
if (ChildIndex == MAX_CON_IN_CHILDREN) {
DEBUG ((EFI_D_WARN, "You have exceeded the number of ConIn devices tracked for Key notifications. Increase MAX_CON_IN_CHILDREN in Consplit.h\n"));
}
// Check if this replaces a default key filter
{
KEY_FILTER_ENTRY *FilterEntry;
UINT32 FilterIndex;
FilterEntry = (KEY_FILTER_ENTRY *)gConSplitterKeyFilter;
for (FilterIndex = 0; FilterIndex < CON_SPLITTER_KEY_FILTER_COUNT; FilterIndex++) {
if (FilterEntry[FilterIndex].ScanCode == KeyData->Key.ScanCode &&
FilterEntry[FilterIndex].UnicodeChar == KeyData->Key.UnicodeChar &&
FilterEntry[FilterIndex].ShiftState == KeyData->KeyState.KeyShiftState &&
(UINT64)NotificationFunction != (UINT64)ConSplitterDefaultKeyNotify)
{
gConSplitterKeyFilterOverride[FilterIndex] = 1;
DEBUG ((EFI_D_INFO, "Default ConIn Function overridden\n"));
}
}
}
// Insert into notification list
NotifyNode->ForwardLink = NULL;
NotifyNode->BackLink = gConSplitterTextInNotifyList;
if (gConSplitterTextInNotifyList != NULL) {
gConSplitterTextInNotifyList->ForwardLink = NotifyNode;
}
gConSplitterTextInNotifyList = NotifyNode;
gConSplitterTextInCountVar2++;
*NotifyHandle = NotifyNode;
return EFI_SUCCESS;
}
/**
* ConSplitterUnregisterKeyNotify
* Address: 0x3D08
*
* Unregisters a previously registered key notification.
* Removes the notification from all child devices and frees
* the notification node.
*/
EFI_STATUS
ConSplitterUnregisterKeyNotify (
IN VOID *This,
IN VOID *NotificationHandle
)
{
KEY_NOTIFY_NODE *NotifyNode;
KEY_NOTIFY_NODE *CurrentNode;
UINT32 ChildIndex;
if (NotificationHandle == NULL) {
return EFI_INVALID_PARAMETER;
}
if (gConSplitterTextOutBlockedFlag) {
return EFI_SPLITTER_PROTOCOL_ERROR;
}
// Find the notification node in the linked list
CurrentNode = gConSplitterTextInNotifyList;
while (CurrentNode != NULL) {
if (CurrentNode == (KEY_NOTIFY_NODE *)NotificationHandle) {
break;
}
CurrentNode = CurrentNode->ForwardLink;
}
if (CurrentNode == NULL) {
return EFI_INVALID_PARAMETER;
}
NotifyNode = CurrentNode;
// Unregister from each child device
if (gConTextInputChildren != NULL) {
TEXT_IN_NODE *TextInNode;
UINT32 Count;
TextInNode = gConTextInputChildren;
Count = (UINT32)gConSplitterTextInActiveCount;
for (ChildIndex = 0; ChildIndex < gConSplitterTextInActiveCount; ChildIndex++) {
VOID *ChildPtr = (VOID *)(UINTN)NotifyNode->PerDeviceHandles[ChildIndex];
VOID *NotifyTag = (VOID *)(UINTN)NotifyNode->PerDeviceBadTags[ChildIndex];
if (ChildPtr != NULL && NotifyTag != (VOID *)0x00BADBAD) {
TEXT_IN_NODE *ChildNode = (TEXT_IN_NODE *)ChildPtr;
if (ChildNode->TextOutInterface != NULL) {
((EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *)ChildNode->TextOutInterface)
->UnregisterKeyNotify (
ChildNode->TextOutInterface,
NotifyTag
);
}
}
TextInNode = TextInNode->ForwardLink;
}
}
// Remove from linked list
if (NotifyNode->BackLink != NULL) {
NotifyNode->BackLink->ForwardLink = NotifyNode->ForwardLink;
}
if (NotifyNode->ForwardLink != NULL) {
NotifyNode->ForwardLink->BackLink = NotifyNode->BackLink;
}
if (gConSplitterTextInNotifyList == NotifyNode) {
gConSplitterTextInNotifyList = NotifyNode->BackLink;
}
gBS->FreePool (NotifyNode);
gConSplitterTextInCountVar2--;
return EFI_SUCCESS;
}
//
// ======================================================================
// SECTION 10: Component Name Protocol
// ======================================================================
//
/**
* ConSplitterGetDriverName
* Address: 0x4A8
*
* Returns the driver name based on which driver binding protocol
* is being queried.
*/
EFI_STATUS
EFIAPI
ConSplitterGetDriverName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN CHAR8 *Language,
OUT CHAR16 **DriverName
)
{
if (Language == NULL || DriverName == NULL) {
return EFI_INVALID_PARAMETER;
}
if (!ConSplitterIsLanguageEnUs (Language)) {
return EFI_UNSUPPORTED;
}
if (This == (EFI_COMPONENT_NAME_PROTOCOL *)&gConSplitterTextOutComponentName) {
*DriverName = L"AMI Console Splitter Text Out Driver";
} else if (This == (EFI_COMPONENT_NAME_PROTOCOL *)&gConSplitterTextInComponentName) {
*DriverName = L"AMI Console Splitter Text In Driver";
} else if (This == (EFI_COMPONENT_NAME_PROTOCOL *)&gConSplitterPointerComponentName) {
*DriverName = L"AMI Console Splitter Pointer Driver";
} else {
*DriverName = L"AMI Console Splitter Driver";
}
return EFI_SUCCESS;
}
/**
* ConSplitterGetControllerName
* Address: 0x44C
*
* Returns the controller name. Only "AMI Console Splitter" is
* returned for all supported controllers.
*/
EFI_STATUS
EFIAPI
ConSplitterGetControllerName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE ChildHandle,
IN CHAR8 *Language,
OUT CHAR16 **ControllerName
)
{
if (ControllerName == NULL ||
ControllerHandle == NULL ||
Language == NULL) {
return EFI_INVALID_PARAMETER;
}
if ((UINT64)ControllerHandle != gBootServicesLocal || !ConSplitterIsLanguageEnUs (Language)) {
return EFI_UNSUPPORTED;
}
*ControllerName = L"AMI Console Splitter";
return EFI_SUCCESS;
}
//
// ======================================================================
// SECTION 11: Console Notification Handlers
// ======================================================================
//
/**
* ConSplitterConOutNotifyHandler
* Address: 0x800
*
* Called when a new ConOut device appears. Registers the device
* path protocol and notifies the console event.
*/
VOID
EFIAPI
ConSplitterConOutNotifyHandler (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut;
// Find any text output handles
Handle = NULL;
while (TRUE) {
Status = gBS->LocateHandle (
ByRegisterNotify,
&gEfiSimpleTextOutProtocolGuid,
gConSplitterConOutNotifyReg,
&Handle
);
if (EFI_ERROR(Status)) {
break;
}
// Try to start the driver on this handle
if (gConSplitterTextOutActiveCount > 0 ||
gConSplitterTextOutActiveCount == 0) {
ConSplitterTextOutDriverBindingStart (
&gConSplitterTextOutDriverBinding,
Handle,
NULL
);
}
}
// Register device path protocol notify if not yet done
if (gConSplitterTextOutActiveCount == 0) {
gBS->RegisterProtocolNotify (
&gEfiDevicePathProtocolGuid,
gConSplitterConOutNotifyEvent,
&gConSplitterConOutNotifyReg
);
}
}
/**
* ConSplitterConInNotifyHandler
* Address: 0x938
*
* Called when a new ConIn device appears. Registers the device
* and sets up the keyboard notification filters.
*/
VOID
EFIAPI
ConSplitterConInNotifyHandler (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
UINT32 ModeValue;
UINT32 SavedMode;
// Find any text input handles
Handle = NULL;
SavedMode = 0;
ModeValue = 0;
while (TRUE) {
Status = gBS->LocateHandle (
ByRegisterNotify,
&gEfiSimpleTextInProtocolGuid,
gConSplitterConInNotifyReg,
&Handle
);
if (EFI_ERROR(Status)) {
break;
}
// Start the driver
if (gConSplitterTextInActiveCount > 0 ||
(gConSplitterTextInActiveCount == 0 && gConSplitterModeTable == NULL)) {
ConSplitterTextInDriverBindingStart (
&gConSplitterTextInDriverBinding,
Handle,
NULL
);
}
}
// Set the system table console input handle and protocol
SystemTable->ConsoleInHandle = gConSplitterConInInterface;
SystemTable->ConIn = gConSplitterConInProtocol;
// Set up system table mode
SystemTable->Mode->Mode = 0;
gBS->CalculateCrc32 (SystemTable, 120, &ModeValue);
SystemTable->Mode->Mode = ModeValue;
// Register the 8 default key filters
{
KEY_FILTER_ENTRY *Filters;
UINT32 i;
Filters = (KEY_FILTER_ENTRY *)gConSplitterKeyFilter;
gConSplitterKeyboardTestPassed = TRUE;
for (i = 0; i < CON_SPLITTER_KEY_FILTER_COUNT; i++) {
EFI_KEY_DATA KeyData;
KeyData.Key.ScanCode = Filters[i].ScanCode;
KeyData.Key.UnicodeChar = Filters[i].UnicodeChar;
KeyData.KeyState.KeyShiftState = Filters[i].ShiftState;
ConSplitterRegisterKeyNotify (
gConSplitterConInInterface,
&KeyData,
ConSplitterDefaultKeyNotify,
NULL
);
}
}
if (gConSplitterTextBuffer != NULL) {
gBS->FreePool (gConSplitterTextBuffer);
gConSplitterTextBuffer = NULL;
}
}
/**
* ConSplitterStdErrNotifyHandler
* Address: Similar to ConOut handler
*
* Called when a new StdErr device appears.
*/
VOID
EFIAPI
ConSplitterStdErrNotifyHandler (
IN EFI_EVENT Event,
IN VOID *Context
)
{
// Similar to ConOutNotifyHandler but for StdErr
// Opens EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on the handle
}
//
// ======================================================================
// SECTION 12: Debug and Diagnostic Functions
// ======================================================================
//
/**
* ConSplitterDumpModeTable
* Address: 0x2D60
*
* Debug function that dumps the mode table to the debug port.
*/
VOID
ConSplitterDumpModeTable (
VOID
)
{
UINT32 Index;
DEBUG ((EFI_D_INFO, "\n"));
for (Index = 0; Index < gConSplitterModeTableSize; Index++) {
UINT32 Columns = *(UINT32 *)(gConSplitterModeTable + 9 * Index + 0);
UINT32 Rows = *(UINT32 *)(gConSplitterModeTable + 9 * Index + 4);
UINT8 Valid = *(UINT8 *)(gConSplitterModeTable + 9 * Index + 8);
DEBUG ((EFI_D_INFO, "Rows = %d Col = %d AllDevices = %d \n", Rows, Columns, Valid));
}
}
/**
* ConSplitterGetDebugLevel
* Address: 0x5214
*
* Reads the debug level from CMOS/RTC, or from a hardcoded
* memory location. Returns a bitmask for use with DEBUG() macros.
*/
UINTN
ConSplitterGetDebugLevel (
VOID
)
{
UINT8 IndexRegister;
UINT8 DataRegister;
// Read CMOS index register 0x70, save high bit (NMI enable)
IndexRegister = __inbyte (0x70);
__outbyte (0x70, IndexRegister & 0x80 | 0x4B);
DataRegister = __inbyte (0x71);
if (DataRegister > 3) {
if (DataRegister == 0) {
// Fallback: read from fixed memory location
DataRegister = *(volatile UINT8 *)0xFDAF0490 & 2 | 1;
}
}
if (DataRegister == 0 || DataRegister > 4) {
return 0;
}
if (DataRegister == 1) {
return 0x80000004; // EFI_D_ERROR only
}
return 0x80000006; // EFI_D_ERROR | EFI_D_WARN
}
/**
* ConSplitterCompareMem
* Address: 0x519C
*
* Compares two memory buffers. Returns 0 if equal, difference otherwise.
*/
INTN
ConSplitterCompareMem (
IN VOID *Destination,
IN VOID *Source,
IN UINTN Length
)
{
return CompareMem (Destination, Source, Length);
}
/**
* ConSplitterCopyMem
* Address: 0x53C0
*
* Copies a memory buffer. Handles overlapping source/destination
* with correct direction.
*/
VOID
ConSplitterCopyMem (
IN VOID *Destination,
IN VOID *Source,
IN UINTN Length
)
{
CopyMem (Destination, Source, Length);
}
/**
* ConSplitterIsLanguageEnUs
* Address: 0x5130
*
* Case-insensitive check if language string equals "en-US".
*/
BOOLEAN
ConSplitterIsLanguageEnUs (
IN CHAR8 *Language
)
{
CHAR8 *EnUs = "en-US";
CHAR8 C1, C2;
if (Language == NULL) {
return FALSE;
}
while (*Language) {
C1 = *Language;
C2 = *EnUs;
if (C1 != C2) {
// Case-insensitive comparison
if (C1 >= 'a' && C1 <= 'z') C1 -= 0x20;
if (C2 >= 'a' && C2 <= 'z') C2 -= 0x20;
if (C1 >= 'A' && C1 <= 'Z') { }
if (C2 >= 'A' && C2 <= 'Z') { }
if (C1 != C2) return FALSE;
}
Language++;
EnUs++;
}
return (*Language == *EnUs);
}
/**
* ConSplitterReadUnaligned64
* Address: 0x5264
*
* Reads a 64-bit value from a possibly unaligned address.
*/
UINT64
ConSplitterReadUnaligned64 (
IN UINT64 *Buffer
)
{
if (Buffer == NULL) {
ASSERT (Buffer != NULL);
}
return *Buffer;
}
/**
* ConSplitterIsProtocolGuidMatch
* Address: 0x5294
*
* Compares two protocol GUID entries to see if they match.
*/
BOOLEAN
ConSplitterIsProtocolGuidMatch (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
return CompareGuid (Guid1, Guid2);
}
/**
* ConSplitterLocateHob
* Address: 0x4BF8
*
* Locates the HOB (Hand-Off Block) list pointer.
*/
EFI_STATUS
ConSplitterLocateHob (
IN EFI_HANDLE ImageHandle
)
{
if (gConSplitterHobListHandle == 0) {
EFI_HOB_LIST *HobList;
// Get HOB list from SystemTable->BootServices
HobList = (EFI_HOB_LIST *)gBS->GetHobList ();
if (HobList == NULL) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
ASSERT_EFI_ERROR (EFI_NOT_FOUND);
return EFI_NOT_FOUND;
}
gConSplitterHobListHandle = (UINT64)HobList;
}
return EFI_SUCCESS;
}
//
// ======================================================================
// SECTION 13: Console Mode Management
// ======================================================================
//
/**
* ConSplitterSynchronizeTextOut
* Address: 0x4188
*
* Synchronizes text output state (mode, cursor position, attribute)
* from the internal buffer to the child devices.
*/
VOID
ConSplitterSynchronizeTextOut (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ReferenceDevice,
IN UINTN ModeNumber
)
{
EFI_UGA_DRAW_PROTOCOL *UgaDraw;
EFI_STATUS Status;
Status = gBS->HandleProtocol (
ReferenceDevice,
&gEfiUgaDrawProtocolGuid,
&UgaDraw
);
if (!EFI_ERROR(Status)) {
// We have UGA Draw protocol - use it for mode switching
if (gConSplitterTextBufDst != NULL) {
// Synchronize display buffer
ConSplitterFlushBuffer (ReferenceDevice);
gBS->FreePool (gConSplitterTextBufDst);
gConSplitterTextBufDst = NULL;
gBS->FreePool (gConSplitterTextBufDstAttr);
gConSplitterTextBufDstAttr = NULL;
}
} else {
// No UGA Draw - use text output scrolling
ConSplitterScrollTextBuffer (ReferenceDevice, NULL);
}
// Restore cursor position and attribute
ReferenceDevice->SetCursorPosition (ReferenceDevice,
gConSplitterCursorColumn, gConSplitterCursorRow);
ReferenceDevice->SetAttribute (ReferenceDevice, gConSplitterDefaultAttr);
ReferenceDevice->SetCursorPosition (ReferenceDevice,
gConSplitterCursorColumn, gConSplitterCursorRow);
}
/**
* ConSplitterFlushBuffer
* Address: 0x4010
*
* Flushes the internal text buffer to the child device using
* UGA Draw protocol (Blt).
*/
VOID
ConSplitterFlushBuffer (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ReferenceDevice
)
{
EFI_UGA_DRAW_PROTOCOL *UgaDraw;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
UINT32 BltSize;
EFI_STATUS Status;
if (gConSplitterTextBufDst != NULL) {
gBS->FreePool (gConSplitterTextBufDst);
gConSplitterTextBufDst = NULL;
}
// Get UGA Draw protocol from the reference device
Status = gBS->HandleProtocol (
ReferenceDevice,
&gEfiUgaDrawProtocolGuid,
&UgaDraw
);
if (EFI_ERROR(Status)) {
return;
}
// Calculate Blt buffer size from current mode dimensions
BltSize = sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) *
gConSplitterColumns * gConSplitterRows;
gConSplitterTextBufDst = AllocatePool (BltSize);
if (gConSplitterTextBufDst == NULL) {
return;
}
// Blt the buffer to the screen
Status = UgaDraw->Blt (
UgaDraw,
gConSplitterTextBufDst,
EfiBltBufferToVideo,
0, 0,
0, 0,
gConSplitterColumns,
gConSplitterRows,
0
);
if (EFI_ERROR(Status)) {
gBS->FreePool (gConSplitterTextBufDst);
gConSplitterTextBufDst = NULL;
}
}
/**
* ConSplitterScrollTextBuffer
* Address: 0x3EDC
*
* Scrolls the text buffer by one line and outputs to the device.
*/
VOID
ConSplitterScrollTextBuffer (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN CHAR16 *String
)
{
CHAR16 *Src;
CHAR16 *SrcEnd;
CHAR16 *Dst;
UINT32 *AttribSrc;
UINT32 Cols;
UINT32 FirstLineAttr;
Src = gConSplitterTextBuffer;
SrcEnd = gConSplitterTextAttrib;
Dst = gConSplitterTextBuffer;
Cols = gConSplitterColumns;
if (gConSplitterBufferClean) {
return;
}
// Read the attribute of the first character in the first line
FirstLineAttr = *(UINT32 *)gConSplitterTextAttrib;
// Set mode and cursor
This->SetMode (This, 0);
This->SetAttribute (This, FirstLineAttr);
// Scroll character by character
while ((UINTN)Src < (UINTN)SrcEnd) {
CHAR16 SaveChar;
if (*(UINT32 *)AttribSrc == FirstLineAttr) {
// End of line marker found
}
SaveChar = *Src;
*Src = 0;
This->OutputString (This, Dst);
*Src = SaveChar;
Dst = Src;
if (*(UINT32 *)AttribSrc != FirstLineAttr) {
break;
}
FirstLineAttr = *(UINT32 *)AttribSrc;
Src++;
AttribSrc++;
}
// Set new attribute
This->SetAttribute (This, *(UINT32 *)AttribSrc);
}
/**
* ConSplitterClearTextBuffer
* Address: 0x3FB4
*
* Fills the text buffer with spaces and default attribute.
*/
VOID
ConSplitterClearTextBuffer (
VOID
)
{
CHAR16 *TextPos;
UINT32 *AttribPos;
UINT64 Count;
TextPos = (CHAR16 *)gConSplitterTextBuffer;
AttribPos = (UINT32 *)gConSplitterTextAttrib;
if (gConSplitterTextBuffer > gConSplitterTextBufEnd) {
Count = 0;
} else {
Count = ((UINT64)gConSplitterTextBufEnd - (UINT64)gConSplitterTextBuffer + 1) >> 1;
}
for (; Count > 0; Count--) {
*TextPos++ = L' ';
*AttribPos++ = gConSplitterDefaultAttr;
}
gConSplitterBufferClean = TRUE;
}
/**
* ConSplitterReconstructModeTable
* Address: 0x2350 + 0x25FC
*
* Reconstructs the mode table from a reference child device.
* Allocates and fills the mode table with rows/columns information.
*/
EFI_STATUS
ConSplitterReconstructModeTable (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ReferenceDevice
)
{
UINT32 MaxMode;
UINT32 *ModePtr;
UINT32 ModeIndex;
UINT32 Columns;
UINT32 Rows;
UINT32 PrevColumns;
UINT32 PrevRows;
UINT32 SavedGlobalMode;
EFI_STATUS Status;
if (ReferenceDevice == gConSplitterConOutInterface) {
return EFI_SUCCESS;
}
// Get max mode from reference device
ModePtr = (UINT32 *)ReferenceDevice->Mode;
MaxMode = *ModePtr;
gConSplitterModeTableSize = MaxMode;
// Allocate mode table: 9 bytes per mode entry
{
UINT32 EntrySize = 9; // sizeof = 4(cols) + 4(rows) + 1(valid)
UINT8 *Table;
UINT32 i;
// Free old table if it exists
if (gConSplitterModeTable != NULL) {
gBS->FreePool (gConSplitterModeTable);
}
Table = AllocateZeroPool (MaxMode * EntrySize);
if (Table == NULL) {
gConSplitterModeTableSize = 0;
return EFI_OUT_OF_RESOURCES;
}
gConSplitterModeTable = Table;
// Fill mode table
for (i = 0; i < MaxMode; i++) {
UINTN Col, Row;
Status = ReferenceDevice->QueryMode (
ReferenceDevice,
i,
&Col,
&Row
);
*(UINT32 *)(Table + EntrySize * i + 0) = (UINT32)Col;
*(UINT32 *)(Table + EntrySize * i + 4) = (UINT32)Row;
*(UINT8 *)(Table + EntrySize * i + 8) = !EFI_ERROR(Status);
}
}
// Store previous mode if valid
SavedGlobalMode = gConSplitterGlobalMode;
// Set mode to 0
gConSplitterGlobalMode = 0xFFFFFFFF;
Status = ConSplitterSetConsoleMode (ReferenceDevice, 0);
// Apply default cursor setting
ConSplitterSetResolutionData ();
return Status;
}