alder_lake_bios/Insyde/InsydeCrPkg/TerminalDxe/Terminal.c

2410 lines
68 KiB
C

/** @file
Produces Simple Text Input Protocol, Simple Text Input Extended Protocol and
Simple Text Output Protocol upon Serial IO Protocol.
;******************************************************************************
;* Copyright (c) 2019, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include "Terminal.h"
//
// Globals
//
EFI_DRIVER_BINDING_PROTOCOL gTerminalDriverBinding = {
TerminalDriverBindingSupported,
TerminalDriverBindingStart,
TerminalDriverBindingStop,
0xa,
NULL,
NULL
};
EFI_GUID *gTerminalType[] = {
&gEfiPcAnsiGuid,
&gEfiVT100Guid,
&gEfiVT100PlusGuid,
&gEfiVTUTF8Guid,
&gH2OCrLogTermGuid
};
TERMINAL_DEV mTerminalDevTemplate = {
TERMINAL_DEV_SIGNATURE,
NULL,
0,
NULL,
NULL,
{ // SimpleTextInput
TerminalConInReset,
TerminalConInReadKeyStroke,
NULL
},
{ // SimpleTextOutput
CommonConOutReset,
CommonConOutOutputString,
CommonConOutTestString,
CommonConOutQueryMode,
CommonConOutSetMode,
CommonConOutSetAttribute,
CommonConOutClearScreen,
CommonConOutSetCursorPosition,
CommonConOutEnableCursor,
NULL
},
{ // SimpleTextOutputMode
1, // MaxMode
0, // Mode
EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK), // Attribute
0, // CursorColumn
0, // CursorRow
TRUE // CursorVisible
},
NULL, // TerminalConsoleModeData
0, // SerialInTimeOut
NULL, // RawFifo
NULL, // UnicodeFiFo
NULL, // EfiKeyFiFo
NULL, // ControllerNameTable
NULL, // TimerEvent
NULL, // TwoSecondTimeOut
INPUT_STATE_DEFAULT,
RESET_STATE_DEFAULT,
FALSE,
{ // SimpleTextInputEx
TerminalConInResetEx,
TerminalConInReadKeyStrokeEx,
NULL,
TerminalConInSetState,
TerminalConInRegisterKeyNotify,
TerminalConInUnregisterKeyNotify,
},
{ // NotifyList
NULL,
NULL,
},
0x00, // feature flag
FALSE, // Enable Console Redirection
{ // TerminalEscCode
0,
gEscSequenceCode,
gEfiToKbScanCodeMap,
0,
gCrSpecialCommand,
},
NULL, // ResetTimerEvent
NULL, // TempFiFo
NULL, // SpcFiFo
NULL, // EventRefreshScreen
NULL, // PtrScrChar
NULL, // PtrTerChar
NULL, // PtrScrAttr
NULL, // PtrTerAttr
0, // LastRow
0, // TerRow
0, // TerCol
0, // TermCurAttr
0, // Vt100CurrentCharSet
NULL, // EventAutoRefresh
FALSE, // RemoteTermExist
FALSE, // LastRemoteTermStatus
FALSE, // NonVt100AltKey
};
TERMINAL_CONSOLE_MODE_DATA mDefaultConsoleModeData[] = {
//
// Mode 0 and mode 1 is for 80x25, 80x50 according to UEFI spec.
//
{80,25}, // Mode0
{80,50}, // Mode1
{100,31}, // Mode2
{128,40}, // Mode3, C.R. default mode.
//
// New modes can be added here.
//
};
TERMINAL_CONSOLE_MODE_DATA *mTerminalConsoleModeData = NULL;
UINTN mModeCount = 0;
UINTN mTerminalCount = 0;
CR_POLICY_VARIABLE *mCrPolicyVar = NULL;
UINT8 mTerminalMode = SYNC;
extern LIST_ENTRY *mCurrentTermEvent;
/**
End of Boot Selection notify function.
@param Event Event whose notification function is being invoked.
@param Context The pointer to the notification function's context,
which is implementation-dependent.
@retval EFI_SUCESS This function always complete successfully.
**/
VOID
EFIAPI
EndofBootSelectionNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
TERMINAL_REFRESH_EVENT_LIST *TermEventList;
if (mTerminalCount == 0) {
return;
}
if (mCrPolicyVar->Feature.Bit.CRAsyncTerm == TRUE) {
//
// Force Async mode: Shell, SCU, FrontPage, Legacy OS.
//
TermEventList = CR (mCurrentTermEvent, TERMINAL_REFRESH_EVENT_LIST, Link, TERM_REFRESH_EVENT_LIST_SIGNATURE);
if (TermEventList->RefreshEvent != NULL) {
mTerminalMode = ASYNC;
gBS->SetTimer (TermEventList->RefreshEvent, TimerRelative, REFRESH_ROUTING_INTERVAL);
}
} else {
//
// Auto switch mode: Async mode only at SCU. others use Sync mode.
//
}
}
/**
Entering SetupUtility notify function.
@param Event Event whose notification function is being invoked.
@param Context The pointer to the notification function's context,
which is implementation-dependent.
@retval EFI_SUCESS This function always complete successfully.
**/
VOID
EFIAPI
SwitchConOutModeNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
EFI_SETUP_UTILITY_APPLICATION_PROTOCOL *SetupUtilityApp;
TERMINAL_REFRESH_EVENT_LIST *TermEventList;
if (mTerminalCount == 0) {
return;
}
if (mCrPolicyVar->Feature.Bit.CRAsyncTerm == TRUE) {
//
// Don't switch terminal mode.
// CRAsyncTerm == TRUE : Force Async mode.
//
return;
}
TermEventList = CR (mCurrentTermEvent, TERMINAL_REFRESH_EVENT_LIST, Link, TERM_REFRESH_EVENT_LIST_SIGNATURE);
Status = gBS->LocateProtocol (&gEfiSetupUtilityApplicationProtocolGuid, NULL, (VOID **) &SetupUtilityApp);
if (EFI_ERROR (Status)) {
return;
}
if (SetupUtilityApp->VfrDriverState == InitializeSetupUtility) {
mTerminalMode = ASYNC;
gBS->SetTimer (TermEventList->RefreshEvent, TimerRelative, REFRESH_ROUTING_INTERVAL);
} else if (SetupUtilityApp->VfrDriverState == ShutdownSetupUtility) {
gBS->SetTimer (TermEventList->RefreshEvent, TimerCancel, REFRESH_ROUTING_INTERVAL);
mTerminalMode = SYNC;
}
}
/**
The user Entry Point for module Terminal. The user code starts with this function.
@param ImageHandle The firmware allocated handle for the EFI image.
@param SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The entry point is executed successfully.
@retval other Some error occurs when executing this entry point.
**/
EFI_STATUS
EFIAPI
InitializeTerminal(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *Registration;
EFI_EVENT Event;
//
// Initial Link List of Refresh Event
//
InitRefreshEventList ();
//
// Install driver model protocol(s).
//
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gTerminalDriverBinding,
ImageHandle,
&gTerminalComponentName,
&gTerminalComponentName2
);
ASSERT_EFI_ERROR (Status);
mCrPolicyVar = CommonGetVariableData (CR_POLICY_NAME, &gH2OCrConfigurationGuid);
if (mCrPolicyVar == NULL) {
//
// Console redirection disable
//
return Status;
}
//
// Before EndOfBdsBootSelection, only support sync mode.
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
EndofBootSelectionNotify,
NULL,
&Event
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = gBS->RegisterProtocolNotify (
&gEndOfBdsBootSelectionProtocolGuid,
Event,
&Registration
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// When execute Setup Utility application, Switch terminal to async mode
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
SwitchConOutModeNotify,
NULL,
&Event
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = gBS->RegisterProtocolNotify (
&gEfiSetupUtilityApplicationProtocolGuid,
Event,
&Registration
);
return Status;
}
/**
Test to see if this driver supports Controller.
@param This Protocol instance pointer.
@param Controller Handle of device to test
@param RemainingDevicePath Optional parameter use to pick a specific child
device to start.
@retval EFI_SUCCESS This driver supports this device.
@retval EFI_ALREADY_STARTED This driver is already running on this device.
@retval other This driver does not support this device.
**/
EFI_STATUS
EFIAPI
TerminalDriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
EFI_SERIAL_IO_PROTOCOL *SerialIo;
VENDOR_DEVICE_PATH *Node;
if (RemainingDevicePath == NULL) {
//
//If remaining device path is NULL, then didn't connect terminal driver.
//SCU locates EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL to output screen, so terminal driver didn't
//connect terminal device when disable Console Redirection.
//
return EFI_UNSUPPORTED;
} else {
//
// If remaining device path is not NULL, then make sure it is a
// device path that describes a terminal communications protocol.
//
//
// Check if RemainingDevicePath is the End of Device Path Node,
// if yes, go on checking other conditions
//
if (!IsDevicePathEnd (RemainingDevicePath)) {
//
// If RemainingDevicePath isn't the End of Device Path Node,
// check its validation
//
Node = (VENDOR_DEVICE_PATH *) RemainingDevicePath;
if (Node->Header.Type != MESSAGING_DEVICE_PATH ||
Node->Header.SubType != MSG_VENDOR_DP ||
DevicePathNodeLength(&Node->Header) != sizeof(VENDOR_DEVICE_PATH)) {
return EFI_UNSUPPORTED;
}
//
// only supports PC ANSI, VT100, VT100+ and VT-UTF8 terminal types
//
if (!CompareGuid (&Node->Guid, &gEfiPcAnsiGuid) &&
!CompareGuid (&Node->Guid, &gEfiVT100Guid) &&
!CompareGuid (&Node->Guid, &gEfiVT100PlusGuid) &&
!CompareGuid (&Node->Guid, &gEfiVTUTF8Guid) &&
!CompareGuid (&Node->Guid, &gH2OCrLogTermGuid)
) {
return EFI_UNSUPPORTED;
}
}
}
//
// Open the IO Abstraction(s) needed to perform the supported test
// The Controller must support the Serial I/O Protocol.
// This driver is a bus driver with at most 1 child device, so it is
// ok for it to be already started.
//
Status = gBS->OpenProtocol (
Controller,
&gEfiSerialIoProtocolGuid,
(VOID **) &SerialIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_ALREADY_STARTED) {
return EFI_SUCCESS;
}
if (EFI_ERROR (Status)) {
return Status;
}
//
// Close the I/O Abstraction(s) used to perform the supported test
//
gBS->CloseProtocol (
Controller,
&gEfiSerialIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
//
// Open the EFI Device Path protocol needed to perform the supported test
//
Status = gBS->OpenProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &ParentDevicePath,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_ALREADY_STARTED) {
return EFI_SUCCESS;
}
if (EFI_ERROR (Status)) {
return Status;
}
//
// Close protocol, don't use device path protocol in the Support() function
//
gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
Controller
);
return Status;
}
/**
Build the terminal device path for the child device according to the
terminal type.
@param ParentDevicePath Parent device path.
@param RemainingDevicePath A specific child device.
@return The child device path built.
**/
EFI_DEVICE_PATH_PROTOCOL*
EFIAPI
BuildTerminalDevpath (
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL *TerminalDevicePath;
UINT8 TerminalType;
VENDOR_DEVICE_PATH *Node;
EFI_STATUS Status;
TerminalDevicePath = NULL;
TerminalType = PCANSITYPE;
//
// Use the RemainingDevicePath to determine the terminal type
//
Node = (VENDOR_DEVICE_PATH *) RemainingDevicePath;
if (Node == NULL) {
TerminalType = PCANSITYPE;
} else if (CompareGuid (&Node->Guid, &gEfiPcAnsiGuid)) {
TerminalType = PCANSITYPE;
} else if (CompareGuid (&Node->Guid, &gEfiVT100Guid)) {
TerminalType = VT100TYPE;
} else if (CompareGuid (&Node->Guid, &gEfiVT100PlusGuid)) {
TerminalType = VT100PLUSTYPE;
} else if (CompareGuid (&Node->Guid, &gEfiVTUTF8Guid)) {
TerminalType = VTUTF8TYPE;
} else if (CompareGuid (&Node->Guid, &gH2OCrLogTermGuid)) {
TerminalType = LOGTERMTYPE;
} else {
return NULL;
}
//
// Build the device path for the child device
//
Status = SetTerminalDevicePath (
TerminalType,
ParentDevicePath,
&TerminalDevicePath
);
if (EFI_ERROR (Status)) {
return NULL;
}
return TerminalDevicePath;
}
/**
Compare a device path data structure to that of all the nodes of a
second device path instance.
@param Multi A pointer to a multi-instance device path data structure.
@param Single A pointer to a single-instance device path data structure.
@retval TRUE If the Single is contained within Multi.
@retval FALSE The Single is not match within Multi.
**/
BOOLEAN
MatchDevicePaths (
IN EFI_DEVICE_PATH_PROTOCOL *Multi,
IN EFI_DEVICE_PATH_PROTOCOL *Single
)
{
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_DEVICE_PATH_PROTOCOL *DevicePathInst;
UINTN Size;
DevicePath = Multi;
DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
//
// Search for the match of 'Single' in 'Multi'
//
while (DevicePathInst != NULL) {
//
// If the single device path is found in multiple device paths,
// return success
//
if (CompareMem (Single, DevicePathInst, Size) == 0) {
FreePool (DevicePathInst);
return TRUE;
}
FreePool (DevicePathInst);
DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
}
return FALSE;
}
/**
Check whether the terminal device path is in the global variable.
@param VariableName Pointer to one global variable.
@param TerminalDevicePath Pointer to the terminal device's device path.
@retval TRUE The devcie is in the global variable.
@retval FALSE The devcie is not in the global variable.
**/
BOOLEAN
IsTerminalInConsoleVariable (
IN CHAR16 *VariableName,
IN EFI_DEVICE_PATH_PROTOCOL *TerminalDevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL *Variable;
BOOLEAN ReturnFlag;
//
// Get global variable and its size according to the name given.
//
GetEfiGlobalVariable2 (VariableName, (VOID**)&Variable, NULL);
if (Variable == NULL) {
return FALSE;
}
//
// Check whether the terminal device path is one of the variable instances.
//
ReturnFlag = MatchDevicePaths (Variable, TerminalDevicePath);
FreePool (Variable);
return ReturnFlag;
}
/**
Free notify functions list.
@param ListHead The list head
@retval EFI_SUCCESS Free the notify list successfully.
@retval EFI_INVALID_PARAMETER ListHead is NULL.
**/
EFI_STATUS
TerminalFreeNotifyList (
IN OUT LIST_ENTRY *ListHead
)
{
TERMINAL_CONSOLE_IN_EX_NOTIFY *NotifyNode;
if (ListHead == NULL) {
return EFI_INVALID_PARAMETER;
}
while (!IsListEmpty (ListHead)) {
NotifyNode = CR (
ListHead->ForwardLink,
TERMINAL_CONSOLE_IN_EX_NOTIFY,
NotifyEntry,
TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE
);
RemoveEntryList (ListHead->ForwardLink);
FreePool (NotifyNode);
}
return EFI_SUCCESS;
}
/**
Initialize all the text modes which the terminal console supports.
It returns information for available text modes that the terminal can support.
@param[out] None.
@retval EFI_SUCCESS The supporting mode information is returned.
@retval EFI_INVALID_PARAMETER The parameters are invalid.
**/
EFI_STATUS
InitializeTerminalConsoleTextMode (
VOID
)
{
UINTN Index;
UINTN NewIndex;
UINTN ModeCount;
UINTN BufferCount;
UINTN Rows;
UINTN Columns;
UINTN CountHandles;
UINTN MaxMode;
UINTN i;
UINTN SizeOfInfo;
BOOLEAN IsInConOutVariable;
EFI_HANDLE *DeviceHandles;
EFI_HANDLE GopHandle;
EFI_STATUS Status;
TERMINAL_CONSOLE_MODE_DATA *ModeBuffer;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
ModeCount = 0;
ModeBuffer = NULL;
DeviceHandles = NULL;
GopHandle = NULL;
IsInConOutVariable = FALSE;
//
// Create a default mode data.
//
ModeCount = sizeof (mDefaultConsoleModeData) / sizeof (TERMINAL_CONSOLE_MODE_DATA);
ModeBuffer = AllocateCopyPool (sizeof (mDefaultConsoleModeData), mDefaultConsoleModeData);
//
// Get only one console out device. Because consplitter select intersection of
// each console out device.
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiGraphicsOutputProtocolGuid,
NULL,
&CountHandles,
&DeviceHandles
);
if (!EFI_ERROR(Status)) {
for (i = 0; i < CountHandles; i++) {
Status = gBS->HandleProtocol (
DeviceHandles[i],
&gEfiDevicePathProtocolGuid,
(VOID *) &DevicePath
);
if (!EFI_ERROR(Status)) {
//
// Check is device path in ConOut variable or not
//
Status = TerminalCheckConsoleDevVariable (L"ConOut", DevicePath);
if (!EFI_ERROR(Status)) {
IsInConOutVariable = TRUE;
GopHandle = DeviceHandles[i];
break;
}
}
}
}
if (!IsInConOutVariable) {
DEBUG ((DEBUG_INFO, "Terminal get console out device fail: %r\n", Status));
DEBUG ((DEBUG_INFO, "Using default mode data!\n"));
goto exit;
}
Status = gBS->HandleProtocol (
GopHandle,
&gEfiGraphicsOutputProtocolGuid,
(VOID **) &GraphicsOutput
);
if (EFI_ERROR(Status)) {
goto exit;
}
MaxMode = GraphicsOutput->Mode->MaxMode;
BufferCount = MaxMode + ModeCount;
ModeBuffer = ReallocatePool (
sizeof (mDefaultConsoleModeData),
sizeof (TERMINAL_CONSOLE_MODE_DATA) * BufferCount,
(VOID*)ModeBuffer
);
if (ModeBuffer == NULL) {
gBS->FreePool (DeviceHandles);
gBS->FreePool (ModeBuffer);
return EFI_OUT_OF_RESOURCES;
}
//
// Add VGA text mode into terminal mode data
//
for (Index = 0; Index < MaxMode; Index++) {
Status = GraphicsOutput->QueryMode (
GraphicsOutput,
(UINT32)Index,
&SizeOfInfo,
&Info
);
if (EFI_ERROR (Status)) {
continue;
}
Columns = Info->HorizontalResolution / EFI_GLYPH_WIDTH;
Rows = Info->VerticalResolution / EFI_GLYPH_HEIGHT;
for (NewIndex = 0; NewIndex < BufferCount; NewIndex++ ) {
if ((ModeBuffer[NewIndex].Columns == Columns) &&
(ModeBuffer[NewIndex].Rows == Rows)) {
//
// Skip the duplicated mode.
//
break;
}
}
if (NewIndex == BufferCount) {
//
// No duplicated. Save mode data.
//
ModeBuffer[ModeCount].Columns = Columns;
ModeBuffer[ModeCount].Rows = Rows;
ModeCount++;
}
}
exit:
if (DeviceHandles != NULL) {
gBS->FreePool (DeviceHandles);
}
if (mCrPolicyVar != NULL) {
if (mCrPolicyVar->Feature.Bit.CRForce80x25) {
//
// Only support mode 0 (80x25)
//
ModeCount = 1;
} else if (mCrPolicyVar->Feature.Bit.CRForce128x40) {
//
// Max support mode 3 (128x40)
//
ModeCount = 4;
}
}
//
// Return valid mode count and mode information buffer.
//
mModeCount = ModeCount;
mTerminalConsoleModeData = ModeBuffer;
return EFI_SUCCESS;
}
/**
Initialize terminal fifo for save raw data.
@param TerminalDevice terminal device
**/
VOID
InitializeRawFiFo (
IN TERMINAL_DEV *TerminalDevice
)
{
//
// Make the raw fifo empty.
//
TerminalDevice->RawFiFo->Head = TerminalDevice->RawFiFo->Tail;
}
/**
Initialize terminal fifo for save unicode.
@param TerminalDevice terminal device
**/
VOID
InitializeUnicodeFiFo (
IN TERMINAL_DEV *TerminalDevice
)
{
//
// Make the unicode fifo empty
//
TerminalDevice->UnicodeFiFo->Head = TerminalDevice->UnicodeFiFo->Tail;
}
/**
Initialize terminal fifo for save EfiKey.
@param TerminalDevice terminal device
**/
VOID
InitializeEfiKeyFiFo (
IN TERMINAL_DEV *TerminalDevice
)
{
//
// Make the efi key fifo empty
//
TerminalDevice->EfiKeyFiFo->Head = TerminalDevice->EfiKeyFiFo->Tail;
}
/**
Initialize terminal fifo for save temporary data.
@param TerminalDevice terminal device
**/
VOID
InitializeTempFifo (
IN TERMINAL_DEV *TerminalDevice
)
{
//
// Make the temp fifo empty
//
ZeroMem (TerminalDevice->TempFiFo, sizeof(UNICODE_FIFO));
}
/**
Initialize terminal fifo for save special command.
@param TerminalDevice terminal device
**/
VOID
InitializeSpcFifo (
IN TERMINAL_DEV *TerminalDevice
)
{
ZeroMem (TerminalDevice->SpcFiFo, sizeof(UNICODE_FIFO));
}
/**
Start this driver on Controller by opening a Serial IO protocol,
reading Device Path, and creating a child handle with a Simple Text In,
Simple Text In Ex and Simple Text Out protocol, and device path protocol.
And store Console Device Environment Variables.
@param This Protocol instance pointer.
@param Controller Handle of device to bind driver to
@param RemainingDevicePath Optional parameter use to pick a specific child
device to start.
@retval EFI_SUCCESS This driver is added to Controller.
@retval EFI_ALREADY_STARTED This driver is already running on Controller.
@retval other This driver does not support this device.
**/
EFI_STATUS
EFIAPI
TerminalDriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_SERIAL_IO_PROTOCOL *SerialIo;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
VENDOR_DEVICE_PATH *Node;
VENDOR_DEVICE_PATH *DefaultNode;
EFI_SERIAL_IO_MODE *Mode;
UINTN SerialInTimeOut;
TERMINAL_DEV *TerminalDevice;
UINT8 TerminalType;
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
UINTN EntryCount;
UINTN Index;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextInput;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOutput;
BOOLEAN ConInSelected;
BOOLEAN ConOutSelected;
BOOLEAN NullRemaining;
BOOLEAN SimTxtInInstalled;
BOOLEAN SimTxtOutInstalled;
BOOLEAN FirstEnter;
TerminalDevice = NULL;
DefaultNode = NULL;
ConInSelected = FALSE;
ConOutSelected = FALSE;
NullRemaining = FALSE;
SimTxtInInstalled = FALSE;
SimTxtOutInstalled = FALSE;
FirstEnter = FALSE;
//
// Get the Device Path Protocol to build the device path of the child device
//
Status = gBS->OpenProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &ParentDevicePath,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
return Status;
}
//
// Open the Serial I/O Protocol BY_DRIVER. It might already be started.
//
Status = gBS->OpenProtocol (
Controller,
&gEfiSerialIoProtocolGuid,
(VOID **) &SerialIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
return Status;
}
if (Status != EFI_ALREADY_STARTED) {
//
// the serial I/O protocol never be opened before, it is the first
// time to start the serial Io controller
//
FirstEnter = TRUE;
}
//
// Serial I/O is not already open by this driver, then tag the handle
// with the Terminal Driver GUID and update the ConInDev, ConOutDev, and
// StdErrDev variables with the list of possible terminal types on this
// serial port.
//
Status = gBS->OpenProtocol (
Controller,
&gEfiCallerIdGuid,
NULL,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (EFI_ERROR (Status)) {
Status = gBS->InstallMultipleProtocolInterfaces (
&Controller,
&gEfiCallerIdGuid,
DuplicateDevicePath (ParentDevicePath),
NULL
);
if (EFI_ERROR (Status)) {
goto Error;
}
if (!IsHotPlugDevice (ParentDevicePath)) {
//
// if the serial device is a hot plug device, do not update the
// ConInDev, ConOutDev, and StdErrDev variables.
//
TerminalUpdateConsoleDevVariable (L"ConInDev", ParentDevicePath);
TerminalUpdateConsoleDevVariable (L"ConOutDev", ParentDevicePath);
TerminalUpdateConsoleDevVariable (L"ErrOutDev", ParentDevicePath);
}
}
//
// Check the requirement for the SimpleTxtIn and SimpleTxtOut protocols
//
// Simple In/Out Protocol will not be installed onto the handle if the
// device path to the handle is not present in the ConIn/ConOut
// environment variable. But If RemainingDevicePath is NULL, then always
// produce both Simple In and Simple Text Output Protocols. This is required
// for the connect all sequences to make sure all possible consoles are
// produced no matter what the current values of ConIn, ConOut, or StdErr are.
//
if (RemainingDevicePath == NULL) {
NullRemaining = TRUE;
}
DevicePath = BuildTerminalDevpath (ParentDevicePath, RemainingDevicePath);
if (DevicePath != NULL) {
ConInSelected = IsTerminalInConsoleVariable (L"ConIn", DevicePath);
ConOutSelected = IsTerminalInConsoleVariable (L"ConOut", DevicePath);
FreePool (DevicePath);
} else {
goto Error;
}
//
// Not create the child terminal handle if both Simple In/In Ex and
// Simple text Out protocols are not required to be published
//
if ((!ConInSelected)&&(!ConOutSelected)&&(!NullRemaining)) {
goto Error;
}
//
// create the child terminal handle during first entry
//
if (FirstEnter) {
//
// First enther the start funciton
//
FirstEnter = FALSE;
//
// Make sure a child handle does not already exist. This driver can only
// produce one child per serial port.
//
Status = gBS->OpenProtocolInformation (
Controller,
&gEfiSerialIoProtocolGuid,
&OpenInfoBuffer,
&EntryCount
);
if (!EFI_ERROR (Status)) {
Status = EFI_SUCCESS;
for (Index = 0; Index < EntryCount; Index++) {
if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
Status = EFI_ALREADY_STARTED;
}
}
FreePool (OpenInfoBuffer);
if (EFI_ERROR (Status)) {
goto Error;
}
}
//
// If RemainingDevicePath is NULL, then create default device path node
//
if (RemainingDevicePath == NULL) {
DefaultNode = AllocateZeroPool (sizeof (VENDOR_DEVICE_PATH));
if (DefaultNode == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error;
}
//
// Set default type as PCANSITYPE
//
if (mCrPolicyVar == NULL) {
TerminalType = PcdGet8 (PcdDefaultTerminalType);
} else {
TerminalType = mCrPolicyVar->GlobalTerminalType;
}
//
// Must be between PCANSITYPE (0) and VTUTF8TYPE (3)
//
ASSERT (TerminalType <= VTUTF8TYPE);
CopyMem (&DefaultNode->Guid, gTerminalType[TerminalType], sizeof (EFI_GUID));
RemainingDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) DefaultNode;
} else if (!IsDevicePathEnd (RemainingDevicePath)) {
//
// If RemainingDevicePath isn't the End of Device Path Node,
// Use the RemainingDevicePath to determine the terminal type
//
Node = (VENDOR_DEVICE_PATH *)RemainingDevicePath;
if (CompareGuid (&Node->Guid, &gEfiPcAnsiGuid)) {
TerminalType = PCANSITYPE;
} else if (CompareGuid (&Node->Guid, &gEfiVT100Guid)) {
TerminalType = VT100TYPE;
} else if (CompareGuid (&Node->Guid, &gEfiVT100PlusGuid)) {
TerminalType = VT100PLUSTYPE;
} else if (CompareGuid (&Node->Guid, &gEfiVTUTF8Guid)) {
TerminalType = VTUTF8TYPE;
} else if (CompareGuid (&Node->Guid, &gH2OCrLogTermGuid)) {
TerminalType = LOGTERMTYPE;
} else {
goto Error;
}
} else {
//
// If RemainingDevicePath is the End of Device Path Node,
// skip enumerate any device and return EFI_SUCESSS
//
return EFI_SUCCESS;
}
//
// Initialize the Terminal Dev
//
TerminalDevice = AllocateCopyPool (sizeof (TERMINAL_DEV), &mTerminalDevTemplate);
if (TerminalDevice == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error;
}
TerminalDevice->TerminalType = TerminalType;
TerminalDevice->SerialIo = SerialIo;
//
// Check CRPolicy
//
if (mCrPolicyVar != NULL && (ConOutSelected || IsHotPlugDevice(ParentDevicePath))) {
TerminalDevice->CrsEnable = TRUE;
//
// Terminal used only eight flag
//
// CRVideoType
// CRComboKey
// CRTerminalKey
// CRTerminalRows
// CR24RowsPolicy
// CRAutoRefresh
// CRManualRefresh
// CRTerminalCharSet
//
TerminalDevice->TerminalFeatureFlag = mCrPolicyVar->Feature.Data16;
}
InitializeListHead (&TerminalDevice->NotifyList);
Status = gBS->CreateEvent (
EVT_NOTIFY_WAIT,
TPL_NOTIFY,
TerminalConInWaitForKeyEx,
TerminalDevice,
&TerminalDevice->SimpleInputEx.WaitForKeyEx
);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = gBS->CreateEvent (
EVT_NOTIFY_WAIT,
TPL_NOTIFY,
TerminalConInWaitForKey,
TerminalDevice,
&TerminalDevice->SimpleInput.WaitForKey
);
if (EFI_ERROR (Status)) {
goto Error;
}
//
// Allocates and initializes the FIFO buffer to be zero, used for accommodating
// the pre-read pending characters.
//
TerminalDevice->RawFiFo = AllocateZeroPool (sizeof (RAW_DATA_FIFO));
if (TerminalDevice->RawFiFo == NULL) {
goto Error;
}
TerminalDevice->UnicodeFiFo = AllocateZeroPool (sizeof (UNICODE_FIFO));
if (TerminalDevice->UnicodeFiFo == NULL) {
goto Error;
}
TerminalDevice->EfiKeyFiFo = AllocateZeroPool (sizeof (EFI_KEY_FIFO));
if (TerminalDevice->EfiKeyFiFo == NULL) {
goto Error;
}
TerminalDevice->TempFiFo = AllocateZeroPool (sizeof (UNICODE_FIFO));
if (TerminalDevice->TempFiFo == NULL) {
goto Error;
}
TerminalDevice->SpcFiFo = AllocateZeroPool (sizeof (UNICODE_FIFO));
if (TerminalDevice->SpcFiFo == NULL) {
goto Error;
}
//
// initialize the FIFO buffer used for accommodating
// the pre-read pending characters
//
InitializeRawFiFo (TerminalDevice);
InitializeUnicodeFiFo (TerminalDevice);
InitializeEfiKeyFiFo (TerminalDevice);
InitializeTempFifo (TerminalDevice);
InitializeSpcFifo (TerminalDevice);
//
// Set the timeout value of serial buffer for
// keystroke response performance issue
//
Mode = TerminalDevice->SerialIo->Mode;
SerialInTimeOut = 0;
if (Mode->BaudRate != 0) {
SerialInTimeOut = (1 + Mode->DataBits + Mode->StopBits) * 2 * 1000000 / (UINTN) Mode->BaudRate;
}
Status = TerminalDevice->SerialIo->SetAttributes (
TerminalDevice->SerialIo,
Mode->BaudRate,
Mode->ReceiveFifoDepth,
(UINT32) SerialInTimeOut,
(EFI_PARITY_TYPE) (Mode->Parity),
(UINT8) Mode->DataBits,
(EFI_STOP_BITS_TYPE) (Mode->StopBits)
);
if (EFI_ERROR (Status)) {
//
// if set attributes operation fails, invalidate
// the value of SerialInTimeOut,thus make it
// inconsistent with the default timeout value
// of serial buffer. This will invoke the recalculation
// in the readkeystroke routine.
//
TerminalDevice->SerialInTimeOut = 0;
} else {
TerminalDevice->SerialInTimeOut = SerialInTimeOut;
}
//
// Update Simple Text Output Protocol
//
TerminalDevice->SimpleTextOutput.Mode = &TerminalDevice->SimpleTextOutputMode;
//
// For terminal devices, cursor is always visible
//
TerminalDevice->SimpleTextOutputMode.CursorVisible = TRUE;
//
// Update TerminalEscCode Protocol
//
TerminalDevice->TerminalEscCode.EscCodeCount = (UINT16) gEscSequenceCodeSize / sizeof(ESC_SEQUENCE_CODE) - 1;
TerminalDevice->TerminalEscCode.CrSpecialCommandCount = (UINT16) gCrSpecialCommandSize / sizeof(CR_SPECIAL_COMMAND) - 1;
if (mTerminalConsoleModeData == NULL) {
//
// Each terminal instance uses the same ConsoleModeData
// We only initialize once.
//
Status = InitializeTerminalConsoleTextMode ();
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto ReportError;
}
}
TerminalDevice->TerminalConsoleModeData = mTerminalConsoleModeData;
TerminalDevice->SimpleTextOutputMode.MaxMode = (INT32) mModeCount;
DEBUG_CODE (
for (Index = 0; Index < (UINTN)TerminalDevice->SimpleTextOutputMode.MaxMode; Index++) {
DEBUG ((EFI_D_INFO, "Terminal - Mode %d, Column = %d, Row = %d\n",
Index,
TerminalDevice->TerminalConsoleModeData[Index].Columns,
TerminalDevice->TerminalConsoleModeData[Index].Rows
));
}
);
mTerminalCount ++;
InitialRefreshScreenRoutine (TerminalDevice, mTerminalCount);
//
// AutoRefresh feature only support in AsyncTerminal
//
if ((TerminalDevice->TerminalFeatureFlag & CR_AUTO_REFRESH_FLAG) == CR_AUTO_REFRESH_ENABLE) {
InitialAutoRefreshRoutine (TerminalDevice);
}
Status = TerminalDevice->SimpleTextOutput.SetAttribute (
&TerminalDevice->SimpleTextOutput,
EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)
);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto ReportError;
}
//
// Build the component name for the child device
//
TerminalDevice->ControllerNameTable = NULL;
switch (TerminalDevice->TerminalType) {
case PCANSITYPE:
AddUnicodeString2 (
"eng",
gTerminalComponentName.SupportedLanguages,
&TerminalDevice->ControllerNameTable,
(CHAR16 *)L"PC-ANSI Serial Console",
TRUE
);
AddUnicodeString2 (
"en",
gTerminalComponentName2.SupportedLanguages,
&TerminalDevice->ControllerNameTable,
(CHAR16 *)L"PC-ANSI Serial Console",
FALSE
);
break;
case VT100TYPE:
AddUnicodeString2 (
"eng",
gTerminalComponentName.SupportedLanguages,
&TerminalDevice->ControllerNameTable,
(CHAR16 *)L"VT-100 Serial Console",
TRUE
);
AddUnicodeString2 (
"en",
gTerminalComponentName2.SupportedLanguages,
&TerminalDevice->ControllerNameTable,
(CHAR16 *)L"VT-100 Serial Console",
FALSE
);
break;
case VT100PLUSTYPE:
AddUnicodeString2 (
"eng",
gTerminalComponentName.SupportedLanguages,
&TerminalDevice->ControllerNameTable,
(CHAR16 *)L"VT-100+ Serial Console",
TRUE
);
AddUnicodeString2 (
"en",
gTerminalComponentName2.SupportedLanguages,
&TerminalDevice->ControllerNameTable,
(CHAR16 *)L"VT-100+ Serial Console",
FALSE
);
break;
case VTUTF8TYPE:
AddUnicodeString2 (
"eng",
gTerminalComponentName.SupportedLanguages,
&TerminalDevice->ControllerNameTable,
(CHAR16 *)L"VT-UTF8 Serial Console",
TRUE
);
AddUnicodeString2 (
"en",
gTerminalComponentName2.SupportedLanguages,
&TerminalDevice->ControllerNameTable,
(CHAR16 *)L"VT-UTF8 Serial Console",
FALSE
);
break;
case LOGTERMTYPE:
AddUnicodeString2 (
"eng",
gTerminalComponentName.SupportedLanguages,
&TerminalDevice->ControllerNameTable,
(CHAR16 *)L"Log Term Serial Console",
TRUE
);
AddUnicodeString2 (
"en",
gTerminalComponentName2.SupportedLanguages,
&TerminalDevice->ControllerNameTable,
(CHAR16 *)L"Log Term Serial Console",
FALSE
);
break;
}
//
// Build the device path for the child device
//
Status = SetTerminalDevicePath (
TerminalDevice->TerminalType,
ParentDevicePath,
&TerminalDevice->DevicePath
);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = TerminalDevice->SimpleTextOutput.Reset (&TerminalDevice->SimpleTextOutput, FALSE);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto ReportError;
}
Status = TerminalDevice->SimpleTextOutput.SetMode (&TerminalDevice->SimpleTextOutput, 0);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto ReportError;
}
Status = TerminalDevice->SimpleTextOutput.EnableCursor (&TerminalDevice->SimpleTextOutput, TRUE);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto ReportError;
}
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
TerminalConInTimerHandler,
TerminalDevice,
&TerminalDevice->TimerEvent
);
ASSERT_EFI_ERROR (Status);
Status = gBS->SetTimer (
TerminalDevice->TimerEvent,
TimerPeriodic,
KEYBOARD_TIMER_INTERVAL
);
ASSERT_EFI_ERROR (Status);
Status = gBS->CreateEvent (
EVT_TIMER,
TPL_CALLBACK,
NULL,
NULL,
&TerminalDevice->TwoSecondTimeOut
);
ASSERT_EFI_ERROR (Status);
Status = gBS->CreateEvent (
EVT_TIMER,
TPL_CALLBACK,
NULL,
NULL,
&TerminalDevice->ResetTimerEvent
);
ASSERT_EFI_ERROR (Status);
Status = gBS->InstallProtocolInterface (
&TerminalDevice->Handle,
&gEfiDevicePathProtocolGuid,
EFI_NATIVE_INTERFACE,
TerminalDevice->DevicePath
);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto Error;
}
//
// Register the Parent-Child relationship via
// EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
//
Status = gBS->OpenProtocol (
Controller,
&gEfiSerialIoProtocolGuid,
(VOID **) &TerminalDevice->SerialIo,
This->DriverBindingHandle,
TerminalDevice->Handle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto Error;
}
}
//
// Find the child handle, and get its TerminalDevice private data
//
Status = gBS->OpenProtocolInformation (
Controller,
&gEfiSerialIoProtocolGuid,
&OpenInfoBuffer,
&EntryCount
);
if (!EFI_ERROR (Status)) {
Status = EFI_NOT_FOUND;
ASSERT (OpenInfoBuffer != NULL);
for (Index = 0; Index < EntryCount; Index++) {
if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
//
// Find the child terminal handle.
// Test whether the SimpleTxtIn and SimpleTxtOut have been published
//
Status = gBS->OpenProtocol (
OpenInfoBuffer[Index].ControllerHandle,
&gEfiSimpleTextInProtocolGuid,
(VOID **) &SimpleTextInput,
This->DriverBindingHandle,
OpenInfoBuffer[Index].ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
SimTxtInInstalled = TRUE;
TerminalDevice = TERMINAL_CON_IN_DEV_FROM_THIS (SimpleTextInput);
}
Status = gBS->OpenProtocol (
OpenInfoBuffer[Index].ControllerHandle,
&gEfiSimpleTextOutProtocolGuid,
(VOID **) &SimpleTextOutput,
This->DriverBindingHandle,
OpenInfoBuffer[Index].ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
SimTxtOutInstalled = TRUE;
TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (SimpleTextOutput);
}
Status = EFI_SUCCESS;
break;
}
}
FreePool (OpenInfoBuffer);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto ReportError;
}
} else {
ASSERT(FALSE);
goto ReportError;
}
if (TerminalDevice == NULL) {
ASSERT (TerminalDevice != NULL);
goto ReportError;
}
//
// Only do the reset if the device path is in the Conout variable
//
if (ConInSelected && !SimTxtInInstalled) {
Status = TerminalDevice->SimpleInput.Reset (
&TerminalDevice->SimpleInput,
FALSE
);
if (EFI_ERROR (Status)) {
//
// Need to report Error Code first
//
ASSERT(FALSE);
goto ReportError;
}
}
//
// Only output the configure string to remote terminal if the device path
// is in the Conout variable
//
if (ConOutSelected && !SimTxtOutInstalled) {
Status = TerminalDevice->SimpleTextOutput.SetAttribute (
&TerminalDevice->SimpleTextOutput,
EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)
);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto ReportError;
}
Status = TerminalDevice->SimpleTextOutput.Reset (
&TerminalDevice->SimpleTextOutput,
FALSE
);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto ReportError;
}
Status = TerminalDevice->SimpleTextOutput.SetMode (
&TerminalDevice->SimpleTextOutput,
0
);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto ReportError;
}
Status = TerminalDevice->SimpleTextOutput.EnableCursor (
&TerminalDevice->SimpleTextOutput,
TRUE
);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto ReportError;
}
}
//
// Simple In/Out Protocol will not be installed onto the handle if the
// device path to the handle is not present in the ConIn/ConOut
// environment variable. But If RemainingDevicePath is NULL, then always
// produce both Simple In and Simple Text Output Protocols. This is required
// for the connect all sequences to make sure all possible consoles are
// produced no matter what the current values of ConIn, ConOut, or StdErr are.
//
if (!SimTxtInInstalled && (ConInSelected || NullRemaining)) {
Status = gBS->InstallMultipleProtocolInterfaces (
&TerminalDevice->Handle,
&gEfiSimpleTextInProtocolGuid,
&TerminalDevice->SimpleInput,
&gEfiSimpleTextInputExProtocolGuid,
&TerminalDevice->SimpleInputEx,
&gTerminalEscCodeProtocolGuid,
&TerminalDevice->TerminalEscCode,
NULL
);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto Error;
}
}
if (!SimTxtOutInstalled && (ConOutSelected || NullRemaining)) {
Status = gBS->InstallProtocolInterface (
&TerminalDevice->Handle,
&gEfiSimpleTextOutProtocolGuid,
EFI_NATIVE_INTERFACE,
&TerminalDevice->SimpleTextOutput
);
if (EFI_ERROR (Status)) {
ASSERT(FALSE);
goto Error;
}
}
if (DefaultNode != NULL) {
FreePool (DefaultNode);
}
return EFI_SUCCESS;
ReportError:
//
// Report error code before exiting
//
DevicePath = ParentDevicePath;
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_ERROR_CODE | EFI_ERROR_MINOR,
(EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_CONTROLLER_ERROR),
DevicePath
);
Error:
//
// Use the Stop() function to free all resources allocated in Start()
//
if (TerminalDevice != NULL) {
if (TerminalDevice->Handle != NULL) {
This->Stop (This, Controller, 1, &TerminalDevice->Handle);
} else {
if (TerminalDevice->TwoSecondTimeOut != NULL) {
gBS->CloseEvent (TerminalDevice->TwoSecondTimeOut);
}
if (TerminalDevice->ResetTimerEvent != NULL) {
gBS->CloseEvent (TerminalDevice->ResetTimerEvent);
}
if (TerminalDevice->TimerEvent != NULL) {
gBS->CloseEvent (TerminalDevice->TimerEvent);
}
if (TerminalDevice->SimpleInput.WaitForKey != NULL) {
gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey);
}
if (TerminalDevice->SimpleInputEx.WaitForKeyEx != NULL) {
gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx);
}
CloseRefreshScreenRoutine (TerminalDevice);
if ((TerminalDevice->TerminalFeatureFlag & CR_AUTO_REFRESH_FLAG) == CR_AUTO_REFRESH_ENABLE) {
CloseAutoRefreshRoutine (TerminalDevice);
}
TerminalFreeNotifyList (&TerminalDevice->NotifyList);
if (TerminalDevice->RawFiFo != NULL) {
FreePool (TerminalDevice->RawFiFo);
}
if (TerminalDevice->UnicodeFiFo != NULL) {
FreePool (TerminalDevice->UnicodeFiFo);
}
if (TerminalDevice->EfiKeyFiFo != NULL) {
FreePool (TerminalDevice->EfiKeyFiFo);
}
if (TerminalDevice->TempFiFo!= NULL) {
FreePool (TerminalDevice->TempFiFo);
}
if (TerminalDevice->SpcFiFo!= NULL) {
FreePool (TerminalDevice->SpcFiFo);
}
if (TerminalDevice->ControllerNameTable != NULL) {
FreeUnicodeStringTable (TerminalDevice->ControllerNameTable);
}
if (TerminalDevice->DevicePath != NULL) {
FreePool (TerminalDevice->DevicePath);
}
if (TerminalDevice->TerminalConsoleModeData != NULL) {
FreePool (TerminalDevice->TerminalConsoleModeData);
mTerminalConsoleModeData = NULL;
}
FreePool (TerminalDevice);
}
}
if (DefaultNode != NULL) {
FreePool (DefaultNode);
}
This->Stop (This, Controller, 0, NULL);
return Status;
}
/**
Stop this driver on Controller by closing Simple Text In, Simple Text
In Ex, Simple Text Out protocol, and removing parent device path from
Console Device Environment Variables.
@param This Protocol instance pointer.
@param Controller Handle of device to stop driver on
@param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
children is zero stop the entire bus driver.
@param ChildHandleBuffer List of Child Handles to Stop.
@retval EFI_SUCCESS This driver is removed Controller.
@retval other This driver could not be removed from this device.
**/
EFI_STATUS
EFIAPI
TerminalDriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
UINTN Index;
BOOLEAN AllChildrenStopped;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOutput;
TERMINAL_DEV *TerminalDevice;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
EFI_SERIAL_IO_PROTOCOL *SerialIo;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
Status = gBS->HandleProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &DevicePath
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Complete all outstanding transactions to Controller.
// Don't allow any new transaction to Controller to be started.
//
if (NumberOfChildren == 0) {
//
// Close the bus driver
//
Status = gBS->OpenProtocol (
Controller,
&gEfiCallerIdGuid,
(VOID **) &ParentDevicePath,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
//
// Remove Parent Device Path from
// the Console Device Environment Variables
//
TerminalRemoveConsoleDevVariable (L"ConInDev", ParentDevicePath);
TerminalRemoveConsoleDevVariable (L"ConOutDev", ParentDevicePath);
TerminalRemoveConsoleDevVariable (L"ErrOutDev", ParentDevicePath);
//
// Uninstall the Terminal Driver's GUID Tag from the Serial controller
//
Status = gBS->UninstallMultipleProtocolInterfaces (
Controller,
&gEfiCallerIdGuid,
ParentDevicePath,
NULL
);
//
// Free the ParentDevicePath that was duplicated in Start()
//
if (!EFI_ERROR (Status)) {
FreePool (ParentDevicePath);
}
}
gBS->CloseProtocol (
Controller,
&gEfiSerialIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
Controller
);
return EFI_SUCCESS;
}
AllChildrenStopped = TRUE;
for (Index = 0; Index < NumberOfChildren; Index++) {
Status = gBS->OpenProtocol (
ChildHandleBuffer[Index],
&gEfiSimpleTextOutProtocolGuid,
(VOID **) &SimpleTextOutput,
This->DriverBindingHandle,
ChildHandleBuffer[Index],
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (SimpleTextOutput);
gBS->CloseProtocol (
Controller,
&gEfiSerialIoProtocolGuid,
This->DriverBindingHandle,
ChildHandleBuffer[Index]
);
Status = gBS->UninstallMultipleProtocolInterfaces (
ChildHandleBuffer[Index],
&gEfiSimpleTextInProtocolGuid,
&TerminalDevice->SimpleInput,
&gEfiSimpleTextInputExProtocolGuid,
&TerminalDevice->SimpleInputEx,
&gEfiSimpleTextOutProtocolGuid,
&TerminalDevice->SimpleTextOutput,
&gEfiDevicePathProtocolGuid,
TerminalDevice->DevicePath,
NULL
);
if (EFI_ERROR (Status)) {
gBS->OpenProtocol (
Controller,
&gEfiSerialIoProtocolGuid,
(VOID **) &SerialIo,
This->DriverBindingHandle,
ChildHandleBuffer[Index],
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
} else {
if (TerminalDevice->ControllerNameTable != NULL) {
FreeUnicodeStringTable (TerminalDevice->ControllerNameTable);
}
gBS->CloseEvent (TerminalDevice->TimerEvent);
gBS->CloseEvent (TerminalDevice->TwoSecondTimeOut);
gBS->CloseEvent (TerminalDevice->ResetTimerEvent);
gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey);
gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx);
if (mTerminalCount >= 1) {
mTerminalCount--;
}
CloseRefreshScreenRoutine (TerminalDevice);
if ((TerminalDevice->TerminalFeatureFlag & CR_AUTO_REFRESH_FLAG) == CR_AUTO_REFRESH_ENABLE) {
CloseAutoRefreshRoutine (TerminalDevice);
}
TerminalFreeNotifyList (&TerminalDevice->NotifyList);
FreePool (TerminalDevice->DevicePath);
if (mTerminalCount == 0 && TerminalDevice->TerminalConsoleModeData != NULL) {
FreePool (TerminalDevice->TerminalConsoleModeData);
mTerminalConsoleModeData = NULL;
}
FreePool (TerminalDevice);
}
}
if (EFI_ERROR (Status)) {
AllChildrenStopped = FALSE;
}
}
if (!AllChildrenStopped) {
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
/**
Update terminal device path in Console Device Environment Variables.
@param VariableName The Console Device Environment Variable.
@param ParentDevicePath The terminal device path to be updated.
**/
VOID
TerminalUpdateConsoleDevVariable (
IN CHAR16 *VariableName,
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath
)
{
EFI_STATUS Status;
UINTN VariableSize;
UINT8 TerminalType;
EFI_DEVICE_PATH_PROTOCOL *Variable;
EFI_DEVICE_PATH_PROTOCOL *NewVariable;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
//
// Get global variable and its size according to the name given.
//
GetEfiGlobalVariable2 (VariableName, (VOID**)&Variable, NULL);
if (Variable == NULL) {
return;
}
//
// Append terminal device path onto the variable.
//
for (TerminalType = PCANSITYPE; TerminalType <= VTUTF8TYPE; TerminalType++) {
SetTerminalDevicePath (TerminalType, ParentDevicePath, &TempDevicePath);
NewVariable = AppendDevicePathInstance (Variable, TempDevicePath);
if (Variable != NULL) {
FreePool (Variable);
}
if (TempDevicePath != NULL) {
FreePool (TempDevicePath);
}
Variable = NewVariable;
}
VariableSize = GetDevicePathSize (Variable);
Status = gRT->SetVariable (
VariableName,
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
VariableSize,
Variable
);
ASSERT_EFI_ERROR (Status);
FreePool (Variable);
return ;
}
/**
Remove terminal device path from Console Device Environment Variables.
@param VariableName Console Device Environment Variables.
@param ParentDevicePath The terminal device path to be updated.
**/
VOID
TerminalRemoveConsoleDevVariable (
IN CHAR16 *VariableName,
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath
)
{
EFI_STATUS Status;
BOOLEAN FoundOne;
BOOLEAN Match;
UINTN VariableSize;
UINTN InstanceSize;
UINT8 TerminalType;
EFI_DEVICE_PATH_PROTOCOL *Instance;
EFI_DEVICE_PATH_PROTOCOL *Variable;
EFI_DEVICE_PATH_PROTOCOL *OriginalVariable;
EFI_DEVICE_PATH_PROTOCOL *NewVariable;
EFI_DEVICE_PATH_PROTOCOL *SavedNewVariable;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
Instance = NULL;
//
// Get global variable and its size according to the name given.
//
GetEfiGlobalVariable2 (VariableName, (VOID**)&Variable, NULL);
if (Variable == NULL) {
return ;
}
FoundOne = FALSE;
OriginalVariable = Variable;
NewVariable = NULL;
//
// Get first device path instance from Variable
//
Instance = GetNextDevicePathInstance (&Variable, &InstanceSize);
if (Instance == NULL) {
FreePool (OriginalVariable);
return ;
}
//
// Loop through all the device path instances of Variable
//
do {
//
// Loop through all the terminal types that this driver supports
//
Match = FALSE;
for (TerminalType = PCANSITYPE; TerminalType <= VTUTF8TYPE; TerminalType++) {
SetTerminalDevicePath (TerminalType, ParentDevicePath, &TempDevicePath);
//
// Compare the generated device path to the current device path instance
//
if (TempDevicePath != NULL) {
if (CompareMem (Instance, TempDevicePath, InstanceSize) == 0) {
Match = TRUE;
FoundOne = TRUE;
}
FreePool (TempDevicePath);
}
}
//
// If a match was not found, then keep the current device path instance
//
if (!Match) {
SavedNewVariable = NewVariable;
NewVariable = AppendDevicePathInstance (NewVariable, Instance);
if (SavedNewVariable != NULL) {
FreePool (SavedNewVariable);
}
}
//
// Get next device path instance from Variable
//
FreePool (Instance);
Instance = GetNextDevicePathInstance (&Variable, &InstanceSize);
} while (Instance != NULL);
FreePool (OriginalVariable);
if (FoundOne) {
VariableSize = GetDevicePathSize (NewVariable);
Status = gRT->SetVariable (
VariableName,
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
VariableSize,
NewVariable
);
ASSERT_EFI_ERROR (Status);
}
if (NewVariable != NULL) {
FreePool (NewVariable);
}
return ;
}
/**
Build terminal device path according to terminal type.
@param TerminalType The terminal type is PC ANSI, VT100, VT100+ or VT-UTF8.
@param ParentDevicePath Parent device path.
@param TerminalDevicePath Returned terminal device path, if building successfully.
@retval EFI_UNSUPPORTED Terminal does not belong to the supported type.
@retval EFI_OUT_OF_RESOURCES Generate terminal device path failed.
@retval EFI_SUCCESS Build terminal device path successfully.
**/
EFI_STATUS
SetTerminalDevicePath (
IN UINT8 TerminalType,
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,
OUT EFI_DEVICE_PATH_PROTOCOL **TerminalDevicePath
)
{
VENDOR_DEVICE_PATH Node;
*TerminalDevicePath = NULL;
Node.Header.Type = MESSAGING_DEVICE_PATH;
Node.Header.SubType = MSG_VENDOR_DP;
//
// Generate terminal device path node according to terminal type.
//
switch (TerminalType) {
case PCANSITYPE:
CopyGuid (&Node.Guid, &gEfiPcAnsiGuid);
break;
case VT100TYPE:
CopyGuid (&Node.Guid, &gEfiVT100Guid);
break;
case VT100PLUSTYPE:
CopyGuid (&Node.Guid, &gEfiVT100PlusGuid);
break;
case VTUTF8TYPE:
CopyGuid (&Node.Guid, &gEfiVTUTF8Guid);
break;
case LOGTERMTYPE:
CopyGuid (&Node.Guid, &gH2OCrLogTermGuid);
break;
default:
return EFI_UNSUPPORTED;
}
//
// Get VENDOR_DEVCIE_PATH size and put into Node.Header
//
SetDevicePathNodeLength (
&Node.Header,
sizeof (VENDOR_DEVICE_PATH)
);
//
// Append the terminal node onto parent device path
// to generate a complete terminal device path.
//
*TerminalDevicePath = AppendDevicePathNode (
ParentDevicePath,
(EFI_DEVICE_PATH_PROTOCOL *) &Node
);
if (*TerminalDevicePath == NULL) {
return EFI_OUT_OF_RESOURCES;
}
return EFI_SUCCESS;
}
/**
Check if the device supports hot-plug through its device path.
This function could be updated to check more types of Hot Plug devices.
Currently, it checks USB and PCCard device.
@param DevicePath Pointer to device's device path.
@retval TRUE The devcie is a hot-plug device
@retval FALSE The devcie is not a hot-plug device.
**/
BOOLEAN
IsHotPlugDevice (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL *CheckDevicePath;
CheckDevicePath = DevicePath;
while (!IsDevicePathEnd (CheckDevicePath)) {
//
// Check device whether is hot plug device or not throught Device Path
//
if ((DevicePathType (CheckDevicePath) == MESSAGING_DEVICE_PATH) &&
(DevicePathSubType (CheckDevicePath) == MSG_USB_DP ||
DevicePathSubType (CheckDevicePath) == MSG_USB_CLASS_DP ||
DevicePathSubType (CheckDevicePath) == MSG_USB_WWID_DP)) {
//
// If Device is USB device
//
return TRUE;
}
if ((DevicePathType (CheckDevicePath) == HARDWARE_DEVICE_PATH) &&
(DevicePathSubType (CheckDevicePath) == HW_PCCARD_DP)) {
//
// If Device is PCCard
//
return TRUE;
}
CheckDevicePath = NextDevicePathNode (CheckDevicePath);
}
return FALSE;
}
/**
Function returns TRUE when the two input device paths point to the two
GOP child handles that have the same parent.
@param Left A pointer to a device path data structure.
@param Right A pointer to a device path data structure.
@retval TRUE Left and Right share the same parent.
@retval FALSE Left and Right don't share the same parent or either of them is not
a GOP device path.
**/
BOOLEAN
IsGopSibling (
IN EFI_DEVICE_PATH_PROTOCOL *Left,
IN EFI_DEVICE_PATH_PROTOCOL *Right
)
{
EFI_DEVICE_PATH_PROTOCOL *NodeLeft;
EFI_DEVICE_PATH_PROTOCOL *NodeRight;
for (NodeLeft = Left; !IsDevicePathEndType (NodeLeft); NodeLeft = NextDevicePathNode (NodeLeft)) {
if ((DevicePathType (NodeLeft) == ACPI_DEVICE_PATH && DevicePathSubType (NodeLeft) == ACPI_ADR_DP) ||
(DevicePathType (NodeLeft) == HARDWARE_DEVICE_PATH && DevicePathSubType (NodeLeft) == HW_CONTROLLER_DP &&
DevicePathType (NextDevicePathNode (NodeLeft)) == ACPI_DEVICE_PATH && DevicePathSubType (NextDevicePathNode (NodeLeft)) == ACPI_ADR_DP)) {
break;
}
}
if (IsDevicePathEndType (NodeLeft)) {
return FALSE;
}
for (NodeRight = Right; !IsDevicePathEndType (NodeRight); NodeRight = NextDevicePathNode (NodeRight)) {
if ((DevicePathType (NodeRight) == ACPI_DEVICE_PATH && DevicePathSubType (NodeRight) == ACPI_ADR_DP) ||
(DevicePathType (NodeRight) == HARDWARE_DEVICE_PATH && DevicePathSubType (NodeRight) == HW_CONTROLLER_DP &&
DevicePathType (NextDevicePathNode (NodeRight)) == ACPI_DEVICE_PATH && DevicePathSubType (NextDevicePathNode (NodeRight)) == ACPI_ADR_DP)) {
break;
}
}
if (IsDevicePathEndType (NodeRight)) {
return FALSE;
}
if (((UINTN) NodeLeft - (UINTN) Left) != ((UINTN) NodeRight - (UINTN) Right)) {
return FALSE;
}
return (BOOLEAN) (CompareMem (Left, Right, (UINTN) NodeLeft - (UINTN) Left) == 0);
}
/**
Function compares a device path data structure to that of all the nodes of a
second device path instance.
@param Multi A pointer to a multi-instance device path data structure.
@param Single A pointer to a single-instance device path data structure.
@retval EFI_SUCCESS If the Single is contained within Multi.
@retval EFI_NOT_FOUND If the Single is not contained within Multi.
@retval EFI_INVALID_PARAMETER Multi is NULL.
@retval EFI_INVALID_PARAMETER Single is NULL.
**/
EFI_STATUS
TerminalMatchDevicePaths (
IN EFI_DEVICE_PATH_PROTOCOL *Multi,
IN EFI_DEVICE_PATH_PROTOCOL *Single
)
{
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath1;
EFI_DEVICE_PATH_PROTOCOL *DevicePathInst;
UINTN Size;
//
// The passed in DevicePath should not be NULL
//
if ((Multi == NULL) || (Single == NULL)) {
return EFI_INVALID_PARAMETER;
}
TempDevicePath1 = NULL;
DevicePath = Multi;
DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
//
// Search for the match of 'Single' in 'Multi'
//
while (DevicePathInst != NULL) {
if ((CompareMem (Single, DevicePathInst, Size) == 0) || IsGopSibling (Single, DevicePathInst)) {
FreePool (DevicePathInst);
return EFI_SUCCESS;
}
FreePool (DevicePathInst);
DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
}
return EFI_NOT_FOUND;
}
/**
Check device path in Console Device Environment Variables or not.
@param VariableName The Console Device Environment Variable.
@param ParentDevicePath The terminal device path to be updated.
@retval EFI_SUCCESS Device path exists in the variables.
@retval EFI_NOT_FOUND Device path not in the variables.
**/
EFI_STATUS
TerminalCheckConsoleDevVariable (
IN CHAR16 *VariableName,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *Variable;
//
// Get global variable and its size according to the name given.
//
GetEfiGlobalVariable2 (VariableName, (VOID**)&Variable, NULL);
if (Variable == NULL) {
return EFI_NOT_FOUND;
}
//
// Match specified DevicePath in Console Variable.
//
Status = TerminalMatchDevicePaths (
Variable,
DevicePath
);
FreePool (Variable);
return Status;
}