audk/IntelFrameworkModulePkg/Csm/BiosThunk/KeyboardDxe/BiosKeyboard.c

2515 lines
72 KiB
C

/** @file
ConsoleOut Routines that speak VGA.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "BiosKeyboard.h"
//
// EFI Driver Binding Protocol Instance
//
EFI_DRIVER_BINDING_PROTOCOL gBiosKeyboardDriverBinding = {
BiosKeyboardDriverBindingSupported,
BiosKeyboardDriverBindingStart,
BiosKeyboardDriverBindingStop,
0x3,
NULL,
NULL
};
/**
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;
}
//
// 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
BiosKeyboardDriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
EFI_ISA_IO_PROTOCOL *IsaIo;
//
// See if the Legacy BIOS Protocol is available
//
Status = gBS->LocateProtocol (
&gEfiLegacyBiosProtocolGuid,
NULL,
(VOID **) &LegacyBios
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Open the IO Abstraction(s) needed to perform the supported test
//
Status = gBS->OpenProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
(VOID **) &IsaIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Use the ISA I/O Protocol to see if Controller is the Keyboard controller
//
if (IsaIo->ResourceList->Device.HID != EISA_PNP_ID (0x303) || IsaIo->ResourceList->Device.UID != 0) {
Status = EFI_UNSUPPORTED;
}
gBS->CloseProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
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
BiosKeyboardDriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
EFI_ISA_IO_PROTOCOL *IsaIo;
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
EFI_IA32_REGISTER_SET Regs;
BOOLEAN CarryFlag;
EFI_PS2_POLICY_PROTOCOL *Ps2Policy;
UINT8 Command;
EFI_STATUS_CODE_VALUE StatusCode;
BiosKeyboardPrivate = NULL;
IsaIo = NULL;
StatusCode = 0;
//
// Get Ps2 policy to set. Will be use if present.
//
gBS->LocateProtocol (
&gEfiPs2PolicyProtocolGuid,
NULL,
(VOID **) &Ps2Policy
);
//
// See if the Legacy BIOS Protocol is available
//
Status = gBS->LocateProtocol (
&gEfiLegacyBiosProtocolGuid,
NULL,
(VOID **) &LegacyBios
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Open the IO Abstraction(s) needed
//
Status = gBS->OpenProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
(VOID **) &IsaIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Allocate the private device structure
//
BiosKeyboardPrivate = (BIOS_KEYBOARD_DEV *) AllocateZeroPool (sizeof (BIOS_KEYBOARD_DEV));
if (NULL == BiosKeyboardPrivate) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
//
// Initialize the private device structure
//
BiosKeyboardPrivate->Signature = BIOS_KEYBOARD_DEV_SIGNATURE;
BiosKeyboardPrivate->Handle = Controller;
BiosKeyboardPrivate->LegacyBios = LegacyBios;
BiosKeyboardPrivate->IsaIo = IsaIo;
BiosKeyboardPrivate->SimpleTextIn.Reset = BiosKeyboardReset;
BiosKeyboardPrivate->SimpleTextIn.ReadKeyStroke = BiosKeyboardReadKeyStroke;
BiosKeyboardPrivate->DataRegisterAddress = KEYBOARD_8042_DATA_REGISTER;
BiosKeyboardPrivate->StatusRegisterAddress = KEYBOARD_8042_STATUS_REGISTER;
BiosKeyboardPrivate->CommandRegisterAddress = KEYBOARD_8042_COMMAND_REGISTER;
BiosKeyboardPrivate->ExtendedKeyboard = TRUE;
BiosKeyboardPrivate->KeyState.KeyShiftState = 0;
BiosKeyboardPrivate->KeyState.KeyToggleState = 0;
BiosKeyboardPrivate->Queue.Front = 0;
BiosKeyboardPrivate->Queue.Rear = 0;
BiosKeyboardPrivate->QueueForNotify.Front = 0;
BiosKeyboardPrivate->QueueForNotify.Rear = 0;
BiosKeyboardPrivate->SimpleTextInputEx.Reset = BiosKeyboardResetEx;
BiosKeyboardPrivate->SimpleTextInputEx.ReadKeyStrokeEx = BiosKeyboardReadKeyStrokeEx;
BiosKeyboardPrivate->SimpleTextInputEx.SetState = BiosKeyboardSetState;
BiosKeyboardPrivate->SimpleTextInputEx.RegisterKeyNotify = BiosKeyboardRegisterKeyNotify;
BiosKeyboardPrivate->SimpleTextInputEx.UnregisterKeyNotify = BiosKeyboardUnregisterKeyNotify;
InitializeListHead (&BiosKeyboardPrivate->NotifyList);
//
// 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,
BiosKeyboardWaitForKey,
&(BiosKeyboardPrivate->SimpleTextIn),
&((BiosKeyboardPrivate->SimpleTextIn).WaitForKey)
);
if (EFI_ERROR (Status)) {
(BiosKeyboardPrivate->SimpleTextIn).WaitForKey = NULL;
goto Done;
}
Status = gBS->CreateEvent (
EVT_NOTIFY_WAIT,
TPL_NOTIFY,
BiosKeyboardWaitForKeyEx,
&(BiosKeyboardPrivate->SimpleTextInputEx),
&(BiosKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx)
);
if (EFI_ERROR (Status)) {
BiosKeyboardPrivate->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,
BiosKeyboardTimerHandler,
BiosKeyboardPrivate,
&BiosKeyboardPrivate->TimerEvent
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
goto Done;
}
Status = gBS->SetTimer (
BiosKeyboardPrivate->TimerEvent,
TimerPeriodic,
KEYBOARD_TIMER_INTERVAL
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
goto Done;
}
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
KeyNotifyProcessHandler,
BiosKeyboardPrivate,
&BiosKeyboardPrivate->KeyNotifyProcessEvent
);
if (EFI_ERROR (Status)) {
Status = EFI_OUT_OF_RESOURCES;
StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
goto Done;
}
//
// Report a Progress Code for an attempt to detect the precense of the keyboard device in the system
//
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_PRESENCE_DETECT
);
//
// Reset the keyboard device
//
Status = BiosKeyboardPrivate->SimpleTextInputEx.Reset (
&BiosKeyboardPrivate->SimpleTextInputEx,
FeaturePcdGet (PcdPs2KbdExtendedVerification)
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "[KBD]Reset Failed. Status - %r\n", Status));
StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED;
goto Done;
}
//
// Do platform specific policy like port swapping and keyboard light default
//
if (Ps2Policy != NULL) {
Ps2Policy->Ps2InitHardware (Controller);
Command = 0;
if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_CAPSLOCK) == EFI_KEYBOARD_CAPSLOCK) {
Command |= 4;
}
if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_NUMLOCK) == EFI_KEYBOARD_NUMLOCK) {
Command |= 2;
}
if ((Ps2Policy->KeyboardLight & EFI_KEYBOARD_SCROLLLOCK) == EFI_KEYBOARD_SCROLLLOCK) {
Command |= 1;
}
KeyboardWrite (BiosKeyboardPrivate, 0xed);
KeyboardWaitForValue (BiosKeyboardPrivate, 0xfa, KEYBOARD_WAITFORVALUE_TIMEOUT);
KeyboardWrite (BiosKeyboardPrivate, Command);
//
// Call Legacy BIOS Protocol to set whatever is necessary
//
LegacyBios->UpdateKeyboardLedStatus (LegacyBios, Command);
}
//
// Get Configuration
//
Regs.H.AH = 0xc0;
CarryFlag = BiosKeyboardPrivate->LegacyBios->Int86 (
BiosKeyboardPrivate->LegacyBios,
0x15,
&Regs
);
if (!CarryFlag) {
//
// Check bit 6 of Feature Byte 2.
// If it is set, then Int 16 Func 09 is supported
//
if (*(UINT8 *) (((UINTN) Regs.X.ES << 4) + Regs.X.BX + 0x06) & 0x40) {
//
// Get Keyboard Functionality
//
Regs.H.AH = 0x09;
CarryFlag = BiosKeyboardPrivate->LegacyBios->Int86 (
BiosKeyboardPrivate->LegacyBios,
0x16,
&Regs
);
if (!CarryFlag) {
//
// Check bit 5 of AH.
// If it is set, then INT 16 Finc 10-12 are supported.
//
if ((Regs.H.AL & 0x40) != 0) {
//
// Set the flag to use INT 16 Func 10-12
//
BiosKeyboardPrivate->ExtendedKeyboard = TRUE;
}
}
}
}
DEBUG ((EFI_D_INFO, "[KBD]Extended keystrokes supported by CSM16 - %02x\n", (UINTN)BiosKeyboardPrivate->ExtendedKeyboard));
//
// Install protocol interfaces for the keyboard device.
//
Status = gBS->InstallMultipleProtocolInterfaces (
&Controller,
&gEfiSimpleTextInProtocolGuid,
&BiosKeyboardPrivate->SimpleTextIn,
&gEfiSimpleTextInputExProtocolGuid,
&BiosKeyboardPrivate->SimpleTextInputEx,
NULL
);
Done:
if (StatusCode != 0) {
//
// Report an Error Code for failing to start the keyboard device
//
REPORT_STATUS_CODE (
EFI_ERROR_CODE | EFI_ERROR_MINOR,
StatusCode
);
}
if (EFI_ERROR (Status)) {
if (BiosKeyboardPrivate != NULL) {
if ((BiosKeyboardPrivate->SimpleTextIn).WaitForKey != NULL) {
gBS->CloseEvent ((BiosKeyboardPrivate->SimpleTextIn).WaitForKey);
}
if ((BiosKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx != NULL) {
gBS->CloseEvent ((BiosKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx);
}
if (BiosKeyboardPrivate->KeyNotifyProcessEvent != NULL) {
gBS->CloseEvent (BiosKeyboardPrivate->KeyNotifyProcessEvent);
}
BiosKeyboardFreeNotifyList (&BiosKeyboardPrivate->NotifyList);
if (BiosKeyboardPrivate->TimerEvent != NULL) {
gBS->CloseEvent (BiosKeyboardPrivate->TimerEvent);
}
FreePool (BiosKeyboardPrivate);
}
if (IsaIo != NULL) {
gBS->CloseProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
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
BiosKeyboardDriverBindingStop (
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 *SimpleTextIn;
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
//
// Disable Keyboard
//
Status = gBS->OpenProtocol (
Controller,
&gEfiSimpleTextInProtocolGuid,
(VOID **) &SimpleTextIn,
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;
}
BiosKeyboardPrivate = BIOS_KEYBOARD_DEV_FROM_THIS (SimpleTextIn);
Status = gBS->UninstallMultipleProtocolInterfaces (
Controller,
&gEfiSimpleTextInProtocolGuid,
&BiosKeyboardPrivate->SimpleTextIn,
&gEfiSimpleTextInputExProtocolGuid,
&BiosKeyboardPrivate->SimpleTextInputEx,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Release the IsaIo protocol on the controller handle
//
gBS->CloseProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
//
// Free other resources
//
gBS->CloseEvent ((BiosKeyboardPrivate->SimpleTextIn).WaitForKey);
gBS->CloseEvent (BiosKeyboardPrivate->TimerEvent);
gBS->CloseEvent (BiosKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx);
gBS->CloseEvent (BiosKeyboardPrivate->KeyNotifyProcessEvent);
BiosKeyboardFreeNotifyList (&BiosKeyboardPrivate->NotifyList);
FreePool (BiosKeyboardPrivate);
return EFI_SUCCESS;
}
/**
Read data byte from output buffer of Keyboard Controller without delay and waiting for buffer-empty state.
@param BiosKeyboardPrivate Keyboard instance pointer.
@return The data byte read from output buffer of Keyboard Controller from data port which often is port 60H.
**/
UINT8
KeyReadDataRegister (
IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate
)
{
UINT8 Data;
//
// Use IsaIo protocol to perform IO operations
//
BiosKeyboardPrivate->IsaIo->Io.Read (
BiosKeyboardPrivate->IsaIo,
EfiIsaIoWidthUint8,
BiosKeyboardPrivate->DataRegisterAddress,
1,
&Data
);
return Data;
}
/**
Read status byte from status register of Keyboard Controller without delay and waiting for buffer-empty state.
@param BiosKeyboardPrivate Keyboard instance pointer.
@return The status byte read from status register of Keyboard Controller from command port which often is port 64H.
**/
UINT8
KeyReadStatusRegister (
IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate
)
{
UINT8 Data;
//
// Use IsaIo protocol to perform IO operations
//
BiosKeyboardPrivate->IsaIo->Io.Read (
BiosKeyboardPrivate->IsaIo,
EfiIsaIoWidthUint8,
BiosKeyboardPrivate->StatusRegisterAddress,
1,
&Data
);
return Data;
}
/**
Write command byte to control register of Keyboard Controller without delay and waiting for buffer-empty state.
@param BiosKeyboardPrivate Keyboard instance pointer.
@param Data Data byte to write.
**/
VOID
KeyWriteCommandRegister (
IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate,
IN UINT8 Data
)
{
//
// Use IsaIo protocol to perform IO operations
//
BiosKeyboardPrivate->IsaIo->Io.Write (
BiosKeyboardPrivate->IsaIo,
EfiIsaIoWidthUint8,
BiosKeyboardPrivate->CommandRegisterAddress,
1,
&Data
);
}
/**
Write data byte to input buffer or input/output ports of Keyboard Controller without delay and waiting for buffer-empty state.
@param BiosKeyboardPrivate Keyboard instance pointer.
@param Data Data byte to write.
**/
VOID
KeyWriteDataRegister (
IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate,
IN UINT8 Data
)
{
//
// Use IsaIo protocol to perform IO operations
//
BiosKeyboardPrivate->IsaIo->Io.Write (
BiosKeyboardPrivate->IsaIo,
EfiIsaIoWidthUint8,
BiosKeyboardPrivate->DataRegisterAddress,
1,
&Data
);
}
/**
Read data byte from output buffer of Keyboard Controller with delay and waiting for buffer-empty state.
@param BiosKeyboardPrivate Keyboard instance pointer.
@param Data The pointer for data that being read out.
@retval EFI_SUCCESS The data byte read out successfully.
@retval EFI_TIMEOUT Timeout occurred during reading out data byte.
**/
EFI_STATUS
KeyboardRead (
IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate,
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 (BiosKeyboardPrivate) & KBC_STSREG_VIA64_OUTB) != 0) {
RegFilled = 1;
*Data = KeyReadDataRegister (BiosKeyboardPrivate);
break;
}
gBS->Stall (30);
}
if (RegFilled == 0) {
return EFI_TIMEOUT;
}
return EFI_SUCCESS;
}
/**
Write data byte to input buffer or input/output ports of Keyboard Controller with delay and waiting for buffer-empty state.
@param BiosKeyboardPrivate Keyboard instance pointer.
@param Data Data byte to write.
@retval EFI_SUCCESS The data byte is written successfully.
@retval EFI_TIMEOUT Timeout occurred during writing.
**/
EFI_STATUS
KeyboardWrite (
IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate,
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 (BiosKeyboardPrivate) & KBC_STSREG_VIA64_INPB) == 0) {
RegEmptied = 1;
break;
}
gBS->Stall (30);
}
if (RegEmptied == 0) {
return EFI_TIMEOUT;
}
//
// Write it
//
KeyWriteDataRegister (BiosKeyboardPrivate, Data);
return EFI_SUCCESS;
}
/**
Write command byte to control register of Keyboard Controller with delay and waiting for buffer-empty state.
@param BiosKeyboardPrivate Keyboard instance pointer.
@param Data Command byte to write.
@retval EFI_SUCCESS The command byte is written successfully.
@retval EFI_TIMEOUT Timeout occurred during writing.
**/
EFI_STATUS
KeyboardCommand (
IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate,
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 (BiosKeyboardPrivate) & KBC_STSREG_VIA64_INPB) == 0) {
RegEmptied = 1;
break;
}
gBS->Stall (30);
}
if (RegEmptied == 0) {
return EFI_TIMEOUT;
}
//
// issue the command
//
KeyWriteCommandRegister (BiosKeyboardPrivate, Data);
//
// Wait For Input Buffer Empty again
//
RegEmptied = 0;
for (TimeOut = 0; TimeOut < KEYBOARD_TIMEOUT; TimeOut += 30) {
if ((KeyReadStatusRegister (BiosKeyboardPrivate) & KBC_STSREG_VIA64_INPB) == 0) {
RegEmptied = 1;
break;
}
gBS->Stall (30);
}
if (RegEmptied == 0) {
return EFI_TIMEOUT;
}
return EFI_SUCCESS;
}
/**
Wait for a specific value to be presented in
Data register of Keyboard Controller by keyboard and then read it,
used in keyboard commands ack
@param BiosKeyboardPrivate Keyboard instance pointer.
@param Value The value to be waited for
@param WaitForValueTimeOut The limit of microseconds for timeout
@retval EFI_SUCCESS The command byte is written successfully.
@retval EFI_TIMEOUT Timeout occurred during writing.
**/
EFI_STATUS
KeyboardWaitForValue (
IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate,
IN UINT8 Value,
IN UINTN WaitForValueTimeOut
)
{
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 (BiosKeyboardPrivate) & KBC_STSREG_VIA64_OUTB) != 0) {
Data = KeyReadDataRegister (BiosKeyboardPrivate);
break;
}
gBS->Stall (30);
}
SumTimeOut += TimeOut;
if (Data == Value) {
GotIt = 1;
break;
}
if (SumTimeOut >= WaitForValueTimeOut) {
break;
}
}
//
// Check results
//
if (GotIt != 0) {
return EFI_SUCCESS;
} else {
return EFI_TIMEOUT;
}
}
/**
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 BiosKeyboardPrivate Bioskeyboard 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 BIOS_KEYBOARD_DEV *BiosKeyboardPrivate,
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.
// Csm will be used to check whether there is a key pending, but the csm will disable all
// interrupt before switch to compatibility16, which mean all the efiCompatibility timer
// event will stop work during the compatibility16. And If a caller recursivly invoke this function,
// 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);
BiosKeyboardTimerHandler (NULL, BiosKeyboardPrivate);
//
// If there's no key, just return
//
Status = CheckQueue (&BiosKeyboardPrivate->Queue);
if (EFI_ERROR (Status)) {
ZeroMem (&KeyData->Key, sizeof (KeyData->Key));
CopyMem (&KeyData->KeyState, &BiosKeyboardPrivate->KeyState, sizeof (EFI_KEY_STATE));
gBS->RestoreTPL (OldTpl);
return EFI_NOT_READY;
}
Status = Dequeue (&BiosKeyboardPrivate->Queue, KeyData);
gBS->RestoreTPL (OldTpl);
return EFI_SUCCESS;
}
//
// 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
BiosKeyboardReset (
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
EFI_STATUS Status;
EFI_TPL OldTpl;
UINT8 CommandByte;
BOOLEAN MouseEnable;
EFI_INPUT_KEY Key;
MouseEnable = FALSE;
BiosKeyboardPrivate = BIOS_KEYBOARD_DEV_FROM_THIS (This);
//
// 1
// Report reset progress code
//
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_RESET
);
//
// Report a Progress Code for clearing the keyboard buffer
//
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_CLEAR_BUFFER
);
//
// 2
// Raise TPL to avoid mouse operation impact
//
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
//
//
// Exhaust output buffer data
//
do {
Status = BiosKeyboardReadKeyStroke (
This,
&Key
);
} while (!EFI_ERROR (Status));
//
// 3
// check for KBC itself firstly for setted-up already or not by reading SYSF (bit2) of status register via 64H
// if not skip step 4&5 and jump to step 6 to selftest KBC and report this
// else go step 4
//
if (!PcdGetBool (PcdFastPS2Detection)) {
if ((KeyReadStatusRegister (BiosKeyboardPrivate) & KBC_STSREG_VIA64_SYSF) != 0) {
//
// 4
// CheckMouseStatus to decide enable it later or not
//
//
// Read the command byte of KBC
//
Status = KeyboardCommand (
BiosKeyboardPrivate,
KBC_CMDREG_VIA64_CMDBYTE_R
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
Status = KeyboardRead (
BiosKeyboardPrivate,
&CommandByte
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
//
// Check mouse enabled or not before
//
if ((CommandByte & KB_CMMBYTE_DISABLE_AUX) != 0) {
MouseEnable = FALSE;
} else {
MouseEnable = TRUE;
}
//
// 5
// disable mouse (via KBC) and Keyborad device
//
Status = KeyboardCommand (
BiosKeyboardPrivate,
KBC_CMDREG_VIA64_AUX_DISABLE
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
Status = KeyboardCommand (
BiosKeyboardPrivate,
KBC_CMDREG_VIA64_KB_DISABLE
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
} else {
//
// 6
// KBC Self Test
//
//
// Report a Progress Code for performing a self test on the keyboard controller
//
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_SELF_TEST
);
Status = KeyboardCommand (
BiosKeyboardPrivate,
KBC_CMDREG_VIA64_KBC_SLFTEST
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
Status = KeyboardWaitForValue (
BiosKeyboardPrivate,
KBC_CMDECHO_KBCSLFTEST_OK,
KEYBOARD_WAITFORVALUE_TIMEOUT
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
}
}
//
// 7
// Disable Mouse interface, enable Keyboard interface and declare selftest success
//
// Mouse device will block keyboard interface before it be configured, so we should disable mouse first.
//
Status = KeyboardCommand (
BiosKeyboardPrivate,
KBC_CMDREG_VIA64_CMDBYTE_W
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
//
// 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
//
CommandByte = 0;
Status = KeyboardWrite (
BiosKeyboardPrivate,
(UINT8) ((CommandByte &
(~KB_CMMBYTE_DISABLE_KB)) |
KB_CMMBYTE_KSCAN2UNI_COV |
KB_CMMBYTE_ENABLE_AUXINT |
KB_CMMBYTE_ENABLE_KBINT |
KB_CMMBYTE_SLFTEST_SUCC |
KB_CMMBYTE_DISABLE_AUX)
);
//
// For resetting keyboard is not mandatory before booting OS and sometimes keyboard responses very slow,
// so we only do the real resetting for keyboard when user asks, and normally during booting an OS, it's skipped.
// Call CheckKeyboardConnect() to check whether keyboard is connected, if it is not connected,
// Real reset will not do.
//
if (ExtendedVerification && CheckKeyboardConnect (BiosKeyboardPrivate)) {
//
// 8
// Send keyboard reset command then read ACK
//
Status = KeyboardWrite (
BiosKeyboardPrivate,
KBC_INPBUF_VIA60_KBRESET
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
Status = KeyboardWaitForValue (
BiosKeyboardPrivate,
KBC_CMDECHO_ACK,
KEYBOARD_WAITFORVALUE_TIMEOUT
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
//
// 9
// Wait for keyboard return test OK.
//
Status = KeyboardWaitForValue (
BiosKeyboardPrivate,
KBC_CMDECHO_BATTEST_OK,
KEYBOARD_WAITFORVALUE_TIMEOUT
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
//
// 10
// set keyboard scan code set = 02 (standard configuration)
//
Status = KeyboardWrite (
BiosKeyboardPrivate,
KBC_INPBUF_VIA60_KBSCODE
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
Status = KeyboardWaitForValue (
BiosKeyboardPrivate,
KBC_CMDECHO_ACK,
KEYBOARD_WAITFORVALUE_TIMEOUT
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
Status = KeyboardWrite (
BiosKeyboardPrivate,
KBC_INPBUF_VIA60_SCODESET2
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
Status = KeyboardWaitForValue (
BiosKeyboardPrivate,
KBC_CMDECHO_ACK,
KEYBOARD_WAITFORVALUE_TIMEOUT
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
//
// 11
// enable keyboard itself (not via KBC) by writing CMD F4 via 60H
//
Status = KeyboardWrite (
BiosKeyboardPrivate,
KBC_INPBUF_VIA60_KBEN
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
Status = KeyboardWaitForValue (
BiosKeyboardPrivate,
KBC_CMDECHO_ACK,
KEYBOARD_WAITFORVALUE_TIMEOUT
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
//
// 12
// Additional validation, do it as follow:
// 1). check for status register of PARE && TIM via 64H
// 2). perform KB checking by writing ABh via 64H
//
if ((KeyReadStatusRegister (BiosKeyboardPrivate) & (KBC_STSREG_VIA64_PARE | KBC_STSREG_VIA64_TIM)) != 0) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
Status = KeyboardCommand (
BiosKeyboardPrivate,
KBC_CMDREG_VIA64_KB_CKECK
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
Status = KeyboardWaitForValue (
BiosKeyboardPrivate,
KBC_CMDECHO_KBCHECK_OK,
KEYBOARD_WAITFORVALUE_TIMEOUT
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
}
//
// 13
// Done for validating keyboard. Enable keyboard (via KBC)
// and recover the command byte to proper value
//
if (!PcdGetBool (PcdFastPS2Detection)) {
Status = KeyboardCommand (
BiosKeyboardPrivate,
KBC_CMDREG_VIA64_KB_ENABLE
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
}
//
// 14
// conditionally enable mouse (via KBC)
//
if (MouseEnable) {
Status = KeyboardCommand (
BiosKeyboardPrivate,
KBC_CMDREG_VIA64_AUX_ENABLE
);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
}
}
Exit:
//
// 15
// resume priority of task level
//
gBS->RestoreTPL (OldTpl);
return Status;
}
/**
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
BiosKeyboardReadKeyStroke (
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
OUT EFI_INPUT_KEY *Key
)
{
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
EFI_STATUS Status;
EFI_KEY_DATA KeyData;
BiosKeyboardPrivate = BIOS_KEYBOARD_DEV_FROM_THIS (This);
Status = KeyboardReadKeyStrokeWorker (BiosKeyboardPrivate, &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;
}
/**
Waiting on the keyboard event, if there's any key pressed by the user, signal the event
@param Event The event that be siganlled when any key has been stroked.
@param Context Pointer of the protocol EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
**/
VOID
EFIAPI
BiosKeyboardWaitForKey (
IN EFI_EVENT Event,
IN VOID *Context
)
{
//
// Stall 1ms to give a chance to let other driver interrupt this routine for their timer event.
// Csm will be used to check whether there is a key pending, but the csm will disable all
// interrupt before switch to compatibility16, which mean all the efiCompatibility timer
// event will stop work during the compatibility16. And If a caller recursivly invoke this function,
// 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
//
BiosKeyboardTimerHandler (NULL, BIOS_KEYBOARD_DEV_FROM_THIS (Context));
if (!EFI_ERROR (BiosKeyboardCheckForKey (Context))) {
gBS->SignalEvent (Event);
}
}
/**
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
BiosKeyboardCheckForKey (
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This
)
{
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
BiosKeyboardPrivate = BIOS_KEYBOARD_DEV_FROM_THIS (This);
return CheckQueue (&BiosKeyboardPrivate->Queue);
}
//
// Private worker functions
//
#define TABLE_END 0x0
typedef struct _CONVERT_TABLE_ENTRY {
UINT16 ScanCode;
UINT16 EfiScanCode;
} CONVERT_TABLE_ENTRY;
CONVERT_TABLE_ENTRY mConvertTable[] = {
{
0x47,
SCAN_HOME
},
{
0x48,
SCAN_UP
},
{
0x49,
SCAN_PAGE_UP
},
{
0x4b,
SCAN_LEFT
},
{
0x4d,
SCAN_RIGHT
},
{
0x4f,
SCAN_END
},
{
0x50,
SCAN_DOWN
},
{
0x51,
SCAN_PAGE_DOWN
},
{
0x52,
SCAN_INSERT
},
{
0x53,
SCAN_DELETE
},
//
// Function Keys are only valid if KeyChar == 0x00
// This function does not require KeyChar to be 0x00
//
{
0x3b,
SCAN_F1
},
{
0x3c,
SCAN_F2
},
{
0x3d,
SCAN_F3
},
{
0x3e,
SCAN_F4
},
{
0x3f,
SCAN_F5
},
{
0x40,
SCAN_F6
},
{
0x41,
SCAN_F7
},
{
0x42,
SCAN_F8
},
{
0x43,
SCAN_F9
},
{
0x44,
SCAN_F10
},
{
0x85,
SCAN_F11
},
{
0x86,
SCAN_F12
},
//
// Convert ALT + Fn keys
//
{
0x68,
SCAN_F1
},
{
0x69,
SCAN_F2
},
{
0x6a,
SCAN_F3
},
{
0x6b,
SCAN_F4
},
{
0x6c,
SCAN_F5
},
{
0x6d,
SCAN_F6
},
{
0x6e,
SCAN_F7
},
{
0x6f,
SCAN_F8
},
{
0x70,
SCAN_F9
},
{
0x71,
SCAN_F10
},
{
TABLE_END,
SCAN_NULL
},
};
/**
Convert unicode combined with scan code of key to the counterpart of EFIScancode of it.
@param KeyChar Unicode of key.
@param ScanCode Scan code of key.
@return The value of EFI Scancode for the key.
@retval SCAN_NULL No corresponding value in the EFI convert table is found for the key.
**/
UINT16
ConvertToEFIScanCode (
IN CHAR16 KeyChar,
IN UINT16 ScanCode
)
{
UINT16 EfiScanCode;
UINT16 Index;
if (KeyChar == CHAR_ESC) {
EfiScanCode = SCAN_ESC;
} else if (KeyChar == 0x00 || KeyChar == 0xe0) {
//
// Movement & Function Keys
//
for (Index = 0; (Index < sizeof (mConvertTable) / sizeof (CONVERT_TABLE_ENTRY)) && (mConvertTable[Index].ScanCode != TABLE_END); Index += 1) {
if (ScanCode == mConvertTable[Index].ScanCode) {
return mConvertTable[Index].EfiScanCode;
}
}
//
// Reach Table end, return default value
//
return SCAN_NULL;
} else {
return SCAN_NULL;
}
return EfiScanCode;
}
/**
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 BiosKeyboardPrivate Keyboard Private Data Struture
@retval TRUE Keyboard in System.
@retval FALSE Keyboard not in System.
**/
BOOLEAN
CheckKeyboardConnect (
IN BIOS_KEYBOARD_DEV *BiosKeyboardPrivate
)
{
EFI_STATUS Status;
Status = EFI_SUCCESS;
//
// enable keyboard itself and wait for its ack
// If can't receive ack, Keyboard should not be connected.
//
if (!PcdGetBool (PcdFastPS2Detection)) {
Status = KeyboardWrite (
BiosKeyboardPrivate,
KBC_INPBUF_VIA60_KBEN
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "[KBD]CheckKeyboardConnect - Keyboard enable failed!\n"));
REPORT_STATUS_CODE (
EFI_ERROR_CODE | EFI_ERROR_MINOR,
EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR
);
return FALSE;
}
Status = KeyboardWaitForValue (
BiosKeyboardPrivate,
KBC_CMDECHO_ACK,
KEYBOARD_WAITFORVALUE_TIMEOUT
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "[KBD]CheckKeyboardConnect - Timeout!\n"));
REPORT_STATUS_CODE (
EFI_ERROR_CODE | EFI_ERROR_MINOR,
EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR
);
return FALSE;
}
return TRUE;
} else {
return TRUE;
}
}
/**
Timer event handler: read a series of key stroke from 8042
and put them into memory key buffer.
It is registered as running under TPL_NOTIFY
@param Event The timer event
@param Context A BIOS_KEYBOARD_DEV pointer
**/
VOID
EFIAPI
BiosKeyboardTimerHandler (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_TPL OldTpl;
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
EFI_IA32_REGISTER_SET Regs;
UINT8 KbFlag1; // 0040h:0017h - KEYBOARD - STATUS FLAGS 1
UINT8 KbFlag2; // 0040h:0018h - KEYBOARD - STATUS FLAGS 2
EFI_KEY_DATA KeyData;
LIST_ENTRY *Link;
BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
BiosKeyboardPrivate = Context;
//
// Enter critical section
//
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
//
// if there is no key present, just return
//
if (BiosKeyboardPrivate->ExtendedKeyboard) {
Regs.H.AH = 0x11;
} else {
Regs.H.AH = 0x01;
}
BiosKeyboardPrivate->LegacyBios->Int86 (
BiosKeyboardPrivate->LegacyBios,
0x16,
&Regs
);
if (Regs.X.Flags.ZF != 0) {
gBS->RestoreTPL (OldTpl);
return;
}
//
// Read the key
//
if (BiosKeyboardPrivate->ExtendedKeyboard) {
Regs.H.AH = 0x10;
} else {
Regs.H.AH = 0x00;
}
BiosKeyboardPrivate->LegacyBios->Int86 (
BiosKeyboardPrivate->LegacyBios,
0x16,
&Regs
);
KeyData.Key.ScanCode = (UINT16) Regs.H.AH;
KeyData.Key.UnicodeChar = (UINT16) Regs.H.AL;
DEBUG ((
EFI_D_INFO,
"[KBD]INT16 returns EFI_INPUT_KEY.ScanCode - %x, EFI_INPUT_KEY.UnicodeChar - %x\n",
KeyData.Key.ScanCode,
KeyData.Key.UnicodeChar
));
KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID;
KeyData.KeyState.KeyToggleState = EFI_TOGGLE_STATE_VALID;
//
// Leagcy Bios use Int 9 which is IRQ1 interrupt handler to get keystroke scancode to KB buffer in BDA (BIOS DATE AREA), then
// Int 16 depend KB buffer and some key bits in BDA to translate the scancode to ASCII code, and return both the scancode and ASCII
// code to Int 16 caller. This translation process works well if the Int 9 could response user input in time. But in Tiano enviorment, the Int 9
// will be disabled after the thunk call finish, which means if user crazy input during int 9 being disabled, some keystrokes will be lost when
// KB device own hardware buffer overflows. And if the lost keystroke code is CTRL or ALT or SHIFT release code, these function key flags bit
// in BDA will not be updated. So the Int 16 will believe the CTRL or ALT or SHIFT is still pressed, and Int 16 will translate later scancode
// to wrong ASCII code. We can increase the Thunk frequence to let Int 9 response in time, but this way will much hurt other drivers
// performance, like USB.
//
// 1. If CTRL or ALT release code is missed, all later input keys will be translated to wrong ASCII codes which the Tiano cannot support. In
// this case, the KB input seems fail to work, and user input is blocked. To solve the problem, we can help to clear the CTRL or ALT flag in BDA
// after every Int 16 finish. Thus persist to press CTRL or ALT has same effection as only press one time. It is Ok, since user not often use the
// CTRL and ALT.
//
// 2. If SHIFT release code is missed, all later lowercase input will become capital. This is ugly, but not block user input. If user press the lost
// SHIFT again, the lowercase will come back to normal. Since user often use the SHIFT, it is not reasonable to help to clear the SHIFT flag in BDA,
// which will let persist to press SHIFT has same effection as only press one time.
//
//0040h:0017h - KEYBOARD - STATUS FLAGS 1
// 7 INSert active
// 6 Caps Lock active
// 5 Num Lock active
// 4 Scroll Lock active
// 3 either Alt pressed
// 2 either Ctrl pressed
// 1 Left Shift pressed
// 0 Right Shift pressed
//
// Clear the CTRL and ALT BDA flag
//
ACCESS_PAGE0_CODE (
KbFlag1 = *((UINT8 *) (UINTN) 0x417); // read the STATUS FLAGS 1
KbFlag2 = *((UINT8 *) (UINTN) 0x418); // read STATUS FLAGS 2
);
DEBUG_CODE (
{
if ((KbFlag1 & KB_CAPS_LOCK_BIT) == KB_CAPS_LOCK_BIT) {
DEBUG ((EFI_D_INFO, "[KBD]Caps Lock Key is pressed.\n"));
}
if ((KbFlag1 & KB_NUM_LOCK_BIT) == KB_NUM_LOCK_BIT) {
DEBUG ((EFI_D_INFO, "[KBD]Num Lock Key is pressed.\n"));
}
if ((KbFlag1 & KB_SCROLL_LOCK_BIT) == KB_SCROLL_LOCK_BIT) {
DEBUG ((EFI_D_INFO, "[KBD]Scroll Lock Key is pressed.\n"));
}
if ((KbFlag1 & KB_ALT_PRESSED) == KB_ALT_PRESSED) {
if ((KbFlag2 & KB_LEFT_ALT_PRESSED) == KB_LEFT_ALT_PRESSED) {
DEBUG ((EFI_D_INFO, "[KBD]Left Alt Key is pressed.\n"));
} else {
DEBUG ((EFI_D_INFO, "[KBD]Right Alt Key is pressed.\n"));
}
}
if ((KbFlag1 & KB_CTRL_PRESSED) == KB_CTRL_PRESSED) {
if ((KbFlag2 & KB_LEFT_CTRL_PRESSED) == KB_LEFT_CTRL_PRESSED) {
DEBUG ((EFI_D_INFO, "[KBD]Left Ctrl Key is pressed.\n"));
} else {
DEBUG ((EFI_D_INFO, "[KBD]Right Ctrl Key is pressed.\n"));
}
}
if ((KbFlag1 & KB_LEFT_SHIFT_PRESSED) == KB_LEFT_SHIFT_PRESSED) {
DEBUG ((EFI_D_INFO, "[KBD]Left Shift Key is pressed.\n"));
}
if ((KbFlag1 & KB_RIGHT_SHIFT_PRESSED) == KB_RIGHT_SHIFT_PRESSED) {
DEBUG ((EFI_D_INFO, "[KBD]Right Shift Key is pressed.\n"));
}
}
);
//
// Record toggle state
//
if ((KbFlag1 & KB_CAPS_LOCK_BIT) == KB_CAPS_LOCK_BIT) {
KeyData.KeyState.KeyToggleState |= EFI_CAPS_LOCK_ACTIVE;
}
if ((KbFlag1 & KB_NUM_LOCK_BIT) == KB_NUM_LOCK_BIT) {
KeyData.KeyState.KeyToggleState |= EFI_NUM_LOCK_ACTIVE;
}
if ((KbFlag1 & KB_SCROLL_LOCK_BIT) == KB_SCROLL_LOCK_BIT) {
KeyData.KeyState.KeyToggleState |= EFI_SCROLL_LOCK_ACTIVE;
}
//
// Record shift state
// BUGBUG: Need add Menu key and Left/Right Logo key state in the future
//
if ((KbFlag1 & KB_ALT_PRESSED) == KB_ALT_PRESSED) {
KeyData.KeyState.KeyShiftState |= ((KbFlag2 & KB_LEFT_ALT_PRESSED) == KB_LEFT_ALT_PRESSED) ? EFI_LEFT_ALT_PRESSED : EFI_RIGHT_ALT_PRESSED;
}
if ((KbFlag1 & KB_CTRL_PRESSED) == KB_CTRL_PRESSED) {
KeyData.KeyState.KeyShiftState |= ((KbFlag2 & KB_LEFT_CTRL_PRESSED) == KB_LEFT_CTRL_PRESSED) ? EFI_LEFT_CONTROL_PRESSED : EFI_RIGHT_CONTROL_PRESSED;
}
if ((KbFlag1 & KB_LEFT_SHIFT_PRESSED) == KB_LEFT_SHIFT_PRESSED) {
KeyData.KeyState.KeyShiftState |= EFI_LEFT_SHIFT_PRESSED;
}
if ((KbFlag1 & KB_RIGHT_SHIFT_PRESSED) == KB_RIGHT_SHIFT_PRESSED) {
KeyData.KeyState.KeyShiftState |= EFI_RIGHT_SHIFT_PRESSED;
}
//
// Clear left alt and left ctrl BDA flag
//
ACCESS_PAGE0_CODE (
KbFlag2 &= ~(KB_LEFT_ALT_PRESSED | KB_LEFT_CTRL_PRESSED);
*((UINT8 *) (UINTN) 0x418) = KbFlag2;
KbFlag1 &= ~0x0C;
*((UINT8 *) (UINTN) 0x417) = KbFlag1;
);
//
// Output EFI input key and shift/toggle state
//
if (KeyData.Key.UnicodeChar == CHAR_NULL || KeyData.Key.UnicodeChar == CHAR_SCANCODE || KeyData.Key.UnicodeChar == CHAR_ESC) {
KeyData.Key.ScanCode = ConvertToEFIScanCode (KeyData.Key.UnicodeChar, KeyData.Key.ScanCode);
KeyData.Key.UnicodeChar = CHAR_NULL;
} else {
KeyData.Key.ScanCode = SCAN_NULL;
}
//
// CSM16 has converted the Ctrl+[a-z] to [1-26], converted it back.
//
if ((KeyData.KeyState.KeyShiftState & (EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED)) != 0) {
if (KeyData.Key.UnicodeChar >= 1 && KeyData.Key.UnicodeChar <= 26) {
if (((KeyData.KeyState.KeyShiftState & (EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED)) != 0) ==
((KeyData.KeyState.KeyToggleState & EFI_CAPS_LOCK_ACTIVE) != 0)
) {
KeyData.Key.UnicodeChar = (UINT16) (KeyData.Key.UnicodeChar + L'a' - 1);
} else {
KeyData.Key.UnicodeChar = (UINT16) (KeyData.Key.UnicodeChar + L'A' - 1);
}
}
}
DEBUG ((
EFI_D_INFO,
"[KBD]Convert to EFI Scan Code, EFI_INPUT_KEY.ScanCode - %x, EFI_INPUT_KEY.UnicodeChar - %x\n",
KeyData.Key.ScanCode,
KeyData.Key.UnicodeChar
));
//
// 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'; Shift Key + 'F' key = 'f'.
//
if ((KeyData.Key.UnicodeChar >= L'A' && KeyData.Key.UnicodeChar <= L'Z') ||
(KeyData.Key.UnicodeChar >= L'a' && KeyData.Key.UnicodeChar <= L'z')
) {
DEBUG ((EFI_D_INFO, "[KBD]Shift key with a~z are pressed, remove shift state in EFI_KEY_STATE.\n"));
KeyData.KeyState.KeyShiftState &= ~(EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED);
}
//
// Signal KeyNotify process event if this key pressed matches any key registered.
//
for (Link = BiosKeyboardPrivate->NotifyList.ForwardLink; Link != &BiosKeyboardPrivate->NotifyList; Link = Link->ForwardLink) {
CurrentNotify = CR (
Link,
BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY,
NotifyEntry,
BIOS_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 (&BiosKeyboardPrivate->QueueForNotify, &KeyData);
gBS->SignalEvent (BiosKeyboardPrivate->KeyNotifyProcessEvent);
break;
}
}
Enqueue (&BiosKeyboardPrivate->Queue, &KeyData);
//
// Save the current key state
//
CopyMem (&BiosKeyboardPrivate->KeyState, &KeyData.KeyState, sizeof (EFI_KEY_STATE));
//
// Leave critical section and return
//
gBS->RestoreTPL (OldTpl);
return ;
}
/**
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;
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
EFI_KEY_DATA KeyData;
LIST_ENTRY *Link;
LIST_ENTRY *NotifyList;
BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
EFI_TPL OldTpl;
BiosKeyboardPrivate = (BIOS_KEYBOARD_DEV *) Context;
//
// Invoke notification functions.
//
NotifyList = &BiosKeyboardPrivate->NotifyList;
while (TRUE) {
//
// Enter critical section
//
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
Status = Dequeue (&BiosKeyboardPrivate->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, BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY, NotifyEntry, BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE);
if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) {
CurrentNotify->KeyNotificationFn (&KeyData);
}
}
}
}
/**
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
BiosKeyboardFreeNotifyList (
IN OUT LIST_ENTRY *ListHead
)
{
BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NotifyNode;
if (ListHead == NULL) {
return EFI_INVALID_PARAMETER;
}
while (!IsListEmpty (ListHead)) {
NotifyNode = CR (
ListHead->ForwardLink,
BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY,
NotifyEntry,
BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE
);
RemoveEntryList (ListHead->ForwardLink);
gBS->FreePool (NotifyNode);
}
return EFI_SUCCESS;
}
/**
Check if key is registered.
@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;
}
/**
Waiting on the keyboard event, if there's any key pressed by the user, signal the event
@param Event The event that be siganlled when any key has been stroked.
@param Context Pointer of the protocol EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
**/
VOID
EFIAPI
BiosKeyboardWaitForKeyEx (
IN EFI_EVENT Event,
IN VOID *Context
)
{
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (Context);
BiosKeyboardWaitForKey (Event, &BiosKeyboardPrivate->SimpleTextIn);
}
/**
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
BiosKeyboardResetEx (
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
EFI_STATUS Status;
EFI_TPL OldTpl;
BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This);
Status = BiosKeyboardPrivate->SimpleTextIn.Reset (
&BiosKeyboardPrivate->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 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
BiosKeyboardReadKeyStrokeEx (
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
OUT EFI_KEY_DATA *KeyData
)
{
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
if (KeyData == NULL) {
return EFI_INVALID_PARAMETER;
}
BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This);
return KeyboardReadKeyStrokeWorker (BiosKeyboardPrivate, 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
BiosKeyboardSetState (
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
IN EFI_KEY_TOGGLE_STATE *KeyToggleState
)
{
EFI_STATUS Status;
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
EFI_TPL OldTpl;
EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
UINT8 Command;
if (KeyToggleState == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Thunk keyboard driver doesn't support partial keystroke.
//
if ((*KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID ||
(*KeyToggleState & EFI_KEY_STATE_EXPOSED) == EFI_KEY_STATE_EXPOSED
) {
return EFI_UNSUPPORTED;
}
BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This);
//
// See if the Legacy BIOS Protocol is available
//
Status = gBS->LocateProtocol (
&gEfiLegacyBiosProtocolGuid,
NULL,
(VOID **) &LegacyBios
);
ASSERT_EFI_ERROR (Status);
//
// Enter critical section
//
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
Command = 0;
if ((*KeyToggleState & EFI_CAPS_LOCK_ACTIVE) == EFI_CAPS_LOCK_ACTIVE) {
Command |= 4;
}
if ((*KeyToggleState & EFI_NUM_LOCK_ACTIVE) == EFI_NUM_LOCK_ACTIVE) {
Command |= 2;
}
if ((*KeyToggleState & EFI_SCROLL_LOCK_ACTIVE) == EFI_SCROLL_LOCK_ACTIVE) {
Command |= 1;
}
Status = KeyboardWrite (BiosKeyboardPrivate, 0xed);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
Status = KeyboardWaitForValue (BiosKeyboardPrivate, 0xfa, KEYBOARD_WAITFORVALUE_TIMEOUT);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
Status = KeyboardWrite (BiosKeyboardPrivate, Command);
if (EFI_ERROR (Status)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
//
// Call Legacy BIOS Protocol to set whatever is necessary
//
LegacyBios->UpdateKeyboardLedStatus (LegacyBios, Command);
Status = EFI_SUCCESS;
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. 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 necesssary data structures.
@retval EFI_INVALID_PARAMETER KeyData or NotifyHandle is NULL.
**/
EFI_STATUS
EFIAPI
BiosKeyboardRegisterKeyNotify (
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;
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
EFI_TPL OldTpl;
BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NewNotify;
LIST_ENTRY *Link;
BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) {
return EFI_INVALID_PARAMETER;
}
BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_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 = BiosKeyboardPrivate->NotifyList.ForwardLink; Link != &BiosKeyboardPrivate->NotifyList; Link = Link->ForwardLink) {
CurrentNotify = CR (
Link,
BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY,
NotifyEntry,
BIOS_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 = (BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY));
if (NewNotify == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Exit;
}
NewNotify->Signature = BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE;
NewNotify->KeyNotificationFn = KeyNotificationFunction;
CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA));
InsertTailList (&BiosKeyboardPrivate->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
BiosKeyboardUnregisterKeyNotify (
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
IN VOID *NotificationHandle
)
{
EFI_STATUS Status;
BIOS_KEYBOARD_DEV *BiosKeyboardPrivate;
EFI_TPL OldTpl;
LIST_ENTRY *Link;
BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
//
// Check incoming notification handle
//
if (NotificationHandle == NULL) {
return EFI_INVALID_PARAMETER;
}
if (((BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY *) NotificationHandle)->Signature != BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
BiosKeyboardPrivate = TEXT_INPUT_EX_BIOS_KEYBOARD_DEV_FROM_THIS (This);
//
// Enter critical section
//
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
for (Link = BiosKeyboardPrivate->NotifyList.ForwardLink; Link != &BiosKeyboardPrivate->NotifyList; Link = Link->ForwardLink) {
CurrentNotify = CR (
Link,
BIOS_KEYBOARD_CONSOLE_IN_EX_NOTIFY,
NotifyEntry,
BIOS_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;
}
/**
The user Entry Point for module BiosKeyboard. 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
InitializeBiosKeyboard(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Install driver model protocol(s).
//
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gBiosKeyboardDriverBinding,
ImageHandle,
&gBiosKeyboardComponentName,
&gBiosKeyboardComponentName2
);
ASSERT_EFI_ERROR (Status);
return Status;
}