/** @file | |
USB Keyboard Driver that manages USB keyboard and produces Simple Text Input | |
Protocol and Simple Text Input Ex Protocol. | |
Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR> | |
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 "EfiKey.h" | |
#include "KeyBoard.h" | |
// | |
// USB Keyboard Driver Global Variables | |
// | |
EFI_DRIVER_BINDING_PROTOCOL gUsbKeyboardDriverBinding = { | |
USBKeyboardDriverBindingSupported, | |
USBKeyboardDriverBindingStart, | |
USBKeyboardDriverBindingStop, | |
0xa, | |
NULL, | |
NULL | |
}; | |
/** | |
Entrypoint of USB Keyboard Driver. | |
This function is the entrypoint of USB Keyboard Driver. It installs Driver Binding | |
Protocols together with Component Name Protocols. | |
@param ImageHandle The firmware allocated handle for the EFI image. | |
@param SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The entry point is executed successfully. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
USBKeyboardDriverBindingEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
Status = EfiLibInstallDriverBindingComponentName2 ( | |
ImageHandle, | |
SystemTable, | |
&gUsbKeyboardDriverBinding, | |
ImageHandle, | |
&gUsbKeyboardComponentName, | |
&gUsbKeyboardComponentName2 | |
); | |
ASSERT_EFI_ERROR (Status); | |
return EFI_SUCCESS; | |
} | |
/** | |
Check whether USB keyboard driver supports this device. | |
@param This The USB keyboard driver binding protocol. | |
@param Controller The controller handle to check. | |
@param RemainingDevicePath The remaining device path. | |
@retval EFI_SUCCESS The driver supports this controller. | |
@retval other This device isn't supported. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
USBKeyboardDriverBindingSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_USB_IO_PROTOCOL *UsbIo; | |
// | |
// Check if USB I/O Protocol is attached on the controller handle. | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiUsbIoProtocolGuid, | |
(VOID **) &UsbIo, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Use the USB I/O Protocol interface to check whether Controller is | |
// a keyboard device that can be managed by this driver. | |
// | |
Status = EFI_SUCCESS; | |
if (!IsUSBKeyboard (UsbIo)) { | |
Status = EFI_UNSUPPORTED; | |
} | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiUsbIoProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
return Status; | |
} | |
/** | |
Starts the keyboard device with this driver. | |
This function produces Simple Text Input Protocol and Simple Text Input Ex Protocol, | |
initializes the keyboard device, and submit Asynchronous Interrupt Transfer to manage | |
this keyboard device. | |
@param This The USB keyboard driver binding instance. | |
@param Controller Handle of device to bind driver to. | |
@param RemainingDevicePath Optional parameter use to pick a specific child | |
device to start. | |
@retval EFI_SUCCESS The controller is controlled by the usb keyboard driver. | |
@retval EFI_UNSUPPORTED No interrupt endpoint can be found. | |
@retval Other This controller cannot be started. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
USBKeyboardDriverBindingStart ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_USB_IO_PROTOCOL *UsbIo; | |
USB_KB_DEV *UsbKeyboardDevice; | |
UINT8 EndpointNumber; | |
EFI_USB_ENDPOINT_DESCRIPTOR EndpointDescriptor; | |
UINT8 Index; | |
UINT8 EndpointAddr; | |
UINT8 PollingInterval; | |
UINT8 PacketSize; | |
BOOLEAN Found; | |
EFI_TPL OldTpl; | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
// | |
// Open USB I/O Protocol | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiUsbIoProtocolGuid, | |
(VOID **) &UsbIo, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit1; | |
} | |
UsbKeyboardDevice = AllocateZeroPool (sizeof (USB_KB_DEV)); | |
ASSERT (UsbKeyboardDevice != NULL); | |
// | |
// Get the Device Path Protocol on Controller's handle | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiDevicePathProtocolGuid, | |
(VOID **) &UsbKeyboardDevice->DevicePath, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
// | |
// Report that the USB keyboard is being enabled | |
// | |
REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
EFI_PROGRESS_CODE, | |
(EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_ENABLE), | |
UsbKeyboardDevice->DevicePath | |
); | |
// | |
// This is pretty close to keyboard detection, so log progress | |
// | |
REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
EFI_PROGRESS_CODE, | |
(EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_PRESENCE_DETECT), | |
UsbKeyboardDevice->DevicePath | |
); | |
UsbKeyboardDevice->UsbIo = UsbIo; | |
// | |
// Get interface & endpoint descriptor | |
// | |
UsbIo->UsbGetInterfaceDescriptor ( | |
UsbIo, | |
&UsbKeyboardDevice->InterfaceDescriptor | |
); | |
EndpointNumber = UsbKeyboardDevice->InterfaceDescriptor.NumEndpoints; | |
// | |
// Traverse endpoints to find interrupt endpoint | |
// | |
Found = FALSE; | |
for (Index = 0; Index < EndpointNumber; Index++) { | |
UsbIo->UsbGetEndpointDescriptor ( | |
UsbIo, | |
Index, | |
&EndpointDescriptor | |
); | |
if ((EndpointDescriptor.Attributes & (BIT0 | BIT1)) == USB_ENDPOINT_INTERRUPT) { | |
// | |
// We only care interrupt endpoint here | |
// | |
CopyMem(&UsbKeyboardDevice->IntEndpointDescriptor, &EndpointDescriptor, sizeof(EndpointDescriptor)); | |
Found = TRUE; | |
break; | |
} | |
} | |
if (!Found) { | |
// | |
// Report Status Code to indicate that there is no USB keyboard | |
// | |
REPORT_STATUS_CODE ( | |
EFI_ERROR_CODE | EFI_ERROR_MINOR, | |
(EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED) | |
); | |
// | |
// No interrupt endpoint found, then return unsupported. | |
// | |
Status = EFI_UNSUPPORTED; | |
goto ErrorExit; | |
} | |
REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
EFI_PROGRESS_CODE, | |
(EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_DETECTED), | |
UsbKeyboardDevice->DevicePath | |
); | |
UsbKeyboardDevice->Signature = USB_KB_DEV_SIGNATURE; | |
UsbKeyboardDevice->SimpleInput.Reset = USBKeyboardReset; | |
UsbKeyboardDevice->SimpleInput.ReadKeyStroke = USBKeyboardReadKeyStroke; | |
UsbKeyboardDevice->SimpleInputEx.Reset = USBKeyboardResetEx; | |
UsbKeyboardDevice->SimpleInputEx.ReadKeyStrokeEx = USBKeyboardReadKeyStrokeEx; | |
UsbKeyboardDevice->SimpleInputEx.SetState = USBKeyboardSetState; | |
UsbKeyboardDevice->SimpleInputEx.RegisterKeyNotify = USBKeyboardRegisterKeyNotify; | |
UsbKeyboardDevice->SimpleInputEx.UnregisterKeyNotify = USBKeyboardUnregisterKeyNotify; | |
InitializeListHead (&UsbKeyboardDevice->NotifyList); | |
Status = gBS->CreateEvent ( | |
EVT_TIMER | EVT_NOTIFY_SIGNAL, | |
TPL_NOTIFY, | |
USBKeyboardTimerHandler, | |
UsbKeyboardDevice, | |
&UsbKeyboardDevice->TimerEvent | |
); | |
if (!EFI_ERROR (Status)) { | |
Status = gBS->SetTimer (UsbKeyboardDevice->TimerEvent, TimerPeriodic, KEYBOARD_TIMER_INTERVAL); | |
} | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_WAIT, | |
TPL_NOTIFY, | |
USBKeyboardWaitForKey, | |
UsbKeyboardDevice, | |
&(UsbKeyboardDevice->SimpleInputEx.WaitForKeyEx) | |
); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_WAIT, | |
TPL_NOTIFY, | |
USBKeyboardWaitForKey, | |
UsbKeyboardDevice, | |
&(UsbKeyboardDevice->SimpleInput.WaitForKey) | |
); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
KeyNotifyProcessHandler, | |
UsbKeyboardDevice, | |
&UsbKeyboardDevice->KeyNotifyProcessEvent | |
); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
// | |
// Install Simple Text Input Protocol and Simple Text Input Ex Protocol | |
// for the USB keyboard device. | |
// USB keyboard is a hot plug device, and expected to work immediately | |
// when plugging into system, other conventional console devices could | |
// distinguish it by its device path. | |
// | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&Controller, | |
&gEfiSimpleTextInProtocolGuid, | |
&UsbKeyboardDevice->SimpleInput, | |
&gEfiSimpleTextInputExProtocolGuid, | |
&UsbKeyboardDevice->SimpleInputEx, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
UsbKeyboardDevice->ControllerHandle = Controller; | |
Status = InitKeyboardLayout (UsbKeyboardDevice); | |
if (EFI_ERROR (Status)) { | |
gBS->UninstallMultipleProtocolInterfaces ( | |
Controller, | |
&gEfiSimpleTextInProtocolGuid, | |
&UsbKeyboardDevice->SimpleInput, | |
&gEfiSimpleTextInputExProtocolGuid, | |
&UsbKeyboardDevice->SimpleInputEx, | |
NULL | |
); | |
goto ErrorExit; | |
} | |
// | |
// Reset USB Keyboard Device exhaustively. | |
// | |
Status = UsbKeyboardDevice->SimpleInputEx.Reset ( | |
&UsbKeyboardDevice->SimpleInputEx, | |
TRUE | |
); | |
if (EFI_ERROR (Status)) { | |
gBS->UninstallMultipleProtocolInterfaces ( | |
Controller, | |
&gEfiSimpleTextInProtocolGuid, | |
&UsbKeyboardDevice->SimpleInput, | |
&gEfiSimpleTextInputExProtocolGuid, | |
&UsbKeyboardDevice->SimpleInputEx, | |
NULL | |
); | |
goto ErrorExit; | |
} | |
// | |
// Submit Asynchronous Interrupt Transfer to manage this device. | |
// | |
EndpointAddr = UsbKeyboardDevice->IntEndpointDescriptor.EndpointAddress; | |
PollingInterval = UsbKeyboardDevice->IntEndpointDescriptor.Interval; | |
PacketSize = (UINT8) (UsbKeyboardDevice->IntEndpointDescriptor.MaxPacketSize); | |
Status = UsbIo->UsbAsyncInterruptTransfer ( | |
UsbIo, | |
EndpointAddr, | |
TRUE, | |
PollingInterval, | |
PacketSize, | |
KeyboardHandler, | |
UsbKeyboardDevice | |
); | |
if (EFI_ERROR (Status)) { | |
gBS->UninstallMultipleProtocolInterfaces ( | |
Controller, | |
&gEfiSimpleTextInProtocolGuid, | |
&UsbKeyboardDevice->SimpleInput, | |
&gEfiSimpleTextInputExProtocolGuid, | |
&UsbKeyboardDevice->SimpleInputEx, | |
NULL | |
); | |
goto ErrorExit; | |
} | |
UsbKeyboardDevice->ControllerNameTable = NULL; | |
AddUnicodeString2 ( | |
"eng", | |
gUsbKeyboardComponentName.SupportedLanguages, | |
&UsbKeyboardDevice->ControllerNameTable, | |
L"Generic Usb Keyboard", | |
TRUE | |
); | |
AddUnicodeString2 ( | |
"en", | |
gUsbKeyboardComponentName2.SupportedLanguages, | |
&UsbKeyboardDevice->ControllerNameTable, | |
L"Generic Usb Keyboard", | |
FALSE | |
); | |
gBS->RestoreTPL (OldTpl); | |
return EFI_SUCCESS; | |
// | |
// Error handler | |
// | |
ErrorExit: | |
if (UsbKeyboardDevice != NULL) { | |
if (UsbKeyboardDevice->TimerEvent != NULL) { | |
gBS->CloseEvent (UsbKeyboardDevice->TimerEvent); | |
} | |
if (UsbKeyboardDevice->SimpleInput.WaitForKey != NULL) { | |
gBS->CloseEvent (UsbKeyboardDevice->SimpleInput.WaitForKey); | |
} | |
if (UsbKeyboardDevice->SimpleInputEx.WaitForKeyEx != NULL) { | |
gBS->CloseEvent (UsbKeyboardDevice->SimpleInputEx.WaitForKeyEx); | |
} | |
if (UsbKeyboardDevice->KeyNotifyProcessEvent != NULL) { | |
gBS->CloseEvent (UsbKeyboardDevice->KeyNotifyProcessEvent); | |
} | |
if (UsbKeyboardDevice->KeyboardLayoutEvent != NULL) { | |
ReleaseKeyboardLayoutResources (UsbKeyboardDevice); | |
gBS->CloseEvent (UsbKeyboardDevice->KeyboardLayoutEvent); | |
} | |
FreePool (UsbKeyboardDevice); | |
UsbKeyboardDevice = NULL; | |
} | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiUsbIoProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
ErrorExit1: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Stop the USB keyboard device handled by this driver. | |
@param This The USB keyboard driver binding protocol. | |
@param Controller The controller to release. | |
@param NumberOfChildren The number of handles in ChildHandleBuffer. | |
@param ChildHandleBuffer The array of child handle. | |
@retval EFI_SUCCESS The device was stopped. | |
@retval EFI_UNSUPPORTED Simple Text In Protocol or Simple Text In Ex Protocol | |
is not installed on Controller. | |
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. | |
@retval Others Fail to uninstall protocols attached on the device. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
USBKeyboardDriverBindingStop ( | |
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 *SimpleInput; | |
USB_KB_DEV *UsbKeyboardDevice; | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiSimpleTextInProtocolGuid, | |
(VOID **) &SimpleInput, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiSimpleTextInputExProtocolGuid, | |
NULL, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_TEST_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} | |
UsbKeyboardDevice = USB_KB_DEV_FROM_THIS (SimpleInput); | |
// | |
// The key data input from this device will be disabled. | |
// | |
REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
EFI_PROGRESS_CODE, | |
(EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_DISABLE), | |
UsbKeyboardDevice->DevicePath | |
); | |
// | |
// Delete the Asynchronous Interrupt Transfer from this device | |
// | |
UsbKeyboardDevice->UsbIo->UsbAsyncInterruptTransfer ( | |
UsbKeyboardDevice->UsbIo, | |
UsbKeyboardDevice->IntEndpointDescriptor.EndpointAddress, | |
FALSE, | |
UsbKeyboardDevice->IntEndpointDescriptor.Interval, | |
0, | |
NULL, | |
NULL | |
); | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiUsbIoProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
Status = gBS->UninstallMultipleProtocolInterfaces ( | |
Controller, | |
&gEfiSimpleTextInProtocolGuid, | |
&UsbKeyboardDevice->SimpleInput, | |
&gEfiSimpleTextInputExProtocolGuid, | |
&UsbKeyboardDevice->SimpleInputEx, | |
NULL | |
); | |
// | |
// Free all resources. | |
// | |
gBS->CloseEvent (UsbKeyboardDevice->TimerEvent); | |
gBS->CloseEvent (UsbKeyboardDevice->RepeatTimer); | |
gBS->CloseEvent (UsbKeyboardDevice->DelayedRecoveryEvent); | |
gBS->CloseEvent (UsbKeyboardDevice->SimpleInput.WaitForKey); | |
gBS->CloseEvent (UsbKeyboardDevice->SimpleInputEx.WaitForKeyEx); | |
gBS->CloseEvent (UsbKeyboardDevice->KeyNotifyProcessEvent); | |
KbdFreeNotifyList (&UsbKeyboardDevice->NotifyList); | |
ReleaseKeyboardLayoutResources (UsbKeyboardDevice); | |
gBS->CloseEvent (UsbKeyboardDevice->KeyboardLayoutEvent); | |
if (UsbKeyboardDevice->ControllerNameTable != NULL) { | |
FreeUnicodeStringTable (UsbKeyboardDevice->ControllerNameTable); | |
} | |
DestroyQueue (&UsbKeyboardDevice->UsbKeyQueue); | |
DestroyQueue (&UsbKeyboardDevice->EfiKeyQueue); | |
DestroyQueue (&UsbKeyboardDevice->EfiKeyQueueForNotify); | |
FreePool (UsbKeyboardDevice); | |
return Status; | |
} | |
/** | |
Internal function to read the next keystroke from the keyboard buffer. | |
@param UsbKeyboardDevice USB keyboard's private structure. | |
@param KeyData A pointer to buffer to hold the keystroke | |
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. | |
@retval Others Fail to translate keycode into EFI_INPUT_KEY | |
**/ | |
EFI_STATUS | |
USBKeyboardReadKeyStrokeWorker ( | |
IN OUT USB_KB_DEV *UsbKeyboardDevice, | |
OUT EFI_KEY_DATA *KeyData | |
) | |
{ | |
if (KeyData == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (IsQueueEmpty (&UsbKeyboardDevice->EfiKeyQueue)) { | |
ZeroMem (&KeyData->Key, sizeof (KeyData->Key)); | |
InitializeKeyState (UsbKeyboardDevice, &KeyData->KeyState); | |
return EFI_NOT_READY; | |
} | |
Dequeue (&UsbKeyboardDevice->EfiKeyQueue, KeyData, sizeof (*KeyData)); | |
return EFI_SUCCESS; | |
} | |
/** | |
Reset the input device and optionally run diagnostics | |
There are 2 types of reset for USB keyboard. | |
For non-exhaustive reset, only keyboard buffer is cleared. | |
For exhaustive reset, in addition to clearance of keyboard buffer, the hardware status | |
is also re-initialized. | |
@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 | |
USBKeyboardReset ( | |
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, | |
IN BOOLEAN ExtendedVerification | |
) | |
{ | |
EFI_STATUS Status; | |
USB_KB_DEV *UsbKeyboardDevice; | |
UsbKeyboardDevice = USB_KB_DEV_FROM_THIS (This); | |
REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
EFI_PROGRESS_CODE, | |
(EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_RESET), | |
UsbKeyboardDevice->DevicePath | |
); | |
// | |
// Non-exhaustive reset: | |
// only reset private data structures. | |
// | |
if (!ExtendedVerification) { | |
REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
EFI_PROGRESS_CODE, | |
(EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_CLEAR_BUFFER), | |
UsbKeyboardDevice->DevicePath | |
); | |
// | |
// Clear the key buffer of this USB keyboard | |
// | |
InitQueue (&UsbKeyboardDevice->UsbKeyQueue, sizeof (USB_KEY)); | |
InitQueue (&UsbKeyboardDevice->EfiKeyQueue, sizeof (EFI_KEY_DATA)); | |
InitQueue (&UsbKeyboardDevice->EfiKeyQueueForNotify, sizeof (EFI_KEY_DATA)); | |
return EFI_SUCCESS; | |
} | |
// | |
// Exhaustive reset | |
// | |
Status = InitUSBKeyboard (UsbKeyboardDevice); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Reads the next keystroke from the input device. | |
@param This The EFI_SIMPLE_TEXT_INPUT_PROTOCOL instance. | |
@param Key A pointer to a buffer that is filled in with the keystroke | |
information 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. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
USBKeyboardReadKeyStroke ( | |
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, | |
OUT EFI_INPUT_KEY *Key | |
) | |
{ | |
USB_KB_DEV *UsbKeyboardDevice; | |
EFI_STATUS Status; | |
EFI_KEY_DATA KeyData; | |
UsbKeyboardDevice = USB_KB_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) { | |
Status = USBKeyboardReadKeyStrokeWorker (UsbKeyboardDevice, &KeyData); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// SimpleTextIn Protocol doesn't support partial keystroke; | |
// | |
if (KeyData.Key.ScanCode == CHAR_NULL && KeyData.Key.UnicodeChar == SCAN_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 registered for EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx | |
and EFI_SIMPLE_TEXT_INPUT_PROTOCOL.WaitForKey. | |
@param Event Event to be signaled when a key is pressed. | |
@param Context Points to USB_KB_DEV instance. | |
**/ | |
VOID | |
EFIAPI | |
USBKeyboardWaitForKey ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
USB_KB_DEV *UsbKeyboardDevice; | |
EFI_KEY_DATA KeyData; | |
EFI_TPL OldTpl; | |
UsbKeyboardDevice = (USB_KB_DEV *) Context; | |
// | |
// Enter critical section | |
// | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
// | |
// 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 (!IsQueueEmpty (&UsbKeyboardDevice->EfiKeyQueue)) { | |
// | |
// If there is pending key, signal the event. | |
// | |
CopyMem ( | |
&KeyData, | |
UsbKeyboardDevice->EfiKeyQueue.Buffer[UsbKeyboardDevice->EfiKeyQueue.Head], | |
sizeof (EFI_KEY_DATA) | |
); | |
if (KeyData.Key.ScanCode == SCAN_NULL && KeyData.Key.UnicodeChar == CHAR_NULL) { | |
Dequeue (&UsbKeyboardDevice->EfiKeyQueue, &KeyData, sizeof (EFI_KEY_DATA)); | |
continue; | |
} | |
gBS->SignalEvent (Event); | |
break; | |
} | |
// | |
// Leave critical section and return | |
// | |
gBS->RestoreTPL (OldTpl); | |
} | |
/** | |
Timer handler to convert the key from USB. | |
@param Event Indicates the event that invoke this function. | |
@param Context Indicates the calling context. | |
**/ | |
VOID | |
EFIAPI | |
USBKeyboardTimerHandler ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
USB_KB_DEV *UsbKeyboardDevice; | |
UINT8 KeyCode; | |
EFI_KEY_DATA KeyData; | |
UsbKeyboardDevice = (USB_KB_DEV *) Context; | |
// | |
// Fetch raw data from the USB keyboard buffer, | |
// and translate it into USB keycode. | |
// | |
Status = USBParseKey (UsbKeyboardDevice, &KeyCode); | |
if (EFI_ERROR (Status)) { | |
return ; | |
} | |
// | |
// Translate saved USB keycode into EFI_INPUT_KEY | |
// | |
Status = UsbKeyCodeToEfiInputKey (UsbKeyboardDevice, KeyCode, &KeyData); | |
if (EFI_ERROR (Status)) { | |
return ; | |
} | |
// | |
// Insert to the EFI Key queue | |
// | |
Enqueue (&UsbKeyboardDevice->EfiKeyQueue, &KeyData, sizeof (KeyData)); | |
} | |
/** | |
Free keyboard notify list. | |
@param NotifyList The keyboard notify list to free. | |
@retval EFI_SUCCESS Free the notify list successfully. | |
@retval EFI_INVALID_PARAMETER NotifyList is NULL. | |
**/ | |
EFI_STATUS | |
KbdFreeNotifyList ( | |
IN OUT LIST_ENTRY *NotifyList | |
) | |
{ | |
KEYBOARD_CONSOLE_IN_EX_NOTIFY *NotifyNode; | |
LIST_ENTRY *Link; | |
if (NotifyList == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
while (!IsListEmpty (NotifyList)) { | |
Link = GetFirstNode (NotifyList); | |
NotifyNode = CR (Link, KEYBOARD_CONSOLE_IN_EX_NOTIFY, NotifyEntry, USB_KB_CONSOLE_IN_EX_NOTIFY_SIGNATURE); | |
RemoveEntryList (Link); | |
FreePool (NotifyNode); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Check whether the pressed key matches a registered key or not. | |
@param RegsiteredData A pointer to keystroke data for the key that was registered. | |
@param InputData A pointer to keystroke data for the key that was pressed. | |
@retval TRUE Key pressed matches a registered key. | |
@retval FLASE Key pressed does not matches a registered key. | |
**/ | |
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; | |
} | |
// | |
// Simple Text Input Ex protocol functions | |
// | |
/** | |
Resets the input device hardware. | |
The Reset() function resets the input device hardware. As part | |
of initialization process, the firmware/device will make a quick | |
but reasonable attempt to verify that the device is functioning. | |
If the ExtendedVerification flag is TRUE the firmware may take | |
an extended amount of time to verify the device is operating on | |
reset. Otherwise the reset operation is to occur as quickly as | |
possible. The hardware verification process is not defined by | |
this specification and is left up to the platform firmware or | |
driver to implement. | |
@param This A pointer to the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL instance. | |
@param ExtendedVerification Indicates that the driver may perform a more exhaustive | |
verification operation of the device during reset. | |
@retval EFI_SUCCESS The device was reset. | |
@retval EFI_DEVICE_ERROR The device is not functioning correctly and could not be reset. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
USBKeyboardResetEx ( | |
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
IN BOOLEAN ExtendedVerification | |
) | |
{ | |
EFI_STATUS Status; | |
USB_KB_DEV *UsbKeyboardDevice; | |
UsbKeyboardDevice = TEXT_INPUT_EX_USB_KB_DEV_FROM_THIS (This); | |
Status = UsbKeyboardDevice->SimpleInput.Reset (&UsbKeyboardDevice->SimpleInput, ExtendedVerification); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
UsbKeyboardDevice->KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID; | |
UsbKeyboardDevice->KeyState.KeyToggleState = EFI_TOGGLE_STATE_VALID; | |
return EFI_SUCCESS; | |
} | |
/** | |
Reads the next keystroke from the input device. | |
@param This Protocol instance pointer. | |
@param KeyData A pointer to a buffer that is filled in with the keystroke | |
state data for the key that was pressed. | |
@retval EFI_SUCCESS The keystroke information was returned. | |
@retval EFI_NOT_READY There was no keystroke data available. | |
@retval EFI_DEVICE_ERROR The keystroke information was not returned due to | |
hardware errors. | |
@retval EFI_INVALID_PARAMETER KeyData is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
USBKeyboardReadKeyStrokeEx ( | |
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
OUT EFI_KEY_DATA *KeyData | |
) | |
{ | |
USB_KB_DEV *UsbKeyboardDevice; | |
if (KeyData == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
UsbKeyboardDevice = TEXT_INPUT_EX_USB_KB_DEV_FROM_THIS (This); | |
return USBKeyboardReadKeyStrokeWorker (UsbKeyboardDevice, 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 appropriately. | |
@retval EFI_DEVICE_ERROR The device is not functioning correctly and could | |
not have the setting adjusted. | |
@retval EFI_UNSUPPORTED The device does not support the ability to have its state set. | |
@retval EFI_INVALID_PARAMETER KeyToggleState is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
USBKeyboardSetState ( | |
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
IN EFI_KEY_TOGGLE_STATE *KeyToggleState | |
) | |
{ | |
USB_KB_DEV *UsbKeyboardDevice; | |
if (KeyToggleState == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
UsbKeyboardDevice = TEXT_INPUT_EX_USB_KB_DEV_FROM_THIS (This); | |
if (((UsbKeyboardDevice->KeyState.KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID) || | |
((*KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID)) { | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Update the status light | |
// | |
UsbKeyboardDevice->ScrollOn = FALSE; | |
UsbKeyboardDevice->NumLockOn = FALSE; | |
UsbKeyboardDevice->CapsOn = FALSE; | |
UsbKeyboardDevice->IsSupportPartialKey = FALSE; | |
if ((*KeyToggleState & EFI_SCROLL_LOCK_ACTIVE) == EFI_SCROLL_LOCK_ACTIVE) { | |
UsbKeyboardDevice->ScrollOn = TRUE; | |
} | |
if ((*KeyToggleState & EFI_NUM_LOCK_ACTIVE) == EFI_NUM_LOCK_ACTIVE) { | |
UsbKeyboardDevice->NumLockOn = TRUE; | |
} | |
if ((*KeyToggleState & EFI_CAPS_LOCK_ACTIVE) == EFI_CAPS_LOCK_ACTIVE) { | |
UsbKeyboardDevice->CapsOn = TRUE; | |
} | |
if ((*KeyToggleState & EFI_KEY_STATE_EXPOSED) == EFI_KEY_STATE_EXPOSED) { | |
UsbKeyboardDevice->IsSupportPartialKey = TRUE; | |
} | |
SetKeyLED (UsbKeyboardDevice); | |
UsbKeyboardDevice->KeyState.KeyToggleState = *KeyToggleState; | |
return EFI_SUCCESS; | |
} | |
/** | |
Register a notification function for a particular keystroke for the input device. | |
@param This Protocol instance pointer. | |
@param KeyData A pointer to a buffer that is filled in with | |
the keystroke information for the key that was | |
pressed. If KeyData.Key, KeyData.KeyState.KeyToggleState | |
and KeyData.KeyState.KeyShiftState are 0, then any incomplete | |
keystroke will trigger a notification of the KeyNotificationFunction. | |
@param KeyNotificationFunction Points to the function to be called when the key | |
sequence is typed specified by KeyData. This notification function | |
should be called at <=TPL_CALLBACK. | |
@param NotifyHandle Points to the unique handle assigned to the registered notification. | |
@retval EFI_SUCCESS The notification function was registered successfully. | |
@retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necessary data structures. | |
@retval EFI_INVALID_PARAMETER KeyData or NotifyHandle or KeyNotificationFunction is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
USBKeyboardRegisterKeyNotify ( | |
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
IN EFI_KEY_DATA *KeyData, | |
IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, | |
OUT VOID **NotifyHandle | |
) | |
{ | |
USB_KB_DEV *UsbKeyboardDevice; | |
KEYBOARD_CONSOLE_IN_EX_NOTIFY *NewNotify; | |
LIST_ENTRY *Link; | |
LIST_ENTRY *NotifyList; | |
KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; | |
if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
UsbKeyboardDevice = TEXT_INPUT_EX_USB_KB_DEV_FROM_THIS (This); | |
// | |
// Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered. | |
// | |
NotifyList = &UsbKeyboardDevice->NotifyList; | |
for (Link = GetFirstNode (NotifyList); | |
!IsNull (NotifyList, Link); | |
Link = GetNextNode (NotifyList, Link)) { | |
CurrentNotify = CR ( | |
Link, | |
KEYBOARD_CONSOLE_IN_EX_NOTIFY, | |
NotifyEntry, | |
USB_KB_CONSOLE_IN_EX_NOTIFY_SIGNATURE | |
); | |
if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) { | |
if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) { | |
*NotifyHandle = CurrentNotify; | |
return EFI_SUCCESS; | |
} | |
} | |
} | |
// | |
// Allocate resource to save the notification function | |
// | |
NewNotify = (KEYBOARD_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (KEYBOARD_CONSOLE_IN_EX_NOTIFY)); | |
if (NewNotify == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
NewNotify->Signature = USB_KB_CONSOLE_IN_EX_NOTIFY_SIGNATURE; | |
NewNotify->KeyNotificationFn = KeyNotificationFunction; | |
CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA)); | |
InsertTailList (&UsbKeyboardDevice->NotifyList, &NewNotify->NotifyEntry); | |
*NotifyHandle = NewNotify; | |
return EFI_SUCCESS; | |
} | |
/** | |
Remove a registered notification function from a particular keystroke. | |
@param This Protocol instance pointer. | |
@param NotificationHandle The handle of the notification function being unregistered. | |
@retval EFI_SUCCESS The notification function was unregistered successfully. | |
@retval EFI_INVALID_PARAMETER The NotificationHandle is invalid | |
**/ | |
EFI_STATUS | |
EFIAPI | |
USBKeyboardUnregisterKeyNotify ( | |
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
IN VOID *NotificationHandle | |
) | |
{ | |
USB_KB_DEV *UsbKeyboardDevice; | |
KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; | |
LIST_ENTRY *Link; | |
LIST_ENTRY *NotifyList; | |
if (NotificationHandle == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
UsbKeyboardDevice = TEXT_INPUT_EX_USB_KB_DEV_FROM_THIS (This); | |
// | |
// Traverse notify list of USB keyboard and remove the entry of NotificationHandle. | |
// | |
NotifyList = &UsbKeyboardDevice->NotifyList; | |
for (Link = GetFirstNode (NotifyList); | |
!IsNull (NotifyList, Link); | |
Link = GetNextNode (NotifyList, Link)) { | |
CurrentNotify = CR ( | |
Link, | |
KEYBOARD_CONSOLE_IN_EX_NOTIFY, | |
NotifyEntry, | |
USB_KB_CONSOLE_IN_EX_NOTIFY_SIGNATURE | |
); | |
if (CurrentNotify == NotificationHandle) { | |
// | |
// Remove the notification function from NotifyList and free resources | |
// | |
RemoveEntryList (&CurrentNotify->NotifyEntry); | |
FreePool (CurrentNotify); | |
return EFI_SUCCESS; | |
} | |
} | |
// | |
// Cannot find the matching entry in database. | |
// | |
return EFI_INVALID_PARAMETER; | |
} | |
/** | |
Process key notify. | |
@param Event Indicates the event that invoke this function. | |
@param Context Indicates the calling context. | |
**/ | |
VOID | |
EFIAPI | |
KeyNotifyProcessHandler ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
USB_KB_DEV *UsbKeyboardDevice; | |
EFI_KEY_DATA KeyData; | |
LIST_ENTRY *Link; | |
LIST_ENTRY *NotifyList; | |
KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; | |
EFI_TPL OldTpl; | |
UsbKeyboardDevice = (USB_KB_DEV *) Context; | |
// | |
// Invoke notification functions. | |
// | |
NotifyList = &UsbKeyboardDevice->NotifyList; | |
while (TRUE) { | |
// | |
// Enter critical section | |
// | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
Status = Dequeue (&UsbKeyboardDevice->EfiKeyQueueForNotify, &KeyData, sizeof (KeyData)); | |
// | |
// Leave critical section | |
// | |
gBS->RestoreTPL (OldTpl); | |
if (EFI_ERROR (Status)) { | |
break; | |
} | |
for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList, Link); Link = GetNextNode (NotifyList, Link)) { | |
CurrentNotify = CR (Link, KEYBOARD_CONSOLE_IN_EX_NOTIFY, NotifyEntry, USB_KB_CONSOLE_IN_EX_NOTIFY_SIGNATURE); | |
if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) { | |
CurrentNotify->KeyNotificationFn (&KeyData); | |
} | |
} | |
} | |
} | |