From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../Universal/Console/TerminalDxe/Terminal.c | 1383 ++++++++++++++++++++ 1 file changed, 1383 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c (limited to 'roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c') diff --git a/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c new file mode 100644 index 000000000..a98b690c8 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c @@ -0,0 +1,1383 @@ +/** @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.
+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 ( + 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)); + } + ); + 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; +} + -- cgit