From 4aa68cbc97e06d93a82189ade1317104f95535c1 Mon Sep 17 00:00:00 2001 From: Ruiyu Ni Date: Fri, 17 Jul 2015 17:39:00 +0800 Subject: [PATCH] MdeModulePkg: Add Ps2KeyboardDxe driver. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ruiyu Ni Reviewed-by: Feng Tian --- .../Bus/Isa/Ps2KeyboardDxe/ComponentName.c | 352 ++++ .../Bus/Isa/Ps2KeyboardDxe/Ps2KbdCtrller.c | 1872 +++++++++++++++++ .../Bus/Isa/Ps2KeyboardDxe/Ps2KbdTextIn.c | 683 ++++++ .../Bus/Isa/Ps2KeyboardDxe/Ps2Keyboard.c | 647 ++++++ .../Bus/Isa/Ps2KeyboardDxe/Ps2Keyboard.h | 551 +++++ .../Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxe.inf | 84 + .../Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxe.uni | 23 + .../Ps2KeyboardDxe/Ps2KeyboardDxeExtra.uni | 20 + MdeModulePkg/Include/Protocol/Ps2Policy.h | 41 + MdeModulePkg/MdeModulePkg.dec | 8 + MdeModulePkg/MdeModulePkg.dsc | 1 + 11 files changed, 4282 insertions(+) create mode 100644 MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/ComponentName.c create mode 100644 MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KbdCtrller.c create mode 100644 MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KbdTextIn.c create mode 100644 MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2Keyboard.c create mode 100644 MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2Keyboard.h create mode 100644 MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxe.inf create mode 100644 MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxe.uni create mode 100644 MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxeExtra.uni create mode 100644 MdeModulePkg/Include/Protocol/Ps2Policy.h diff --git a/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/ComponentName.c b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/ComponentName.c new file mode 100644 index 0000000000..e64e46bb45 --- /dev/null +++ b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/ComponentName.c @@ -0,0 +1,352 @@ +/** @file + Routines related Component Name protocol. + +Copyright (c) 2006 - 2016, 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 "Ps2Keyboard.h" + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ps2KeyboardComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ps2KeyboardComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gPs2KeyboardComponentName = { + Ps2KeyboardComponentNameGetDriverName, + Ps2KeyboardComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gPs2KeyboardComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Ps2KeyboardComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Ps2KeyboardComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mPs2KeyboardDriverNameTable[] = { + { + "eng;en", + L"PS/2 Keyboard Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ps2KeyboardComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mPs2KeyboardDriverNameTable, + DriverName, + (BOOLEAN)(This == &gPs2KeyboardComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ps2KeyboardComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; + KEYBOARD_CONSOLE_IN_DEV *ConsoleIn; + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Check Controller's handle + // + Status = EfiTestManagedDevice (ControllerHandle, gKeyboardControllerDriver.DriverBindingHandle, &gEfiSioProtocolGuid); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Get the device context + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSimpleTextInProtocolGuid, + (VOID **) &ConIn, + gKeyboardControllerDriver.DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ConsoleIn = KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (ConIn); + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + ConsoleIn->ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gPs2KeyboardComponentName) + ); +} diff --git a/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KbdCtrller.c b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KbdCtrller.c new file mode 100644 index 0000000000..4f064f7651 --- /dev/null +++ b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KbdCtrller.c @@ -0,0 +1,1872 @@ +/** @file + Routines that access 8042 keyboard controller + +Copyright (c) 2006 - 2016, 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 "Ps2Keyboard.h" + +struct { + UINT8 ScanCode; ///< follows value defined in Scan Code Set1 + UINT16 EfiScanCode; + CHAR16 UnicodeChar; + CHAR16 ShiftUnicodeChar; +} +ConvertKeyboardScanCodeToEfiKey[] = { + + { + 0x01, // Escape + SCAN_ESC, + 0x0000, + 0x0000 + }, + { + 0x02, + SCAN_NULL, + L'1', + L'!' + }, + { + 0x03, + SCAN_NULL, + L'2', + L'@' + }, + { + 0x04, + SCAN_NULL, + L'3', + L'#' + }, + { + 0x05, + SCAN_NULL, + L'4', + L'$' + }, + { + 0x06, + SCAN_NULL, + L'5', + L'%' + }, + { + 0x07, + SCAN_NULL, + L'6', + L'^' + }, + { + 0x08, + SCAN_NULL, + L'7', + L'&' + }, + { + 0x09, + SCAN_NULL, + L'8', + L'*' + }, + { + 0x0A, + SCAN_NULL, + L'9', + L'(' + }, + { + 0x0B, + SCAN_NULL, + L'0', + L')' + }, + { + 0x0C, + SCAN_NULL, + L'-', + L'_' + }, + { + 0x0D, + SCAN_NULL, + L'=', + L'+' + }, + { + 0x0E, // BackSpace + SCAN_NULL, + 0x0008, + 0x0008 + }, + { + 0x0F, // Tab + SCAN_NULL, + 0x0009, + 0x0009 + }, + { + 0x10, + SCAN_NULL, + L'q', + L'Q' + }, + { + 0x11, + SCAN_NULL, + L'w', + L'W' + }, + { + 0x12, + SCAN_NULL, + L'e', + L'E' + }, + { + 0x13, + SCAN_NULL, + L'r', + L'R' + }, + { + 0x14, + SCAN_NULL, + L't', + L'T' + }, + { + 0x15, + SCAN_NULL, + L'y', + L'Y' + }, + { + 0x16, + SCAN_NULL, + L'u', + L'U' + }, + { + 0x17, + SCAN_NULL, + L'i', + L'I' + }, + { + 0x18, + SCAN_NULL, + L'o', + L'O' + }, + { + 0x19, + SCAN_NULL, + L'p', + L'P' + }, + { + 0x1a, + SCAN_NULL, + L'[', + L'{' + }, + { + 0x1b, + SCAN_NULL, + L']', + L'}' + }, + { + 0x1c, // Enter + SCAN_NULL, + 0x000d, + 0x000d + }, + { + 0x1d, + SCAN_NULL, + 0x0000, + 0x0000 + }, + { + 0x1e, + SCAN_NULL, + L'a', + L'A' + }, + { + 0x1f, + SCAN_NULL, + L's', + L'S' + }, + { + 0x20, + SCAN_NULL, + L'd', + L'D' + }, + { + 0x21, + SCAN_NULL, + L'f', + L'F' + }, + { + 0x22, + SCAN_NULL, + L'g', + L'G' + }, + { + 0x23, + SCAN_NULL, + L'h', + L'H' + }, + { + 0x24, + SCAN_NULL, + L'j', + L'J' + }, + { + 0x25, + SCAN_NULL, + L'k', + L'K' + }, + { + 0x26, + SCAN_NULL, + L'l', + L'L' + }, + { + 0x27, + SCAN_NULL, + L';', + L':' + }, + { + 0x28, + SCAN_NULL, + L'\'', + L'"' + }, + { + 0x29, + SCAN_NULL, + L'`', + L'~' + }, + { + 0x2a, // Left Shift + SCAN_NULL, + 0x0000, + 0x0000 + }, + { + 0x2b, + SCAN_NULL, + L'\\', + L'|' + }, + { + 0x2c, + SCAN_NULL, + L'z', + L'Z' + }, + { + 0x2d, + SCAN_NULL, + L'x', + L'X' + }, + { + 0x2e, + SCAN_NULL, + L'c', + L'C' + }, + { + 0x2f, + SCAN_NULL, + L'v', + L'V' + }, + { + 0x30, + SCAN_NULL, + L'b', + L'B' + }, + { + 0x31, + SCAN_NULL, + L'n', + L'N' + }, + { + 0x32, + SCAN_NULL, + L'm', + L'M' + }, + { + 0x33, + SCAN_NULL, + L',', + L'<' + }, + { + 0x34, + SCAN_NULL, + L'.', + L'>' + }, + { + 0x35, + SCAN_NULL, + L'/', + L'?' + }, + { + 0x36, //Right Shift + SCAN_NULL, + 0x0000, + 0x0000 + }, + { + 0x37, // Numeric Keypad * + SCAN_NULL, + L'*', + L'*' + }, + { + 0x38, //Left Alt/Extended Right Alt + SCAN_NULL, + 0x0000, + 0x0000 + }, + { + 0x39, + SCAN_NULL, + L' ', + L' ' + }, + { + 0x3A, //CapsLock + SCAN_NULL, + 0x0000, + 0x0000 + }, + { + 0x3B, + SCAN_F1, + 0x0000, + 0x0000 + }, + { + 0x3C, + SCAN_F2, + 0x0000, + 0x0000 + }, + { + 0x3D, + SCAN_F3, + 0x0000, + 0x0000 + }, + { + 0x3E, + SCAN_F4, + 0x0000, + 0x0000 + }, + { + 0x3F, + SCAN_F5, + 0x0000, + 0x0000 + }, + { + 0x40, + SCAN_F6, + 0x0000, + 0x0000 + }, + { + 0x41, + SCAN_F7, + 0x0000, + 0x0000 + }, + { + 0x42, + SCAN_F8, + 0x0000, + 0x0000 + }, + { + 0x43, + SCAN_F9, + 0x0000, + 0x0000 + }, + { + 0x44, + SCAN_F10, + 0x0000, + 0x0000 + }, + { + 0x45, // NumLock + SCAN_NULL, + 0x0000, + 0x0000 + }, + { + 0x46, // ScrollLock + SCAN_NULL, + 0x0000, + 0x0000 + }, + { + 0x47, + SCAN_HOME, + L'7', + L'7' + }, + { + 0x48, + SCAN_UP, + L'8', + L'8' + }, + { + 0x49, + SCAN_PAGE_UP, + L'9', + L'9' + }, + { + 0x4a, + SCAN_NULL, + L'-', + L'-' + }, + { + 0x4b, + SCAN_LEFT, + L'4', + L'4' + }, + { + 0x4c, // Numeric Keypad 5 + SCAN_NULL, + L'5', + L'5' + }, + { + 0x4d, + SCAN_RIGHT, + L'6', + L'6' + }, + { + 0x4e, + SCAN_NULL, + L'+', + L'+' + }, + { + 0x4f, + SCAN_END, + L'1', + L'1' + }, + { + 0x50, + SCAN_DOWN, + L'2', + L'2' + }, + { + 0x51, + SCAN_PAGE_DOWN, + L'3', + L'3' + }, + { + 0x52, + SCAN_INSERT, + L'0', + L'0' + }, + { + 0x53, + SCAN_DELETE, + L'.', + L'.' + }, + { + 0x57, + SCAN_F11, + 0x0000, + 0x0000 + }, + { + 0x58, + SCAN_F12, + 0x0000, + 0x0000 + }, + { + 0x5B, //Left LOGO + SCAN_NULL, + 0x0000, + 0x0000 + }, + { + 0x5C, //Right LOGO + SCAN_NULL, + 0x0000, + 0x0000 + }, + { + 0x5D, //Menu key + SCAN_NULL, + 0x0000, + 0x0000 + }, + { + TABLE_END, + TABLE_END, + SCAN_NULL, + SCAN_NULL + }, +}; + +// +// The WaitForValue time out +// +UINTN mWaitForValueTimeOut = KEYBOARD_WAITFORVALUE_TIMEOUT; + +BOOLEAN mEnableMouseInterface; + + + +/** + Return the count of scancode in the queue. + + @param Queue Pointer to instance of SCAN_CODE_QUEUE. + + @return Count of the scancode. +**/ +UINTN +GetScancodeBufCount ( + IN SCAN_CODE_QUEUE *Queue + ) +{ + if (Queue->Head <= Queue->Tail) { + return Queue->Tail - Queue->Head; + } else { + return Queue->Tail + KEYBOARD_SCAN_CODE_MAX_COUNT - Queue->Head; + } +} + +/** + Read several bytes from the scancode buffer without removing them. + This function is called to see if there are enough bytes of scancode + representing a single key. + + @param Queue Pointer to instance of SCAN_CODE_QUEUE. + @param Count Number of bytes to be read + @param Buf Store the results + + @retval EFI_SUCCESS success to scan the keyboard code + @retval EFI_NOT_READY invalid parameter +**/ +EFI_STATUS +GetScancodeBufHead ( + IN SCAN_CODE_QUEUE *Queue, + IN UINTN Count, + OUT UINT8 *Buf + ) +{ + UINTN Index; + UINTN Pos; + + // + // check the valid range of parameter 'Count' + // + if (GetScancodeBufCount (Queue) < Count) { + return EFI_NOT_READY; + } + // + // retrieve the values + // + for (Index = 0, Pos = Queue->Head; Index < Count; Index++, Pos = (Pos + 1) % KEYBOARD_SCAN_CODE_MAX_COUNT) { + Buf[Index] = Queue->Buffer[Pos]; + } + + return EFI_SUCCESS; +} + +/** + + Read & remove several bytes from the scancode buffer. + This function is usually called after GetScancodeBufHead() + + @param Queue Pointer to instance of SCAN_CODE_QUEUE. + @param Count Number of bytes to be read + @param Buf Store the results + + @retval EFI_SUCCESS success to scan the keyboard code + @retval EFI_NOT_READY invalid parameter +**/ +EFI_STATUS +PopScancodeBufHead ( + IN SCAN_CODE_QUEUE *Queue, + IN UINTN Count, + OUT UINT8 *Buf OPTIONAL + ) +{ + UINTN Index; + + // + // Check the valid range of parameter 'Count' + // + if (GetScancodeBufCount (Queue) < Count) { + return EFI_NOT_READY; + } + // + // Retrieve and remove the values + // + for (Index = 0; Index < Count; Index++, Queue->Head = (Queue->Head + 1) % KEYBOARD_SCAN_CODE_MAX_COUNT) { + if (Buf != NULL) { + Buf[Index] = Queue->Buffer[Queue->Head]; + } + } + + return EFI_SUCCESS; +} + +/** + Push one byte to the scancode buffer. + + @param Queue Pointer to instance of SCAN_CODE_QUEUE. + @param Scancode The byte to push. +**/ +VOID +PushScancodeBufTail ( + IN SCAN_CODE_QUEUE *Queue, + IN UINT8 Scancode + ) +{ + if (GetScancodeBufCount (Queue) == KEYBOARD_SCAN_CODE_MAX_COUNT - 1) { + PopScancodeBufHead (Queue, 1, NULL); + } + + Queue->Buffer[Queue->Tail] = Scancode; + Queue->Tail = (Queue->Tail + 1) % KEYBOARD_SCAN_CODE_MAX_COUNT; +} + +/** + Read data register . + + @param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + + @return return the value + +**/ +UINT8 +KeyReadDataRegister ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn + ) + +{ + return IoRead8 (ConsoleIn->DataRegisterAddress); +} + +/** + Write data register. + + @param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + @param Data value wanted to be written + +**/ +VOID +KeyWriteDataRegister ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn, + IN UINT8 Data + ) +{ + IoWrite8 (ConsoleIn->DataRegisterAddress, Data); +} + +/** + Read status register. + + @param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + + @return value in status register + +**/ +UINT8 +KeyReadStatusRegister ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn + ) +{ + return IoRead8 (ConsoleIn->StatusRegisterAddress); +} + +/** + Write command register . + + @param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + @param Data The value wanted to be written + +**/ +VOID +KeyWriteCommandRegister ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn, + IN UINT8 Data + ) +{ + IoWrite8 (ConsoleIn->CommandRegisterAddress, Data); +} + +/** + Display error message. + + @param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + @param ErrMsg Unicode string of error message + +**/ +VOID +KeyboardError ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn, + IN CHAR16 *ErrMsg + ) +{ + ConsoleIn->KeyboardErr = TRUE; +} + +/** + Timer event handler: read a series of scancodes from 8042 + and put them into memory scancode buffer. + it read as much scancodes to either fill + the memory buffer or empty the keyboard buffer. + It is registered as running under TPL_NOTIFY + + @param Event The timer event + @param Context A KEYBOARD_CONSOLE_IN_DEV pointer + +**/ +VOID +EFIAPI +KeyboardTimerHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) + +{ + UINT8 Data; + EFI_TPL OldTpl; + KEYBOARD_CONSOLE_IN_DEV *ConsoleIn; + + ConsoleIn = (KEYBOARD_CONSOLE_IN_DEV *) Context; + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + if (((KEYBOARD_CONSOLE_IN_DEV *) Context)->KeyboardErr) { + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); + return ; + } + + // + // To let KB driver support Hot plug, here should skip the 'resend' command for the case that + // KB is not connected to system. If KB is not connected to system, driver will find there's something + // error in the following code and wait for the input buffer empty, this waiting time shoulb be short enough since + // this is a NOTIFY TPL period function, or the system performance will degrade hardly when KB is not connected. + // Just skip the 'resend' process simply. + // + + while ((KeyReadStatusRegister (ConsoleIn) & (KEYBOARD_STATUS_REGISTER_TRANSMIT_TIMEOUT|KEYBOARD_STATUS_REGISTER_HAS_OUTPUT_DATA)) == + KEYBOARD_STATUS_REGISTER_HAS_OUTPUT_DATA + ) { + // + // Read one byte of the scan code and store it into the memory buffer + // + Data = KeyReadDataRegister (ConsoleIn); + PushScancodeBufTail (&ConsoleIn->ScancodeQueue, Data); + } + KeyGetchar (ConsoleIn); + + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); +} + +/** + Read key value . + + @param ConsoleIn - Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + @param Data - Pointer to outof buffer for keeping key value + + @retval EFI_TIMEOUT Status resigter time out + @retval EFI_SUCCESS Success to read keyboard + +**/ +EFI_STATUS +KeyboardRead ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn, + OUT UINT8 *Data + ) + +{ + UINT32 TimeOut; + UINT32 RegFilled; + + TimeOut = 0; + RegFilled = 0; + + // + // wait till output buffer full then perform the read + // + for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) { + if (KeyReadStatusRegister (ConsoleIn) & KEYBOARD_STATUS_REGISTER_HAS_OUTPUT_DATA) { + RegFilled = 1; + *Data = KeyReadDataRegister (ConsoleIn); + break; + } + + MicroSecondDelay (30); + } + + if (RegFilled == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +/** + write key to keyboard + + @param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + @param Data value wanted to be written + + @retval EFI_TIMEOUT The input buffer register is full for putting new value util timeout + @retval EFI_SUCCESS The new value is sucess put into input buffer register. + +**/ +EFI_STATUS +KeyboardWrite ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn, + IN UINT8 Data + ) +{ + UINT32 TimeOut; + UINT32 RegEmptied; + + TimeOut = 0; + RegEmptied = 0; + + // + // wait for input buffer empty + // + for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) { + if ((KeyReadStatusRegister (ConsoleIn) & 0x02) == 0) { + RegEmptied = 1; + break; + } + + MicroSecondDelay (30); + } + + if (RegEmptied == 0) { + return EFI_TIMEOUT; + } + // + // Write it + // + KeyWriteDataRegister (ConsoleIn, Data); + + return EFI_SUCCESS; +} + +/** + Issue keyboard command. + + @param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + @param Data The buff holding the command + + @retval EFI_TIMEOUT Keyboard is not ready to issuing + @retval EFI_SUCCESS Success to issue keyboard command + +**/ +EFI_STATUS +KeyboardCommand ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn, + IN UINT8 Data + ) +{ + UINT32 TimeOut; + UINT32 RegEmptied; + + TimeOut = 0; + RegEmptied = 0; + + // + // Wait For Input Buffer Empty + // + for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) { + if ((KeyReadStatusRegister (ConsoleIn) & 0x02) == 0) { + RegEmptied = 1; + break; + } + + MicroSecondDelay (30); + } + + if (RegEmptied == 0) { + return EFI_TIMEOUT; + } + // + // issue the command + // + KeyWriteCommandRegister (ConsoleIn, Data); + + // + // Wait For Input Buffer Empty again + // + RegEmptied = 0; + for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) { + if ((KeyReadStatusRegister (ConsoleIn) & 0x02) == 0) { + RegEmptied = 1; + break; + } + + MicroSecondDelay (30); + } + + if (RegEmptied == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +/** + wait for a specific value to be presented on + 8042 Data register by keyboard and then read it, + used in keyboard commands ack + + @param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + @param Value the value wanted to be waited. + + @retval EFI_TIMEOUT Fail to get specific value in given time + @retval EFI_SUCCESS Success to get specific value in given time. + +**/ +EFI_STATUS +KeyboardWaitForValue ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn, + IN UINT8 Value + ) +{ + UINT8 Data; + UINT32 TimeOut; + UINT32 SumTimeOut; + UINT32 GotIt; + + GotIt = 0; + TimeOut = 0; + SumTimeOut = 0; + + // + // Make sure the initial value of 'Data' is different from 'Value' + // + Data = 0; + if (Data == Value) { + Data = 1; + } + // + // Read from 8042 (multiple times if needed) + // until the expected value appears + // use SumTimeOut to control the iteration + // + while (1) { + // + // Perform a read + // + for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) { + if (KeyReadStatusRegister (ConsoleIn) & 0x01) { + Data = KeyReadDataRegister (ConsoleIn); + break; + } + + MicroSecondDelay (30); + } + + SumTimeOut += TimeOut; + + if (Data == Value) { + GotIt = 1; + break; + } + + if (SumTimeOut >= mWaitForValueTimeOut) { + break; + } + } + // + // Check results + // + if (GotIt == 1) { + return EFI_SUCCESS; + } else { + return EFI_TIMEOUT; + } + +} + +/** + Show keyboard status lights according to + indicators in ConsoleIn. + + @param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + + @return status of updating keyboard register + +**/ +EFI_STATUS +UpdateStatusLights ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn + ) +{ + EFI_STATUS Status; + UINT8 Command; + + // + // Send keyboard command + // + Status = KeyboardWrite (ConsoleIn, 0xed); + if (EFI_ERROR (Status)) { + return Status; + } + + KeyboardWaitForValue (ConsoleIn, 0xfa); + + // + // Light configuration + // + Command = 0; + if (ConsoleIn->CapsLock) { + Command |= 4; + } + + if (ConsoleIn->NumLock) { + Command |= 2; + } + + if (ConsoleIn->ScrollLock) { + Command |= 1; + } + + Status = KeyboardWrite (ConsoleIn, Command); + + if (EFI_ERROR (Status)) { + return Status; + } + + KeyboardWaitForValue (ConsoleIn, 0xfa); + return Status; +} + +/** + Get scancode from scancode buffer and translate into EFI-scancode and unicode defined by EFI spec. + + The function is always called in TPL_NOTIFY. + + @param ConsoleIn KEYBOARD_CONSOLE_IN_DEV instance pointer + +**/ +VOID +KeyGetchar ( + IN OUT KEYBOARD_CONSOLE_IN_DEV *ConsoleIn + ) +{ + EFI_STATUS Status; + UINT16 ScanCode; + BOOLEAN Extend0; + BOOLEAN Extend1; + UINTN Index; + EFI_KEY_DATA KeyData; + LIST_ENTRY *Link; + KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + // + // 3 bytes most + // + UINT8 ScancodeArr[3]; + UINT32 ScancodeArrPos; + + // + // Check if there are enough bytes of scancode representing a single key + // available in the buffer + // + while (TRUE) { + Extend0 = FALSE; + Extend1 = FALSE; + ScancodeArrPos = 0; + Status = GetScancodeBufHead (&ConsoleIn->ScancodeQueue, ScancodeArrPos + 1, ScancodeArr); + if (EFI_ERROR (Status)) { + return ; + } + + if (ScancodeArr[ScancodeArrPos] == SCANCODE_EXTENDED0) { + // + // E0 to look ahead 2 bytes + // + Extend0 = TRUE; + ScancodeArrPos = 1; + Status = GetScancodeBufHead (&ConsoleIn->ScancodeQueue, ScancodeArrPos + 1, ScancodeArr); + if (EFI_ERROR (Status)) { + return ; + } + } else if (ScancodeArr[ScancodeArrPos] == SCANCODE_EXTENDED1) { + // + // E1 to look ahead 3 bytes + // + Extend1 = TRUE; + ScancodeArrPos = 2; + Status = GetScancodeBufHead (&ConsoleIn->ScancodeQueue, ScancodeArrPos + 1, ScancodeArr); + if (EFI_ERROR (Status)) { + return ; + } + } + // + // if we reach this position, scancodes for a key is in buffer now,pop them + // + Status = PopScancodeBufHead (&ConsoleIn->ScancodeQueue, ScancodeArrPos + 1, ScancodeArr); + ASSERT_EFI_ERROR (Status); + + // + // store the last available byte, this byte of scancode will be checked + // + ScanCode = ScancodeArr[ScancodeArrPos]; + + if (!Extend1) { + // + // Check for special keys and update the driver state. + // + switch (ScanCode) { + + case SCANCODE_CTRL_MAKE: + if (Extend0) { + ConsoleIn->RightCtrl = TRUE; + } else { + ConsoleIn->LeftCtrl = TRUE; + } + break; + case SCANCODE_CTRL_BREAK: + if (Extend0) { + ConsoleIn->RightCtrl = FALSE; + } else { + ConsoleIn->LeftCtrl = FALSE; + } + break; + + case SCANCODE_ALT_MAKE: + if (Extend0) { + ConsoleIn->RightAlt = TRUE; + } else { + ConsoleIn->LeftAlt = TRUE; + } + break; + case SCANCODE_ALT_BREAK: + if (Extend0) { + ConsoleIn->RightAlt = FALSE; + } else { + ConsoleIn->LeftAlt = FALSE; + } + break; + + case SCANCODE_LEFT_SHIFT_MAKE: + // + // To avoid recognize PRNT_SCRN key as a L_SHIFT key + // because PRNT_SCRN key generates E0 followed by L_SHIFT scan code. + // If it the second byte of the PRNT_ScRN skip it. + // + if (!Extend0) { + ConsoleIn->LeftShift = TRUE; + break; + } + continue; + + case SCANCODE_LEFT_SHIFT_BREAK: + if (!Extend0) { + ConsoleIn->LeftShift = FALSE; + } + break; + + case SCANCODE_RIGHT_SHIFT_MAKE: + ConsoleIn->RightShift = TRUE; + break; + case SCANCODE_RIGHT_SHIFT_BREAK: + ConsoleIn->RightShift = FALSE; + break; + + case SCANCODE_LEFT_LOGO_MAKE: + ConsoleIn->LeftLogo = TRUE; + break; + case SCANCODE_LEFT_LOGO_BREAK: + ConsoleIn->LeftLogo = FALSE; + break; + + case SCANCODE_RIGHT_LOGO_MAKE: + ConsoleIn->RightLogo = TRUE; + break; + case SCANCODE_RIGHT_LOGO_BREAK: + ConsoleIn->RightLogo = FALSE; + break; + + case SCANCODE_MENU_MAKE: + ConsoleIn->Menu = TRUE; + break; + case SCANCODE_MENU_BREAK: + ConsoleIn->Menu = FALSE; + break; + + case SCANCODE_SYS_REQ_MAKE: + if (Extend0) { + ConsoleIn->SysReq = TRUE; + } + break; + case SCANCODE_SYS_REQ_BREAK: + if (Extend0) { + ConsoleIn->SysReq = FALSE; + } + break; + + case SCANCODE_SYS_REQ_MAKE_WITH_ALT: + ConsoleIn->SysReq = TRUE; + break; + case SCANCODE_SYS_REQ_BREAK_WITH_ALT: + ConsoleIn->SysReq = FALSE; + break; + + case SCANCODE_CAPS_LOCK_MAKE: + ConsoleIn->CapsLock = (BOOLEAN)!ConsoleIn->CapsLock; + UpdateStatusLights (ConsoleIn); + break; + case SCANCODE_NUM_LOCK_MAKE: + ConsoleIn->NumLock = (BOOLEAN)!ConsoleIn->NumLock; + UpdateStatusLights (ConsoleIn); + break; + case SCANCODE_SCROLL_LOCK_MAKE: + if (!Extend0) { + ConsoleIn->ScrollLock = (BOOLEAN)!ConsoleIn->ScrollLock; + UpdateStatusLights (ConsoleIn); + } + break; + } + } + + // + // If this is above the valid range, ignore it + // + if (ScanCode >= SCANCODE_MAX_MAKE) { + continue; + } else { + break; + } + } + + // + // Handle Ctrl+Alt+Del hotkey + // + if ((ConsoleIn->LeftCtrl || ConsoleIn->RightCtrl) && + (ConsoleIn->LeftAlt || ConsoleIn->RightAlt ) && + ScanCode == SCANCODE_DELETE_MAKE + ) { + gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); + } + + // + // Save the Shift/Toggle state + // + KeyData.KeyState.KeyShiftState = (UINT32) (EFI_SHIFT_STATE_VALID + | (ConsoleIn->LeftCtrl ? EFI_LEFT_CONTROL_PRESSED : 0) + | (ConsoleIn->RightCtrl ? EFI_RIGHT_CONTROL_PRESSED : 0) + | (ConsoleIn->LeftAlt ? EFI_LEFT_ALT_PRESSED : 0) + | (ConsoleIn->RightAlt ? EFI_RIGHT_ALT_PRESSED : 0) + | (ConsoleIn->LeftShift ? EFI_LEFT_SHIFT_PRESSED : 0) + | (ConsoleIn->RightShift ? EFI_RIGHT_SHIFT_PRESSED : 0) + | (ConsoleIn->LeftLogo ? EFI_LEFT_LOGO_PRESSED : 0) + | (ConsoleIn->RightLogo ? EFI_RIGHT_LOGO_PRESSED : 0) + | (ConsoleIn->Menu ? EFI_MENU_KEY_PRESSED : 0) + | (ConsoleIn->SysReq ? EFI_SYS_REQ_PRESSED : 0) + ); + KeyData.KeyState.KeyToggleState = (EFI_KEY_TOGGLE_STATE) (EFI_TOGGLE_STATE_VALID + | (ConsoleIn->CapsLock ? EFI_CAPS_LOCK_ACTIVE : 0) + | (ConsoleIn->NumLock ? EFI_NUM_LOCK_ACTIVE : 0) + | (ConsoleIn->ScrollLock ? EFI_SCROLL_LOCK_ACTIVE : 0) + | (ConsoleIn->IsSupportPartialKey ? EFI_KEY_STATE_EXPOSED : 0) + ); + + KeyData.Key.ScanCode = SCAN_NULL; + KeyData.Key.UnicodeChar = CHAR_NULL; + + // + // Key Pad "/" shares the same scancode as that of "/" except Key Pad "/" has E0 prefix + // + if (Extend0 && ScanCode == 0x35) { + KeyData.Key.UnicodeChar = L'/'; + KeyData.Key.ScanCode = SCAN_NULL; + + // + // PAUSE shares the same scancode as that of NUM except PAUSE has E1 prefix + // + } else if (Extend1 && ScanCode == SCANCODE_NUM_LOCK_MAKE) { + KeyData.Key.UnicodeChar = CHAR_NULL; + KeyData.Key.ScanCode = SCAN_PAUSE; + + // + // PAUSE shares the same scancode as that of SCROLL except PAUSE (CTRL pressed) has E0 prefix + // + } else if (Extend0 && ScanCode == SCANCODE_SCROLL_LOCK_MAKE) { + KeyData.Key.UnicodeChar = CHAR_NULL; + KeyData.Key.ScanCode = SCAN_PAUSE; + + // + // PRNT_SCRN shares the same scancode as that of Key Pad "*" except PRNT_SCRN has E0 prefix + // + } else if (Extend0 && ScanCode == SCANCODE_SYS_REQ_MAKE) { + KeyData.Key.UnicodeChar = CHAR_NULL; + KeyData.Key.ScanCode = SCAN_NULL; + + // + // Except the above special case, all others can be handled by convert table + // + } else { + for (Index = 0; ConvertKeyboardScanCodeToEfiKey[Index].ScanCode != TABLE_END; Index++) { + if (ScanCode == ConvertKeyboardScanCodeToEfiKey[Index].ScanCode) { + KeyData.Key.ScanCode = ConvertKeyboardScanCodeToEfiKey[Index].EfiScanCode; + KeyData.Key.UnicodeChar = ConvertKeyboardScanCodeToEfiKey[Index].UnicodeChar; + + if ((ConsoleIn->LeftShift || ConsoleIn->RightShift) && + (ConvertKeyboardScanCodeToEfiKey[Index].UnicodeChar != ConvertKeyboardScanCodeToEfiKey[Index].ShiftUnicodeChar)) { + KeyData.Key.UnicodeChar = ConvertKeyboardScanCodeToEfiKey[Index].ShiftUnicodeChar; + // + // Need not return associated shift state if a class of printable characters that + // are normally adjusted by shift modifiers. e.g. Shift Key + 'f' key = 'F' + // + KeyData.KeyState.KeyShiftState &= ~(EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED); + } + // + // alphabetic key is affected by CapsLock State + // + if (ConsoleIn->CapsLock) { + if (KeyData.Key.UnicodeChar >= L'a' && KeyData.Key.UnicodeChar <= L'z') { + KeyData.Key.UnicodeChar = (UINT16) (KeyData.Key.UnicodeChar - L'a' + L'A'); + } else if (KeyData.Key.UnicodeChar >= L'A' && KeyData.Key.UnicodeChar <= L'Z') { + KeyData.Key.UnicodeChar = (UINT16) (KeyData.Key.UnicodeChar - L'A' + L'a'); + } + } + break; + } + } + } + + // + // distinguish numeric key pad keys' 'up symbol' and 'down symbol' + // + if (ScanCode >= 0x47 && ScanCode <= 0x53) { + if (ConsoleIn->NumLock && !(ConsoleIn->LeftShift || ConsoleIn->RightShift) && !Extend0) { + KeyData.Key.ScanCode = SCAN_NULL; + } else if (ScanCode != 0x4a && ScanCode != 0x4e) { + KeyData.Key.UnicodeChar = CHAR_NULL; + } + } + + // + // If the key can not be converted then just return. + // + if (KeyData.Key.ScanCode == SCAN_NULL && KeyData.Key.UnicodeChar == CHAR_NULL) { + if (!ConsoleIn->IsSupportPartialKey) { + return ; + } + } + + // + // Invoke notification functions if exist + // + for (Link = GetFirstNode (&ConsoleIn->NotifyList); !IsNull (&ConsoleIn->NotifyList, Link); Link = GetNextNode (&ConsoleIn->NotifyList, Link)) { + CurrentNotify = CR ( + Link, + KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) { + CurrentNotify->KeyNotificationFn (&KeyData); + } + } + + PushEfikeyBufTail (&ConsoleIn->EfiKeyQueue, &KeyData); +} + +/** + Perform 8042 controller and keyboard Initialization. + If ExtendedVerification is TRUE, do additional test for + the keyboard interface + + @param ConsoleIn - KEYBOARD_CONSOLE_IN_DEV instance pointer + @param ExtendedVerification - indicates a thorough initialization + + @retval EFI_DEVICE_ERROR Fail to init keyboard + @retval EFI_SUCCESS Success to init keyboard +**/ +EFI_STATUS +InitKeyboard ( + IN OUT KEYBOARD_CONSOLE_IN_DEV *ConsoleIn, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + EFI_STATUS Status1; + UINT8 CommandByte; + EFI_PS2_POLICY_PROTOCOL *Ps2Policy; + UINT32 TryTime; + + Status = EFI_SUCCESS; + mEnableMouseInterface = TRUE; + TryTime = 0; + + // + // Get Ps2 policy to set this + // + gBS->LocateProtocol ( + &gEfiPs2PolicyProtocolGuid, + NULL, + (VOID **) &Ps2Policy + ); + + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_CLEAR_BUFFER, + ConsoleIn->DevicePath + ); + + // + // Perform a read to cleanup the Status Register's + // output buffer full bits within MAX TRY times + // + if ((KeyReadStatusRegister (ConsoleIn) & KEYBOARD_STATUS_REGISTER_HAS_OUTPUT_DATA) != 0) { + while (!EFI_ERROR (Status) && TryTime < KEYBOARD_MAX_TRY) { + Status = KeyboardRead (ConsoleIn, &CommandByte); + TryTime ++; + } + // + // Exceed the max try times. The device may be error. + // + if (TryTime == KEYBOARD_MAX_TRY) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + } + // + // We should disable mouse interface during the initialization process + // since mouse device output could block keyboard device output in the + // 60H port of 8042 controller. + // + // So if we are not initializing 8042 controller for the + // first time, we have to remember the previous mouse interface + // enabling state + // + // Test the system flag in to determine whether this is the first + // time initialization + // + if ((KeyReadStatusRegister (ConsoleIn) & KEYBOARD_STATUS_REGISTER_SYSTEM_FLAG) != 0) { + if (!PcdGetBool (PcdFastPS2Detection)) { + // + // 8042 controller is already setup (by myself or by mouse driver): + // See whether mouse interface is already enabled + // which determines whether we should enable it later + // + // + // Read the command byte of 8042 controller + // + Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_READ); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"\n\r"); + goto Done; + } + + Status = KeyboardRead (ConsoleIn, &CommandByte); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"\n\r"); + goto Done; + } + // + // Test the mouse enabling bit + // + if ((CommandByte & 0x20) != 0) { + mEnableMouseInterface = FALSE; + } else { + mEnableMouseInterface = TRUE; + } + } else { + mEnableMouseInterface = FALSE; + } + } else { + // + // 8042 controller is not setup yet: + // 8042 controller selftest; + // Don't enable mouse interface later. + // + // + // Disable keyboard and mouse interfaces + // + if (!PcdGetBool (PcdFastPS2Detection)) { + Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_DISABLE_KEYBOARD_INTERFACE); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"\n\r"); + goto Done; + } + + Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_DISABLE_MOUSE_INTERFACE); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"\n\r"); + goto Done; + } + + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_SELF_TEST, + ConsoleIn->DevicePath + ); + // + // 8042 Controller Self Test + // + Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_CONTROLLER_SELF_TEST); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"8042 controller command write error!\n\r"); + goto Done; + } + + Status = KeyboardWaitForValue (ConsoleIn, 0x55); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"8042 controller self test failed!\n\r"); + goto Done; + } + } + // + // Don't enable mouse interface later + // + mEnableMouseInterface = FALSE; + + } + + if (Ps2Policy != NULL) { + Ps2Policy->Ps2InitHardware (ConsoleIn->Handle); + } + // + // Write 8042 Command Byte, set System Flag + // While at the same time: + // 1. disable mouse interface, + // 2. enable kbd interface, + // 3. enable PC/XT kbd translation mode + // 4. enable mouse and kbd interrupts + // + // ( Command Byte bits: + // 7: Reserved + // 6: PC/XT translation mode + // 5: Disable Auxiliary device interface + // 4: Disable keyboard interface + // 3: Reserved + // 2: System Flag + // 1: Enable Auxiliary device interrupt + // 0: Enable Keyboard interrupt ) + // + Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_WRITE); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"8042 controller command write error!\n\r"); + goto Done; + } + + Status = KeyboardWrite (ConsoleIn, 0x67); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"8042 controller data write error!\n\r"); + goto Done; + } + + // + // Clear Memory Scancode Buffer + // + ConsoleIn->ScancodeQueue.Head = 0; + ConsoleIn->ScancodeQueue.Tail = 0; + ConsoleIn->EfiKeyQueue.Head = 0; + ConsoleIn->EfiKeyQueue.Tail = 0; + + // + // Reset the status indicators + // + ConsoleIn->CapsLock = FALSE; + ConsoleIn->NumLock = FALSE; + ConsoleIn->ScrollLock = FALSE; + ConsoleIn->LeftCtrl = FALSE; + ConsoleIn->RightCtrl = FALSE; + ConsoleIn->LeftAlt = FALSE; + ConsoleIn->RightAlt = FALSE; + ConsoleIn->LeftShift = FALSE; + ConsoleIn->RightShift = FALSE; + ConsoleIn->LeftLogo = FALSE; + ConsoleIn->RightLogo = FALSE; + ConsoleIn->Menu = FALSE; + ConsoleIn->SysReq = FALSE; + + ConsoleIn->IsSupportPartialKey = FALSE; + // + // For reseting keyboard is not mandatory before booting OS and sometimes keyboard responses very slow, + // and to support KB hot plug, we need to let the InitKB succeed no matter whether there is a KB device connected + // to system. So we only do the real reseting for keyboard when user asks and there is a real KB connected t system, + // and normally during booting an OS, it's skipped. + // + if (ExtendedVerification && CheckKeyboardConnect (ConsoleIn)) { + // + // Additional verifications for keyboard interface + // + // + // Keyboard Interface Test + // + Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_KEYBOARD_INTERFACE_SELF_TEST); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"8042 controller command write error!\n\r"); + goto Done; + } + + Status = KeyboardWaitForValue (ConsoleIn, 0x00); + if (EFI_ERROR (Status)) { + KeyboardError ( + ConsoleIn, + L"Some specific value not aquired from 8042 controller!\n\r" + ); + goto Done; + } + // + // Keyboard reset with a BAT(Basic Assurance Test) + // + Status = KeyboardWrite (ConsoleIn, KEYBOARD_8048_COMMAND_RESET); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"8042 controller data write error!\n\r"); + goto Done; + } + + Status = KeyboardWaitForValue (ConsoleIn, KEYBOARD_8048_RETURN_8042_ACK); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"Some specific value not aquired from 8042 controller!\n\r"); + goto Done; + } + // + // wait for BAT completion code + // + mWaitForValueTimeOut = KEYBOARD_BAT_TIMEOUT; + + Status = KeyboardWaitForValue (ConsoleIn, KEYBOARD_8048_RETURN_8042_BAT_SUCCESS); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"Keyboard self test failed!\n\r"); + goto Done; + } + + mWaitForValueTimeOut = KEYBOARD_WAITFORVALUE_TIMEOUT; + + // + // Set Keyboard to use Scan Code Set 2 + // + Status = KeyboardWrite (ConsoleIn, KEYBOARD_8048_COMMAND_SELECT_SCAN_CODE_SET); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"8042 controller data write error!\n\r"); + goto Done; + } + + Status = KeyboardWaitForValue (ConsoleIn, KEYBOARD_8048_RETURN_8042_ACK); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"Some specific value not aquired from 8042 controller!\n\r"); + goto Done; + } + + Status = KeyboardWrite (ConsoleIn, 0x02); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"8042 controller data write error!!\n\r"); + goto Done; + } + + Status = KeyboardWaitForValue (ConsoleIn, KEYBOARD_8048_RETURN_8042_ACK); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"Some specific value not aquired from 8042 controller!\n\r"); + goto Done; + } + + // + // Clear Keyboard Scancode Buffer + // + Status = KeyboardWrite (ConsoleIn, KEYBOARD_8048_COMMAND_CLEAR_OUTPUT_DATA); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"8042 controller data write error!\n\r"); + goto Done; + } + + Status = KeyboardWaitForValue (ConsoleIn, KEYBOARD_8048_RETURN_8042_ACK); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"Some specific value not aquired from 8042 controller!\n\r"); + goto Done; + } + // + if (Ps2Policy != NULL) { + if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_CAPSLOCK) == EFI_KEYBOARD_CAPSLOCK) { + ConsoleIn->CapsLock = TRUE; + } + + if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_NUMLOCK) == EFI_KEYBOARD_NUMLOCK) { + ConsoleIn->NumLock = TRUE; + } + + if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_SCROLLLOCK) == EFI_KEYBOARD_SCROLLLOCK) { + ConsoleIn->ScrollLock = TRUE; + } + } + // + // Update Keyboard Lights + // + Status = UpdateStatusLights (ConsoleIn); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"Update keyboard status lights error!\n\r"); + goto Done; + } + } + // + // At last, we can now enable the mouse interface if appropriate + // +Done: + + if (mEnableMouseInterface) { + // + // Enable mouse interface + // + Status1 = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_ENABLE_MOUSE_INTERFACE); + if (EFI_ERROR (Status1)) { + KeyboardError (ConsoleIn, L"8042 controller command write error!\n\r"); + return EFI_DEVICE_ERROR; + } + } + + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } else { + return EFI_DEVICE_ERROR; + } + +} + +/** + Disable the keyboard interface of the 8042 controller. + + @param ConsoleIn The device instance + + @return status of issuing disable command + +**/ +EFI_STATUS +DisableKeyboard ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn + ) +{ + EFI_STATUS Status; + + // + // Disable keyboard interface + // + Status = KeyboardCommand (ConsoleIn, KEYBOARD_8042_COMMAND_DISABLE_KEYBOARD_INTERFACE); + if (EFI_ERROR (Status)) { + KeyboardError (ConsoleIn, L"\n\r"); + return EFI_DEVICE_ERROR; + } + + return Status; +} + +/** + Check whether there is Ps/2 Keyboard device in system by 0xF4 Keyboard Command + If Keyboard receives 0xF4, it will respond with 'ACK'. If it doesn't respond, the device + should not be in system. + + @param[in] ConsoleIn Keyboard Private Data Structure + + @retval TRUE Keyboard in System. + @retval FALSE Keyboard not in System. +**/ +BOOLEAN +EFIAPI +CheckKeyboardConnect ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn + ) +{ + EFI_STATUS Status; + UINTN WaitForValueTimeOutBcakup; + + // + // enable keyboard itself and wait for its ack + // If can't receive ack, Keyboard should not be connected. + // + if (!PcdGetBool (PcdFastPS2Detection)) { + Status = KeyboardWrite ( + ConsoleIn, + KEYBOARD_KBEN + ); + + if (EFI_ERROR (Status)) { + return FALSE; + } + // + // wait for 1s + // + WaitForValueTimeOutBcakup = mWaitForValueTimeOut; + mWaitForValueTimeOut = KEYBOARD_WAITFORVALUE_TIMEOUT; + Status = KeyboardWaitForValue ( + ConsoleIn, + KEYBOARD_CMDECHO_ACK + ); + mWaitForValueTimeOut = WaitForValueTimeOutBcakup; + + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; + } else { + return TRUE; + } +} + diff --git a/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KbdTextIn.c b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KbdTextIn.c new file mode 100644 index 0000000000..f6ccd9598f --- /dev/null +++ b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KbdTextIn.c @@ -0,0 +1,683 @@ +/** @file + Routines implements SIMPLE_TEXT_IN protocol's interfaces based on 8042 interfaces + provided by Ps2KbdCtrller.c. + +Copyright (c) 2006 - 2016, 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 "Ps2Keyboard.h" + +/** + Check whether the EFI key buffer is empty. + + @param Queue Pointer to instance of EFI_KEY_QUEUE. + + @retval TRUE The EFI key buffer is empty. + @retval FALSE The EFI key buffer isn't empty. +**/ +BOOLEAN +IsEfikeyBufEmpty ( + IN EFI_KEY_QUEUE *Queue + ) +{ + return (BOOLEAN) (Queue->Head == Queue->Tail); +} + +/** + Read & remove one key data from the EFI key buffer. + + @param Queue Pointer to instance of EFI_KEY_QUEUE. + @param KeyData Receive the key data. + + @retval EFI_SUCCESS The key data is popped successfully. + @retval EFI_NOT_READY There is no key data available. +**/ +EFI_STATUS +PopEfikeyBufHead ( + IN EFI_KEY_QUEUE *Queue, + OUT EFI_KEY_DATA *KeyData OPTIONAL + ) +{ + if (IsEfikeyBufEmpty (Queue)) { + return EFI_NOT_READY; + } + // + // Retrieve and remove the values + // + if (KeyData != NULL) { + CopyMem (KeyData, &Queue->Buffer[Queue->Head], sizeof (EFI_KEY_DATA)); + } + Queue->Head = (Queue->Head + 1) % KEYBOARD_EFI_KEY_MAX_COUNT; + return EFI_SUCCESS; +} + +/** + Push one key data to the EFI key buffer. + + @param Queue Pointer to instance of EFI_KEY_QUEUE. + @param KeyData The key data to push. +**/ +VOID +PushEfikeyBufTail ( + IN EFI_KEY_QUEUE *Queue, + IN EFI_KEY_DATA *KeyData + ) +{ + if ((Queue->Tail + 1) % KEYBOARD_EFI_KEY_MAX_COUNT == Queue->Head) { + // + // If Queue is full, pop the one from head. + // + PopEfikeyBufHead (Queue, NULL); + } + CopyMem (&Queue->Buffer[Queue->Tail], KeyData, sizeof (EFI_KEY_DATA)); + Queue->Tail = (Queue->Tail + 1) % KEYBOARD_EFI_KEY_MAX_COUNT; +} + +/** + Judge whether is a registed key + + @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; + } + + // + // Assume KeyShiftState/KeyToggleState = 0 in Registered key data means these state could be ignored. + // + if (RegsiteredData->KeyState.KeyShiftState != 0 && + RegsiteredData->KeyState.KeyShiftState != InputData->KeyState.KeyShiftState) { + return FALSE; + } + if (RegsiteredData->KeyState.KeyToggleState != 0 && + RegsiteredData->KeyState.KeyToggleState != InputData->KeyState.KeyToggleState) { + return FALSE; + } + + return TRUE; + +} + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param ConsoleInDev Ps2 Keyboard 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 availiable. + @retval EFI_DEVICE_ERROR The keystroke information was not returned due to + hardware errors. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +KeyboardReadKeyStrokeWorker ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleInDev, + OUT EFI_KEY_DATA *KeyData + ) + +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (KeyData == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + KeyboardTimerHandler (NULL, ConsoleInDev); + + if (ConsoleInDev->KeyboardErr) { + Status = EFI_DEVICE_ERROR; + } else { + Status = PopEfikeyBufHead (&ConsoleInDev->EfiKeyQueue, KeyData); + } + + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Perform 8042 controller and keyboard initialization which implement SIMPLE_TEXT_IN.Reset() + + @param This Pointer to instance of EFI_SIMPLE_TEXT_INPUT_PROTOCOL + @param ExtendedVerification Indicate that the driver may perform a more + exhaustive verification operation of the device during + reset, now this par is ignored in this driver + +**/ +EFI_STATUS +EFIAPI +KeyboardEfiReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + KEYBOARD_CONSOLE_IN_DEV *ConsoleIn; + EFI_TPL OldTpl; + + ConsoleIn = KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This); + if (ConsoleIn->KeyboardErr) { + return EFI_DEVICE_ERROR; + } + + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_RESET, + ConsoleIn->DevicePath + ); + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + // + // Call InitKeyboard to initialize the keyboard + // + Status = InitKeyboard (ConsoleIn, ExtendedVerification); + if (EFI_ERROR (Status)) { + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); + return EFI_DEVICE_ERROR; + } + + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); + + // + // Report the status If a stuck key was detected + // + if (KeyReadStatusRegister (ConsoleIn) & 0x01) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_EC_STUCK_KEY, + ConsoleIn->DevicePath + ); + } + // + // Report the status If keyboard is locked + // + if ((KeyReadStatusRegister (ConsoleIn) & 0x10) == 0) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_EC_LOCKED, + ConsoleIn->DevicePath + ); + } + + return EFI_SUCCESS; +} + +/** + Retrieve key values for driver user which implement SIMPLE_TEXT_IN.ReadKeyStroke(). + + @param This Pointer to instance of EFI_SIMPLE_TEXT_INPUT_PROTOCOL + @param Key The output buffer for key value + + @retval EFI_SUCCESS success to read key stroke +**/ +EFI_STATUS +EFIAPI +KeyboardReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ) +{ + EFI_STATUS Status; + KEYBOARD_CONSOLE_IN_DEV *ConsoleIn; + EFI_KEY_DATA KeyData; + + ConsoleIn = KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This); + + // + // Considering if the partial keystroke is enabled, there maybe a partial + // keystroke in the queue, so here skip the partial keystroke and get the + // next key from the queue + // + while (1) { + // + // If there is no pending key, then return. + // + Status = KeyboardReadKeyStrokeWorker (ConsoleIn, &KeyData); + if (EFI_ERROR (Status)) { + return Status; + } + // + // If it is partial keystroke, skip it. + // + if (KeyData.Key.ScanCode == SCAN_NULL && KeyData.Key.UnicodeChar == CHAR_NULL) { + continue; + } + // + // Translate the CTRL-Alpha characters to their corresponding control value + // (ctrl-a = 0x0001 through ctrl-Z = 0x001A) + // + if ((KeyData.KeyState.KeyShiftState & (EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED)) != 0) { + if (KeyData.Key.UnicodeChar >= L'a' && KeyData.Key.UnicodeChar <= L'z') { + KeyData.Key.UnicodeChar = (CHAR16) (KeyData.Key.UnicodeChar - L'a' + 1); + } else if (KeyData.Key.UnicodeChar >= L'A' && KeyData.Key.UnicodeChar <= L'Z') { + KeyData.Key.UnicodeChar = (CHAR16) (KeyData.Key.UnicodeChar - L'A' + 1); + } + } + + CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY)); + return EFI_SUCCESS; + } +} + +/** + Event notification function for SIMPLE_TEXT_IN.WaitForKey event + Signal the event if there is key available + + @param Event the event object + @param Context waitting context + +**/ +VOID +EFIAPI +KeyboardWaitForKey ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_TPL OldTpl; + KEYBOARD_CONSOLE_IN_DEV *ConsoleIn; + EFI_KEY_DATA KeyData; + + ConsoleIn = (KEYBOARD_CONSOLE_IN_DEV *) Context; + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + KeyboardTimerHandler (NULL, ConsoleIn); + + if (!ConsoleIn->KeyboardErr) { + // + // WaitforKey doesn't suppor the partial key. + // Considering if the partial keystroke is enabled, there maybe a partial + // keystroke in the queue, so here skip the partial keystroke and get the + // next key from the queue + // + while (!IsEfikeyBufEmpty (&ConsoleIn->EfiKeyQueue)) { + CopyMem ( + &KeyData, + &(ConsoleIn->EfiKeyQueue.Buffer[ConsoleIn->EfiKeyQueue.Head]), + sizeof (EFI_KEY_DATA) + ); + if (KeyData.Key.ScanCode == SCAN_NULL && KeyData.Key.UnicodeChar == CHAR_NULL) { + PopEfikeyBufHead (&ConsoleIn->EfiKeyQueue, &KeyData); + continue; + } + // + // if there is pending value key, signal the event. + // + gBS->SignalEvent (Event); + break; + } + } + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); +} + +/** + Event notification function for SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx event + Signal the event if there is key available + + @param Event event object + @param Context waiting context + +**/ +VOID +EFIAPI +KeyboardWaitForKeyEx ( + IN EFI_EVENT Event, + IN VOID *Context + ) + +{ + KeyboardWaitForKey (Event, Context); +} + +/** + Reset the input device and optionaly 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 +KeyboardEfiResetEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) + +{ + KEYBOARD_CONSOLE_IN_DEV *ConsoleInDev; + + ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This); + + return ConsoleInDev->ConIn.Reset ( + &ConsoleInDev->ConIn, + ExtendedVerification + ); +} + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance 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 availiable. + @retval EFI_DEVICE_ERROR The keystroke information was not returned due to + hardware errors. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +EFIAPI +KeyboardReadKeyStrokeEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + OUT EFI_KEY_DATA *KeyData + ) + +{ + KEYBOARD_CONSOLE_IN_DEV *ConsoleInDev; + + if (KeyData == NULL) { + return EFI_INVALID_PARAMETER; + } + + ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This); + return KeyboardReadKeyStrokeWorker (ConsoleInDev, 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 +KeyboardSetState ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_TOGGLE_STATE *KeyToggleState + ) + +{ + EFI_STATUS Status; + KEYBOARD_CONSOLE_IN_DEV *ConsoleInDev; + EFI_TPL OldTpl; + + if (KeyToggleState == NULL) { + return EFI_INVALID_PARAMETER; + } + + ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This); + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + if (ConsoleInDev->KeyboardErr) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + if ((*KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + + // + // Update the status light + // + ConsoleInDev->ScrollLock = FALSE; + ConsoleInDev->NumLock = FALSE; + ConsoleInDev->CapsLock = FALSE; + ConsoleInDev->IsSupportPartialKey = FALSE; + + if ((*KeyToggleState & EFI_SCROLL_LOCK_ACTIVE) == EFI_SCROLL_LOCK_ACTIVE) { + ConsoleInDev->ScrollLock = TRUE; + } + if ((*KeyToggleState & EFI_NUM_LOCK_ACTIVE) == EFI_NUM_LOCK_ACTIVE) { + ConsoleInDev->NumLock = TRUE; + } + if ((*KeyToggleState & EFI_CAPS_LOCK_ACTIVE) == EFI_CAPS_LOCK_ACTIVE) { + ConsoleInDev->CapsLock = TRUE; + } + if ((*KeyToggleState & EFI_KEY_STATE_EXPOSED) == EFI_KEY_STATE_EXPOSED) { + ConsoleInDev->IsSupportPartialKey = TRUE; + } + + Status = UpdateStatusLights (ConsoleInDev); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + } + +Exit: + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); + + return Status; + +} + +/** + 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 necesssary data structures. + @retval EFI_INVALID_PARAMETER KeyData or NotifyHandle or KeyNotificationFunction is NULL. + +**/ +EFI_STATUS +EFIAPI +KeyboardRegisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_DATA *KeyData, + IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, + OUT VOID **NotifyHandle + ) +{ + EFI_STATUS Status; + KEYBOARD_CONSOLE_IN_DEV *ConsoleInDev; + EFI_TPL OldTpl; + LIST_ENTRY *Link; + KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + KEYBOARD_CONSOLE_IN_EX_NOTIFY *NewNotify; + + if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) { + return EFI_INVALID_PARAMETER; + } + + ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This); + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + // + // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered. + // + for (Link = ConsoleInDev->NotifyList.ForwardLink; Link != &ConsoleInDev->NotifyList; Link = Link->ForwardLink) { + CurrentNotify = CR ( + Link, + KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) { + if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) { + *NotifyHandle = CurrentNotify; + Status = EFI_SUCCESS; + goto Exit; + } + } + } + + // + // Allocate resource to save the notification function + // + NewNotify = (KEYBOARD_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (KEYBOARD_CONSOLE_IN_EX_NOTIFY)); + if (NewNotify == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + NewNotify->Signature = KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE; + NewNotify->KeyNotificationFn = KeyNotificationFunction; + CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA)); + InsertTailList (&ConsoleInDev->NotifyList, &NewNotify->NotifyEntry); + + *NotifyHandle = NewNotify; + Status = EFI_SUCCESS; + +Exit: + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); + return Status; + +} + +/** + 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. + +**/ +EFI_STATUS +EFIAPI +KeyboardUnregisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN VOID *NotificationHandle + ) +{ + EFI_STATUS Status; + KEYBOARD_CONSOLE_IN_DEV *ConsoleInDev; + EFI_TPL OldTpl; + LIST_ENTRY *Link; + KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + + if (NotificationHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This); + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + for (Link = ConsoleInDev->NotifyList.ForwardLink; Link != &ConsoleInDev->NotifyList; Link = Link->ForwardLink) { + CurrentNotify = CR ( + Link, + KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (CurrentNotify == NotificationHandle) { + // + // Remove the notification function from NotifyList and free resources + // + RemoveEntryList (&CurrentNotify->NotifyEntry); + + gBS->FreePool (CurrentNotify); + Status = EFI_SUCCESS; + goto Exit; + } + } + + // + // Can not find the specified Notification Handle + // + Status = EFI_INVALID_PARAMETER; +Exit: + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); + return Status; +} + diff --git a/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2Keyboard.c b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2Keyboard.c new file mode 100644 index 0000000000..4384935443 --- /dev/null +++ b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2Keyboard.c @@ -0,0 +1,647 @@ +/** @file + + PS/2 Keyboard driver. Routines that interacts with callers, + conforming to EFI driver model + +Copyright (c) 2006 - 2016, 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 "Ps2Keyboard.h" + +// +// Function prototypes +// +/** + Test controller is a keyboard Controller. + + @param This Pointer of EFI_DRIVER_BINDING_PROTOCOL + @param Controller driver's controller + @param RemainingDevicePath children device path + + @retval EFI_UNSUPPORTED controller is not floppy disk + @retval EFI_SUCCESS controller is floppy disk +**/ +EFI_STATUS +EFIAPI +KbdControllerDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Create KEYBOARD_CONSOLE_IN_DEV instance on controller. + + @param This Pointer of EFI_DRIVER_BINDING_PROTOCOL + @param Controller driver controller handle + @param RemainingDevicePath Children's device path + + @retval whether success to create floppy control instance. +**/ +EFI_STATUS +EFIAPI +KbdControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop this driver on ControllerHandle. Support stoping any child handles + created by this driver. + + @param This Protocol instance pointer. + @param ControllerHandle 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 ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +KbdControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Free the waiting key notify list. + + @param ListHead Pointer to list head + + @retval EFI_INVALID_PARAMETER ListHead is NULL + @retval EFI_SUCCESS Sucess to free NotifyList +**/ +EFI_STATUS +KbdFreeNotifyList ( + IN OUT LIST_ENTRY *ListHead + ); + +// +// DriverBinding Protocol Instance +// +EFI_DRIVER_BINDING_PROTOCOL gKeyboardControllerDriver = { + KbdControllerDriverSupported, + KbdControllerDriverStart, + KbdControllerDriverStop, + 0xa, + NULL, + NULL +}; + +/** + Test controller is a keyboard Controller. + + @param This Pointer of EFI_DRIVER_BINDING_PROTOCOL + @param Controller driver's controller + @param RemainingDevicePath children device path + + @retval EFI_UNSUPPORTED controller is not floppy disk + @retval EFI_SUCCESS controller is floppy disk +**/ +EFI_STATUS +EFIAPI +KbdControllerDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_SIO_PROTOCOL *Sio; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + ACPI_HID_DEVICE_PATH *Acpi; + + // + // Check whether the controller is keyboard. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + do { + Acpi = (ACPI_HID_DEVICE_PATH *) DevicePath; + DevicePath = NextDevicePathNode (DevicePath); + } while (!IsDevicePathEnd (DevicePath)); + + if (DevicePathType (Acpi) != ACPI_DEVICE_PATH || + (DevicePathSubType (Acpi) != ACPI_DP && DevicePathSubType (Acpi) != ACPI_EXTENDED_DP)) { + return EFI_UNSUPPORTED; + } + + if (Acpi->HID != EISA_PNP_ID (0x303) || Acpi->UID != 0) { + return EFI_UNSUPPORTED; + } + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiSioProtocolGuid, + (VOID **) &Sio, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + Controller, + &gEfiSioProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + +/** + Create KEYBOARD_CONSOLE_IN_DEV instance on controller. + + @param This Pointer of EFI_DRIVER_BINDING_PROTOCOL + @param Controller driver controller handle + @param RemainingDevicePath Children's device path + + @retval whether success to create floppy control instance. +**/ +EFI_STATUS +EFIAPI +KbdControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_STATUS Status1; + EFI_SIO_PROTOCOL *Sio; + KEYBOARD_CONSOLE_IN_DEV *ConsoleIn; + UINT8 Data; + EFI_STATUS_CODE_VALUE StatusCode; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + StatusCode = 0; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Report that the keyboard is being enabled + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_ENABLE, + DevicePath + ); + + // + // Get the ISA I/O Protocol on Controller's handle + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiSioProtocolGuid, + (VOID **) &Sio, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Allocate private data + // + ConsoleIn = AllocateZeroPool (sizeof (KEYBOARD_CONSOLE_IN_DEV)); + if (ConsoleIn == NULL) { + Status = EFI_OUT_OF_RESOURCES; + StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; + goto ErrorExit; + } + // + // Setup the device instance + // + ConsoleIn->Signature = KEYBOARD_CONSOLE_IN_DEV_SIGNATURE; + ConsoleIn->Handle = Controller; + (ConsoleIn->ConIn).Reset = KeyboardEfiReset; + (ConsoleIn->ConIn).ReadKeyStroke = KeyboardReadKeyStroke; + ConsoleIn->DataRegisterAddress = KEYBOARD_8042_DATA_REGISTER; + ConsoleIn->StatusRegisterAddress = KEYBOARD_8042_STATUS_REGISTER; + ConsoleIn->CommandRegisterAddress = KEYBOARD_8042_COMMAND_REGISTER; + ConsoleIn->DevicePath = DevicePath; + + ConsoleIn->ConInEx.Reset = KeyboardEfiResetEx; + ConsoleIn->ConInEx.ReadKeyStrokeEx = KeyboardReadKeyStrokeEx; + ConsoleIn->ConInEx.SetState = KeyboardSetState; + ConsoleIn->ConInEx.RegisterKeyNotify = KeyboardRegisterKeyNotify; + ConsoleIn->ConInEx.UnregisterKeyNotify = KeyboardUnregisterKeyNotify; + + InitializeListHead (&ConsoleIn->NotifyList); + + // + // Fix for random hangs in System waiting for the Key if no KBC is present in BIOS. + // When KBC decode (IO port 0x60/0x64 decode) is not enabled, + // KeyboardRead will read back as 0xFF and return status is EFI_SUCCESS. + // So instead we read status register to detect after read if KBC decode is enabled. + // + + // + // Return code is ignored on purpose. + // + if (!PcdGetBool (PcdFastPS2Detection)) { + KeyboardRead (ConsoleIn, &Data); + if ((KeyReadStatusRegister (ConsoleIn) & (KBC_PARE | KBC_TIM)) == (KBC_PARE | KBC_TIM)) { + // + // If nobody decodes KBC I/O port, it will read back as 0xFF. + // Check the Time-Out and Parity bit to see if it has an active KBC in system + // + Status = EFI_DEVICE_ERROR; + StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED; + goto ErrorExit; + } + } + + // + // Setup the WaitForKey event + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + KeyboardWaitForKey, + ConsoleIn, + &((ConsoleIn->ConIn).WaitForKey) + ); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; + goto ErrorExit; + } + // + // Setup the WaitForKeyEx event + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + KeyboardWaitForKeyEx, + ConsoleIn, + &(ConsoleIn->ConInEx.WaitForKeyEx) + ); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; + goto ErrorExit; + } + // Setup a periodic timer, used for reading keystrokes at a fixed interval + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + KeyboardTimerHandler, + ConsoleIn, + &ConsoleIn->TimerEvent + ); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; + goto ErrorExit; + } + + Status = gBS->SetTimer ( + ConsoleIn->TimerEvent, + TimerPeriodic, + KEYBOARD_TIMER_INTERVAL + ); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; + goto ErrorExit; + } + + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_PRESENCE_DETECT, + DevicePath + ); + + // + // Reset the keyboard device + // + Status = ConsoleIn->ConInEx.Reset (&ConsoleIn->ConInEx, FeaturePcdGet (PcdPs2KbdExtendedVerification)); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED; + goto ErrorExit; + } + + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_DETECTED, + DevicePath + ); + + ConsoleIn->ControllerNameTable = NULL; + AddUnicodeString2 ( + "eng", + gPs2KeyboardComponentName.SupportedLanguages, + &ConsoleIn->ControllerNameTable, + L"PS/2 Keyboard Device", + TRUE + ); + AddUnicodeString2 ( + "en", + gPs2KeyboardComponentName2.SupportedLanguages, + &ConsoleIn->ControllerNameTable, + L"PS/2 Keyboard Device", + FALSE + ); + + + // + // Install protocol interfaces for the keyboard device. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiSimpleTextInProtocolGuid, + &ConsoleIn->ConIn, + &gEfiSimpleTextInputExProtocolGuid, + &ConsoleIn->ConInEx, + NULL + ); + if (EFI_ERROR (Status)) { + StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; + goto ErrorExit; + } + + return Status; + +ErrorExit: + // + // Report error code + // + if (StatusCode != 0) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + StatusCode, + DevicePath + ); + } + + if ((ConsoleIn != NULL) && (ConsoleIn->ConIn.WaitForKey != NULL)) { + gBS->CloseEvent (ConsoleIn->ConIn.WaitForKey); + } + + if ((ConsoleIn != NULL) && (ConsoleIn->TimerEvent != NULL)) { + gBS->CloseEvent (ConsoleIn->TimerEvent); + } + if ((ConsoleIn != NULL) && (ConsoleIn->ConInEx.WaitForKeyEx != NULL)) { + gBS->CloseEvent (ConsoleIn->ConInEx.WaitForKeyEx); + } + KbdFreeNotifyList (&ConsoleIn->NotifyList); + if ((ConsoleIn != NULL) && (ConsoleIn->ControllerNameTable != NULL)) { + FreeUnicodeStringTable (ConsoleIn->ControllerNameTable); + } + // + // Since there will be no timer handler for keyboard input any more, + // exhaust input data just in case there is still keyboard data left + // + if (ConsoleIn != NULL) { + Status1 = EFI_SUCCESS; + while (!EFI_ERROR (Status1) && (Status != EFI_DEVICE_ERROR)) { + Status1 = KeyboardRead (ConsoleIn, &Data);; + } + } + + if (ConsoleIn != NULL) { + gBS->FreePool (ConsoleIn); + } + + gBS->CloseProtocol ( + Controller, + &gEfiSioProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + +/** + Stop this driver on ControllerHandle. Support stoping any child handles + created by this driver. + + @param This Protocol instance pointer. + @param ControllerHandle 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 ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +KbdControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; + KEYBOARD_CONSOLE_IN_DEV *ConsoleIn; + UINT8 Data; + + // + // Disable Keyboard + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiSimpleTextInProtocolGuid, + (VOID **) &ConIn, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = gBS->OpenProtocol ( + Controller, + &gEfiSimpleTextInputExProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ConsoleIn = KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (ConIn); + + // + // Report that the keyboard is being disabled + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_DISABLE, + ConsoleIn->DevicePath + ); + + if (ConsoleIn->TimerEvent != NULL) { + gBS->CloseEvent (ConsoleIn->TimerEvent); + ConsoleIn->TimerEvent = NULL; + } + + // + // Since there will be no timer handler for keyboard input any more, + // exhaust input data just in case there is still keyboard data left + // + Status = EFI_SUCCESS; + while (!EFI_ERROR (Status)) { + Status = KeyboardRead (ConsoleIn, &Data);; + } + // + // Uninstall the SimpleTextIn and SimpleTextInEx protocols + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiSimpleTextInProtocolGuid, + &ConsoleIn->ConIn, + &gEfiSimpleTextInputExProtocolGuid, + &ConsoleIn->ConInEx, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiSioProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Free other resources + // + if ((ConsoleIn->ConIn).WaitForKey != NULL) { + gBS->CloseEvent ((ConsoleIn->ConIn).WaitForKey); + (ConsoleIn->ConIn).WaitForKey = NULL; + } + if (ConsoleIn->ConInEx.WaitForKeyEx != NULL) { + gBS->CloseEvent (ConsoleIn->ConInEx.WaitForKeyEx); + ConsoleIn->ConInEx.WaitForKeyEx = NULL; + } + KbdFreeNotifyList (&ConsoleIn->NotifyList); + FreeUnicodeStringTable (ConsoleIn->ControllerNameTable); + gBS->FreePool (ConsoleIn); + + return EFI_SUCCESS; +} + +/** + Free the waiting key notify list. + + @param ListHead Pointer to list head + + @retval EFI_INVALID_PARAMETER ListHead is NULL + @retval EFI_SUCCESS Sucess to free NotifyList +**/ +EFI_STATUS +KbdFreeNotifyList ( + IN OUT LIST_ENTRY *ListHead + ) +{ + KEYBOARD_CONSOLE_IN_EX_NOTIFY *NotifyNode; + + if (ListHead == NULL) { + return EFI_INVALID_PARAMETER; + } + while (!IsListEmpty (ListHead)) { + NotifyNode = CR ( + ListHead->ForwardLink, + KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + RemoveEntryList (ListHead->ForwardLink); + gBS->FreePool (NotifyNode); + } + + return EFI_SUCCESS; +} + +/** + The module Entry Point for module Ps2Keyboard. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] 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 +InitializePs2Keyboard( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gKeyboardControllerDriver, + ImageHandle, + &gPs2KeyboardComponentName, + &gPs2KeyboardComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + + return Status; +} + diff --git a/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2Keyboard.h b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2Keyboard.h new file mode 100644 index 0000000000..d0aecfb087 --- /dev/null +++ b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2Keyboard.h @@ -0,0 +1,551 @@ +/** @file + PS/2 keyboard driver header file + +Copyright (c) 2006 - 2016, 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. + +**/ + +#ifndef _PS2KEYBOARD_H_ +#define _PS2KEYBOARD_H_ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gKeyboardControllerDriver; +extern EFI_COMPONENT_NAME_PROTOCOL gPs2KeyboardComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gPs2KeyboardComponentName2; + +// +// Driver Private Data +// +#define KEYBOARD_CONSOLE_IN_DEV_SIGNATURE SIGNATURE_32 ('k', 'k', 'e', 'y') +#define KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE SIGNATURE_32 ('k', 'c', 'e', 'n') + +typedef struct _KEYBOARD_CONSOLE_IN_EX_NOTIFY { + UINTN Signature; + EFI_KEY_DATA KeyData; + EFI_KEY_NOTIFY_FUNCTION KeyNotificationFn; + LIST_ENTRY NotifyEntry; +} KEYBOARD_CONSOLE_IN_EX_NOTIFY; + +#define KEYBOARD_SCAN_CODE_MAX_COUNT 32 +typedef struct { + UINT8 Buffer[KEYBOARD_SCAN_CODE_MAX_COUNT]; + UINTN Head; + UINTN Tail; +} SCAN_CODE_QUEUE; + +#define KEYBOARD_EFI_KEY_MAX_COUNT 256 +typedef struct { + EFI_KEY_DATA Buffer[KEYBOARD_EFI_KEY_MAX_COUNT]; + UINTN Head; + UINTN Tail; +} EFI_KEY_QUEUE; + +typedef struct { + UINTN Signature; + + EFI_HANDLE Handle; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL ConIn; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL ConInEx; + + EFI_EVENT TimerEvent; + + UINT32 DataRegisterAddress; + UINT32 StatusRegisterAddress; + UINT32 CommandRegisterAddress; + + BOOLEAN LeftCtrl; + BOOLEAN RightCtrl; + BOOLEAN LeftAlt; + BOOLEAN RightAlt; + BOOLEAN LeftShift; + BOOLEAN RightShift; + BOOLEAN LeftLogo; + BOOLEAN RightLogo; + BOOLEAN Menu; + BOOLEAN SysReq; + + BOOLEAN CapsLock; + BOOLEAN NumLock; + BOOLEAN ScrollLock; + + BOOLEAN IsSupportPartialKey; + // + // Queue storing key scancodes + // + SCAN_CODE_QUEUE ScancodeQueue; + EFI_KEY_QUEUE EfiKeyQueue; + + // + // Error state + // + BOOLEAN KeyboardErr; + + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + // + // Notification Function List + // + LIST_ENTRY NotifyList; +} KEYBOARD_CONSOLE_IN_DEV; + +#define KEYBOARD_CONSOLE_IN_DEV_FROM_THIS(a) CR (a, KEYBOARD_CONSOLE_IN_DEV, ConIn, KEYBOARD_CONSOLE_IN_DEV_SIGNATURE) +#define TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS(a) \ + CR (a, \ + KEYBOARD_CONSOLE_IN_DEV, \ + ConInEx, \ + KEYBOARD_CONSOLE_IN_DEV_SIGNATURE \ + ) + +#define TABLE_END 0x0 + +// +// Driver entry point +// +/** + The user Entry Point for module Ps2Keyboard. The user code starts with this function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] 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 +InstallPs2KeyboardDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +#define KEYBOARD_8042_DATA_REGISTER 0x60 +#define KEYBOARD_8042_STATUS_REGISTER 0x64 +#define KEYBOARD_8042_COMMAND_REGISTER 0x64 + +#define KEYBOARD_KBEN 0xF4 +#define KEYBOARD_CMDECHO_ACK 0xFA + +#define KEYBOARD_MAX_TRY 256 // 256 +#define KEYBOARD_TIMEOUT 65536 // 0.07s +#define KEYBOARD_WAITFORVALUE_TIMEOUT 1000000 // 1s +#define KEYBOARD_BAT_TIMEOUT 4000000 // 4s +#define KEYBOARD_TIMER_INTERVAL 200000 // 0.02s +#define SCANCODE_EXTENDED0 0xE0 +#define SCANCODE_EXTENDED1 0xE1 +#define SCANCODE_CTRL_MAKE 0x1D +#define SCANCODE_CTRL_BREAK 0x9D +#define SCANCODE_ALT_MAKE 0x38 +#define SCANCODE_ALT_BREAK 0xB8 +#define SCANCODE_LEFT_SHIFT_MAKE 0x2A +#define SCANCODE_LEFT_SHIFT_BREAK 0xAA +#define SCANCODE_RIGHT_SHIFT_MAKE 0x36 +#define SCANCODE_RIGHT_SHIFT_BREAK 0xB6 +#define SCANCODE_CAPS_LOCK_MAKE 0x3A +#define SCANCODE_NUM_LOCK_MAKE 0x45 +#define SCANCODE_SCROLL_LOCK_MAKE 0x46 +#define SCANCODE_DELETE_MAKE 0x53 +#define SCANCODE_LEFT_LOGO_MAKE 0x5B //GUI key defined in Keyboard scan code +#define SCANCODE_LEFT_LOGO_BREAK 0xDB +#define SCANCODE_RIGHT_LOGO_MAKE 0x5C +#define SCANCODE_RIGHT_LOGO_BREAK 0xDC +#define SCANCODE_MENU_MAKE 0x5D //APPS key defined in Keyboard scan code +#define SCANCODE_MENU_BREAK 0xDD +#define SCANCODE_SYS_REQ_MAKE 0x37 +#define SCANCODE_SYS_REQ_BREAK 0xB7 +#define SCANCODE_SYS_REQ_MAKE_WITH_ALT 0x54 +#define SCANCODE_SYS_REQ_BREAK_WITH_ALT 0xD4 + +#define SCANCODE_MAX_MAKE 0x60 + + +#define KEYBOARD_STATUS_REGISTER_HAS_OUTPUT_DATA BIT0 ///< 0 - Output register has no data; 1 - Output register has data +#define KEYBOARD_STATUS_REGISTER_HAS_INPUT_DATA BIT1 ///< 0 - Input register has no data; 1 - Input register has data +#define KEYBOARD_STATUS_REGISTER_SYSTEM_FLAG BIT2 ///< Set to 0 after power on reset +#define KEYBOARD_STATUS_REGISTER_INPUT_DATA_TYPE BIT3 ///< 0 - Data in input register is data; 1 - Data in input register is command +#define KEYBOARD_STATUS_REGISTER_ENABLE_FLAG BIT4 ///< 0 - Keyboard is disable; 1 - Keyboard is enable +#define KEYBOARD_STATUS_REGISTER_TRANSMIT_TIMEOUT BIT5 ///< 0 - Transmit is complete without timeout; 1 - Transmit is timeout without complete +#define KEYBOARD_STATUS_REGISTER_RECEIVE_TIMEOUT BIT6 ///< 0 - Receive is complete without timeout; 1 - Receive is timeout without complete +#define KEYBOARD_STATUS_REGISTER_PARITY BIT7 ///< 0 - Odd parity; 1 - Even parity + +#define KEYBOARD_8042_COMMAND_READ 0x20 +#define KEYBOARD_8042_COMMAND_WRITE 0x60 +#define KEYBOARD_8042_COMMAND_DISABLE_MOUSE_INTERFACE 0xA7 +#define KEYBOARD_8042_COMMAND_ENABLE_MOUSE_INTERFACE 0xA8 +#define KEYBOARD_8042_COMMAND_CONTROLLER_SELF_TEST 0xAA +#define KEYBOARD_8042_COMMAND_KEYBOARD_INTERFACE_SELF_TEST 0xAB +#define KEYBOARD_8042_COMMAND_DISABLE_KEYBOARD_INTERFACE 0xAD + +#define KEYBOARD_8048_COMMAND_CLEAR_OUTPUT_DATA 0xF4 +#define KEYBOARD_8048_COMMAND_RESET 0xFF +#define KEYBOARD_8048_COMMAND_SELECT_SCAN_CODE_SET 0xF0 + +#define KEYBOARD_8048_RETURN_8042_BAT_SUCCESS 0xAA +#define KEYBOARD_8048_RETURN_8042_BAT_ERROR 0xFC +#define KEYBOARD_8048_RETURN_8042_ACK 0xFA + + +// +// Keyboard Controller Status +// +#define KBC_PARE 0x80 // Parity Error +#define KBC_TIM 0x40 // General Time Out + +// +// Other functions that are used among .c files +// +/** + Show keyboard status lights according to + indicators in ConsoleIn. + + @param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + + @return status + +**/ +EFI_STATUS +UpdateStatusLights ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn + ); + +/** + write key to keyboard. + + @param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + @param Data value wanted to be written + + @retval EFI_TIMEOUT - GC_TODO: Add description for return value + @retval EFI_SUCCESS - GC_TODO: Add description for return value + +**/ +EFI_STATUS +KeyboardRead ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn, + OUT UINT8 *Data + ); + +/** + Get scancode from scancode buffer and translate into EFI-scancode and unicode defined by EFI spec. + + The function is always called in TPL_NOTIFY. + + @param ConsoleIn KEYBOARD_CONSOLE_IN_DEV instance pointer + +**/ +VOID +KeyGetchar ( + IN OUT KEYBOARD_CONSOLE_IN_DEV *ConsoleIn + ); + +/** + Perform 8042 controller and keyboard Initialization. + If ExtendedVerification is TRUE, do additional test for + the keyboard interface + + @param ConsoleIn - KEYBOARD_CONSOLE_IN_DEV instance pointer + @param ExtendedVerification - indicates a thorough initialization + + @retval EFI_DEVICE_ERROR Fail to init keyboard + @retval EFI_SUCCESS Success to init keyboard +**/ +EFI_STATUS +InitKeyboard ( + IN OUT KEYBOARD_CONSOLE_IN_DEV *ConsoleIn, + IN BOOLEAN ExtendedVerification + ); + +/** + Disable the keyboard interface of the 8042 controller. + + @param ConsoleIn - the device instance + + @return status of issuing disable command + +**/ +EFI_STATUS +DisableKeyboard ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn + ); + +/** + Timer event handler: read a series of scancodes from 8042 + and put them into memory scancode buffer. + it read as much scancodes to either fill + the memory buffer or empty the keyboard buffer. + It is registered as running under TPL_NOTIFY + + @param Event - The timer event + @param Context - A KEYBOARD_CONSOLE_IN_DEV pointer + +**/ +VOID +EFIAPI +KeyboardTimerHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + logic reset keyboard + Implement SIMPLE_TEXT_IN.Reset() + Perform 8042 controller and keyboard initialization + + @param This Pointer to instance of EFI_SIMPLE_TEXT_INPUT_PROTOCOL + @param ExtendedVerification Indicate that the driver may perform a more + exhaustive verification operation of the device during + reset, now this par is ignored in this driver + +**/ +EFI_STATUS +EFIAPI +KeyboardEfiReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Implement SIMPLE_TEXT_IN.ReadKeyStroke(). + Retrieve key values for driver user. + + @param This Pointer to instance of EFI_SIMPLE_TEXT_INPUT_PROTOCOL + @param Key The output buffer for key value + + @retval EFI_SUCCESS success to read key stroke +**/ +EFI_STATUS +EFIAPI +KeyboardReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ); + +/** + Event notification function for SIMPLE_TEXT_IN.WaitForKey event + Signal the event if there is key available + + @param Event the event object + @param Context waitting context + +**/ +VOID +EFIAPI +KeyboardWaitForKey ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Read status register. + + @param ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + + @return value in status register + +**/ +UINT8 +KeyReadStatusRegister ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn + ); + +/** + Check whether there is Ps/2 Keyboard device in system by 0xF4 Keyboard Command + If Keyboard receives 0xF4, it will respond with 'ACK'. If it doesn't respond, the device + should not be in system. + + @param[in] ConsoleIn Pointer to instance of KEYBOARD_CONSOLE_IN_DEV + + @retval TRUE Keyboard in System. + @retval FALSE Keyboard not in System. +**/ +BOOLEAN +EFIAPI +CheckKeyboardConnect ( + IN KEYBOARD_CONSOLE_IN_DEV *ConsoleIn + ); + +/** + Event notification function for SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx event + Signal the event if there is key available + + @param Event event object + @param Context waiting context + +**/ +VOID +EFIAPI +KeyboardWaitForKeyEx ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +// +// Simple Text Input Ex protocol function prototypes +// + +/** + Reset the input device and optionaly 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 +KeyboardEfiResetEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance 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 availiable. + @retval EFI_DEVICE_ERROR - The keystroke information was not returned due to + hardware errors. + @retval EFI_INVALID_PARAMETER - KeyData is NULL. + +**/ +EFI_STATUS +EFIAPI +KeyboardReadKeyStrokeEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + OUT EFI_KEY_DATA *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 +KeyboardSetState ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_TOGGLE_STATE *KeyToggleState + ); + +/** + 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 necesssary data structures. + @retval EFI_INVALID_PARAMETER - KeyData or NotifyHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +KeyboardRegisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_DATA *KeyData, + IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, + OUT VOID **NotifyHandle + ); + +/** + 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 +KeyboardUnregisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN VOID *NotificationHandle + ); + +/** + Push one key data to the EFI key buffer. + + @param Queue Pointer to instance of EFI_KEY_QUEUE. + @param KeyData The key data to push. +**/ +VOID +PushEfikeyBufTail ( + IN EFI_KEY_QUEUE *Queue, + IN EFI_KEY_DATA *KeyData + ); + +/** + Judge whether is a registed key + + @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 + ); + +#endif diff --git a/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxe.inf b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxe.inf new file mode 100644 index 0000000000..81a31e0bb7 --- /dev/null +++ b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxe.inf @@ -0,0 +1,84 @@ +## @file +# Ps2 Keyboard Driver. +# +# Ps2 Keyboard Driver for UEFI. The keyboard type implemented follows IBM +# compatible PS2 protocol using Scan Code Set 1. +# +# Copyright (c) 2006 - 2016, 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. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Ps2KeyboardDxe + MODULE_UNI_FILE = Ps2KeyboardDxe.uni + FILE_GUID = 3DC82376-637B-40a6-A8FC-A565417F2C38 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializePs2Keyboard + +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# DRIVER_BINDING = gKeyboardControllerDriver; +# COMPONENT_NAME = gPs2KeyboardComponentName; +# COMPONENT_NAME2 = gPs2KeyboardComponentName2; +# + +[Sources] + ComponentName.c + Ps2Keyboard.h + Ps2KbdCtrller.c + Ps2KbdTextIn.c + Ps2Keyboard.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + MemoryAllocationLib + UefiRuntimeServicesTableLib + DebugLib + ReportStatusCodeLib + UefiBootServicesTableLib + UefiLib + UefiDriverEntryPoint + BaseLib + BaseMemoryLib + TimerLib + PcdLib + IoLib + +[Protocols] + gEfiSimpleTextInProtocolGuid ## BY_START + gEfiSimpleTextInputExProtocolGuid ## BY_START + gEfiPs2PolicyProtocolGuid ## SOMETIMES_CONSUMES + gEfiSioProtocolGuid ## TO_START + gEfiDevicePathProtocolGuid ## TO_START + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdPs2KbdExtendedVerification ## CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFastPS2Detection ## SOMETIMES_CONSUMES + +# +# [Event] +# +# ## +# # Timer event used to read key strokes at a regular interval. +# # +# EVENT_TYPE_PERIODIC_TIMER ## CONSUMES +# + +[UserExtensions.TianoCore."ExtraFiles"] + Ps2KeyboardDxeExtra.uni diff --git a/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxe.uni b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxe.uni new file mode 100644 index 0000000000..358cc637ac --- /dev/null +++ b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxe.uni @@ -0,0 +1,23 @@ +// /** @file +// Ps2 Keyboard Driver. +// +// Ps2 Keyboard Driver for UEFI. The keyboard type implemented follows IBM +// compatible PS2 protocol using Scan Code Set 1. +// +// Copyright (c) 2006 - 2016, 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. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Ps2 Keyboard Driver" + +#string STR_MODULE_DESCRIPTION #language en-US "Ps2 Keyboard Driver for UEFI. The keyboard type implemented follows IBM compatible PS2 protocol using Scan Code Set 1." + diff --git a/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxeExtra.uni b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxeExtra.uni new file mode 100644 index 0000000000..7bca518812 --- /dev/null +++ b/MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxeExtra.uni @@ -0,0 +1,20 @@ +// /** @file +// Ps2KeyboardDxe Localized Strings and Content +// +// Copyright (c) 2013 - 2016, 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. +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"PS2 Keyboard DXE Driver" + + diff --git a/MdeModulePkg/Include/Protocol/Ps2Policy.h b/MdeModulePkg/Include/Protocol/Ps2Policy.h new file mode 100644 index 0000000000..14346eed29 --- /dev/null +++ b/MdeModulePkg/Include/Protocol/Ps2Policy.h @@ -0,0 +1,41 @@ +/** @file + PS/2 policy protocol abstracts the specific platform initialization and settings. + +Copyright (c) 2006 - 2016, 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 that 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. + +**/ + + +#ifndef _PS2_POLICY_PROTOCOL_H_ +#define _PS2_POLICY_PROTOCOL_H_ + +#define EFI_PS2_POLICY_PROTOCOL_GUID \ + { \ + 0x4df19259, 0xdc71, 0x4d46, {0xbe, 0xf1, 0x35, 0x7b, 0xb5, 0x78, 0xc4, 0x18 } \ + } + +#define EFI_KEYBOARD_CAPSLOCK 0x0004 +#define EFI_KEYBOARD_NUMLOCK 0x0002 +#define EFI_KEYBOARD_SCROLLLOCK 0x0001 + +typedef +EFI_STATUS +(EFIAPI *EFI_PS2_INIT_HARDWARE) ( + IN EFI_HANDLE Handle + ); + +typedef struct { + UINT8 KeyboardLight; + EFI_PS2_INIT_HARDWARE Ps2InitHardware; +} EFI_PS2_POLICY_PROTOCOL; + +extern EFI_GUID gEfiPs2PolicyProtocolGuid; + +#endif diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 7f50c88eeb..e74b0d92ac 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -709,6 +709,14 @@ # @Prompt Enable S3 performance data support. gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwarePerformanceDataTableS3Support|TRUE|BOOLEAN|0x00010064 + ## Indicates if PS2 keyboard does a extended verification during start. + # Add this PCD mainly consider the use case of simulator. This PCD maybe set to FALSE for + # Extended verification will take some performance. It can be set to FALSE for boot performance.

+ # TRUE - Turn on PS2 keyboard extended verification.
+ # FALSE - Turn off PS2 keyboard extended verification.
+ # @Prompt Turn on PS2 Keyboard Extended Verification + gEfiMdeModulePkgTokenSpaceGuid.PcdPs2KbdExtendedVerification|TRUE|BOOLEAN|0x00010072 + ## Indicates if Serial device uses half hand shake.

# TRUE - Serial device uses half hand shake.
# FALSE - Serial device doesn't use half hand shake.
diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index b1ece7b2cf..f1d7a1470a 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -247,6 +247,7 @@ MdeModulePkg/Bus/I2c/I2cDxe/I2cHostDxe.inf MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.inf MdeModulePkg/Bus/Isa/IsaBusDxe/IsaBusDxe.inf + MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxe.inf MdeModulePkg/Core/Dxe/DxeMain.inf { -- 2.39.2