diff options
Diffstat (limited to 'MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c')
| -rw-r--r-- | MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c | 2822 |
1 files changed, 1411 insertions, 1411 deletions
diff --git a/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c b/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c index e2d779c783..44b6089873 100644 --- a/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c +++ b/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c @@ -1,1411 +1,1411 @@ -/** @file
- Produces Simple Text Input Protocol, Simple Text Input Extended Protocol and
- Simple Text Output Protocol upon Serial IO Protocol.
-
-Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "Terminal.h"
-
-//
-// Globals
-//
-EFI_DRIVER_BINDING_PROTOCOL gTerminalDriverBinding = {
- TerminalDriverBindingSupported,
- TerminalDriverBindingStart,
- TerminalDriverBindingStop,
- 0xa,
- NULL,
- NULL
-};
-
-EFI_GUID *mTerminalType[] = {
- &gEfiPcAnsiGuid,
- &gEfiVT100Guid,
- &gEfiVT100PlusGuid,
- &gEfiVTUTF8Guid,
- &gEfiTtyTermGuid,
- &gEdkiiLinuxTermGuid,
- &gEdkiiXtermR6Guid,
- &gEdkiiVT400Guid,
- &gEdkiiSCOTermGuid
-};
-
-CHAR16 *mSerialConsoleNames[] = {
- L"PC-ANSI Serial Console",
- L"VT-100 Serial Console",
- L"VT-100+ Serial Console",
- L"VT-UTF8 Serial Console",
- L"Tty Terminal Serial Console",
- L"Linux Terminal Serial Console",
- L"Xterm R6 Serial Console",
- L"VT-400 Serial Console",
- L"SCO Terminal Serial Console"
-};
-
-TERMINAL_DEV mTerminalDevTemplate = {
- TERMINAL_DEV_SIGNATURE,
- NULL,
- 0,
- NULL,
- NULL,
- { // SimpleTextInput
- TerminalConInReset,
- TerminalConInReadKeyStroke,
- NULL
- },
- { // SimpleTextOutput
- TerminalConOutReset,
- TerminalConOutOutputString,
- TerminalConOutTestString,
- TerminalConOutQueryMode,
- TerminalConOutSetMode,
- TerminalConOutSetAttribute,
- TerminalConOutClearScreen,
- TerminalConOutSetCursorPosition,
- TerminalConOutEnableCursor,
- 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, // EfiKeyFiFoForNotify
-
- NULL, // ControllerNameTable
- NULL, // TimerEvent
- NULL, // TwoSecondTimeOut
- INPUT_STATE_DEFAULT,
- RESET_STATE_DEFAULT,
- {
- 0,
- 0,
- 0
- },
- 0,
- FALSE,
- { // SimpleTextInputEx
- TerminalConInResetEx,
- TerminalConInReadKeyStrokeEx,
- NULL,
- TerminalConInSetState,
- TerminalConInRegisterKeyNotify,
- TerminalConInUnregisterKeyNotify,
- },
- { // NotifyList
- NULL,
- NULL,
- },
- NULL // KeyNotifyProcessEvent
-};
-
-TERMINAL_CONSOLE_MODE_DATA mTerminalConsoleModeData[] = {
- { 80, 25 },
- { 80, 50 },
- { 100, 31 },
- //
- // New modes can be added here.
- //
-};
-
-/**
- Convert the GUID representation of terminal type to enum type.
-
- @param Guid The GUID representation of terminal type.
-
- @return The terminal type in enum type.
-**/
-TERMINAL_TYPE
-TerminalTypeFromGuid (
- IN EFI_GUID *Guid
- )
-{
- TERMINAL_TYPE Type;
-
- for (Type = 0; Type < ARRAY_SIZE (mTerminalType); Type++) {
- if (CompareGuid (Guid, mTerminalType[Type])) {
- break;
- }
- }
-
- return Type;
-}
-
-/**
- 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 remaining device path is not NULL, then make sure it is a
- // device path that describes a terminal communications protocol.
- //
- if (RemainingDevicePath != NULL) {
- //
- // 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+, VT-UTF8, TtyTerm
- // Linux, XtermR6, VT400 and SCO terminal types
- //
- if (TerminalTypeFromGuid (&Node->Guid) == ARRAY_SIZE (mTerminalType)) {
- 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;
-}
-
-/**
- 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] TextModeCount The total number of text modes that terminal console supports.
-
- @return The buffer to the text modes column and row information.
- Caller is responsible to free it when it's non-NULL.
-
-**/
-TERMINAL_CONSOLE_MODE_DATA *
-InitializeTerminalConsoleTextMode (
- OUT INT32 *TextModeCount
- )
-{
- TERMINAL_CONSOLE_MODE_DATA *TextModeData;
-
- ASSERT (TextModeCount != NULL);
-
- TextModeData = AllocateCopyPool (sizeof (mTerminalConsoleModeData), mTerminalConsoleModeData);
- if (TextModeData == NULL) {
- return NULL;
- }
-
- *TextModeCount = ARRAY_SIZE (mTerminalConsoleModeData);
-
- DEBUG_CODE_BEGIN ();
- INT32 Index;
-
- for (Index = 0; Index < *TextModeCount; Index++) {
- DEBUG ((
- DEBUG_INFO,
- "Terminal - Mode %d, Column = %d, Row = %d\n",
- Index,
- TextModeData[Index].Columns,
- TextModeData[Index].Rows
- ));
- }
-
- DEBUG_CODE_END ();
- return TextModeData;
-}
-
-/**
- Stop the terminal state machine.
-
- @param TerminalDevice The terminal device.
-**/
-VOID
-StopTerminalStateMachine (
- TERMINAL_DEV *TerminalDevice
- )
-{
- EFI_TPL OriginalTpl;
-
- OriginalTpl = gBS->RaiseTPL (TPL_NOTIFY);
-
- gBS->CloseEvent (TerminalDevice->TimerEvent);
- gBS->CloseEvent (TerminalDevice->TwoSecondTimeOut);
-
- gBS->RestoreTPL (OriginalTpl);
-}
-
-/**
- Start the terminal state machine.
-
- @param TerminalDevice The terminal device.
-**/
-VOID
-StartTerminalStateMachine (
- TERMINAL_DEV *TerminalDevice
- )
-{
- EFI_STATUS Status;
-
- 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);
-}
-
-/**
- Initialize the controller name table.
-
- @param TerminalType The terminal type.
- @param ControllerNameTable The controller name table.
-
- @retval EFI_SUCCESS The controller name table is initialized successfully.
- @retval others Return status of AddUnicodeString2 ().
-**/
-EFI_STATUS
-InitializeControllerNameTable (
- TERMINAL_TYPE TerminalType,
- EFI_UNICODE_STRING_TABLE **ControllerNameTable
- )
-{
- EFI_STATUS Status;
- EFI_UNICODE_STRING_TABLE *Table;
-
- ASSERT (TerminalType < ARRAY_SIZE (mTerminalType));
- Table = NULL;
- Status = AddUnicodeString2 (
- "eng",
- gTerminalComponentName.SupportedLanguages,
- &Table,
- mSerialConsoleNames[TerminalType],
- TRUE
- );
- if (!EFI_ERROR (Status)) {
- Status = AddUnicodeString2 (
- "en",
- gTerminalComponentName2.SupportedLanguages,
- &Table,
- mSerialConsoleNames[TerminalType],
- FALSE
- );
- if (EFI_ERROR (Status)) {
- FreeUnicodeStringTable (Table);
- }
- }
-
- if (!EFI_ERROR (Status)) {
- *ControllerNameTable = Table;
- }
-
- return Status;
-}
-
-/**
- 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;
- EFI_DEVICE_PATH_PROTOCOL *Vendor;
- EFI_HANDLE SerialIoHandle;
- EFI_SERIAL_IO_MODE *Mode;
- UINTN SerialInTimeOut;
- TERMINAL_DEV *TerminalDevice;
- UINT8 TerminalType;
- EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
- UINTN EntryCount;
- UINTN Index;
- EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOutput;
- EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextInput;
- EFI_UNICODE_STRING_TABLE *ControllerNameTable;
-
- //
- // 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
- );
- ASSERT ((Status == EFI_SUCCESS) || (Status == EFI_ALREADY_STARTED));
- 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
- );
- ASSERT ((Status == EFI_SUCCESS) || (Status == EFI_ALREADY_STARTED));
- if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
- return Status;
- }
-
- if (!IsHotPlugDevice (ParentDevicePath)) {
- //
- // if the serial device is a hot plug device, do not update the
- // ConInDev, ConOutDev, and StdErrDev variables.
- //
- TerminalUpdateConsoleDevVariable (EFI_CON_IN_DEV_VARIABLE_NAME, ParentDevicePath);
- TerminalUpdateConsoleDevVariable (EFI_CON_OUT_DEV_VARIABLE_NAME, ParentDevicePath);
- TerminalUpdateConsoleDevVariable (EFI_ERR_OUT_DEV_VARIABLE_NAME, ParentDevicePath);
- }
-
- //
- // Do not create any child for END remaining device path.
- //
- if ((RemainingDevicePath != NULL) && IsDevicePathEnd (RemainingDevicePath)) {
- return EFI_SUCCESS;
- }
-
- if (Status == EFI_ALREADY_STARTED) {
- if (RemainingDevicePath == NULL) {
- //
- // If RemainingDevicePath is NULL or is the End of Device Path Node
- //
- return EFI_SUCCESS;
- }
-
- //
- // This driver can only produce one child per serial port.
- // Change its terminal type as remaining device path requests.
- //
- Status = gBS->OpenProtocolInformation (
- Controller,
- &gEfiSerialIoProtocolGuid,
- &OpenInfoBuffer,
- &EntryCount
- );
- if (!EFI_ERROR (Status)) {
- Status = EFI_NOT_FOUND;
- for (Index = 0; Index < EntryCount; Index++) {
- if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
- Status = gBS->OpenProtocol (
- OpenInfoBuffer[Index].ControllerHandle,
- &gEfiSimpleTextInProtocolGuid,
- (VOID **)&SimpleTextInput,
- This->DriverBindingHandle,
- Controller,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (!EFI_ERROR (Status)) {
- TerminalDevice = TERMINAL_CON_IN_DEV_FROM_THIS (SimpleTextInput);
- TerminalType = TerminalTypeFromGuid (&((VENDOR_DEVICE_PATH *)RemainingDevicePath)->Guid);
- ASSERT (TerminalType < ARRAY_SIZE (mTerminalType));
- if (TerminalDevice->TerminalType != TerminalType) {
- Status = InitializeControllerNameTable (TerminalType, &ControllerNameTable);
- if (!EFI_ERROR (Status)) {
- StopTerminalStateMachine (TerminalDevice);
- //
- // Update the device path
- //
- Vendor = TerminalDevice->DevicePath;
- Status = gBS->LocateDevicePath (&gEfiSerialIoProtocolGuid, &Vendor, &SerialIoHandle);
- ASSERT_EFI_ERROR (Status);
- CopyGuid (&((VENDOR_DEVICE_PATH *)Vendor)->Guid, mTerminalType[TerminalType]);
- Status = gBS->ReinstallProtocolInterface (
- TerminalDevice->Handle,
- &gEfiDevicePathProtocolGuid,
- TerminalDevice->DevicePath,
- TerminalDevice->DevicePath
- );
- if (!EFI_ERROR (Status)) {
- TerminalDevice->TerminalType = TerminalType;
- StartTerminalStateMachine (TerminalDevice);
- FreeUnicodeStringTable (TerminalDevice->ControllerNameTable);
- TerminalDevice->ControllerNameTable = ControllerNameTable;
- } else {
- //
- // Restore the device path on failure
- //
- CopyGuid (&((VENDOR_DEVICE_PATH *)Vendor)->Guid, mTerminalType[TerminalDevice->TerminalType]);
- FreeUnicodeStringTable (ControllerNameTable);
- }
- }
- }
- }
-
- break;
- }
- }
-
- FreePool (OpenInfoBuffer);
- }
-
- return Status;
- }
-
- //
- // Initialize the Terminal Dev
- //
- TerminalDevice = AllocateCopyPool (sizeof (TERMINAL_DEV), &mTerminalDevTemplate);
- if (TerminalDevice == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto CloseProtocols;
- }
-
- if (RemainingDevicePath == NULL) {
- //
- // If RemainingDevicePath is NULL, use default terminal type
- //
- TerminalDevice->TerminalType = PcdGet8 (PcdDefaultTerminalType);
- } else {
- //
- // End of Device Path Node is handled in above.
- //
- ASSERT (!IsDevicePathEnd (RemainingDevicePath));
- //
- // If RemainingDevicePath isn't the End of Device Path Node,
- // Use the RemainingDevicePath to determine the terminal type
- //
- TerminalDevice->TerminalType = TerminalTypeFromGuid (&((VENDOR_DEVICE_PATH *)RemainingDevicePath)->Guid);
- }
-
- ASSERT (TerminalDevice->TerminalType < ARRAY_SIZE (mTerminalType));
- TerminalDevice->SerialIo = SerialIo;
-
- //
- // Build the component name for the child device
- //
- Status = InitializeControllerNameTable (TerminalDevice->TerminalType, &TerminalDevice->ControllerNameTable);
- if (EFI_ERROR (Status)) {
- goto FreeResources;
- }
-
- //
- // Build the device path for the child device
- //
- Status = SetTerminalDevicePath (TerminalDevice->TerminalType, ParentDevicePath, &TerminalDevice->DevicePath);
- if (EFI_ERROR (Status)) {
- goto FreeResources;
- }
-
- InitializeListHead (&TerminalDevice->NotifyList);
- Status = gBS->CreateEvent (
- EVT_NOTIFY_WAIT,
- TPL_NOTIFY,
- TerminalConInWaitForKeyEx,
- TerminalDevice,
- &TerminalDevice->SimpleInputEx.WaitForKeyEx
- );
- ASSERT_EFI_ERROR (Status);
-
- Status = gBS->CreateEvent (
- EVT_NOTIFY_WAIT,
- TPL_NOTIFY,
- TerminalConInWaitForKey,
- TerminalDevice,
- &TerminalDevice->SimpleInput.WaitForKey
- );
- ASSERT_EFI_ERROR (Status);
- Status = gBS->CreateEvent (
- EVT_NOTIFY_SIGNAL,
- TPL_CALLBACK,
- KeyNotifyProcessHandler,
- TerminalDevice,
- &TerminalDevice->KeyNotifyProcessEvent
- );
- ASSERT_EFI_ERROR (Status);
-
- //
- // 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 FreeResources;
- }
-
- TerminalDevice->UnicodeFiFo = AllocateZeroPool (sizeof (UNICODE_FIFO));
- if (TerminalDevice->UnicodeFiFo == NULL) {
- goto FreeResources;
- }
-
- TerminalDevice->EfiKeyFiFo = AllocateZeroPool (sizeof (EFI_KEY_FIFO));
- if (TerminalDevice->EfiKeyFiFo == NULL) {
- goto FreeResources;
- }
-
- TerminalDevice->EfiKeyFiFoForNotify = AllocateZeroPool (sizeof (EFI_KEY_FIFO));
- if (TerminalDevice->EfiKeyFiFoForNotify == NULL) {
- goto FreeResources;
- }
-
- //
- // 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;
- }
-
- SimpleTextOutput = &TerminalDevice->SimpleTextOutput;
- SimpleTextInput = &TerminalDevice->SimpleInput;
-
- //
- // Initialize SimpleTextOut instance
- //
- SimpleTextOutput->Mode = &TerminalDevice->SimpleTextOutputMode;
- TerminalDevice->TerminalConsoleModeData = InitializeTerminalConsoleTextMode (
- &SimpleTextOutput->Mode->MaxMode
- );
- if (TerminalDevice->TerminalConsoleModeData == NULL) {
- goto FreeResources;
- }
-
- //
- // For terminal devices, cursor is always visible
- //
- SimpleTextOutput->Mode->CursorVisible = TRUE;
- Status = SimpleTextOutput->SetAttribute (SimpleTextOutput, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
- if (!EFI_ERROR (Status)) {
- Status = SimpleTextOutput->Reset (SimpleTextOutput, FALSE);
- }
-
- if (EFI_ERROR (Status)) {
- goto ReportError;
- }
-
- //
- // Initialize SimpleTextInput instance
- //
- Status = SimpleTextInput->Reset (SimpleTextInput, FALSE);
- if (EFI_ERROR (Status)) {
- goto ReportError;
- }
-
- Status = gBS->InstallMultipleProtocolInterfaces (
- &TerminalDevice->Handle,
- &gEfiSimpleTextInProtocolGuid,
- &TerminalDevice->SimpleInput,
- &gEfiSimpleTextInputExProtocolGuid,
- &TerminalDevice->SimpleInputEx,
- &gEfiSimpleTextOutProtocolGuid,
- &TerminalDevice->SimpleTextOutput,
- &gEfiDevicePathProtocolGuid,
- TerminalDevice->DevicePath,
- NULL
- );
- if (!EFI_ERROR (Status)) {
- Status = gBS->OpenProtocol (
- Controller,
- &gEfiSerialIoProtocolGuid,
- (VOID **)&TerminalDevice->SerialIo,
- This->DriverBindingHandle,
- TerminalDevice->Handle,
- EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
- );
- ASSERT_EFI_ERROR (Status);
- StartTerminalStateMachine (TerminalDevice);
- return Status;
- }
-
-ReportError:
- REPORT_STATUS_CODE_WITH_DEVICE_PATH (
- EFI_ERROR_CODE | EFI_ERROR_MINOR,
- (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_CONTROLLER_ERROR),
- ParentDevicePath
- );
-
-FreeResources:
- ASSERT (TerminalDevice != NULL);
-
- if (TerminalDevice->SimpleInput.WaitForKey != NULL) {
- gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey);
- }
-
- if (TerminalDevice->SimpleInputEx.WaitForKeyEx != NULL) {
- gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx);
- }
-
- if (TerminalDevice->KeyNotifyProcessEvent != NULL) {
- gBS->CloseEvent (TerminalDevice->KeyNotifyProcessEvent);
- }
-
- if (TerminalDevice->RawFiFo != NULL) {
- FreePool (TerminalDevice->RawFiFo);
- }
-
- if (TerminalDevice->UnicodeFiFo != NULL) {
- FreePool (TerminalDevice->UnicodeFiFo);
- }
-
- if (TerminalDevice->EfiKeyFiFo != NULL) {
- FreePool (TerminalDevice->EfiKeyFiFo);
- }
-
- if (TerminalDevice->EfiKeyFiFoForNotify != NULL) {
- FreePool (TerminalDevice->EfiKeyFiFoForNotify);
- }
-
- if (TerminalDevice->ControllerNameTable != NULL) {
- FreeUnicodeStringTable (TerminalDevice->ControllerNameTable);
- }
-
- if (TerminalDevice->DevicePath != NULL) {
- FreePool (TerminalDevice->DevicePath);
- }
-
- if (TerminalDevice->TerminalConsoleModeData != NULL) {
- FreePool (TerminalDevice->TerminalConsoleModeData);
- }
-
- FreePool (TerminalDevice);
-
-CloseProtocols:
-
- //
- // Remove Parent Device Path from
- // the Console Device Environment Variables
- //
- TerminalRemoveConsoleDevVariable (EFI_CON_IN_DEV_VARIABLE_NAME, ParentDevicePath);
- TerminalRemoveConsoleDevVariable (EFI_CON_OUT_DEV_VARIABLE_NAME, ParentDevicePath);
- TerminalRemoveConsoleDevVariable (EFI_ERR_OUT_DEV_VARIABLE_NAME, ParentDevicePath);
-
- Status = gBS->CloseProtocol (
- Controller,
- &gEfiSerialIoProtocolGuid,
- This->DriverBindingHandle,
- Controller
- );
- ASSERT_EFI_ERROR (Status);
-
- Status = gBS->CloseProtocol (
- Controller,
- &gEfiDevicePathProtocolGuid,
- This->DriverBindingHandle,
- Controller
- );
- ASSERT_EFI_ERROR (Status);
- 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;
-
- //
- // 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,
- &gEfiDevicePathProtocolGuid,
- (VOID **)&ParentDevicePath,
- This->DriverBindingHandle,
- Controller,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- ASSERT_EFI_ERROR (Status);
-
- //
- // Remove Parent Device Path from
- // the Console Device Environment Variables
- //
- TerminalRemoveConsoleDevVariable (EFI_CON_IN_DEV_VARIABLE_NAME, ParentDevicePath);
- TerminalRemoveConsoleDevVariable (EFI_CON_OUT_DEV_VARIABLE_NAME, ParentDevicePath);
- TerminalRemoveConsoleDevVariable (EFI_ERR_OUT_DEV_VARIABLE_NAME, 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 {
- FreeUnicodeStringTable (TerminalDevice->ControllerNameTable);
- StopTerminalStateMachine (TerminalDevice);
- gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey);
- gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx);
- gBS->CloseEvent (TerminalDevice->KeyNotifyProcessEvent);
- TerminalFreeNotifyList (&TerminalDevice->NotifyList);
- FreePool (TerminalDevice->DevicePath);
- FreePool (TerminalDevice->TerminalConsoleModeData);
- FreePool (TerminalDevice);
- }
- }
-
- if (EFI_ERROR (Status)) {
- AllChildrenStopped = FALSE;
- }
- }
-
- if (!AllChildrenStopped) {
- return EFI_DEVICE_ERROR;
- }
-
- return EFI_SUCCESS;
-}
-
-/**
- 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;
-}
-
-/**
- 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 NameSize;
- UINTN VariableSize;
- TERMINAL_TYPE TerminalType;
- EFI_DEVICE_PATH_PROTOCOL *Variable;
- EFI_DEVICE_PATH_PROTOCOL *NewVariable;
- EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
- EDKII_SET_VARIABLE_STATUS *SetVariableStatus;
-
- //
- // Get global variable and its size according to the name given.
- //
- Status = GetEfiGlobalVariable2 (VariableName, (VOID **)&Variable, NULL);
- if (Status == EFI_NOT_FOUND) {
- Status = EFI_SUCCESS;
- Variable = NULL;
- }
-
- if (EFI_ERROR (Status)) {
- return;
- }
-
- //
- // Append terminal device path onto the variable.
- //
- for (TerminalType = 0; TerminalType < ARRAY_SIZE (mTerminalType); TerminalType++) {
- SetTerminalDevicePath (TerminalType, ParentDevicePath, &TempDevicePath);
-
- if (TempDevicePath != NULL) {
- if (!MatchDevicePaths (Variable, TempDevicePath)) {
- NewVariable = AppendDevicePathInstance (Variable, TempDevicePath);
- if (NewVariable != NULL) {
- if (Variable != NULL) {
- FreePool (Variable);
- }
-
- Variable = NewVariable;
- }
- }
-
- FreePool (TempDevicePath);
- }
- }
-
- VariableSize = GetDevicePathSize (Variable);
-
- Status = gRT->SetVariable (
- VariableName,
- &gEfiGlobalVariableGuid,
- EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
- VariableSize,
- Variable
- );
-
- if (EFI_ERROR (Status)) {
- NameSize = StrSize (VariableName);
- SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + VariableSize);
- if (SetVariableStatus != NULL) {
- CopyGuid (&SetVariableStatus->Guid, &gEfiGlobalVariableGuid);
- SetVariableStatus->NameSize = NameSize;
- SetVariableStatus->DataSize = VariableSize;
- SetVariableStatus->SetStatus = Status;
- SetVariableStatus->Attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
- CopyMem (SetVariableStatus + 1, VariableName, NameSize);
- CopyMem (((UINT8 *)(SetVariableStatus + 1)) + NameSize, Variable, VariableSize);
-
- REPORT_STATUS_CODE_EX (
- EFI_ERROR_CODE,
- PcdGet32 (PcdErrorCodeSetVariable),
- 0,
- NULL,
- &gEdkiiStatusCodeDataTypeVariableGuid,
- SetVariableStatus,
- sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + VariableSize
- );
-
- FreePool (SetVariableStatus);
- }
- }
-
- 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;
- TERMINAL_TYPE 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 = 0; TerminalType < ARRAY_SIZE (mTerminalType); 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
- );
- //
- // Shrinking variable with existing variable driver implementation shouldn't fail.
- //
- 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 TERMINAL_TYPE TerminalType,
- IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,
- OUT EFI_DEVICE_PATH_PROTOCOL **TerminalDevicePath
- )
-{
- VENDOR_DEVICE_PATH Node;
-
- ASSERT (TerminalType < ARRAY_SIZE (mTerminalType));
- Node.Header.Type = MESSAGING_DEVICE_PATH;
- Node.Header.SubType = MSG_VENDOR_DP;
- SetDevicePathNodeLength (&Node.Header, sizeof (VENDOR_DEVICE_PATH));
- CopyGuid (&Node.Guid, mTerminalType[TerminalType]);
-
- //
- // 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;
-}
-
-/**
- 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;
-
- //
- // Install driver model protocol(s).
- //
- Status = EfiLibInstallDriverBindingComponentName2 (
- ImageHandle,
- SystemTable,
- &gTerminalDriverBinding,
- ImageHandle,
- &gTerminalComponentName,
- &gTerminalComponentName2
- );
- ASSERT_EFI_ERROR (Status);
-
- return Status;
-}
-
-/**
- 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;
-}
+/** @file + Produces Simple Text Input Protocol, Simple Text Input Extended Protocol and + Simple Text Output Protocol upon Serial IO Protocol. + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Terminal.h" + +// +// Globals +// +EFI_DRIVER_BINDING_PROTOCOL gTerminalDriverBinding = { + TerminalDriverBindingSupported, + TerminalDriverBindingStart, + TerminalDriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_GUID *mTerminalType[] = { + &gEfiPcAnsiGuid, + &gEfiVT100Guid, + &gEfiVT100PlusGuid, + &gEfiVTUTF8Guid, + &gEfiTtyTermGuid, + &gEdkiiLinuxTermGuid, + &gEdkiiXtermR6Guid, + &gEdkiiVT400Guid, + &gEdkiiSCOTermGuid +}; + +CHAR16 *mSerialConsoleNames[] = { + L"PC-ANSI Serial Console", + L"VT-100 Serial Console", + L"VT-100+ Serial Console", + L"VT-UTF8 Serial Console", + L"Tty Terminal Serial Console", + L"Linux Terminal Serial Console", + L"Xterm R6 Serial Console", + L"VT-400 Serial Console", + L"SCO Terminal Serial Console" +}; + +TERMINAL_DEV mTerminalDevTemplate = { + TERMINAL_DEV_SIGNATURE, + NULL, + 0, + NULL, + NULL, + { // SimpleTextInput + TerminalConInReset, + TerminalConInReadKeyStroke, + NULL + }, + { // SimpleTextOutput + TerminalConOutReset, + TerminalConOutOutputString, + TerminalConOutTestString, + TerminalConOutQueryMode, + TerminalConOutSetMode, + TerminalConOutSetAttribute, + TerminalConOutClearScreen, + TerminalConOutSetCursorPosition, + TerminalConOutEnableCursor, + 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, // EfiKeyFiFoForNotify + + NULL, // ControllerNameTable + NULL, // TimerEvent + NULL, // TwoSecondTimeOut + INPUT_STATE_DEFAULT, + RESET_STATE_DEFAULT, + { + 0, + 0, + 0 + }, + 0, + FALSE, + { // SimpleTextInputEx + TerminalConInResetEx, + TerminalConInReadKeyStrokeEx, + NULL, + TerminalConInSetState, + TerminalConInRegisterKeyNotify, + TerminalConInUnregisterKeyNotify, + }, + { // NotifyList + NULL, + NULL, + }, + NULL // KeyNotifyProcessEvent +}; + +TERMINAL_CONSOLE_MODE_DATA mTerminalConsoleModeData[] = { + { 80, 25 }, + { 80, 50 }, + { 100, 31 }, + // + // New modes can be added here. + // +}; + +/** + Convert the GUID representation of terminal type to enum type. + + @param Guid The GUID representation of terminal type. + + @return The terminal type in enum type. +**/ +TERMINAL_TYPE +TerminalTypeFromGuid ( + IN EFI_GUID *Guid + ) +{ + TERMINAL_TYPE Type; + + for (Type = 0; Type < ARRAY_SIZE (mTerminalType); Type++) { + if (CompareGuid (Guid, mTerminalType[Type])) { + break; + } + } + + return Type; +} + +/** + 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 remaining device path is not NULL, then make sure it is a + // device path that describes a terminal communications protocol. + // + if (RemainingDevicePath != NULL) { + // + // 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+, VT-UTF8, TtyTerm + // Linux, XtermR6, VT400 and SCO terminal types + // + if (TerminalTypeFromGuid (&Node->Guid) == ARRAY_SIZE (mTerminalType)) { + 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; +} + +/** + 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] TextModeCount The total number of text modes that terminal console supports. + + @return The buffer to the text modes column and row information. + Caller is responsible to free it when it's non-NULL. + +**/ +TERMINAL_CONSOLE_MODE_DATA * +InitializeTerminalConsoleTextMode ( + OUT INT32 *TextModeCount + ) +{ + TERMINAL_CONSOLE_MODE_DATA *TextModeData; + + ASSERT (TextModeCount != NULL); + + TextModeData = AllocateCopyPool (sizeof (mTerminalConsoleModeData), mTerminalConsoleModeData); + if (TextModeData == NULL) { + return NULL; + } + + *TextModeCount = ARRAY_SIZE (mTerminalConsoleModeData); + + DEBUG_CODE_BEGIN (); + INT32 Index; + + for (Index = 0; Index < *TextModeCount; Index++) { + DEBUG (( + DEBUG_INFO, + "Terminal - Mode %d, Column = %d, Row = %d\n", + Index, + TextModeData[Index].Columns, + TextModeData[Index].Rows + )); + } + + DEBUG_CODE_END (); + return TextModeData; +} + +/** + Stop the terminal state machine. + + @param TerminalDevice The terminal device. +**/ +VOID +StopTerminalStateMachine ( + TERMINAL_DEV *TerminalDevice + ) +{ + EFI_TPL OriginalTpl; + + OriginalTpl = gBS->RaiseTPL (TPL_NOTIFY); + + gBS->CloseEvent (TerminalDevice->TimerEvent); + gBS->CloseEvent (TerminalDevice->TwoSecondTimeOut); + + gBS->RestoreTPL (OriginalTpl); +} + +/** + Start the terminal state machine. + + @param TerminalDevice The terminal device. +**/ +VOID +StartTerminalStateMachine ( + TERMINAL_DEV *TerminalDevice + ) +{ + EFI_STATUS Status; + + 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); +} + +/** + Initialize the controller name table. + + @param TerminalType The terminal type. + @param ControllerNameTable The controller name table. + + @retval EFI_SUCCESS The controller name table is initialized successfully. + @retval others Return status of AddUnicodeString2 (). +**/ +EFI_STATUS +InitializeControllerNameTable ( + TERMINAL_TYPE TerminalType, + EFI_UNICODE_STRING_TABLE **ControllerNameTable + ) +{ + EFI_STATUS Status; + EFI_UNICODE_STRING_TABLE *Table; + + ASSERT (TerminalType < ARRAY_SIZE (mTerminalType)); + Table = NULL; + Status = AddUnicodeString2 ( + "eng", + gTerminalComponentName.SupportedLanguages, + &Table, + mSerialConsoleNames[TerminalType], + TRUE + ); + if (!EFI_ERROR (Status)) { + Status = AddUnicodeString2 ( + "en", + gTerminalComponentName2.SupportedLanguages, + &Table, + mSerialConsoleNames[TerminalType], + FALSE + ); + if (EFI_ERROR (Status)) { + FreeUnicodeStringTable (Table); + } + } + + if (!EFI_ERROR (Status)) { + *ControllerNameTable = Table; + } + + return Status; +} + +/** + 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; + EFI_DEVICE_PATH_PROTOCOL *Vendor; + EFI_HANDLE SerialIoHandle; + EFI_SERIAL_IO_MODE *Mode; + UINTN SerialInTimeOut; + TERMINAL_DEV *TerminalDevice; + UINT8 TerminalType; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + UINTN EntryCount; + UINTN Index; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOutput; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextInput; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + + // + // 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 + ); + ASSERT ((Status == EFI_SUCCESS) || (Status == EFI_ALREADY_STARTED)); + 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 + ); + ASSERT ((Status == EFI_SUCCESS) || (Status == EFI_ALREADY_STARTED)); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + return Status; + } + + if (!IsHotPlugDevice (ParentDevicePath)) { + // + // if the serial device is a hot plug device, do not update the + // ConInDev, ConOutDev, and StdErrDev variables. + // + TerminalUpdateConsoleDevVariable (EFI_CON_IN_DEV_VARIABLE_NAME, ParentDevicePath); + TerminalUpdateConsoleDevVariable (EFI_CON_OUT_DEV_VARIABLE_NAME, ParentDevicePath); + TerminalUpdateConsoleDevVariable (EFI_ERR_OUT_DEV_VARIABLE_NAME, ParentDevicePath); + } + + // + // Do not create any child for END remaining device path. + // + if ((RemainingDevicePath != NULL) && IsDevicePathEnd (RemainingDevicePath)) { + return EFI_SUCCESS; + } + + if (Status == EFI_ALREADY_STARTED) { + if (RemainingDevicePath == NULL) { + // + // If RemainingDevicePath is NULL or is the End of Device Path Node + // + return EFI_SUCCESS; + } + + // + // This driver can only produce one child per serial port. + // Change its terminal type as remaining device path requests. + // + Status = gBS->OpenProtocolInformation ( + Controller, + &gEfiSerialIoProtocolGuid, + &OpenInfoBuffer, + &EntryCount + ); + if (!EFI_ERROR (Status)) { + Status = EFI_NOT_FOUND; + for (Index = 0; Index < EntryCount; Index++) { + if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { + Status = gBS->OpenProtocol ( + OpenInfoBuffer[Index].ControllerHandle, + &gEfiSimpleTextInProtocolGuid, + (VOID **)&SimpleTextInput, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + TerminalDevice = TERMINAL_CON_IN_DEV_FROM_THIS (SimpleTextInput); + TerminalType = TerminalTypeFromGuid (&((VENDOR_DEVICE_PATH *)RemainingDevicePath)->Guid); + ASSERT (TerminalType < ARRAY_SIZE (mTerminalType)); + if (TerminalDevice->TerminalType != TerminalType) { + Status = InitializeControllerNameTable (TerminalType, &ControllerNameTable); + if (!EFI_ERROR (Status)) { + StopTerminalStateMachine (TerminalDevice); + // + // Update the device path + // + Vendor = TerminalDevice->DevicePath; + Status = gBS->LocateDevicePath (&gEfiSerialIoProtocolGuid, &Vendor, &SerialIoHandle); + ASSERT_EFI_ERROR (Status); + CopyGuid (&((VENDOR_DEVICE_PATH *)Vendor)->Guid, mTerminalType[TerminalType]); + Status = gBS->ReinstallProtocolInterface ( + TerminalDevice->Handle, + &gEfiDevicePathProtocolGuid, + TerminalDevice->DevicePath, + TerminalDevice->DevicePath + ); + if (!EFI_ERROR (Status)) { + TerminalDevice->TerminalType = TerminalType; + StartTerminalStateMachine (TerminalDevice); + FreeUnicodeStringTable (TerminalDevice->ControllerNameTable); + TerminalDevice->ControllerNameTable = ControllerNameTable; + } else { + // + // Restore the device path on failure + // + CopyGuid (&((VENDOR_DEVICE_PATH *)Vendor)->Guid, mTerminalType[TerminalDevice->TerminalType]); + FreeUnicodeStringTable (ControllerNameTable); + } + } + } + } + + break; + } + } + + FreePool (OpenInfoBuffer); + } + + return Status; + } + + // + // Initialize the Terminal Dev + // + TerminalDevice = AllocateCopyPool (sizeof (TERMINAL_DEV), &mTerminalDevTemplate); + if (TerminalDevice == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto CloseProtocols; + } + + if (RemainingDevicePath == NULL) { + // + // If RemainingDevicePath is NULL, use default terminal type + // + TerminalDevice->TerminalType = PcdGet8 (PcdDefaultTerminalType); + } else { + // + // End of Device Path Node is handled in above. + // + ASSERT (!IsDevicePathEnd (RemainingDevicePath)); + // + // If RemainingDevicePath isn't the End of Device Path Node, + // Use the RemainingDevicePath to determine the terminal type + // + TerminalDevice->TerminalType = TerminalTypeFromGuid (&((VENDOR_DEVICE_PATH *)RemainingDevicePath)->Guid); + } + + ASSERT (TerminalDevice->TerminalType < ARRAY_SIZE (mTerminalType)); + TerminalDevice->SerialIo = SerialIo; + + // + // Build the component name for the child device + // + Status = InitializeControllerNameTable (TerminalDevice->TerminalType, &TerminalDevice->ControllerNameTable); + if (EFI_ERROR (Status)) { + goto FreeResources; + } + + // + // Build the device path for the child device + // + Status = SetTerminalDevicePath (TerminalDevice->TerminalType, ParentDevicePath, &TerminalDevice->DevicePath); + if (EFI_ERROR (Status)) { + goto FreeResources; + } + + InitializeListHead (&TerminalDevice->NotifyList); + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + TerminalConInWaitForKeyEx, + TerminalDevice, + &TerminalDevice->SimpleInputEx.WaitForKeyEx + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + TerminalConInWaitForKey, + TerminalDevice, + &TerminalDevice->SimpleInput.WaitForKey + ); + ASSERT_EFI_ERROR (Status); + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + KeyNotifyProcessHandler, + TerminalDevice, + &TerminalDevice->KeyNotifyProcessEvent + ); + ASSERT_EFI_ERROR (Status); + + // + // 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 FreeResources; + } + + TerminalDevice->UnicodeFiFo = AllocateZeroPool (sizeof (UNICODE_FIFO)); + if (TerminalDevice->UnicodeFiFo == NULL) { + goto FreeResources; + } + + TerminalDevice->EfiKeyFiFo = AllocateZeroPool (sizeof (EFI_KEY_FIFO)); + if (TerminalDevice->EfiKeyFiFo == NULL) { + goto FreeResources; + } + + TerminalDevice->EfiKeyFiFoForNotify = AllocateZeroPool (sizeof (EFI_KEY_FIFO)); + if (TerminalDevice->EfiKeyFiFoForNotify == NULL) { + goto FreeResources; + } + + // + // 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; + } + + SimpleTextOutput = &TerminalDevice->SimpleTextOutput; + SimpleTextInput = &TerminalDevice->SimpleInput; + + // + // Initialize SimpleTextOut instance + // + SimpleTextOutput->Mode = &TerminalDevice->SimpleTextOutputMode; + TerminalDevice->TerminalConsoleModeData = InitializeTerminalConsoleTextMode ( + &SimpleTextOutput->Mode->MaxMode + ); + if (TerminalDevice->TerminalConsoleModeData == NULL) { + goto FreeResources; + } + + // + // For terminal devices, cursor is always visible + // + SimpleTextOutput->Mode->CursorVisible = TRUE; + Status = SimpleTextOutput->SetAttribute (SimpleTextOutput, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + if (!EFI_ERROR (Status)) { + Status = SimpleTextOutput->Reset (SimpleTextOutput, FALSE); + } + + if (EFI_ERROR (Status)) { + goto ReportError; + } + + // + // Initialize SimpleTextInput instance + // + Status = SimpleTextInput->Reset (SimpleTextInput, FALSE); + if (EFI_ERROR (Status)) { + goto ReportError; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &TerminalDevice->Handle, + &gEfiSimpleTextInProtocolGuid, + &TerminalDevice->SimpleInput, + &gEfiSimpleTextInputExProtocolGuid, + &TerminalDevice->SimpleInputEx, + &gEfiSimpleTextOutProtocolGuid, + &TerminalDevice->SimpleTextOutput, + &gEfiDevicePathProtocolGuid, + TerminalDevice->DevicePath, + NULL + ); + if (!EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiSerialIoProtocolGuid, + (VOID **)&TerminalDevice->SerialIo, + This->DriverBindingHandle, + TerminalDevice->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + ASSERT_EFI_ERROR (Status); + StartTerminalStateMachine (TerminalDevice); + return Status; + } + +ReportError: + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_CONTROLLER_ERROR), + ParentDevicePath + ); + +FreeResources: + ASSERT (TerminalDevice != NULL); + + if (TerminalDevice->SimpleInput.WaitForKey != NULL) { + gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey); + } + + if (TerminalDevice->SimpleInputEx.WaitForKeyEx != NULL) { + gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx); + } + + if (TerminalDevice->KeyNotifyProcessEvent != NULL) { + gBS->CloseEvent (TerminalDevice->KeyNotifyProcessEvent); + } + + if (TerminalDevice->RawFiFo != NULL) { + FreePool (TerminalDevice->RawFiFo); + } + + if (TerminalDevice->UnicodeFiFo != NULL) { + FreePool (TerminalDevice->UnicodeFiFo); + } + + if (TerminalDevice->EfiKeyFiFo != NULL) { + FreePool (TerminalDevice->EfiKeyFiFo); + } + + if (TerminalDevice->EfiKeyFiFoForNotify != NULL) { + FreePool (TerminalDevice->EfiKeyFiFoForNotify); + } + + if (TerminalDevice->ControllerNameTable != NULL) { + FreeUnicodeStringTable (TerminalDevice->ControllerNameTable); + } + + if (TerminalDevice->DevicePath != NULL) { + FreePool (TerminalDevice->DevicePath); + } + + if (TerminalDevice->TerminalConsoleModeData != NULL) { + FreePool (TerminalDevice->TerminalConsoleModeData); + } + + FreePool (TerminalDevice); + +CloseProtocols: + + // + // Remove Parent Device Path from + // the Console Device Environment Variables + // + TerminalRemoveConsoleDevVariable (EFI_CON_IN_DEV_VARIABLE_NAME, ParentDevicePath); + TerminalRemoveConsoleDevVariable (EFI_CON_OUT_DEV_VARIABLE_NAME, ParentDevicePath); + TerminalRemoveConsoleDevVariable (EFI_ERR_OUT_DEV_VARIABLE_NAME, ParentDevicePath); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiSerialIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + ASSERT_EFI_ERROR (Status); + 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; + + // + // 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, + &gEfiDevicePathProtocolGuid, + (VOID **)&ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT_EFI_ERROR (Status); + + // + // Remove Parent Device Path from + // the Console Device Environment Variables + // + TerminalRemoveConsoleDevVariable (EFI_CON_IN_DEV_VARIABLE_NAME, ParentDevicePath); + TerminalRemoveConsoleDevVariable (EFI_CON_OUT_DEV_VARIABLE_NAME, ParentDevicePath); + TerminalRemoveConsoleDevVariable (EFI_ERR_OUT_DEV_VARIABLE_NAME, 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 { + FreeUnicodeStringTable (TerminalDevice->ControllerNameTable); + StopTerminalStateMachine (TerminalDevice); + gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey); + gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx); + gBS->CloseEvent (TerminalDevice->KeyNotifyProcessEvent); + TerminalFreeNotifyList (&TerminalDevice->NotifyList); + FreePool (TerminalDevice->DevicePath); + FreePool (TerminalDevice->TerminalConsoleModeData); + FreePool (TerminalDevice); + } + } + + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + 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; +} + +/** + 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 NameSize; + UINTN VariableSize; + TERMINAL_TYPE TerminalType; + EFI_DEVICE_PATH_PROTOCOL *Variable; + EFI_DEVICE_PATH_PROTOCOL *NewVariable; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + EDKII_SET_VARIABLE_STATUS *SetVariableStatus; + + // + // Get global variable and its size according to the name given. + // + Status = GetEfiGlobalVariable2 (VariableName, (VOID **)&Variable, NULL); + if (Status == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + Variable = NULL; + } + + if (EFI_ERROR (Status)) { + return; + } + + // + // Append terminal device path onto the variable. + // + for (TerminalType = 0; TerminalType < ARRAY_SIZE (mTerminalType); TerminalType++) { + SetTerminalDevicePath (TerminalType, ParentDevicePath, &TempDevicePath); + + if (TempDevicePath != NULL) { + if (!MatchDevicePaths (Variable, TempDevicePath)) { + NewVariable = AppendDevicePathInstance (Variable, TempDevicePath); + if (NewVariable != NULL) { + if (Variable != NULL) { + FreePool (Variable); + } + + Variable = NewVariable; + } + } + + FreePool (TempDevicePath); + } + } + + VariableSize = GetDevicePathSize (Variable); + + Status = gRT->SetVariable ( + VariableName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + VariableSize, + Variable + ); + + if (EFI_ERROR (Status)) { + NameSize = StrSize (VariableName); + SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + VariableSize); + if (SetVariableStatus != NULL) { + CopyGuid (&SetVariableStatus->Guid, &gEfiGlobalVariableGuid); + SetVariableStatus->NameSize = NameSize; + SetVariableStatus->DataSize = VariableSize; + SetVariableStatus->SetStatus = Status; + SetVariableStatus->Attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; + CopyMem (SetVariableStatus + 1, VariableName, NameSize); + CopyMem (((UINT8 *)(SetVariableStatus + 1)) + NameSize, Variable, VariableSize); + + REPORT_STATUS_CODE_EX ( + EFI_ERROR_CODE, + PcdGet32 (PcdErrorCodeSetVariable), + 0, + NULL, + &gEdkiiStatusCodeDataTypeVariableGuid, + SetVariableStatus, + sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + VariableSize + ); + + FreePool (SetVariableStatus); + } + } + + 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; + TERMINAL_TYPE 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 = 0; TerminalType < ARRAY_SIZE (mTerminalType); 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 + ); + // + // Shrinking variable with existing variable driver implementation shouldn't fail. + // + 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 TERMINAL_TYPE TerminalType, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, + OUT EFI_DEVICE_PATH_PROTOCOL **TerminalDevicePath + ) +{ + VENDOR_DEVICE_PATH Node; + + ASSERT (TerminalType < ARRAY_SIZE (mTerminalType)); + Node.Header.Type = MESSAGING_DEVICE_PATH; + Node.Header.SubType = MSG_VENDOR_DP; + SetDevicePathNodeLength (&Node.Header, sizeof (VENDOR_DEVICE_PATH)); + CopyGuid (&Node.Guid, mTerminalType[TerminalType]); + + // + // 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; +} + +/** + 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; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gTerminalDriverBinding, + ImageHandle, + &gTerminalComponentName, + &gTerminalComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + 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; +} |
