/** @file Implementation for EFI_SIMPLE_TEXT_INPUT_PROTOCOL protocol. Copyright (c) 2006 - 2008, Intel Corporation.
All rights reserved. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include "Terminal.h" /** Reads the next keystroke from the input device. The WaitForKey Event can be used to test for existence of a keystroke via WaitForEvent () call. @param TerminalDevice Terminal driver private structure @param KeyData A pointer to a buffer that is filled in with the keystroke state data for the key that was pressed. @retval EFI_SUCCESS The keystroke information was returned. @retval EFI_NOT_READY There was no keystroke data available. @retval EFI_DEVICE_ERROR The keystroke information was not returned due to hardware errors. @retval EFI_INVALID_PARAMETER KeyData is NULL. **/ EFI_STATUS ReadKeyStrokeWorker ( IN TERMINAL_DEV *TerminalDevice, OUT EFI_KEY_DATA *KeyData ) { EFI_STATUS Status; LIST_ENTRY *Link; LIST_ENTRY *NotifyList; TERMINAL_CONSOLE_IN_EX_NOTIFY *CurrentNotify; if (KeyData == NULL) { return EFI_INVALID_PARAMETER; } // // Initialize *Key to nonsense value. // KeyData->Key.ScanCode = SCAN_NULL; KeyData->Key.UnicodeChar = 0; Status = TerminalConInCheckForKey (&TerminalDevice->SimpleInput); if (EFI_ERROR (Status)) { return EFI_NOT_READY; } if (!EfiKeyFiFoRemoveOneKey (TerminalDevice, &KeyData->Key)) { return EFI_NOT_READY; } KeyData->KeyState.KeyShiftState = 0; KeyData->KeyState.KeyToggleState = 0; // // Invoke notification functions if exist // NotifyList = &TerminalDevice->NotifyList; for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) { CurrentNotify = CR ( Link, TERMINAL_CONSOLE_IN_EX_NOTIFY, NotifyEntry, TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE ); if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) { CurrentNotify->KeyNotificationFn (KeyData); } } return EFI_SUCCESS; } /** Implements EFI_SIMPLE_TEXT_INPUT_PROTOCOL.Reset(). This driver only perform dependent serial device reset regardless of the value of ExtendeVerification @param This Indicates the calling context. @param ExtendedVerification Skip by this driver. @retval EFI_SUCCESS The reset operation succeeds. @retval EFI_DEVICE_ERROR The dependent serial port reset fails. **/ EFI_STATUS EFIAPI TerminalConInReset ( IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, IN BOOLEAN ExtendedVerification ) { EFI_STATUS Status; TERMINAL_DEV *TerminalDevice; TerminalDevice = TERMINAL_CON_IN_DEV_FROM_THIS (This); // // Report progress code here // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, PcdGet32 (PcdStatusCodeValueRemoteConsoleReset), TerminalDevice->DevicePath ); Status = TerminalDevice->SerialIo->Reset (TerminalDevice->SerialIo); // // clear all the internal buffer for keys // InitializeRawFiFo (TerminalDevice); InitializeUnicodeFiFo (TerminalDevice); InitializeEfiKeyFiFo (TerminalDevice); if (EFI_ERROR (Status)) { REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_ERROR_CODE | EFI_ERROR_MINOR, PcdGet32 (PcdStatusCodeValueRemoteConsoleError), TerminalDevice->DevicePath ); } return Status; } /** Implements EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke(). @param This Indicates the calling context. @param Key A pointer to a buffer that is filled in with the keystroke information for the key that was sent from terminal. @retval EFI_SUCCESS The keystroke information is returned successfully. @retval EFI_NOT_READY There is no keystroke data available. @retval EFI_DEVICE_ERROR The dependent serial device encounters error. **/ EFI_STATUS EFIAPI TerminalConInReadKeyStroke ( IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, OUT EFI_INPUT_KEY *Key ) { TERMINAL_DEV *TerminalDevice; EFI_STATUS Status; EFI_KEY_DATA KeyData; // // get TERMINAL_DEV from "This" parameter. // TerminalDevice = TERMINAL_CON_IN_DEV_FROM_THIS (This); Status = ReadKeyStrokeWorker (TerminalDevice, &KeyData); if (EFI_ERROR (Status)) { return Status; } CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY)); return EFI_SUCCESS; } /** Check if the key already has been registered. If both RegsiteredData and InputData is NULL, then ASSERT(). @param RegsiteredData A pointer to a buffer that is filled in with the keystroke state data for the key that was registered. @param InputData A pointer to a buffer that is filled in with the keystroke state data for the key that was pressed. @retval TRUE Key be pressed matches a registered key. @retval FLASE Match failed. **/ BOOLEAN IsKeyRegistered ( IN EFI_KEY_DATA *RegsiteredData, IN EFI_KEY_DATA *InputData ) { ASSERT (RegsiteredData != NULL && InputData != NULL); if ((RegsiteredData->Key.ScanCode != InputData->Key.ScanCode) || (RegsiteredData->Key.UnicodeChar != InputData->Key.UnicodeChar)) { return FALSE; } return TRUE; } /** Event notification function for EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx event Signal the event if there is key available @param Event Indicates the event that invoke this function. @param Context Indicates the calling context. **/ VOID EFIAPI TerminalConInWaitForKeyEx ( IN EFI_EVENT Event, IN VOID *Context ) { TERMINAL_DEV *TerminalDevice; TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (Context); TerminalConInWaitForKey (Event, &TerminalDevice->SimpleInput); } // // Simple Text Input Ex protocol functions // /** Reset the input device and optionally run diagnostics @param This Protocol instance pointer. @param ExtendedVerification Driver may perform diagnostics on reset. @retval EFI_SUCCESS The device was reset. @retval EFI_DEVICE_ERROR The device is not functioning properly and could not be reset. **/ EFI_STATUS EFIAPI TerminalConInResetEx ( IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, IN BOOLEAN ExtendedVerification ) { EFI_STATUS Status; TERMINAL_DEV *TerminalDevice; TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This); Status = TerminalDevice->SimpleInput.Reset (&TerminalDevice->SimpleInput, ExtendedVerification); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } return EFI_SUCCESS; } /** Reads the next keystroke from the input device. The WaitForKey Event can be used to test for existence of a keystroke via WaitForEvent () call. @param This Protocol instance pointer. @param KeyData A pointer to a buffer that is filled in with the keystroke state data for the key that was pressed. @retval EFI_SUCCESS The keystroke information was returned. @retval EFI_NOT_READY There was no keystroke data available. @retval EFI_DEVICE_ERROR The keystroke information was not returned due to hardware errors. @retval EFI_INVALID_PARAMETER KeyData is NULL. **/ EFI_STATUS EFIAPI TerminalConInReadKeyStrokeEx ( IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, OUT EFI_KEY_DATA *KeyData ) { TERMINAL_DEV *TerminalDevice; if (KeyData == NULL) { return EFI_INVALID_PARAMETER; } TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This); return ReadKeyStrokeWorker (TerminalDevice, KeyData); } /** Set certain state for the input device. @param This Protocol instance pointer. @param KeyToggleState A pointer to the EFI_KEY_TOGGLE_STATE to set the state for the input device. @retval EFI_SUCCESS The device state was set successfully. @retval EFI_DEVICE_ERROR The device is not functioning correctly and could not have the setting adjusted. @retval EFI_UNSUPPORTED The device does not have the ability to set its state. @retval EFI_INVALID_PARAMETER KeyToggleState is NULL. **/ EFI_STATUS EFIAPI TerminalConInSetState ( IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, IN EFI_KEY_TOGGLE_STATE *KeyToggleState ) { if (KeyToggleState == NULL) { return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; } /** Register a notification function for a particular keystroke for the input device. @param This Protocol instance pointer. @param KeyData A pointer to a buffer that is filled in with the keystroke information data for the key that was pressed. @param KeyNotificationFunction Points to the function to be called when the key sequence is typed specified by KeyData. @param NotifyHandle Points to the unique handle assigned to the registered notification. @retval EFI_SUCCESS The notification function was registered successfully. @retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necessary data structures. @retval EFI_INVALID_PARAMETER KeyData or NotifyHandle is NULL. **/ EFI_STATUS EFIAPI TerminalConInRegisterKeyNotify ( IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, IN EFI_KEY_DATA *KeyData, IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, OUT EFI_HANDLE *NotifyHandle ) { EFI_STATUS Status; TERMINAL_DEV *TerminalDevice; TERMINAL_CONSOLE_IN_EX_NOTIFY *NewNotify; LIST_ENTRY *Link; LIST_ENTRY *NotifyList; TERMINAL_CONSOLE_IN_EX_NOTIFY *CurrentNotify; if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) { return EFI_INVALID_PARAMETER; } TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This); // // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered. // NotifyList = &TerminalDevice->NotifyList; for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) { CurrentNotify = CR ( Link, TERMINAL_CONSOLE_IN_EX_NOTIFY, NotifyEntry, TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE ); if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) { if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) { *NotifyHandle = CurrentNotify->NotifyHandle; return EFI_SUCCESS; } } } // // Allocate resource to save the notification function // NewNotify = (TERMINAL_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (TERMINAL_CONSOLE_IN_EX_NOTIFY)); if (NewNotify == NULL) { return EFI_OUT_OF_RESOURCES; } NewNotify->Signature = TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE; NewNotify->KeyNotificationFn = KeyNotificationFunction; CopyMem (&NewNotify->KeyData, KeyData, sizeof (KeyData)); InsertTailList (&TerminalDevice->NotifyList, &NewNotify->NotifyEntry); // // Use gSimpleTextInExNotifyGuid to get a valid EFI_HANDLE // Status = gBS->InstallMultipleProtocolInterfaces ( &NewNotify->NotifyHandle, &gSimpleTextInExNotifyGuid, NULL, NULL ); ASSERT_EFI_ERROR (Status); *NotifyHandle = NewNotify->NotifyHandle; return EFI_SUCCESS; } /** Remove a registered notification function from a particular keystroke. @param This Protocol instance pointer. @param NotificationHandle The handle of the notification function being unregistered. @retval EFI_SUCCESS The notification function was unregistered successfully. @retval EFI_INVALID_PARAMETER The NotificationHandle is invalid. @retval EFI_NOT_FOUND Can not find the matching entry in database. **/ EFI_STATUS EFIAPI TerminalConInUnregisterKeyNotify ( IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, IN EFI_HANDLE NotificationHandle ) { EFI_STATUS Status; TERMINAL_DEV *TerminalDevice; LIST_ENTRY *Link; TERMINAL_CONSOLE_IN_EX_NOTIFY *CurrentNotify; LIST_ENTRY *NotifyList; if (NotificationHandle == NULL) { return EFI_INVALID_PARAMETER; } Status = gBS->OpenProtocol ( NotificationHandle, &gSimpleTextInExNotifyGuid, NULL, NULL, NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL ); if (EFI_ERROR (Status)) { return EFI_INVALID_PARAMETER; } TerminalDevice = TERMINAL_CON_IN_EX_DEV_FROM_THIS (This); NotifyList = &TerminalDevice->NotifyList; for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) { CurrentNotify = CR ( Link, TERMINAL_CONSOLE_IN_EX_NOTIFY, NotifyEntry, TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE ); if (CurrentNotify->NotifyHandle == NotificationHandle) { // // Remove the notification function from NotifyList and free resources // RemoveEntryList (&CurrentNotify->NotifyEntry); Status = gBS->UninstallMultipleProtocolInterfaces ( CurrentNotify->NotifyHandle, &gSimpleTextInExNotifyGuid, NULL, NULL ); ASSERT_EFI_ERROR (Status); gBS->FreePool (CurrentNotify); return EFI_SUCCESS; } } return EFI_NOT_FOUND; } /** Translate raw data into Unicode (according to different encode), and translate Unicode into key information. (according to different standard). @param TerminalDevice Terminal driver private structure. **/ VOID TranslateRawDataToEfiKey ( IN TERMINAL_DEV *TerminalDevice ) { switch (TerminalDevice->TerminalType) { case PCANSITYPE: case VT100TYPE: case VT100PLUSTYPE: AnsiRawDataToUnicode (TerminalDevice); UnicodeToEfiKey (TerminalDevice); break; case VTUTF8TYPE: // // Process all the raw data in the RawFIFO, // put the processed key into UnicodeFIFO. // VTUTF8RawDataToUnicode (TerminalDevice); // // Translate all the Unicode data in the UnicodeFIFO to Efi key, // then put into EfiKeyFIFO. // UnicodeToEfiKey (TerminalDevice); break; } } /** Event notification function for EFI_SIMPLE_TEXT_INPUT_PROTOCOL.WaitForKey event Signal the event if there is key available @param Event Indicates the event that invoke this function. @param Context Indicates the calling context. **/ VOID EFIAPI TerminalConInWaitForKey ( IN EFI_EVENT Event, IN VOID *Context ) { // // Someone is waiting on the keystroke event, if there's // a key pending, signal the event // // Context is the pointer to EFI_SIMPLE_TEXT_INPUT_PROTOCOL // if (!EFI_ERROR (TerminalConInCheckForKey (Context))) { gBS->SignalEvent (Event); } } /** Check for a pending key in the Efi Key FIFO or Serial device buffer. @param This Indicates the calling context. @retval EFI_SUCCESS There is key pending. @retval EFI_NOT_READY There is no key pending. @retval EFI_DEVICE_ERROR If Serial IO is not attached to serial device. **/ EFI_STATUS TerminalConInCheckForKey ( IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This ) { EFI_STATUS Status; TERMINAL_DEV *TerminalDevice; UINT32 Control; UINT8 Input; EFI_SERIAL_IO_MODE *Mode; EFI_SERIAL_IO_PROTOCOL *SerialIo; UINTN SerialInTimeOut; TerminalDevice = TERMINAL_CON_IN_DEV_FROM_THIS (This); SerialIo = TerminalDevice->SerialIo; if (SerialIo == NULL) { return EFI_DEVICE_ERROR; } // // if current timeout value for serial device is not identical with // the value saved in TERMINAL_DEV structure, then recalculate the // timeout value again and set serial attribute according to this value. // Mode = SerialIo->Mode; if (Mode->Timeout != TerminalDevice->SerialInTimeOut) { SerialInTimeOut = 0; if (Mode->BaudRate != 0) { // // According to BAUD rate to calculate the timeout value. // SerialInTimeOut = (1 + Mode->DataBits + Mode->StopBits) * 2 * 1000000 / (UINTN) Mode->BaudRate; } Status = SerialIo->SetAttributes ( 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)) { TerminalDevice->SerialInTimeOut = 0; } else { TerminalDevice->SerialInTimeOut = SerialInTimeOut; } } // // Check whether serial buffer is empty. // Status = SerialIo->GetControl (SerialIo, &Control); if ((Control & EFI_SERIAL_INPUT_BUFFER_EMPTY) != 0) { // // Translate all the raw data in RawFIFO into EFI Key, // according to different terminal type supported. // TranslateRawDataToEfiKey (TerminalDevice); // // if there is pre-fetched Efi Key in EfiKeyFIFO buffer, // return directly. // if (!IsEfiKeyFiFoEmpty (TerminalDevice)) { return EFI_SUCCESS; } else { return EFI_NOT_READY; } } // // Fetch all the keys in the serial buffer, // and insert the byte stream into RawFIFO. // do { Status = GetOneKeyFromSerial (TerminalDevice->SerialIo, &Input); if (EFI_ERROR (Status)) { if (Status == EFI_DEVICE_ERROR) { REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_ERROR_CODE | EFI_ERROR_MINOR, PcdGet32 (PcdStatusCodeValueRemoteConsoleInputError), TerminalDevice->DevicePath ); } break; } RawFiFoInsertOneKey (TerminalDevice, Input); } while (TRUE); // // Translate all the raw data in RawFIFO into EFI Key, // according to different terminal type supported. // TranslateRawDataToEfiKey (TerminalDevice); if (IsEfiKeyFiFoEmpty (TerminalDevice)) { return EFI_NOT_READY; } return EFI_SUCCESS; } /** Get one key out of serial buffer. @param SerialIo Serial I/O protocol attached to the serial device. @param Output The fetched key. @retval EFI_NOT_READY If serial buffer is empty. @retval EFI_DEVICE_ERROR If reading serial buffer encounter error. @retval EFI_SUCCESS If reading serial buffer successfully, put the fetched key to the parameter output. **/ EFI_STATUS GetOneKeyFromSerial ( EFI_SERIAL_IO_PROTOCOL *SerialIo, UINT8 *Output ) { EFI_STATUS Status; UINTN Size; Size = 1; *Output = 0; // // Read one key from serial I/O device. // Status = SerialIo->Read (SerialIo, &Size, Output); if (EFI_ERROR (Status)) { if (Status == EFI_TIMEOUT) { return EFI_NOT_READY; } return EFI_DEVICE_ERROR; } if (*Output == 0) { return EFI_NOT_READY; } return EFI_SUCCESS; } /** Insert one byte raw data into the Raw Data FIFO. @param TerminalDevice Terminal driver private structure. @param Input The key will be input. @retval TRUE If insert successfully. @retval FLASE If Raw Data buffer is full before key insertion, and the key is lost. **/ BOOLEAN RawFiFoInsertOneKey ( TERMINAL_DEV *TerminalDevice, UINT8 Input ) { UINT8 Tail; Tail = TerminalDevice->RawFiFo.Tail; if (IsRawFiFoFull (TerminalDevice)) { // // Raw FIFO is full // return FALSE; } TerminalDevice->RawFiFo.Data[Tail] = Input; TerminalDevice->RawFiFo.Tail = (UINT8) ((Tail + 1) % (RAW_FIFO_MAX_NUMBER + 1)); return TRUE; } /** Remove one pre-fetched key out of the Raw Data FIFO. @param TerminalDevice Terminal driver private structure. @param Output The key will be removed. @retval TRUE If insert successfully. @retval FLASE If Raw Data FIFO buffer is empty before remove operation. **/ BOOLEAN RawFiFoRemoveOneKey ( TERMINAL_DEV *TerminalDevice, UINT8 *Output ) { UINT8 Head; Head = TerminalDevice->RawFiFo.Head; if (IsRawFiFoEmpty (TerminalDevice)) { // // FIFO is empty // *Output = 0; return FALSE; } *Output = TerminalDevice->RawFiFo.Data[Head]; TerminalDevice->RawFiFo.Head = (UINT8) ((Head + 1) % (RAW_FIFO_MAX_NUMBER + 1)); return TRUE; } /** Clarify whether Raw Data FIFO buffer is empty. @param TerminalDevice Terminal driver private structure @retval TRUE If Raw Data FIFO buffer is empty. @retval FLASE If Raw Data FIFO buffer is not empty. **/ BOOLEAN IsRawFiFoEmpty ( TERMINAL_DEV *TerminalDevice ) { if (TerminalDevice->RawFiFo.Head == TerminalDevice->RawFiFo.Tail) { return TRUE; } else { return FALSE; } } /** Clarify whether Raw Data FIFO buffer is full. @param TerminalDevice Terminal driver private structure @retval TRUE If Raw Data FIFO buffer is full. @retval FLASE If Raw Data FIFO buffer is not full. **/ BOOLEAN IsRawFiFoFull ( TERMINAL_DEV *TerminalDevice ) { UINT8 Tail; UINT8 Head; Tail = TerminalDevice->RawFiFo.Tail; Head = TerminalDevice->RawFiFo.Head; if (((Tail + 1) % (RAW_FIFO_MAX_NUMBER + 1)) == Head) { return TRUE; } return FALSE; } /** Insert one pre-fetched key into the FIFO buffer. @param TerminalDevice Terminal driver private structure. @param Key The key will be input. @retval TRUE If insert successfully. @retval FLASE If FIFO buffer is full before key insertion, and the key is lost. **/ BOOLEAN EfiKeyFiFoInsertOneKey ( TERMINAL_DEV *TerminalDevice, EFI_INPUT_KEY Key ) { UINT8 Tail; Tail = TerminalDevice->EfiKeyFiFo.Tail; if (IsEfiKeyFiFoFull (TerminalDevice)) { // // Efi Key FIFO is full // return FALSE; } TerminalDevice->EfiKeyFiFo.Data[Tail] = Key; TerminalDevice->EfiKeyFiFo.Tail = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1)); return TRUE; } /** Remove one pre-fetched key out of the FIFO buffer. @param TerminalDevice Terminal driver private structure. @param Output The key will be removed. @retval TRUE If insert successfully. @retval FLASE If FIFO buffer is empty before remove operation. **/ BOOLEAN EfiKeyFiFoRemoveOneKey ( TERMINAL_DEV *TerminalDevice, EFI_INPUT_KEY *Output ) { UINT8 Head; Head = TerminalDevice->EfiKeyFiFo.Head; if (IsEfiKeyFiFoEmpty (TerminalDevice)) { // // FIFO is empty // Output->ScanCode = SCAN_NULL; Output->UnicodeChar = 0; return FALSE; } *Output = TerminalDevice->EfiKeyFiFo.Data[Head]; TerminalDevice->EfiKeyFiFo.Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1)); return TRUE; } /** Clarify whether FIFO buffer is empty. @param TerminalDevice Terminal driver private structure @retval TRUE If FIFO buffer is empty. @retval FLASE If FIFO buffer is not empty. **/ BOOLEAN IsEfiKeyFiFoEmpty ( TERMINAL_DEV *TerminalDevice ) { if (TerminalDevice->EfiKeyFiFo.Head == TerminalDevice->EfiKeyFiFo.Tail) { return TRUE; } else { return FALSE; } } /** Clarify whether FIFO buffer is full. @param TerminalDevice Terminal driver private structure @retval TRUE If FIFO buffer is full. @retval FLASE If FIFO buffer is not full. **/ BOOLEAN IsEfiKeyFiFoFull ( TERMINAL_DEV *TerminalDevice ) { UINT8 Tail; UINT8 Head; Tail = TerminalDevice->EfiKeyFiFo.Tail; Head = TerminalDevice->EfiKeyFiFo.Head; if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) { return TRUE; } return FALSE; } /** Insert one pre-fetched key into the Unicode FIFO buffer. @param TerminalDevice Terminal driver private structure. @param Input The key will be input. @retval TRUE If insert successfully. @retval FLASE If Unicode FIFO buffer is full before key insertion, and the key is lost. **/ BOOLEAN UnicodeFiFoInsertOneKey ( TERMINAL_DEV *TerminalDevice, UINT16 Input ) { UINT8 Tail; Tail = TerminalDevice->UnicodeFiFo.Tail; if (IsUnicodeFiFoFull (TerminalDevice)) { // // Unicode FIFO is full // return FALSE; } TerminalDevice->UnicodeFiFo.Data[Tail] = Input; TerminalDevice->UnicodeFiFo.Tail = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1)); return TRUE; } /** Remove one pre-fetched key out of the Unicode FIFO buffer. @param TerminalDevice Terminal driver private structure. @param Output The key will be removed. @retval TRUE If insert successfully. @retval FLASE If Unicode FIFO buffer is empty before remove operation. **/ BOOLEAN UnicodeFiFoRemoveOneKey ( TERMINAL_DEV *TerminalDevice, UINT16 *Output ) { UINT8 Head; Head = TerminalDevice->UnicodeFiFo.Head; if (IsUnicodeFiFoEmpty (TerminalDevice)) { // // FIFO is empty // Output = NULL; return FALSE; } *Output = TerminalDevice->UnicodeFiFo.Data[Head]; TerminalDevice->UnicodeFiFo.Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1)); return TRUE; } /** Clarify whether Unicode FIFO buffer is empty. @param TerminalDevice Terminal driver private structure @retval TRUE If Unicode FIFO buffer is empty. @retval FLASE If Unicode FIFO buffer is not empty. **/ BOOLEAN IsUnicodeFiFoEmpty ( TERMINAL_DEV *TerminalDevice ) { if (TerminalDevice->UnicodeFiFo.Head == TerminalDevice->UnicodeFiFo.Tail) { return TRUE; } else { return FALSE; } } /** Clarify whether Unicode FIFO buffer is full. @param TerminalDevice Terminal driver private structure @retval TRUE If Unicode FIFO buffer is full. @retval FLASE If Unicode FIFO buffer is not full. **/ BOOLEAN IsUnicodeFiFoFull ( TERMINAL_DEV *TerminalDevice ) { UINT8 Tail; UINT8 Head; Tail = TerminalDevice->UnicodeFiFo.Tail; Head = TerminalDevice->UnicodeFiFo.Head; if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) { return TRUE; } return FALSE; } /** Count Unicode FIFO buffer. @param TerminalDevice Terminal driver private structure @return The count in bytes of Unicode FIFO. **/ UINT8 UnicodeFiFoGetKeyCount ( TERMINAL_DEV *TerminalDevice ) { UINT8 Tail; UINT8 Head; Tail = TerminalDevice->UnicodeFiFo.Tail; Head = TerminalDevice->UnicodeFiFo.Head; if (Tail >= Head) { return (UINT8) (Tail - Head); } else { return (UINT8) (Tail + FIFO_MAX_NUMBER + 1 - Head); } } /** Update the Unicode characters from a terminal input device into EFI Keys FIFO. @param TerminalDevice The terminal device to use to translate raw input into EFI Keys **/ VOID UnicodeToEfiKeyFlushState ( IN TERMINAL_DEV *TerminalDevice ) { EFI_INPUT_KEY Key; UINT32 InputState; InputState = TerminalDevice->InputState; if ((InputState & INPUT_STATE_ESC) != 0) { Key.ScanCode = SCAN_ESC; Key.UnicodeChar = 0; EfiKeyFiFoInsertOneKey (TerminalDevice, Key); } if ((InputState & INPUT_STATE_CSI) != 0) { Key.ScanCode = SCAN_NULL; Key.UnicodeChar = CSI; EfiKeyFiFoInsertOneKey (TerminalDevice, Key); } if ((InputState & INPUT_STATE_LEFTOPENBRACKET) != 0) { Key.ScanCode = SCAN_NULL; Key.UnicodeChar = LEFTOPENBRACKET; EfiKeyFiFoInsertOneKey (TerminalDevice, Key); } if ((InputState & INPUT_STATE_O) != 0) { Key.ScanCode = SCAN_NULL; Key.UnicodeChar = 'O'; EfiKeyFiFoInsertOneKey (TerminalDevice, Key); } if ((InputState & INPUT_STATE_2) != 0) { Key.ScanCode = SCAN_NULL; Key.UnicodeChar = '2'; EfiKeyFiFoInsertOneKey (TerminalDevice, Key); } // // Cancel the timer. // gBS->SetTimer ( TerminalDevice->TwoSecondTimeOut, TimerCancel, 0 ); TerminalDevice->InputState = INPUT_STATE_DEFAULT; } /** Converts a stream of Unicode characters from a terminal input device into EFI Keys that can be read through the Simple Input Protocol. The table below shows the keyboard input mappings that this function supports. If the ESC sequence listed in one of the columns is presented, then it is translated into the corresponding EFI Scan Code. If a matching sequence is not found, then the raw key strokes are converted into EFI Keys. 2 seconds are allowed for an ESC sequence to be completed. If the ESC sequence is not completed in 2 seconds, then the raw key strokes of the partial ESC sequence are converted into EFI Keys. There is one special input sequence that will force the system to reset. This is ESC R ESC r ESC R. Symbols used in table below =========================== ESC = 0x1B CSI = 0x9B DEL = 0x7f ^ = CTRL +=========+======+===========+==========+==========+ | | EFI | UEFI 2.0 | | | | | Scan | | VT100+ | | | KEY | Code | PC ANSI | VTUTF8 | VT100 | +=========+======+===========+==========+==========+ | NULL | 0x00 | | | | | UP | 0x01 | ESC [ A | ESC [ A | ESC [ A | | DOWN | 0x02 | ESC [ B | ESC [ B | ESC [ B | | RIGHT | 0x03 | ESC [ C | ESC [ C | ESC [ C | | LEFT | 0x04 | ESC [ D | ESC [ D | ESC [ D | | HOME | 0x05 | ESC [ H | ESC h | ESC [ H | | END | 0x06 | ESC [ F | ESC k | ESC [ K | | INSERT | 0x07 | ESC [ @ | ESC + | ESC [ @ | | | | ESC [ L | | ESC [ L | | DELETE | 0x08 | ESC [ X | ESC - | ESC [ P | | PG UP | 0x09 | ESC [ I | ESC ? | ESC [ V | | | | | | ESC [ ? | | PG DOWN | 0x0A | ESC [ G | ESC / | ESC [ U | | | | | | ESC [ / | | F1 | 0x0B | ESC [ M | ESC 1 | ESC O P | | F2 | 0x0C | ESC [ N | ESC 2 | ESC O Q | | F3 | 0x0D | ESC [ O | ESC 3 | ESC O w | | F4 | 0x0E | ESC [ P | ESC 4 | ESC O x | | F5 | 0x0F | ESC [ Q | ESC 5 | ESC O t | | F6 | 0x10 | ESC [ R | ESC 6 | ESC O u | | F7 | 0x11 | ESC [ S | ESC 7 | ESC O q | | F8 | 0x12 | ESC [ T | ESC 8 | ESC O r | | F9 | 0x13 | ESC [ U | ESC 9 | ESC O p | | F10 | 0x14 | ESC [ V | ESC 0 | ESC O M | | Escape | 0x17 | ESC | ESC | ESC | | F11 | 0x15 | | ESC ! | | | F12 | 0x16 | | ESC @ | | +=========+======+===========+==========+==========+ Special Mappings ================ ESC R ESC r ESC R = Reset System @param TerminalDevice The terminal device to use to translate raw input into EFI Keys **/ VOID UnicodeToEfiKey ( IN TERMINAL_DEV *TerminalDevice ) { EFI_STATUS Status; EFI_STATUS TimerStatus; UINT16 UnicodeChar; EFI_INPUT_KEY Key; BOOLEAN SetDefaultResetState; TimerStatus = gBS->CheckEvent (TerminalDevice->TwoSecondTimeOut); if (!EFI_ERROR (TimerStatus)) { UnicodeToEfiKeyFlushState (TerminalDevice); TerminalDevice->ResetState = RESET_STATE_DEFAULT; } while (!IsUnicodeFiFoEmpty(TerminalDevice)) { if (TerminalDevice->InputState != INPUT_STATE_DEFAULT) { // // Check to see if the 2 second timer has expired // TimerStatus = gBS->CheckEvent (TerminalDevice->TwoSecondTimeOut); if (!EFI_ERROR (TimerStatus)) { UnicodeToEfiKeyFlushState (TerminalDevice); TerminalDevice->ResetState = RESET_STATE_DEFAULT; } } // // Fetch one Unicode character from the Unicode FIFO // UnicodeFiFoRemoveOneKey (TerminalDevice, &UnicodeChar); SetDefaultResetState = TRUE; switch (TerminalDevice->InputState) { case INPUT_STATE_DEFAULT: break; case INPUT_STATE_ESC: if (UnicodeChar == LEFTOPENBRACKET) { TerminalDevice->InputState |= INPUT_STATE_LEFTOPENBRACKET; TerminalDevice->ResetState = RESET_STATE_DEFAULT; continue; } if (UnicodeChar == 'O' && TerminalDevice->TerminalType == VT100TYPE) { TerminalDevice->InputState |= INPUT_STATE_O; TerminalDevice->ResetState = RESET_STATE_DEFAULT; continue; } Key.ScanCode = SCAN_NULL; if (TerminalDevice->TerminalType == VT100PLUSTYPE || TerminalDevice->TerminalType == VTUTF8TYPE) { switch (UnicodeChar) { case '1': Key.ScanCode = SCAN_F1; break; case '2': Key.ScanCode = SCAN_F2; break; case '3': Key.ScanCode = SCAN_F3; break; case '4': Key.ScanCode = SCAN_F4; break; case '5': Key.ScanCode = SCAN_F5; break; case '6': Key.ScanCode = SCAN_F6; break; case '7': Key.ScanCode = SCAN_F7; break; case '8': Key.ScanCode = SCAN_F8; break; case '9': Key.ScanCode = SCAN_F9; break; case '0': Key.ScanCode = SCAN_F10; break; case '!': Key.ScanCode = SCAN_F11; break; case '@': Key.ScanCode = SCAN_F12; break; case 'h': Key.ScanCode = SCAN_HOME; break; case 'k': Key.ScanCode = SCAN_END; break; case '+': Key.ScanCode = SCAN_INSERT; break; case '-': Key.ScanCode = SCAN_DELETE; break; case '/': Key.ScanCode = SCAN_PAGE_DOWN; break; case '?': Key.ScanCode = SCAN_PAGE_UP; break; default : break; } } switch (UnicodeChar) { case 'R': if (TerminalDevice->ResetState == RESET_STATE_DEFAULT) { TerminalDevice->ResetState = RESET_STATE_ESC_R; SetDefaultResetState = FALSE; } else if (TerminalDevice->ResetState == RESET_STATE_ESC_R_ESC_r) { gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); } Key.ScanCode = SCAN_NULL; break; case 'r': if (TerminalDevice->ResetState == RESET_STATE_ESC_R) { TerminalDevice->ResetState = RESET_STATE_ESC_R_ESC_r; SetDefaultResetState = FALSE; } Key.ScanCode = SCAN_NULL; break; default : break; } if (SetDefaultResetState) { TerminalDevice->ResetState = RESET_STATE_DEFAULT; } if (Key.ScanCode != SCAN_NULL) { Key.UnicodeChar = 0; EfiKeyFiFoInsertOneKey (TerminalDevice,Key); TerminalDevice->InputState = INPUT_STATE_DEFAULT; UnicodeToEfiKeyFlushState (TerminalDevice); continue; } UnicodeToEfiKeyFlushState (TerminalDevice); break; case INPUT_STATE_ESC | INPUT_STATE_O: TerminalDevice->ResetState = RESET_STATE_DEFAULT; Key.ScanCode = SCAN_NULL; if (TerminalDevice->TerminalType == VT100TYPE) { switch (UnicodeChar) { case 'P': Key.ScanCode = SCAN_F1; break; case 'Q': Key.ScanCode = SCAN_F2; break; case 'w': Key.ScanCode = SCAN_F3; break; case 'x': Key.ScanCode = SCAN_F4; break; case 't': Key.ScanCode = SCAN_F5; break; case 'u': Key.ScanCode = SCAN_F6; break; case 'q': Key.ScanCode = SCAN_F7; break; case 'r': Key.ScanCode = SCAN_F8; break; case 'p': Key.ScanCode = SCAN_F9; break; case 'M': Key.ScanCode = SCAN_F10; break; default : break; } } if (Key.ScanCode != SCAN_NULL) { Key.UnicodeChar = 0; EfiKeyFiFoInsertOneKey (TerminalDevice,Key); TerminalDevice->InputState = INPUT_STATE_DEFAULT; UnicodeToEfiKeyFlushState (TerminalDevice); continue; } UnicodeToEfiKeyFlushState (TerminalDevice); break; case INPUT_STATE_ESC | INPUT_STATE_LEFTOPENBRACKET: TerminalDevice->ResetState = RESET_STATE_DEFAULT; Key.ScanCode = SCAN_NULL; if (TerminalDevice->TerminalType == PCANSITYPE || TerminalDevice->TerminalType == VT100TYPE || TerminalDevice->TerminalType == VT100PLUSTYPE || TerminalDevice->TerminalType == VTUTF8TYPE) { switch (UnicodeChar) { case 'A': Key.ScanCode = SCAN_UP; break; case 'B': Key.ScanCode = SCAN_DOWN; break; case 'C': Key.ScanCode = SCAN_RIGHT; break; case 'D': Key.ScanCode = SCAN_LEFT; break; case 'H': if (TerminalDevice->TerminalType == PCANSITYPE || TerminalDevice->TerminalType == VT100TYPE) { Key.ScanCode = SCAN_HOME; } break; case 'F': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_END; } break; case 'K': if (TerminalDevice->TerminalType == VT100TYPE) { Key.ScanCode = SCAN_END; } break; case 'L': case '@': if (TerminalDevice->TerminalType == PCANSITYPE || TerminalDevice->TerminalType == VT100TYPE) { Key.ScanCode = SCAN_INSERT; } break; case 'X': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_DELETE; } break; case 'P': if (TerminalDevice->TerminalType == VT100TYPE) { Key.ScanCode = SCAN_DELETE; } else if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_F4; } break; case 'I': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_PAGE_UP; } break; case 'V': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_F10; } case '?': if (TerminalDevice->TerminalType == VT100TYPE) { Key.ScanCode = SCAN_PAGE_UP; } break; case 'G': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_PAGE_DOWN; } break; case 'U': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_F9; } case '/': if (TerminalDevice->TerminalType == VT100TYPE) { Key.ScanCode = SCAN_PAGE_DOWN; } break; case 'M': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_F1; } break; case 'N': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_F2; } break; case 'O': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_F3; } break; case 'Q': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_F5; } break; case 'R': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_F6; } break; case 'S': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_F7; } break; case 'T': if (TerminalDevice->TerminalType == PCANSITYPE) { Key.ScanCode = SCAN_F8; } break; default : break; } } if (Key.ScanCode != SCAN_NULL) { Key.UnicodeChar = 0; EfiKeyFiFoInsertOneKey (TerminalDevice,Key); TerminalDevice->InputState = INPUT_STATE_DEFAULT; UnicodeToEfiKeyFlushState (TerminalDevice); continue; } UnicodeToEfiKeyFlushState (TerminalDevice); break; default: // // Invalid state. This should never happen. // ASSERT (FALSE); UnicodeToEfiKeyFlushState (TerminalDevice); break; } if (UnicodeChar == ESC) { TerminalDevice->InputState = INPUT_STATE_ESC; } if (UnicodeChar == CSI) { TerminalDevice->InputState = INPUT_STATE_CSI; } if (TerminalDevice->InputState != INPUT_STATE_DEFAULT) { Status = gBS->SetTimer( TerminalDevice->TwoSecondTimeOut, TimerRelative, (UINT64)20000000 ); ASSERT_EFI_ERROR (Status); continue; } if (SetDefaultResetState) { TerminalDevice->ResetState = RESET_STATE_DEFAULT; } if (UnicodeChar == DEL) { Key.ScanCode = SCAN_DELETE; Key.UnicodeChar = 0; } else { Key.ScanCode = SCAN_NULL; Key.UnicodeChar = UnicodeChar; } EfiKeyFiFoInsertOneKey (TerminalDevice,Key); } }