mirror of
https://github.com/tianocore/edk2
synced 2026-08-27 00:23:19 -04:00
OvmfPkg/VirtioInputDxe: Split keyboard logic into VirtioKeyboard.c
Move all keyboard-specific code including probing, initialization, keycode translation and Simple Text Input (Ex) protocol implementation into new VirtioKeyboard.c. VirtioInput.c retains only the shared virtio transport, the input poll timer and the event dispatcher. This is also the groundwork for the mouse and tablet support added in later commits. No functional change. Signed-off-by: Jiaqing Zhao <Zhao.Jiaqing@amd.com>
This commit is contained in:
parent
d0c42935e2
commit
fd63e80508
4 changed files with 687 additions and 624 deletions
|
|
@ -351,7 +351,6 @@ VirtioInputRingFillRx (
|
|||
Dev->VirtIo->SetQueueNotify (Dev->VirtIo, Index);
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
VirtioInputConfigQuerySize (
|
||||
IN VIRTIO_INPUT_DEV *Dev,
|
||||
|
|
@ -376,236 +375,6 @@ VirtioInputConfigQuerySize (
|
|||
return Status;
|
||||
}
|
||||
|
||||
STATIC
|
||||
BOOLEAN
|
||||
VirtioInputHasKeyboard (
|
||||
IN VIRTIO_INPUT_DEV *Dev
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT8 Size;
|
||||
UINT8 Bitmap;
|
||||
UINTN Index;
|
||||
|
||||
Status = VirtioInputConfigQuerySize (Dev, VirtioInputCfgEvBits, EV_KEY, &Size);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Keyboard keys are 0 ~ 255, so if any of them is supported, we have a keyboard
|
||||
Size = MIN (Size, (MAX_KEYBOARD_CODE / 8) + 1);
|
||||
for (Index = 0; Index < Size; Index++) {
|
||||
Status = Dev->VirtIo->ReadDevice (Dev->VirtIo, OFFSET_OF_VINPUT (Data) + Index, 1, 1, &Bitmap);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (Bitmap) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioInputSimpleTextInputReset (
|
||||
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
|
||||
IN BOOLEAN ExtendedVerification
|
||||
)
|
||||
{
|
||||
VIRTIO_INPUT_DEV *Dev;
|
||||
EFI_TPL OldTpl;
|
||||
|
||||
Dev = VIRTIO_INPUT_FROM_THIS (This);
|
||||
|
||||
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
||||
|
||||
Dev->KeyReady = FALSE;
|
||||
Dev->LastKey.ScanCode = SCAN_NULL;
|
||||
Dev->LastKey.UnicodeChar = CHAR_NULL;
|
||||
ZeroMem (Dev->KeyActive, sizeof (Dev->KeyActive));
|
||||
|
||||
gBS->RestoreTPL (OldTpl);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioInputSimpleTextInputReadKeyStroke (
|
||||
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
|
||||
OUT EFI_INPUT_KEY *Key
|
||||
)
|
||||
{
|
||||
VIRTIO_INPUT_DEV *Dev;
|
||||
EFI_TPL OldTpl;
|
||||
|
||||
if (Key == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Dev = VIRTIO_INPUT_FROM_THIS (This);
|
||||
|
||||
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
||||
if (Dev->KeyReady) {
|
||||
// Get last key from the buffer
|
||||
*Key = Dev->LastKey;
|
||||
|
||||
// Mark key as consumed
|
||||
Dev->KeyReady = FALSE;
|
||||
|
||||
gBS->RestoreTPL (OldTpl);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
gBS->RestoreTPL (OldTpl);
|
||||
|
||||
return EFI_NOT_READY;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function converting VirtIO key codes to UEFI key codes
|
||||
STATIC
|
||||
VOID
|
||||
EFIAPI
|
||||
VirtioInputConvertKeyCode (
|
||||
IN OUT VIRTIO_INPUT_DEV *Dev,
|
||||
IN UINT16 Code,
|
||||
OUT EFI_INPUT_KEY *Key
|
||||
)
|
||||
{
|
||||
// Key mapping in between Linux and UEFI
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h
|
||||
// https://dox.ipxe.org/SimpleTextIn_8h_source.html#l00048
|
||||
// https://uefi.org/specs/UEFI/2.10/Apx_B_Console.html
|
||||
|
||||
static const UINT16 Map[] = {
|
||||
[KEY_1] = '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||
[KEY_MINUS] = '-', '=',
|
||||
[KEY_Q] = 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',
|
||||
[KEY_LEFTBRACE] = '[', ']',
|
||||
[KEY_A] = 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l',
|
||||
[KEY_SEMICOLON] = ';', '\'', '`',
|
||||
[KEY_BACKSLASH] = '\\',
|
||||
[KEY_Z] = 'z', 'x', 'c', 'v', 'b', 'n', 'm',
|
||||
[KEY_COMMA] = ',', '.', '/',
|
||||
[KEY_SPACE] = ' ',
|
||||
[MAX_KEYBOARD_CODE] = 0x00
|
||||
};
|
||||
|
||||
static const UINT16 MapShift[] = {
|
||||
[KEY_1] = '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
|
||||
[KEY_MINUS] = '_', '+',
|
||||
[KEY_Q] = 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P',
|
||||
[KEY_LEFTBRACE] = '{', '}',
|
||||
[KEY_A] = 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L',
|
||||
[KEY_SEMICOLON] = ':', '\"', '~',
|
||||
[KEY_BACKSLASH] = '|',
|
||||
[KEY_Z] = 'Z', 'X', 'C', 'V', 'B', 'N', 'M',
|
||||
[KEY_COMMA] = '<', '>', '?',
|
||||
[KEY_SPACE] = ' ',
|
||||
[MAX_KEYBOARD_CODE] = 0x00
|
||||
};
|
||||
|
||||
// Set default readings
|
||||
Key->ScanCode = SCAN_NULL;
|
||||
Key->UnicodeChar = CHAR_NULL;
|
||||
|
||||
// Check if key code is not out of the keyboard mapping boundaries
|
||||
if (Code >= MAX_KEYBOARD_CODE) {
|
||||
DEBUG ((DEBUG_INFO, "%a: Key code out of range \n", __func__));
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle F1 - F10 keys
|
||||
if ((Code >= KEY_F1) && (Code <= KEY_F10)) {
|
||||
Key->ScanCode = SCAN_F1 + (Code - KEY_F1);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (Code) {
|
||||
case KEY_PAGEUP:
|
||||
Key->ScanCode = SCAN_PAGE_UP;
|
||||
break;
|
||||
|
||||
case KEY_PAGEDOWN:
|
||||
Key->ScanCode = SCAN_PAGE_DOWN;
|
||||
break;
|
||||
|
||||
case KEY_HOME:
|
||||
Key->ScanCode = SCAN_HOME;
|
||||
break;
|
||||
|
||||
case KEY_END:
|
||||
Key->ScanCode = SCAN_END;
|
||||
break;
|
||||
|
||||
case KEY_DELETE:
|
||||
Key->ScanCode = SCAN_DELETE;
|
||||
break;
|
||||
|
||||
case KEY_INSERT:
|
||||
Key->ScanCode = SCAN_INSERT;
|
||||
break;
|
||||
|
||||
case KEY_UP:
|
||||
Key->ScanCode = SCAN_UP;
|
||||
break;
|
||||
|
||||
case KEY_LEFT:
|
||||
Key->ScanCode = SCAN_LEFT;
|
||||
break;
|
||||
|
||||
case KEY_RIGHT:
|
||||
Key->ScanCode = SCAN_RIGHT;
|
||||
break;
|
||||
|
||||
case KEY_DOWN:
|
||||
Key->ScanCode = SCAN_DOWN;
|
||||
break;
|
||||
|
||||
case KEY_BACKSPACE:
|
||||
Key->UnicodeChar = CHAR_BACKSPACE;
|
||||
break;
|
||||
|
||||
case KEY_TAB:
|
||||
Key->UnicodeChar = CHAR_TAB;
|
||||
break;
|
||||
|
||||
case KEY_ENTER:
|
||||
// Key->UnicodeChar = CHAR_LINEFEED;
|
||||
Key->UnicodeChar = CHAR_CARRIAGE_RETURN;
|
||||
break;
|
||||
|
||||
case KEY_ESC:
|
||||
Key->ScanCode = SCAN_ESC;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (Dev->KeyActive[KEY_LEFTSHIFT] || Dev->KeyActive[KEY_RIGHTSHIFT]) {
|
||||
Key->ScanCode = MapShift[Code];
|
||||
Key->UnicodeChar = MapShift[Code];
|
||||
} else {
|
||||
Key->ScanCode = Map[Code];
|
||||
Key->UnicodeChar = Map[Code];
|
||||
}
|
||||
|
||||
if (Dev->KeyActive[KEY_LEFTCTRL] || Dev->KeyActive[KEY_RIGHTCTRL]) {
|
||||
// Convert Ctrl+[a-z] and Ctrl+[A-Z] into [1-26] ASCII table entries
|
||||
Key->UnicodeChar &= 0x1F;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Main function processing virtio input events
|
||||
STATIC
|
||||
|
|
@ -633,13 +402,10 @@ VirtioInputGetDeviceData (
|
|||
continue;
|
||||
}
|
||||
|
||||
// Clearing last character is not needed as it will be overwritten anyway
|
||||
// Dev->LastKey.ScanCode = SCAN_NULL;
|
||||
// Dev->LastKey.UnicodeChar = CHAR_NULL;
|
||||
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
||||
|
||||
CopyMem (&Event, Data, sizeof (Event));
|
||||
|
||||
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
||||
switch (Event.Type) {
|
||||
case EV_SYN:
|
||||
// Sync event received
|
||||
|
|
@ -650,20 +416,7 @@ VirtioInputGetDeviceData (
|
|||
// DEBUG ((DEBUG_INFO, "%a: ---------------------- \nType: %x Code: %x Value: %x\n",
|
||||
// __func__, Event.Type, Event.Code, Event.Value));
|
||||
|
||||
if (Event.Value == KEY_PRESSED) {
|
||||
// Key pressed event received
|
||||
Dev->KeyActive[(UINT8)Event.Code] = TRUE;
|
||||
|
||||
// Evaluate key
|
||||
VirtioInputConvertKeyCode (Dev, Event.Code, &Dev->LastKey);
|
||||
|
||||
// Flag that printable character is ready to be send
|
||||
Dev->KeyReady = TRUE;
|
||||
} else {
|
||||
// Key released event received
|
||||
Dev->KeyActive[(UINT8)Event.Code] = FALSE;
|
||||
}
|
||||
|
||||
VirtioKeyboardHandleEvent (Dev, &Event);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -677,7 +430,6 @@ VirtioInputGetDeviceData (
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Callback hook for timer interrupt
|
||||
STATIC
|
||||
VOID
|
||||
EFIAPI
|
||||
VirtioInputTimer (
|
||||
|
|
@ -690,308 +442,6 @@ VirtioInputTimer (
|
|||
VirtioInputGetDeviceData (Dev);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_PROTOCOL API
|
||||
STATIC
|
||||
VOID
|
||||
EFIAPI
|
||||
VirtioInputWaitForKey (
|
||||
IN EFI_EVENT Event,
|
||||
IN VOID *Context
|
||||
)
|
||||
{
|
||||
VIRTIO_INPUT_DEV *Dev = (VIRTIO_INPUT_DEV *)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
|
||||
VirtioInputTimer (NULL, Dev);
|
||||
|
||||
// If there is a new key ready - send signal
|
||||
if (Dev->KeyReady) {
|
||||
gBS->SignalEvent (Event);
|
||||
}
|
||||
}
|
||||
|
||||
/// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioInputResetEx (
|
||||
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
|
||||
IN BOOLEAN ExtendedVerification
|
||||
)
|
||||
{
|
||||
VIRTIO_INPUT_DEV *Dev;
|
||||
EFI_STATUS Status;
|
||||
|
||||
Dev = VIRTIO_INPUT_EX_FROM_THIS (This);
|
||||
|
||||
// Call the reset function from SIMPLE_TEXT_INPUT protocol
|
||||
Status = Dev->Txt.Reset (
|
||||
&Dev->Txt,
|
||||
ExtendedVerification
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioInputReadKeyStrokeEx (
|
||||
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
|
||||
OUT EFI_KEY_DATA *KeyData
|
||||
)
|
||||
{
|
||||
VIRTIO_INPUT_DEV *Dev;
|
||||
EFI_STATUS Status;
|
||||
EFI_INPUT_KEY Key;
|
||||
EFI_KEY_STATE KeyState;
|
||||
|
||||
if (KeyData == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Dev = VIRTIO_INPUT_EX_FROM_THIS (This);
|
||||
|
||||
// Get the last pressed key
|
||||
Status = Dev->Txt.ReadKeyStroke (&Dev->Txt, &Key);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// Add key state informations
|
||||
KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID;
|
||||
KeyState.KeyToggleState = EFI_TOGGLE_STATE_VALID;
|
||||
|
||||
// Shift key modifier
|
||||
if (Dev->KeyActive[KEY_LEFTSHIFT]) {
|
||||
KeyState.KeyShiftState |= EFI_LEFT_SHIFT_PRESSED;
|
||||
}
|
||||
|
||||
if (Dev->KeyActive[KEY_RIGHTSHIFT]) {
|
||||
KeyState.KeyShiftState |= EFI_RIGHT_SHIFT_PRESSED;
|
||||
}
|
||||
|
||||
// Ctrl key modifier
|
||||
if (Dev->KeyActive[KEY_LEFTCTRL]) {
|
||||
KeyState.KeyShiftState |= EFI_LEFT_CONTROL_PRESSED;
|
||||
}
|
||||
|
||||
if (Dev->KeyActive[KEY_RIGHTCTRL]) {
|
||||
KeyState.KeyShiftState |= EFI_RIGHT_CONTROL_PRESSED;
|
||||
}
|
||||
|
||||
// ALt key modifier
|
||||
if (Dev->KeyActive[KEY_LEFTALT]) {
|
||||
KeyState.KeyShiftState |= EFI_LEFT_ALT_PRESSED;
|
||||
}
|
||||
|
||||
if (Dev->KeyActive[KEY_RIGHTALT]) {
|
||||
KeyState.KeyShiftState |= EFI_RIGHT_ALT_PRESSED;
|
||||
}
|
||||
|
||||
// Return value only when there is no failure
|
||||
KeyData->Key = Key;
|
||||
KeyData->KeyState = KeyState;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioInputSetState (
|
||||
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
|
||||
IN EFI_KEY_TOGGLE_STATE *KeyToggleState
|
||||
)
|
||||
{
|
||||
if (KeyToggleState == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
STATIC
|
||||
BOOLEAN
|
||||
IsKeyRegistered (
|
||||
IN EFI_KEY_DATA *RegisteredData,
|
||||
IN EFI_KEY_DATA *InputData
|
||||
)
|
||||
|
||||
{
|
||||
ASSERT (RegisteredData != NULL && InputData != NULL);
|
||||
|
||||
if ((RegisteredData->Key.ScanCode != InputData->Key.ScanCode) ||
|
||||
(RegisteredData->Key.UnicodeChar != InputData->Key.UnicodeChar))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// Assume KeyShiftState/KeyToggleState = 0 in Registered key data means
|
||||
// these state could be ignored.
|
||||
//
|
||||
if ((RegisteredData->KeyState.KeyShiftState != 0) &&
|
||||
(RegisteredData->KeyState.KeyShiftState != InputData->KeyState.KeyShiftState))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((RegisteredData->KeyState.KeyToggleState != 0) &&
|
||||
(RegisteredData->KeyState.KeyToggleState != InputData->KeyState.KeyToggleState))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioInputRegisterKeyNotify (
|
||||
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;
|
||||
VIRTIO_INPUT_DEV *Dev;
|
||||
EFI_TPL OldTpl;
|
||||
LIST_ENTRY *Link;
|
||||
VIRTIO_INPUT_IN_EX_NOTIFY *NewNotify;
|
||||
VIRTIO_INPUT_IN_EX_NOTIFY *CurrentNotify;
|
||||
|
||||
if ((KeyData == NULL) ||
|
||||
(NotifyHandle == NULL) ||
|
||||
(KeyNotificationFunction == NULL))
|
||||
{
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Dev = VIRTIO_INPUT_EX_FROM_THIS (This);
|
||||
|
||||
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
||||
|
||||
// Check if the (KeyData, NotificationFunction) pair is already registered.
|
||||
for (Link = Dev->KeyNotifyList.ForwardLink;
|
||||
Link != &Dev->KeyNotifyList;
|
||||
Link = Link->ForwardLink)
|
||||
{
|
||||
CurrentNotify = CR (
|
||||
Link,
|
||||
VIRTIO_INPUT_IN_EX_NOTIFY,
|
||||
NotifyEntry,
|
||||
VIRTIO_INPUT_SIG
|
||||
);
|
||||
if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) {
|
||||
if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) {
|
||||
*NotifyHandle = CurrentNotify;
|
||||
Status = EFI_SUCCESS;
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NewNotify = (VIRTIO_INPUT_IN_EX_NOTIFY *)AllocateZeroPool (sizeof (VIRTIO_INPUT_IN_EX_NOTIFY));
|
||||
if (NewNotify == NULL) {
|
||||
Status = EFI_OUT_OF_RESOURCES;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
NewNotify->Signature = VIRTIO_INPUT_SIG;
|
||||
NewNotify->KeyNotificationFn = KeyNotificationFunction;
|
||||
CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA));
|
||||
InsertTailList (&Dev->KeyNotifyList, &NewNotify->NotifyEntry);
|
||||
|
||||
*NotifyHandle = NewNotify;
|
||||
Status = EFI_SUCCESS;
|
||||
|
||||
Exit:
|
||||
gBS->RestoreTPL (OldTpl);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioInputUnregisterKeyNotify (
|
||||
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
|
||||
IN VOID *NotificationHandle
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
VIRTIO_INPUT_DEV *Dev;
|
||||
EFI_TPL OldTpl;
|
||||
LIST_ENTRY *Link;
|
||||
VIRTIO_INPUT_IN_EX_NOTIFY *CurrentNotify;
|
||||
|
||||
if (NotificationHandle == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (((VIRTIO_INPUT_IN_EX_NOTIFY *)NotificationHandle)->Signature != VIRTIO_INPUT_SIG) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Dev = VIRTIO_INPUT_EX_FROM_THIS (This);
|
||||
|
||||
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
||||
|
||||
for (Link = Dev->KeyNotifyList.ForwardLink;
|
||||
Link != &Dev->KeyNotifyList;
|
||||
Link = Link->ForwardLink)
|
||||
{
|
||||
CurrentNotify = CR (
|
||||
Link,
|
||||
VIRTIO_INPUT_IN_EX_NOTIFY,
|
||||
NotifyEntry,
|
||||
VIRTIO_INPUT_SIG
|
||||
);
|
||||
if (CurrentNotify == NotificationHandle) {
|
||||
RemoveEntryList (&CurrentNotify->NotifyEntry);
|
||||
|
||||
Status = EFI_SUCCESS;
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Notification has not been found
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
|
||||
Exit:
|
||||
gBS->RestoreTPL (OldTpl);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Driver init
|
||||
STATIC
|
||||
|
|
@ -1083,7 +533,7 @@ VirtioInputInit (
|
|||
//
|
||||
// Check input device capabilities
|
||||
//
|
||||
Dev->HasKeyboard = VirtioInputHasKeyboard (Dev);
|
||||
Dev->HasKeyboard = VirtioKeyboardProbe (Dev);
|
||||
if (!Dev->HasKeyboard) {
|
||||
Status = EFI_UNSUPPORTED;
|
||||
goto Failed;
|
||||
|
|
@ -1093,41 +543,7 @@ VirtioInputInit (
|
|||
// populate the exported interface's attributes
|
||||
//
|
||||
if (Dev->HasKeyboard) {
|
||||
Dev->Txt.Reset = VirtioInputSimpleTextInputReset;
|
||||
Dev->Txt.ReadKeyStroke = VirtioInputSimpleTextInputReadKeyStroke;
|
||||
Dev->Txt.WaitForKey = (EFI_EVENT)VirtioInputWaitForKey;
|
||||
|
||||
Dev->TxtEx.Reset = VirtioInputResetEx;
|
||||
Dev->TxtEx.ReadKeyStrokeEx = VirtioInputReadKeyStrokeEx;
|
||||
Dev->TxtEx.SetState = VirtioInputSetState;
|
||||
Dev->TxtEx.RegisterKeyNotify = VirtioInputRegisterKeyNotify;
|
||||
Dev->TxtEx.UnregisterKeyNotify = VirtioInputUnregisterKeyNotify;
|
||||
InitializeListHead (&Dev->KeyNotifyList);
|
||||
|
||||
//
|
||||
// Setup the WaitForKey event
|
||||
//
|
||||
Status = gBS->CreateEvent (
|
||||
EVT_NOTIFY_WAIT,
|
||||
TPL_NOTIFY,
|
||||
VirtioInputWaitForKey,
|
||||
Dev,
|
||||
&(Dev->Txt.WaitForKey)
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto Failed;
|
||||
}
|
||||
|
||||
//
|
||||
// Setup the WaitForKeyEx event
|
||||
//
|
||||
Status = gBS->CreateEvent (
|
||||
EVT_NOTIFY_WAIT,
|
||||
TPL_NOTIFY,
|
||||
VirtioInputWaitForKey,
|
||||
Dev,
|
||||
&(Dev->TxtEx.WaitForKeyEx)
|
||||
);
|
||||
Status = VirtioKeyboardInit (Dev);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto Failed;
|
||||
}
|
||||
|
|
@ -1185,8 +601,7 @@ VirtioInputUninit (
|
|||
gBS->CloseEvent (Dev->PollTimer);
|
||||
|
||||
if (Dev->HasKeyboard) {
|
||||
gBS->CloseEvent (Dev->Txt.WaitForKey);
|
||||
gBS->CloseEvent (Dev->TxtEx.WaitForKeyEx);
|
||||
VirtioKeyboardUninit (Dev);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -55,29 +55,11 @@ typedef struct {
|
|||
// Declaration of data structure representing driver context
|
||||
typedef struct {
|
||||
// Device signature
|
||||
UINT32 Signature;
|
||||
UINT32 Signature;
|
||||
|
||||
// Hook for the function which shall be caled when driver is closed
|
||||
// before system state changes to boot
|
||||
EFI_EVENT ExitBoot;
|
||||
|
||||
// Hooks for functions required by UEFI keyboard API
|
||||
// struct _EFI_SIMPLE_TEXT_INPUT_PROTOCOL {
|
||||
// EFI_INPUT_RESET Reset;
|
||||
// EFI_INPUT_READ_KEY ReadKeyStroke;
|
||||
// EFI_EVENT WaitForKey;
|
||||
// };
|
||||
EFI_SIMPLE_TEXT_INPUT_PROTOCOL Txt;
|
||||
|
||||
// struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL {
|
||||
// EFI_INPUT_RESET_EX Reset;
|
||||
// EFI_INPUT_READ_KEY_EX ReadKeyStrokeEx;
|
||||
// EFI_EVENT WaitForKeyEx;
|
||||
// EFI_SET_STATE SetState;
|
||||
// EFI_REGISTER_KEYSTROKE_NOTIFY RegisterKeyNotify;
|
||||
// EFI_UNREGISTER_KEYSTROKE_NOTIFY UnregisterKeyNotify;
|
||||
// }
|
||||
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL TxtEx;
|
||||
EFI_EVENT ExitBoot;
|
||||
|
||||
// Virtio device hook
|
||||
VIRTIO_DEVICE_PROTOCOL *VirtIo;
|
||||
|
|
@ -88,22 +70,13 @@ typedef struct {
|
|||
// Timer event for checking input from VirtIo
|
||||
EFI_EVENT PollTimer;
|
||||
|
||||
// List for notifications
|
||||
LIST_ENTRY KeyNotifyList;
|
||||
|
||||
// Keyboard implementation
|
||||
BOOLEAN HasKeyboard;
|
||||
|
||||
// Last pressed key
|
||||
// typedef struct {
|
||||
// UINT16 ScanCode;
|
||||
// CHAR16 UnicodeChar;
|
||||
// } EFI_INPUT_KEY;
|
||||
EFI_SIMPLE_TEXT_INPUT_PROTOCOL Txt;
|
||||
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL TxtEx;
|
||||
EFI_INPUT_KEY LastKey;
|
||||
|
||||
// Key modifiers
|
||||
BOOLEAN KeyActive[MAX_KEYBOARD_CODE];
|
||||
|
||||
// If key is ready
|
||||
LIST_ENTRY KeyNotifyList;
|
||||
BOOLEAN KeyActive[MAX_KEYBOARD_CODE + 1]; // Key modifiers
|
||||
BOOLEAN KeyReady;
|
||||
} VIRTIO_INPUT_DEV;
|
||||
|
||||
|
|
@ -115,3 +88,45 @@ typedef struct {
|
|||
|
||||
// Bellow candidates to be included as Linux header
|
||||
#define KEY_PRESSED 1
|
||||
|
||||
//
|
||||
// VirtioInput.c
|
||||
//
|
||||
EFI_STATUS
|
||||
VirtioInputConfigQuerySize (
|
||||
IN VIRTIO_INPUT_DEV *Dev,
|
||||
IN VIRTIO_INPUT_CONFIG_SELECT Select,
|
||||
IN UINT8 Subsel,
|
||||
OUT UINT8 *Size
|
||||
);
|
||||
|
||||
VOID
|
||||
EFIAPI
|
||||
VirtioInputTimer (
|
||||
IN EFI_EVENT Event,
|
||||
IN VOID *Context
|
||||
);
|
||||
|
||||
//
|
||||
// VirtioKeyboard.c
|
||||
//
|
||||
BOOLEAN
|
||||
VirtioKeyboardProbe (
|
||||
IN VIRTIO_INPUT_DEV *Dev
|
||||
);
|
||||
|
||||
VOID
|
||||
VirtioKeyboardHandleEvent (
|
||||
IN OUT VIRTIO_INPUT_DEV *Dev,
|
||||
IN VIRTIO_INPUT_EVENT *Event
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
VirtioKeyboardInit (
|
||||
IN OUT VIRTIO_INPUT_DEV *Dev
|
||||
);
|
||||
|
||||
VOID
|
||||
VirtioKeyboardUninit (
|
||||
IN OUT VIRTIO_INPUT_DEV *Dev
|
||||
);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
VirtioInput.c
|
||||
VirtioInput.h
|
||||
VirtioKeyCodes.h
|
||||
VirtioKeyboard.c
|
||||
|
||||
[Packages]
|
||||
MdePkg/MdePkg.dec
|
||||
|
|
|
|||
632
OvmfPkg/VirtioInputDxe/VirtioKeyboard.c
Normal file
632
OvmfPkg/VirtioInputDxe/VirtioKeyboard.c
Normal file
|
|
@ -0,0 +1,632 @@
|
|||
/** @file
|
||||
|
||||
EFI_SIMPLE_TEXT_INPUT_PROTOCOL and EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
|
||||
implementation for virtio keyboard.
|
||||
|
||||
Copyright (C) 2026, Advanced Micro Devices, Inc.
|
||||
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
**/
|
||||
|
||||
#include <Library/BaseMemoryLib.h>
|
||||
#include <Library/DebugLib.h>
|
||||
#include <Library/MemoryAllocationLib.h>
|
||||
#include <Library/UefiBootServicesTableLib.h>
|
||||
#include <Library/UefiLib.h>
|
||||
#include <Library/VirtioLib.h>
|
||||
|
||||
#include <IndustryStandard/VirtioInput.h>
|
||||
|
||||
#include "VirtioInput.h"
|
||||
#include "VirtioKeyCodes.h"
|
||||
|
||||
BOOLEAN
|
||||
VirtioKeyboardProbe (
|
||||
IN VIRTIO_INPUT_DEV *Dev
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT8 Size;
|
||||
UINT8 Bitmap;
|
||||
UINTN Index;
|
||||
|
||||
Status = VirtioInputConfigQuerySize (Dev, VirtioInputCfgEvBits, EV_KEY, &Size);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Keyboard keys are 0 ~ 255, so if any of them is supported, we have a keyboard
|
||||
Size = MIN (Size, (MAX_KEYBOARD_CODE / 8) + 1);
|
||||
for (Index = 0; Index < Size; Index++) {
|
||||
Status = Dev->VirtIo->ReadDevice (Dev->VirtIo, OFFSET_OF_VINPUT (Data) + Index, 1, 1, &Bitmap);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (Bitmap) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function converting VirtIO key codes to UEFI key codes
|
||||
STATIC
|
||||
VOID
|
||||
VirtioKeyboardConvertKeyCode (
|
||||
IN OUT VIRTIO_INPUT_DEV *Dev,
|
||||
IN UINT16 Code,
|
||||
OUT EFI_INPUT_KEY *Key
|
||||
)
|
||||
{
|
||||
// Key mapping in between Linux and UEFI
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h
|
||||
// https://dox.ipxe.org/SimpleTextIn_8h_source.html#l00048
|
||||
// https://uefi.org/specs/UEFI/2.10/Apx_B_Console.html
|
||||
|
||||
static const UINT16 Map[] = {
|
||||
[KEY_1] = '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||
[KEY_MINUS] = '-', '=',
|
||||
[KEY_Q] = 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',
|
||||
[KEY_LEFTBRACE] = '[', ']',
|
||||
[KEY_A] = 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l',
|
||||
[KEY_SEMICOLON] = ';', '\'', '`',
|
||||
[KEY_BACKSLASH] = '\\',
|
||||
[KEY_Z] = 'z', 'x', 'c', 'v', 'b', 'n', 'm',
|
||||
[KEY_COMMA] = ',', '.', '/',
|
||||
[KEY_SPACE] = ' ',
|
||||
[MAX_KEYBOARD_CODE] = 0x00
|
||||
};
|
||||
|
||||
static const UINT16 MapShift[] = {
|
||||
[KEY_1] = '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
|
||||
[KEY_MINUS] = '_', '+',
|
||||
[KEY_Q] = 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P',
|
||||
[KEY_LEFTBRACE] = '{', '}',
|
||||
[KEY_A] = 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L',
|
||||
[KEY_SEMICOLON] = ':', '\"', '~',
|
||||
[KEY_BACKSLASH] = '|',
|
||||
[KEY_Z] = 'Z', 'X', 'C', 'V', 'B', 'N', 'M',
|
||||
[KEY_COMMA] = '<', '>', '?',
|
||||
[KEY_SPACE] = ' ',
|
||||
[MAX_KEYBOARD_CODE] = 0x00
|
||||
};
|
||||
|
||||
// Set default readings
|
||||
Key->ScanCode = SCAN_NULL;
|
||||
Key->UnicodeChar = CHAR_NULL;
|
||||
|
||||
// Check if key code is not out of the keyboard mapping boundaries
|
||||
if (Code >= MAX_KEYBOARD_CODE) {
|
||||
DEBUG ((DEBUG_INFO, "%a: Key code out of range \n", __func__));
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle F1 - F10 keys
|
||||
if ((Code >= KEY_F1) && (Code <= KEY_F10)) {
|
||||
Key->ScanCode = SCAN_F1 + (Code - KEY_F1);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (Code) {
|
||||
case KEY_PAGEUP:
|
||||
Key->ScanCode = SCAN_PAGE_UP;
|
||||
break;
|
||||
|
||||
case KEY_PAGEDOWN:
|
||||
Key->ScanCode = SCAN_PAGE_DOWN;
|
||||
break;
|
||||
|
||||
case KEY_HOME:
|
||||
Key->ScanCode = SCAN_HOME;
|
||||
break;
|
||||
|
||||
case KEY_END:
|
||||
Key->ScanCode = SCAN_END;
|
||||
break;
|
||||
|
||||
case KEY_DELETE:
|
||||
Key->ScanCode = SCAN_DELETE;
|
||||
break;
|
||||
|
||||
case KEY_INSERT:
|
||||
Key->ScanCode = SCAN_INSERT;
|
||||
break;
|
||||
|
||||
case KEY_UP:
|
||||
Key->ScanCode = SCAN_UP;
|
||||
break;
|
||||
|
||||
case KEY_LEFT:
|
||||
Key->ScanCode = SCAN_LEFT;
|
||||
break;
|
||||
|
||||
case KEY_RIGHT:
|
||||
Key->ScanCode = SCAN_RIGHT;
|
||||
break;
|
||||
|
||||
case KEY_DOWN:
|
||||
Key->ScanCode = SCAN_DOWN;
|
||||
break;
|
||||
|
||||
case KEY_BACKSPACE:
|
||||
Key->UnicodeChar = CHAR_BACKSPACE;
|
||||
break;
|
||||
|
||||
case KEY_TAB:
|
||||
Key->UnicodeChar = CHAR_TAB;
|
||||
break;
|
||||
|
||||
case KEY_ENTER:
|
||||
Key->UnicodeChar = CHAR_CARRIAGE_RETURN;
|
||||
break;
|
||||
|
||||
case KEY_ESC:
|
||||
Key->ScanCode = SCAN_ESC;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (Dev->KeyActive[KEY_LEFTSHIFT] || Dev->KeyActive[KEY_RIGHTSHIFT]) {
|
||||
Key->ScanCode = MapShift[Code];
|
||||
Key->UnicodeChar = MapShift[Code];
|
||||
} else {
|
||||
Key->ScanCode = Map[Code];
|
||||
Key->UnicodeChar = Map[Code];
|
||||
}
|
||||
|
||||
if (Dev->KeyActive[KEY_LEFTCTRL] || Dev->KeyActive[KEY_RIGHTCTRL]) {
|
||||
// Convert Ctrl+[a-z] and Ctrl+[A-Z] into [1-26] ASCII table entries
|
||||
Key->UnicodeChar &= 0x1F;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function handling VirtIO keyboard events
|
||||
VOID
|
||||
VirtioKeyboardHandleEvent (
|
||||
IN OUT VIRTIO_INPUT_DEV *Dev,
|
||||
IN VIRTIO_INPUT_EVENT *Event
|
||||
)
|
||||
{
|
||||
if (Event->Value == KEY_PRESSED) {
|
||||
// Key pressed event received
|
||||
Dev->KeyActive[(UINT8)Event->Code] = TRUE;
|
||||
|
||||
// Evaluate key
|
||||
VirtioKeyboardConvertKeyCode (Dev, Event->Code, &Dev->LastKey);
|
||||
|
||||
// Flag that printable character is ready to be send
|
||||
Dev->KeyReady = TRUE;
|
||||
} else {
|
||||
// Key released event received
|
||||
Dev->KeyActive[(UINT8)Event->Code] = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioKeyboardReset (
|
||||
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
|
||||
IN BOOLEAN ExtendedVerification
|
||||
)
|
||||
{
|
||||
VIRTIO_INPUT_DEV *Dev;
|
||||
EFI_TPL OldTpl;
|
||||
|
||||
Dev = VIRTIO_INPUT_FROM_THIS (This);
|
||||
|
||||
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
||||
|
||||
Dev->KeyReady = FALSE;
|
||||
Dev->LastKey.ScanCode = SCAN_NULL;
|
||||
Dev->LastKey.UnicodeChar = CHAR_NULL;
|
||||
ZeroMem (Dev->KeyActive, sizeof (Dev->KeyActive));
|
||||
|
||||
gBS->RestoreTPL (OldTpl);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioKeyboardReadKeyStroke (
|
||||
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
|
||||
OUT EFI_INPUT_KEY *Key
|
||||
)
|
||||
{
|
||||
VIRTIO_INPUT_DEV *Dev;
|
||||
EFI_TPL OldTpl;
|
||||
|
||||
if (Key == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Dev = VIRTIO_INPUT_FROM_THIS (This);
|
||||
|
||||
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
||||
if (Dev->KeyReady) {
|
||||
// Get last key from the buffer
|
||||
*Key = Dev->LastKey;
|
||||
|
||||
// Mark key as consumed
|
||||
Dev->KeyReady = FALSE;
|
||||
|
||||
gBS->RestoreTPL (OldTpl);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
gBS->RestoreTPL (OldTpl);
|
||||
|
||||
return EFI_NOT_READY;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_PROTOCOL API
|
||||
STATIC
|
||||
VOID
|
||||
EFIAPI
|
||||
VirtioKeyboardWaitForKey (
|
||||
IN EFI_EVENT Event,
|
||||
IN VOID *Context
|
||||
)
|
||||
{
|
||||
VIRTIO_INPUT_DEV *Dev = (VIRTIO_INPUT_DEV *)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
|
||||
VirtioInputTimer (NULL, Dev);
|
||||
|
||||
// If there is a new key ready - send signal
|
||||
if (Dev->KeyReady) {
|
||||
gBS->SignalEvent (Event);
|
||||
}
|
||||
}
|
||||
|
||||
/// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioKeyboardResetEx (
|
||||
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
|
||||
IN BOOLEAN ExtendedVerification
|
||||
)
|
||||
{
|
||||
VIRTIO_INPUT_DEV *Dev;
|
||||
EFI_STATUS Status;
|
||||
|
||||
Dev = VIRTIO_INPUT_EX_FROM_THIS (This);
|
||||
|
||||
// Call the reset function from SIMPLE_TEXT_INPUT protocol
|
||||
Status = Dev->Txt.Reset (
|
||||
&Dev->Txt,
|
||||
ExtendedVerification
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioKeyboardReadKeyStrokeEx (
|
||||
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
|
||||
OUT EFI_KEY_DATA *KeyData
|
||||
)
|
||||
{
|
||||
VIRTIO_INPUT_DEV *Dev;
|
||||
EFI_STATUS Status;
|
||||
EFI_INPUT_KEY Key;
|
||||
EFI_KEY_STATE KeyState;
|
||||
|
||||
if (KeyData == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Dev = VIRTIO_INPUT_EX_FROM_THIS (This);
|
||||
|
||||
// Get the last pressed key
|
||||
Status = Dev->Txt.ReadKeyStroke (&Dev->Txt, &Key);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// Add key state informations
|
||||
KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID;
|
||||
KeyState.KeyToggleState = EFI_TOGGLE_STATE_VALID;
|
||||
|
||||
// Shift key modifier
|
||||
if (Dev->KeyActive[KEY_LEFTSHIFT]) {
|
||||
KeyState.KeyShiftState |= EFI_LEFT_SHIFT_PRESSED;
|
||||
}
|
||||
|
||||
if (Dev->KeyActive[KEY_RIGHTSHIFT]) {
|
||||
KeyState.KeyShiftState |= EFI_RIGHT_SHIFT_PRESSED;
|
||||
}
|
||||
|
||||
// Ctrl key modifier
|
||||
if (Dev->KeyActive[KEY_LEFTCTRL]) {
|
||||
KeyState.KeyShiftState |= EFI_LEFT_CONTROL_PRESSED;
|
||||
}
|
||||
|
||||
if (Dev->KeyActive[KEY_RIGHTCTRL]) {
|
||||
KeyState.KeyShiftState |= EFI_RIGHT_CONTROL_PRESSED;
|
||||
}
|
||||
|
||||
// ALt key modifier
|
||||
if (Dev->KeyActive[KEY_LEFTALT]) {
|
||||
KeyState.KeyShiftState |= EFI_LEFT_ALT_PRESSED;
|
||||
}
|
||||
|
||||
if (Dev->KeyActive[KEY_RIGHTALT]) {
|
||||
KeyState.KeyShiftState |= EFI_RIGHT_ALT_PRESSED;
|
||||
}
|
||||
|
||||
// Return value only when there is no failure
|
||||
KeyData->Key = Key;
|
||||
KeyData->KeyState = KeyState;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioKeyboardSetStateEx (
|
||||
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
|
||||
IN EFI_KEY_TOGGLE_STATE *KeyToggleState
|
||||
)
|
||||
{
|
||||
if (KeyToggleState == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
STATIC
|
||||
BOOLEAN
|
||||
IsKeyRegistered (
|
||||
IN EFI_KEY_DATA *RegisteredData,
|
||||
IN EFI_KEY_DATA *InputData
|
||||
)
|
||||
|
||||
{
|
||||
ASSERT (RegisteredData != NULL && InputData != NULL);
|
||||
|
||||
if ((RegisteredData->Key.ScanCode != InputData->Key.ScanCode) ||
|
||||
(RegisteredData->Key.UnicodeChar != InputData->Key.UnicodeChar))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// Assume KeyShiftState/KeyToggleState = 0 in Registered key data means
|
||||
// these state could be ignored.
|
||||
//
|
||||
if ((RegisteredData->KeyState.KeyShiftState != 0) &&
|
||||
(RegisteredData->KeyState.KeyShiftState != InputData->KeyState.KeyShiftState))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((RegisteredData->KeyState.KeyToggleState != 0) &&
|
||||
(RegisteredData->KeyState.KeyToggleState != InputData->KeyState.KeyToggleState))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioKeyboardRegisterKeyNotifyEx (
|
||||
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;
|
||||
VIRTIO_INPUT_DEV *Dev;
|
||||
EFI_TPL OldTpl;
|
||||
LIST_ENTRY *Link;
|
||||
VIRTIO_INPUT_IN_EX_NOTIFY *NewNotify;
|
||||
VIRTIO_INPUT_IN_EX_NOTIFY *CurrentNotify;
|
||||
|
||||
if ((KeyData == NULL) ||
|
||||
(NotifyHandle == NULL) ||
|
||||
(KeyNotificationFunction == NULL))
|
||||
{
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Dev = VIRTIO_INPUT_EX_FROM_THIS (This);
|
||||
|
||||
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
||||
|
||||
// Check if the (KeyData, NotificationFunction) pair is already registered.
|
||||
for (Link = Dev->KeyNotifyList.ForwardLink;
|
||||
Link != &Dev->KeyNotifyList;
|
||||
Link = Link->ForwardLink)
|
||||
{
|
||||
CurrentNotify = CR (
|
||||
Link,
|
||||
VIRTIO_INPUT_IN_EX_NOTIFY,
|
||||
NotifyEntry,
|
||||
VIRTIO_INPUT_SIG
|
||||
);
|
||||
if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) {
|
||||
if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) {
|
||||
*NotifyHandle = CurrentNotify;
|
||||
Status = EFI_SUCCESS;
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NewNotify = (VIRTIO_INPUT_IN_EX_NOTIFY *)AllocateZeroPool (sizeof (VIRTIO_INPUT_IN_EX_NOTIFY));
|
||||
if (NewNotify == NULL) {
|
||||
Status = EFI_OUT_OF_RESOURCES;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
NewNotify->Signature = VIRTIO_INPUT_SIG;
|
||||
NewNotify->KeyNotificationFn = KeyNotificationFunction;
|
||||
CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA));
|
||||
InsertTailList (&Dev->KeyNotifyList, &NewNotify->NotifyEntry);
|
||||
|
||||
*NotifyHandle = NewNotify;
|
||||
Status = EFI_SUCCESS;
|
||||
|
||||
Exit:
|
||||
gBS->RestoreTPL (OldTpl);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
VirtioKeyboardUnregisterKeyNotifyEx (
|
||||
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
|
||||
IN VOID *NotificationHandle
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
VIRTIO_INPUT_DEV *Dev;
|
||||
EFI_TPL OldTpl;
|
||||
LIST_ENTRY *Link;
|
||||
VIRTIO_INPUT_IN_EX_NOTIFY *CurrentNotify;
|
||||
|
||||
if (NotificationHandle == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (((VIRTIO_INPUT_IN_EX_NOTIFY *)NotificationHandle)->Signature != VIRTIO_INPUT_SIG) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Dev = VIRTIO_INPUT_EX_FROM_THIS (This);
|
||||
|
||||
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
||||
|
||||
for (Link = Dev->KeyNotifyList.ForwardLink;
|
||||
Link != &Dev->KeyNotifyList;
|
||||
Link = Link->ForwardLink)
|
||||
{
|
||||
CurrentNotify = CR (
|
||||
Link,
|
||||
VIRTIO_INPUT_IN_EX_NOTIFY,
|
||||
NotifyEntry,
|
||||
VIRTIO_INPUT_SIG
|
||||
);
|
||||
if (CurrentNotify == NotificationHandle) {
|
||||
RemoveEntryList (&CurrentNotify->NotifyEntry);
|
||||
|
||||
Status = EFI_SUCCESS;
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Notification has not been found
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
|
||||
Exit:
|
||||
gBS->RestoreTPL (OldTpl);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
VirtioKeyboardInit (
|
||||
IN OUT VIRTIO_INPUT_DEV *Dev
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
|
||||
InitializeListHead (&Dev->KeyNotifyList);
|
||||
|
||||
Dev->Txt.Reset = VirtioKeyboardReset;
|
||||
Dev->Txt.ReadKeyStroke = VirtioKeyboardReadKeyStroke;
|
||||
|
||||
Dev->TxtEx.Reset = VirtioKeyboardResetEx;
|
||||
Dev->TxtEx.ReadKeyStrokeEx = VirtioKeyboardReadKeyStrokeEx;
|
||||
Dev->TxtEx.SetState = VirtioKeyboardSetStateEx;
|
||||
Dev->TxtEx.RegisterKeyNotify = VirtioKeyboardRegisterKeyNotifyEx;
|
||||
Dev->TxtEx.UnregisterKeyNotify = VirtioKeyboardUnregisterKeyNotifyEx;
|
||||
|
||||
//
|
||||
// Setup the WaitForKey event
|
||||
//
|
||||
Status = gBS->CreateEvent (
|
||||
EVT_NOTIFY_WAIT,
|
||||
TPL_NOTIFY,
|
||||
VirtioKeyboardWaitForKey,
|
||||
Dev,
|
||||
&(Dev->Txt.WaitForKey)
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
//
|
||||
// Setup the WaitForKeyEx event
|
||||
//
|
||||
Status = gBS->CreateEvent (
|
||||
EVT_NOTIFY_WAIT,
|
||||
TPL_NOTIFY,
|
||||
VirtioKeyboardWaitForKey,
|
||||
Dev,
|
||||
&(Dev->TxtEx.WaitForKeyEx)
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
VOID
|
||||
VirtioKeyboardUninit (
|
||||
IN OUT VIRTIO_INPUT_DEV *Dev
|
||||
)
|
||||
{
|
||||
gBS->CloseEvent (Dev->Txt.WaitForKey);
|
||||
gBS->CloseEvent (Dev->TxtEx.WaitForKeyEx);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue