2410 lines
68 KiB
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;
|
|
}
|
|
|