mirror of https://github.com/acidanthera/audk.git
1150 lines
34 KiB
C
1150 lines
34 KiB
C
/** @file
|
|
VirtualKeyboard driver
|
|
|
|
Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
|
|
Copyright (c) 2018, Linaro Ltd. 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 "VirtualKeyboard.h"
|
|
|
|
//
|
|
// RAM Keyboard Driver Binding Protocol Instance
|
|
//
|
|
EFI_DRIVER_BINDING_PROTOCOL gVirtualKeyboardDriverBinding = {
|
|
VirtualKeyboardDriverBindingSupported,
|
|
VirtualKeyboardDriverBindingStart,
|
|
VirtualKeyboardDriverBindingStop,
|
|
0x10,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
//
|
|
// EFI Driver Binding Protocol Functions
|
|
//
|
|
|
|
/**
|
|
Check whether the driver supports this device.
|
|
|
|
@param This The Udriver 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
|
|
VirtualKeyboardDriverBindingSupported (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
PLATFORM_VIRTUAL_KBD_PROTOCOL *PlatformVirtual;
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gPlatformVirtualKeyboardProtocolGuid,
|
|
(VOID **) &PlatformVirtual,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gPlatformVirtualKeyboardProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Starts the device with this driver.
|
|
|
|
@param This The 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 driver.
|
|
@retval Other This controller cannot be started.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
VirtualKeyboardDriverBindingStart (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate;
|
|
PLATFORM_VIRTUAL_KBD_PROTOCOL *PlatformVirtual;
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gPlatformVirtualKeyboardProtocolGuid,
|
|
(VOID **) &PlatformVirtual,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Allocate the private device structure
|
|
//
|
|
VirtualKeyboardPrivate = (VIRTUAL_KEYBOARD_DEV *) AllocateZeroPool (sizeof (VIRTUAL_KEYBOARD_DEV));
|
|
if (VirtualKeyboardPrivate == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Initialize the private device structure
|
|
//
|
|
VirtualKeyboardPrivate->Signature = VIRTUAL_KEYBOARD_DEV_SIGNATURE;
|
|
VirtualKeyboardPrivate->Handle = Controller;
|
|
VirtualKeyboardPrivate->PlatformVirtual = PlatformVirtual;
|
|
VirtualKeyboardPrivate->Queue.Front = 0;
|
|
VirtualKeyboardPrivate->Queue.Rear = 0;
|
|
VirtualKeyboardPrivate->QueueForNotify.Front = 0;
|
|
VirtualKeyboardPrivate->QueueForNotify.Rear = 0;
|
|
|
|
VirtualKeyboardPrivate->SimpleTextIn.Reset = VirtualKeyboardReset;
|
|
VirtualKeyboardPrivate->SimpleTextIn.ReadKeyStroke = VirtualKeyboardReadKeyStroke;
|
|
|
|
VirtualKeyboardPrivate->SimpleTextInputEx.Reset = VirtualKeyboardResetEx;
|
|
VirtualKeyboardPrivate->SimpleTextInputEx.ReadKeyStrokeEx = VirtualKeyboardReadKeyStrokeEx;
|
|
VirtualKeyboardPrivate->SimpleTextInputEx.SetState = VirtualKeyboardSetState;
|
|
|
|
VirtualKeyboardPrivate->SimpleTextInputEx.RegisterKeyNotify = VirtualKeyboardRegisterKeyNotify;
|
|
VirtualKeyboardPrivate->SimpleTextInputEx.UnregisterKeyNotify = VirtualKeyboardUnregisterKeyNotify;
|
|
InitializeListHead (&VirtualKeyboardPrivate->NotifyList);
|
|
|
|
Status = PlatformVirtual->Register ();
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Report that the keyboard is being enabled
|
|
//
|
|
REPORT_STATUS_CODE (
|
|
EFI_PROGRESS_CODE,
|
|
EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_ENABLE
|
|
);
|
|
|
|
//
|
|
// Setup the WaitForKey event
|
|
//
|
|
Status = gBS->CreateEvent (
|
|
EVT_NOTIFY_WAIT,
|
|
TPL_NOTIFY,
|
|
VirtualKeyboardWaitForKey,
|
|
&(VirtualKeyboardPrivate->SimpleTextIn),
|
|
&((VirtualKeyboardPrivate->SimpleTextIn).WaitForKey)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
(VirtualKeyboardPrivate->SimpleTextIn).WaitForKey = NULL;
|
|
goto Done;
|
|
}
|
|
Status = gBS->CreateEvent (
|
|
EVT_NOTIFY_WAIT,
|
|
TPL_NOTIFY,
|
|
VirtualKeyboardWaitForKeyEx,
|
|
&(VirtualKeyboardPrivate->SimpleTextInputEx),
|
|
&(VirtualKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx)
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
VirtualKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx = NULL;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Setup a periodic timer, used for reading keystrokes at a fixed interval
|
|
//
|
|
Status = gBS->CreateEvent (
|
|
EVT_TIMER | EVT_NOTIFY_SIGNAL,
|
|
TPL_NOTIFY,
|
|
VirtualKeyboardTimerHandler,
|
|
VirtualKeyboardPrivate,
|
|
&VirtualKeyboardPrivate->TimerEvent
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
Status = gBS->SetTimer (
|
|
VirtualKeyboardPrivate->TimerEvent,
|
|
TimerPeriodic,
|
|
KEYBOARD_TIMER_INTERVAL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
Status = gBS->CreateEvent (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_CALLBACK,
|
|
KeyNotifyProcessHandler,
|
|
VirtualKeyboardPrivate,
|
|
&VirtualKeyboardPrivate->KeyNotifyProcessEvent
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Reset the keyboard device
|
|
//
|
|
Status = VirtualKeyboardPrivate->SimpleTextInputEx.Reset (
|
|
&VirtualKeyboardPrivate->SimpleTextInputEx,
|
|
FALSE
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "[KBD]Reset Failed. Status - %r\n", Status));
|
|
goto Done;
|
|
}
|
|
//
|
|
// Install protocol interfaces for the keyboard device.
|
|
//
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&Controller,
|
|
&gEfiSimpleTextInProtocolGuid,
|
|
&VirtualKeyboardPrivate->SimpleTextIn,
|
|
&gEfiSimpleTextInputExProtocolGuid,
|
|
&VirtualKeyboardPrivate->SimpleTextInputEx,
|
|
NULL
|
|
);
|
|
|
|
Done:
|
|
if (EFI_ERROR (Status)) {
|
|
if (VirtualKeyboardPrivate != NULL) {
|
|
if ((VirtualKeyboardPrivate->SimpleTextIn).WaitForKey != NULL) {
|
|
gBS->CloseEvent ((VirtualKeyboardPrivate->SimpleTextIn).WaitForKey);
|
|
}
|
|
|
|
if ((VirtualKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx != NULL) {
|
|
gBS->CloseEvent (
|
|
(VirtualKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx
|
|
);
|
|
}
|
|
|
|
if (VirtualKeyboardPrivate->KeyNotifyProcessEvent != NULL) {
|
|
gBS->CloseEvent (VirtualKeyboardPrivate->KeyNotifyProcessEvent);
|
|
}
|
|
|
|
VirtualKeyboardFreeNotifyList (&VirtualKeyboardPrivate->NotifyList);
|
|
|
|
if (VirtualKeyboardPrivate->TimerEvent != NULL) {
|
|
gBS->CloseEvent (VirtualKeyboardPrivate->TimerEvent);
|
|
}
|
|
FreePool (VirtualKeyboardPrivate);
|
|
}
|
|
}
|
|
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gPlatformVirtualKeyboardProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Stop the device handled by this driver.
|
|
|
|
@param This The 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_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
|
|
VirtualKeyboardDriverBindingStop (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN UINTN NumberOfChildren,
|
|
IN EFI_HANDLE *ChildHandleBuffer
|
|
)
|
|
{
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Enqueue the key.
|
|
|
|
@param Queue The queue to be enqueued.
|
|
@param KeyData The key data to be enqueued.
|
|
|
|
@retval EFI_NOT_READY The queue is full.
|
|
@retval EFI_SUCCESS Successfully enqueued the key data.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Enqueue (
|
|
IN SIMPLE_QUEUE *Queue,
|
|
IN EFI_KEY_DATA *KeyData
|
|
)
|
|
{
|
|
if ((Queue->Rear + 1) % QUEUE_MAX_COUNT == Queue->Front) {
|
|
return EFI_NOT_READY;
|
|
}
|
|
|
|
CopyMem (&Queue->Buffer[Queue->Rear], KeyData, sizeof (EFI_KEY_DATA));
|
|
Queue->Rear = (Queue->Rear + 1) % QUEUE_MAX_COUNT;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Dequeue the key.
|
|
|
|
@param Queue The queue to be dequeued.
|
|
@param KeyData The key data to be dequeued.
|
|
|
|
@retval EFI_NOT_READY The queue is empty.
|
|
@retval EFI_SUCCESS Successfully dequeued the key data.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Dequeue (
|
|
IN SIMPLE_QUEUE *Queue,
|
|
IN EFI_KEY_DATA *KeyData
|
|
)
|
|
{
|
|
if (Queue->Front == Queue->Rear) {
|
|
return EFI_NOT_READY;
|
|
}
|
|
|
|
CopyMem (KeyData, &Queue->Buffer[Queue->Front], sizeof (EFI_KEY_DATA));
|
|
Queue->Front = (Queue->Front + 1) % QUEUE_MAX_COUNT;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Check whether the queue is empty.
|
|
|
|
@param Queue The queue to be checked.
|
|
|
|
@retval EFI_NOT_READY The queue is empty.
|
|
@retval EFI_SUCCESS The queue is not empty.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
CheckQueue (
|
|
IN SIMPLE_QUEUE *Queue
|
|
)
|
|
{
|
|
if (Queue->Front == Queue->Rear) {
|
|
return EFI_NOT_READY;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Check key buffer to get the key stroke status.
|
|
|
|
@param This Pointer of the protocol EFI_SIMPLE_TEXT_IN_PROTOCOL.
|
|
|
|
@retval EFI_SUCCESS A key is being pressed now.
|
|
@retval Other No key is now pressed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
VirtualKeyboardCheckForKey (
|
|
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This
|
|
)
|
|
{
|
|
VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate;
|
|
|
|
VirtualKeyboardPrivate = VIRTUAL_KEYBOARD_DEV_FROM_THIS (This);
|
|
|
|
return CheckQueue (&VirtualKeyboardPrivate->Queue);
|
|
}
|
|
|
|
/**
|
|
Free keyboard notify list.
|
|
|
|
@param ListHead The list head
|
|
|
|
@retval EFI_SUCCESS Free the notify list successfully
|
|
@retval EFI_INVALID_PARAMETER ListHead is invalid.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
VirtualKeyboardFreeNotifyList (
|
|
IN OUT LIST_ENTRY *ListHead
|
|
)
|
|
{
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NotifyNode;
|
|
|
|
if (ListHead == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
while (!IsListEmpty (ListHead)) {
|
|
NotifyNode = CR (
|
|
ListHead->ForwardLink,
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY,
|
|
NotifyEntry,
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE
|
|
);
|
|
RemoveEntryList (ListHead->ForwardLink);
|
|
gBS->FreePool (NotifyNode);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
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;
|
|
|
|
}
|
|
|
|
/**
|
|
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
|
|
VirtualKeyboardWaitForKey (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
//
|
|
// Stall 1ms to give a chance to let other driver interrupt this routine
|
|
// for their timer event.
|
|
// e.g. UI setup or Shell, other drivers which are driven by timer event
|
|
// will have a bad performance during this period,
|
|
// e.g. usb keyboard driver.
|
|
// Add a stall period can greatly increate other driver performance during
|
|
// the WaitForKey is recursivly invoked. 1ms delay will make little impact
|
|
// to the thunk keyboard driver, and user can not feel the delay at all when
|
|
// input.
|
|
//
|
|
gBS->Stall (1000);
|
|
//
|
|
// Use TimerEvent callback function to check whether there's any key pressed
|
|
//
|
|
VirtualKeyboardTimerHandler (NULL, VIRTUAL_KEYBOARD_DEV_FROM_THIS (Context));
|
|
|
|
if (!EFI_ERROR (VirtualKeyboardCheckForKey (Context))) {
|
|
gBS->SignalEvent (Event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
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
|
|
VirtualKeyboardWaitForKeyEx (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
|
|
{
|
|
VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate;
|
|
|
|
VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (Context);
|
|
VirtualKeyboardWaitForKey (Event, &VirtualKeyboardPrivate->SimpleTextIn);
|
|
|
|
}
|
|
|
|
//
|
|
// EFI Simple Text In Protocol Functions
|
|
//
|
|
/**
|
|
Reset the Keyboard and do BAT test for it, if (ExtendedVerification == TRUE)
|
|
then do some extra keyboard validations.
|
|
|
|
@param This Pointer of simple text Protocol.
|
|
@param ExtendedVerification Whether perform the extra validation of
|
|
keyboard. True: perform; FALSE: skip.
|
|
|
|
@retval EFI_SUCCESS The command byte is written successfully.
|
|
@retval EFI_DEVICE_ERROR Errors occurred during resetting keyboard.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
VirtualKeyboardReset (
|
|
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
|
|
IN BOOLEAN ExtendedVerification
|
|
)
|
|
{
|
|
VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate;
|
|
EFI_STATUS Status;
|
|
EFI_TPL OldTpl;
|
|
|
|
VirtualKeyboardPrivate = VIRTUAL_KEYBOARD_DEV_FROM_THIS (This);
|
|
|
|
//
|
|
// Raise TPL to avoid mouse operation impact
|
|
//
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
if (VirtualKeyboardPrivate->PlatformVirtual &&
|
|
VirtualKeyboardPrivate->PlatformVirtual->Reset) {
|
|
Status = VirtualKeyboardPrivate->PlatformVirtual->Reset ();
|
|
} else {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// resume priority of task level
|
|
//
|
|
gBS->RestoreTPL (OldTpl);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
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
|
|
VirtualKeyboardResetEx (
|
|
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
|
|
IN BOOLEAN ExtendedVerification
|
|
)
|
|
{
|
|
VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate;
|
|
EFI_STATUS Status;
|
|
EFI_TPL OldTpl;
|
|
|
|
VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This);
|
|
|
|
Status = VirtualKeyboardPrivate->SimpleTextIn.Reset (
|
|
&VirtualKeyboardPrivate->SimpleTextIn,
|
|
ExtendedVerification
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
/**
|
|
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 VirtualKeyboardPrivate Virtualkeyboard driver private structure.
|
|
@param KeyData A pointer to a buffer that is filled in
|
|
with the keystroke state data for the key
|
|
that was pressed.
|
|
|
|
@retval EFI_SUCCESS The keystroke information was returned.
|
|
@retval EFI_NOT_READY There was no keystroke data 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 VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate,
|
|
OUT EFI_KEY_DATA *KeyData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_TPL OldTpl;
|
|
if (KeyData == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Use TimerEvent callback function to check whether there's any key pressed
|
|
//
|
|
|
|
//
|
|
// Stall 1ms to give a chance to let other driver interrupt this routine for
|
|
// their timer event.
|
|
// e.g. OS loader, other drivers which are driven by timer event will have a
|
|
// bad performance during this period,
|
|
// e.g. usb keyboard driver.
|
|
// Add a stall period can greatly increate other driver performance during
|
|
// the WaitForKey is recursivly invoked. 1ms delay will make little impact
|
|
// to the thunk keyboard driver, and user can not feel the delay at all when
|
|
// input.
|
|
//
|
|
gBS->Stall (1000);
|
|
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
VirtualKeyboardTimerHandler (NULL, VirtualKeyboardPrivate);
|
|
//
|
|
// If there's no key, just return
|
|
//
|
|
Status = CheckQueue (&VirtualKeyboardPrivate->Queue);
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->RestoreTPL (OldTpl);
|
|
return EFI_NOT_READY;
|
|
}
|
|
|
|
Status = Dequeue (&VirtualKeyboardPrivate->Queue, KeyData);
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Read out the scan code of the key that has just been stroked.
|
|
|
|
@param This Pointer of simple text Protocol.
|
|
@param Key Pointer for store the key that read out.
|
|
|
|
@retval EFI_SUCCESS The key is read out successfully.
|
|
@retval other The key reading failed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
VirtualKeyboardReadKeyStroke (
|
|
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
|
|
OUT EFI_INPUT_KEY *Key
|
|
)
|
|
{
|
|
VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate;
|
|
EFI_STATUS Status;
|
|
EFI_KEY_DATA KeyData;
|
|
|
|
VirtualKeyboardPrivate = VIRTUAL_KEYBOARD_DEV_FROM_THIS (This);
|
|
|
|
Status = KeyboardReadKeyStrokeWorker (VirtualKeyboardPrivate, &KeyData);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Convert the Ctrl+[a-z] to Ctrl+[1-26]
|
|
//
|
|
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;
|
|
}
|
|
|
|
/**
|
|
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
|
|
VirtualKeyboardReadKeyStrokeEx (
|
|
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
|
|
OUT EFI_KEY_DATA *KeyData
|
|
)
|
|
{
|
|
VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate;
|
|
|
|
if (KeyData == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This);
|
|
|
|
return KeyboardReadKeyStrokeWorker (VirtualKeyboardPrivate, 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
|
|
VirtualKeyboardSetState (
|
|
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
|
|
IN EFI_KEY_TOGGLE_STATE *KeyToggleState
|
|
)
|
|
{
|
|
if (KeyToggleState == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Register a notification function for a particular keystroke for the
|
|
input device.
|
|
|
|
@param This Protocol instance pointer.
|
|
@param KeyData A pointer to a buffer that is filled in with
|
|
the keystroke information data for the key
|
|
that was pressed.
|
|
@param KeyNotificationFunction Points to the function to be called when the
|
|
key sequence is typed specified by KeyData.
|
|
@param NotifyHandle Points to the unique handle assigned to the
|
|
registered notification.
|
|
|
|
|
|
@retval EFI_SUCCESS The notification function was registered
|
|
successfully.
|
|
@retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necesssary
|
|
data structures.
|
|
@retval EFI_INVALID_PARAMETER KeyData or NotifyHandle is NULL.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
VirtualKeyboardRegisterKeyNotify (
|
|
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;
|
|
VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate;
|
|
EFI_TPL OldTpl;
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NewNotify;
|
|
LIST_ENTRY *Link;
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
|
|
|
|
if (KeyData == NULL ||
|
|
NotifyHandle == NULL ||
|
|
KeyNotificationFunction == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This);
|
|
|
|
//
|
|
// Enter critical section
|
|
//
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
//
|
|
// Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already
|
|
// registered.
|
|
//
|
|
for (Link = VirtualKeyboardPrivate->NotifyList.ForwardLink;
|
|
Link != &VirtualKeyboardPrivate->NotifyList;
|
|
Link = Link->ForwardLink) {
|
|
CurrentNotify = CR (
|
|
Link,
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY,
|
|
NotifyEntry,
|
|
VIRTUAL_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 = (VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY));
|
|
if (NewNotify == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
NewNotify->Signature = VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE;
|
|
NewNotify->KeyNotificationFn = KeyNotificationFunction;
|
|
CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA));
|
|
InsertTailList (&VirtualKeyboardPrivate->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
|
|
VirtualKeyboardUnregisterKeyNotify (
|
|
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
|
|
IN VOID *NotificationHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate;
|
|
EFI_TPL OldTpl;
|
|
LIST_ENTRY *Link;
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
|
|
|
|
//
|
|
// Check incoming notification handle
|
|
//
|
|
if (NotificationHandle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (((VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *) NotificationHandle)->Signature !=
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This);
|
|
|
|
//
|
|
// Enter critical section
|
|
//
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
for (Link = VirtualKeyboardPrivate->NotifyList.ForwardLink;
|
|
Link != &VirtualKeyboardPrivate->NotifyList;
|
|
Link = Link->ForwardLink) {
|
|
CurrentNotify = CR (
|
|
Link,
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY,
|
|
NotifyEntry,
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE
|
|
);
|
|
if (CurrentNotify == NotificationHandle) {
|
|
//
|
|
// Remove the notification function from NotifyList and free resources
|
|
//
|
|
RemoveEntryList (&CurrentNotify->NotifyEntry);
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
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
|
|
VirtualKeyboardTimerHandler (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_TPL OldTpl;
|
|
LIST_ENTRY *Link;
|
|
EFI_KEY_DATA KeyData;
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
|
|
VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate;
|
|
VIRTUAL_KBD_KEY VirtualKey;
|
|
|
|
VirtualKeyboardPrivate = Context;
|
|
|
|
//
|
|
// Enter critical section
|
|
//
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
if (VirtualKeyboardPrivate->PlatformVirtual &&
|
|
VirtualKeyboardPrivate->PlatformVirtual->Query) {
|
|
if (VirtualKeyboardPrivate->PlatformVirtual->Query (&VirtualKey) ==
|
|
FALSE) {
|
|
goto Exit;
|
|
}
|
|
// Found key
|
|
KeyData.Key.ScanCode = VirtualKey.Key.ScanCode;
|
|
KeyData.Key.UnicodeChar = VirtualKey.Key.UnicodeChar;
|
|
KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID;
|
|
KeyData.KeyState.KeyToggleState = EFI_TOGGLE_STATE_VALID;
|
|
if (VirtualKeyboardPrivate->PlatformVirtual->Clear) {
|
|
VirtualKeyboardPrivate->PlatformVirtual->Clear (&VirtualKey);
|
|
}
|
|
} else {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Signal KeyNotify process event if this key pressed matches any key registered.
|
|
//
|
|
for (Link = VirtualKeyboardPrivate->NotifyList.ForwardLink;
|
|
Link != &VirtualKeyboardPrivate->NotifyList;
|
|
Link = Link->ForwardLink) {
|
|
CurrentNotify = CR (
|
|
Link,
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY,
|
|
NotifyEntry,
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE
|
|
);
|
|
if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) {
|
|
//
|
|
// The key notification function needs to run at TPL_CALLBACK
|
|
// while current TPL is TPL_NOTIFY. It will be invoked in
|
|
// KeyNotifyProcessHandler() which runs at TPL_CALLBACK.
|
|
//
|
|
Enqueue (&VirtualKeyboardPrivate->QueueForNotify, &KeyData);
|
|
gBS->SignalEvent (VirtualKeyboardPrivate->KeyNotifyProcessEvent);
|
|
}
|
|
}
|
|
|
|
Enqueue (&VirtualKeyboardPrivate->Queue, &KeyData);
|
|
|
|
Exit:
|
|
//
|
|
// Leave critical section and return
|
|
//
|
|
gBS->RestoreTPL (OldTpl);
|
|
}
|
|
|
|
/**
|
|
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;
|
|
VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate;
|
|
EFI_KEY_DATA KeyData;
|
|
LIST_ENTRY *Link;
|
|
LIST_ENTRY *NotifyList;
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
|
|
EFI_TPL OldTpl;
|
|
|
|
VirtualKeyboardPrivate = (VIRTUAL_KEYBOARD_DEV *) Context;
|
|
|
|
//
|
|
// Invoke notification functions.
|
|
//
|
|
NotifyList = &VirtualKeyboardPrivate->NotifyList;
|
|
while (TRUE) {
|
|
//
|
|
// Enter critical section
|
|
//
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
Status = Dequeue (&VirtualKeyboardPrivate->QueueForNotify, &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,
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY,
|
|
NotifyEntry,
|
|
VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE
|
|
);
|
|
if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) {
|
|
CurrentNotify->KeyNotificationFn (&KeyData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
The user Entry Point for module VirtualKeyboard. 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
|
|
InitializeVirtualKeyboard(
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Install driver model protocol(s).
|
|
//
|
|
Status = EfiLibInstallDriverBindingComponentName2 (
|
|
ImageHandle,
|
|
SystemTable,
|
|
&gVirtualKeyboardDriverBinding,
|
|
ImageHandle,
|
|
&gVirtualKeyboardComponentName,
|
|
&gVirtualKeyboardComponentName2
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return Status;
|
|
}
|