/** @file | |
This driver produces EFI_SIMPLE_TEXT_INPUT_PROTOCOL for virtarm devices. | |
Copyright (C) 2024, Red Hat, 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 <VirtioKeyboard.h> | |
#include <VirtioKeyCodes.h> | |
// ----------------------------------------------------------------------------- | |
// Return buffer pointer out of the ring buffer | |
STATIC | |
VOID * | |
BufferPtr ( | |
IN VIRTIO_KBD_RING *Ring, | |
IN UINT32 BufferNr | |
) | |
{ | |
return Ring->Buffers + Ring->BufferSize * BufferNr; | |
} | |
// ----------------------------------------------------------------------------- | |
// Return buffer physical address out of the ring buffer | |
STATIC | |
EFI_PHYSICAL_ADDRESS | |
BufferAddr ( | |
IN VIRTIO_KBD_RING *Ring, | |
IN UINT32 BufferNr | |
) | |
{ | |
return Ring->DeviceAddress + Ring->BufferSize * BufferNr; | |
} | |
// Return next buffer from ring | |
STATIC | |
UINT32 | |
BufferNext ( | |
IN VIRTIO_KBD_RING *Ring | |
) | |
{ | |
return Ring->Indices.NextDescIdx % Ring->Ring.QueueSize; | |
} | |
// ----------------------------------------------------------------------------- | |
// Push the buffer to the device | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardRingSendBuffer ( | |
IN OUT VIRTIO_KBD_DEV *Dev, | |
IN UINT16 Index, | |
IN VOID *Data, | |
IN UINT32 DataSize, | |
IN BOOLEAN Notify | |
) | |
{ | |
VIRTIO_KBD_RING *Ring = Dev->Rings + Index; | |
UINT32 BufferNr = BufferNext (Ring); | |
UINT16 Idx = *Ring->Ring.Avail.Idx; | |
UINT16 Flags = 0; | |
ASSERT (DataSize <= Ring->BufferSize); | |
if (Data) { | |
/* driver -> device */ | |
CopyMem (BufferPtr (Ring, BufferNr), Data, DataSize); | |
} else { | |
/* device -> driver */ | |
Flags |= VRING_DESC_F_WRITE; | |
} | |
VirtioAppendDesc ( | |
&Ring->Ring, | |
BufferAddr (Ring, BufferNr), | |
DataSize, | |
Flags, | |
&Ring->Indices | |
); | |
Ring->Ring.Avail.Ring[Idx % Ring->Ring.QueueSize] = | |
Ring->Indices.HeadDescIdx % Ring->Ring.QueueSize; | |
Ring->Indices.HeadDescIdx = Ring->Indices.NextDescIdx; | |
Idx++; | |
// Force compiler to not optimize this code | |
MemoryFence (); | |
*Ring->Ring.Avail.Idx = Idx; | |
MemoryFence (); | |
if (Notify) { | |
Dev->VirtIo->SetQueueNotify (Dev->VirtIo, Index); | |
} | |
return EFI_SUCCESS; | |
} | |
// ----------------------------------------------------------------------------- | |
// Look for buffer ready to be processed | |
BOOLEAN | |
EFIAPI | |
VirtioKeyboardRingHasBuffer ( | |
IN OUT VIRTIO_KBD_DEV *Dev, | |
IN UINT16 Index | |
) | |
{ | |
VIRTIO_KBD_RING *Ring = Dev->Rings + Index; | |
UINT16 UsedIdx = *Ring->Ring.Used.Idx; | |
if (!Ring->Ready) { | |
return FALSE; | |
} | |
if (Ring->LastUsedIdx == UsedIdx) { | |
return FALSE; | |
} | |
return TRUE; | |
} | |
// ----------------------------------------------------------------------------- | |
// Get data from buffer which is marked as ready from device | |
BOOLEAN | |
EFIAPI | |
VirtioKeyboardRingGetBuffer ( | |
IN OUT VIRTIO_KBD_DEV *Dev, | |
IN UINT16 Index, | |
OUT VOID *Data, | |
OUT UINT32 *DataSize | |
) | |
{ | |
VIRTIO_KBD_RING *Ring = Dev->Rings + Index; | |
UINT16 UsedIdx = *Ring->Ring.Used.Idx; | |
volatile VRING_USED_ELEM *UsedElem; | |
if (!Ring->Ready) { | |
return FALSE; | |
} | |
if (Ring->LastUsedIdx == UsedIdx) { | |
return FALSE; | |
} | |
UsedElem = Ring->Ring.Used.UsedElem + (Ring->LastUsedIdx % Ring->Ring.QueueSize); | |
if (UsedElem->Len > Ring->BufferSize) { | |
DEBUG ((DEBUG_ERROR, "%a:%d: %d: invalid length\n", __func__, __LINE__, Index)); | |
UsedElem->Len = 0; | |
} | |
if (Data && DataSize) { | |
CopyMem (Data, BufferPtr (Ring, UsedElem->Id), UsedElem->Len); | |
*DataSize = UsedElem->Len; | |
} | |
if (Index % 2 == 0) { | |
/* RX - re-queue buffer */ | |
VirtioKeyboardRingSendBuffer (Dev, Index, NULL, Ring->BufferSize, FALSE); | |
} | |
Ring->LastUsedIdx++; | |
return TRUE; | |
} | |
// ----------------------------------------------------------------------------- | |
// Initialize ring buffer | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardInitRing ( | |
IN OUT VIRTIO_KBD_DEV *Dev, | |
IN UINT16 Index, | |
IN UINT32 BufferSize | |
) | |
{ | |
VIRTIO_KBD_RING *Ring = Dev->Rings + Index; | |
EFI_STATUS Status; | |
UINT16 QueueSize; | |
UINT64 RingBaseShift; | |
// | |
// step 4b -- allocate request virtqueue | |
// | |
Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, Index); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// VirtioKeyboard uses one descriptor | |
// | |
if (QueueSize < 1) { | |
Status = EFI_UNSUPPORTED; | |
goto Failed; | |
} | |
Status = VirtioRingInit (Dev->VirtIo, QueueSize, &Ring->Ring); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// If anything fails from here on, we must release the ring resources. | |
// | |
Status = VirtioRingMap ( | |
Dev->VirtIo, | |
&Ring->Ring, | |
&RingBaseShift, | |
&Ring->RingMap | |
); | |
if (EFI_ERROR (Status)) { | |
goto ReleaseQueue; | |
} | |
// | |
// Additional steps for MMIO: align the queue appropriately, and set the | |
// size. If anything fails from here on, we must unmap the ring resources. | |
// | |
Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize); | |
if (EFI_ERROR (Status)) { | |
goto UnmapQueue; | |
} | |
Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE); | |
if (EFI_ERROR (Status)) { | |
goto UnmapQueue; | |
} | |
// | |
// step 4c -- Report GPFN (guest-physical frame number) of queue. | |
// | |
Status = Dev->VirtIo->SetQueueAddress ( | |
Dev->VirtIo, | |
&Ring->Ring, | |
RingBaseShift | |
); | |
if (EFI_ERROR (Status)) { | |
goto UnmapQueue; | |
} | |
Ring->BufferCount = QueueSize; | |
Ring->BufferSize = BufferSize; | |
Ring->BufferPages = EFI_SIZE_TO_PAGES (Ring->BufferCount * Ring->BufferSize); | |
Status = Dev->VirtIo->AllocateSharedPages (Dev->VirtIo, Ring->BufferPages, (VOID **)&Ring->Buffers); | |
if (EFI_ERROR (Status)) { | |
goto UnmapQueue; | |
} | |
Status = VirtioMapAllBytesInSharedBuffer ( | |
Dev->VirtIo, | |
VirtioOperationBusMasterCommonBuffer, | |
Ring->Buffers, | |
EFI_PAGES_TO_SIZE (Ring->BufferPages), | |
&Ring->DeviceAddress, | |
&Ring->BufferMap | |
); | |
if (EFI_ERROR (Status)) { | |
goto ReleasePages; | |
} | |
VirtioPrepare (&Ring->Ring, &Ring->Indices); | |
Ring->Ready = TRUE; | |
return EFI_SUCCESS; | |
ReleasePages: | |
Dev->VirtIo->FreeSharedPages ( | |
Dev->VirtIo, | |
Ring->BufferPages, | |
Ring->Buffers | |
); | |
Ring->Buffers = NULL; | |
UnmapQueue: | |
Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Ring->RingMap); | |
Ring->RingMap = NULL; | |
ReleaseQueue: | |
VirtioRingUninit (Dev->VirtIo, &Ring->Ring); | |
Failed: | |
return Status; | |
} | |
// ----------------------------------------------------------------------------- | |
// Deinitialize ring buffer | |
VOID | |
EFIAPI | |
VirtioKeyboardUninitRing ( | |
IN OUT VIRTIO_KBD_DEV *Dev, | |
IN UINT16 Index | |
) | |
{ | |
VIRTIO_KBD_RING *Ring = Dev->Rings + Index; | |
if (Ring->BufferMap) { | |
Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Ring->BufferMap); | |
Ring->BufferMap = NULL; | |
} | |
if (Ring->Buffers) { | |
Dev->VirtIo->FreeSharedPages ( | |
Dev->VirtIo, | |
Ring->BufferPages, | |
Ring->Buffers | |
); | |
Ring->Buffers = NULL; | |
} | |
if (!Ring->RingMap) { | |
Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Ring->RingMap); | |
Ring->RingMap = NULL; | |
} | |
if (Ring->Ring.Base) { | |
VirtioRingUninit (Dev->VirtIo, &Ring->Ring); | |
} | |
ZeroMem (Ring, sizeof (*Ring)); | |
} | |
// ----------------------------------------------------------------------------- | |
// Deinitialize all rings allocated in driver | |
STATIC | |
VOID | |
EFIAPI | |
VirtioKeyboardUninitAllRings ( | |
IN OUT VIRTIO_KBD_DEV *Dev | |
) | |
{ | |
UINT16 Index; | |
for (Index = 0; Index < KEYBOARD_MAX_RINGS; Index++) { | |
VirtioKeyboardUninitRing (Dev, Index); | |
} | |
} | |
// ----------------------------------------------------------------------------- | |
// Mark all buffers as ready to write and push to device | |
VOID | |
EFIAPI | |
VirtioKeyboardRingFillRx ( | |
IN OUT VIRTIO_KBD_DEV *Dev, | |
IN UINT16 Index | |
) | |
{ | |
VIRTIO_KBD_RING *Ring = Dev->Rings + Index; | |
UINT32 BufferNr; | |
for (BufferNr = 0; BufferNr < Ring->BufferCount; BufferNr++) { | |
VirtioKeyboardRingSendBuffer (Dev, Index, NULL, Ring->BufferSize, FALSE); | |
} | |
Dev->VirtIo->SetQueueNotify (Dev->VirtIo, Index); | |
} | |
// Forward declaration of module Uninit function | |
STATIC | |
VOID | |
EFIAPI | |
VirtioKeyboardUninit ( | |
IN OUT VIRTIO_KBD_DEV *Dev | |
); | |
// Forward declaration of module Init function | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardInit ( | |
IN OUT VIRTIO_KBD_DEV *Dev | |
); | |
// ----------------------------------------------------------------------------- | |
// EFI_SIMPLE_TEXT_INPUT_PROTOCOL API | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardSimpleTextInputReset ( | |
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
IN BOOLEAN ExtendedVerification | |
) | |
{ | |
VIRTIO_KBD_DEV *Dev; | |
Dev = VIRTIO_KEYBOARD_FROM_THIS (This); | |
VirtioKeyboardUninit (Dev); | |
VirtioKeyboardInit (Dev); | |
return EFI_SUCCESS; | |
} | |
// ----------------------------------------------------------------------------- | |
// EFI_SIMPLE_TEXT_INPUT_PROTOCOL API | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardSimpleTextInputReadKeyStroke ( | |
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, | |
OUT EFI_INPUT_KEY *Key | |
) | |
{ | |
VIRTIO_KBD_DEV *Dev; | |
EFI_TPL OldTpl; | |
if (Key == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Dev = VIRTIO_KEYBOARD_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 | |
VirtioKeyboardConvertKeyCode ( | |
IN OUT VIRTIO_KBD_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 keyboard events | |
STATIC | |
VOID | |
EFIAPI | |
VirtioKeyboardGetDeviceData ( | |
IN OUT VIRTIO_KBD_DEV *Dev | |
) | |
{ | |
BOOLEAN HasData; | |
UINT8 Data[KEYBOARD_RX_BUFSIZE + 1]; | |
UINT32 DataSize; | |
VIRTIO_KBD_EVENT Event; | |
EFI_TPL OldTpl; | |
for ( ; ; ) { | |
HasData = VirtioKeyboardRingGetBuffer (Dev, 0, Data, &DataSize); | |
// Exit if no new data | |
if (!HasData) { | |
return; | |
} | |
if (DataSize < sizeof (Event)) { | |
continue; | |
} | |
// Clearing last character is not needed as it will be overwritten anyway | |
// Dev->LastKey.ScanCode = SCAN_NULL; | |
// Dev->LastKey.UnicodeChar = CHAR_NULL; | |
CopyMem (&Event, Data, sizeof (Event)); | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
switch (Event.Type) { | |
case EV_SYN: | |
// Sync event received | |
break; | |
case EV_KEY: | |
// Key press event received | |
// 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 | |
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; | |
} | |
break; | |
default: | |
DEBUG ((DEBUG_INFO, "%a: Unhandled VirtIo event\n", __func__)); | |
break; | |
} | |
gBS->RestoreTPL (OldTpl); | |
} | |
} | |
// ----------------------------------------------------------------------------- | |
// Callback hook for timer interrupt | |
STATIC | |
VOID | |
EFIAPI | |
VirtioKeyboardTimer ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
VIRTIO_KBD_DEV *Dev = Context; | |
VirtioKeyboardGetDeviceData (Dev); | |
} | |
// ----------------------------------------------------------------------------- | |
// EFI_SIMPLE_TEXT_INPUT_PROTOCOL API | |
VOID | |
EFIAPI | |
VirtioKeyboardWaitForKey ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
VIRTIO_KBD_DEV *Dev = VIRTIO_KEYBOARD_FROM_THIS (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 | |
VirtioKeyboardTimer (NULL, Dev); | |
// If there is a new key ready - send signal | |
if (Dev->KeyReady) { | |
gBS->SignalEvent (Event); | |
} | |
} | |
/// ----------------------------------------------------------------------------- | |
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardResetEx ( | |
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
IN BOOLEAN ExtendedVerification | |
) | |
{ | |
VIRTIO_KBD_DEV *Dev; | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
Dev = VIRTIO_KEYBOARD_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; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
gBS->RestoreTPL (OldTpl); | |
return EFI_SUCCESS; | |
} | |
// ----------------------------------------------------------------------------- | |
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardReadKeyStrokeEx ( | |
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
OUT EFI_KEY_DATA *KeyData | |
) | |
{ | |
VIRTIO_KBD_DEV *Dev; | |
EFI_STATUS Status; | |
EFI_INPUT_KEY Key; | |
EFI_KEY_STATE KeyState; | |
if (KeyData == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Dev = VIRTIO_KEYBOARD_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 | |
VOID | |
EFIAPI | |
VirtioKeyboardWaitForKeyEx ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
VIRTIO_KBD_DEV *Dev; | |
Dev = VIRTIO_KEYBOARD_EX_FROM_THIS (Context); | |
VirtioKeyboardWaitForKey (Event, &Dev->Txt); | |
} | |
// ----------------------------------------------------------------------------- | |
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardSetState ( | |
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
IN EFI_KEY_TOGGLE_STATE *KeyToggleState | |
) | |
{ | |
if (KeyToggleState == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
return EFI_SUCCESS; | |
} | |
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; | |
} | |
// ----------------------------------------------------------------------------- | |
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardRegisterKeyNotify ( | |
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_KBD_DEV *Dev; | |
EFI_TPL OldTpl; | |
LIST_ENTRY *Link; | |
VIRTIO_KBD_IN_EX_NOTIFY *NewNotify; | |
VIRTIO_KBD_IN_EX_NOTIFY *CurrentNotify; | |
if ((KeyData == NULL) || | |
(NotifyHandle == NULL) || | |
(KeyNotificationFunction == NULL)) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
Dev = VIRTIO_KEYBOARD_EX_FROM_THIS (This); | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
// Check if the (KeyData, NotificationFunction) pair is already registered. | |
for (Link = Dev->NotifyList.ForwardLink; | |
Link != &Dev->NotifyList; | |
Link = Link->ForwardLink) | |
{ | |
CurrentNotify = CR ( | |
Link, | |
VIRTIO_KBD_IN_EX_NOTIFY, | |
NotifyEntry, | |
VIRTIO_KBD_SIG | |
); | |
if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) { | |
if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) { | |
*NotifyHandle = CurrentNotify; | |
Status = EFI_SUCCESS; | |
goto Exit; | |
} | |
} | |
} | |
NewNotify = (VIRTIO_KBD_IN_EX_NOTIFY *)AllocateZeroPool (sizeof (VIRTIO_KBD_IN_EX_NOTIFY)); | |
if (NewNotify == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Exit; | |
} | |
NewNotify->Signature = VIRTIO_KBD_SIG; | |
NewNotify->KeyNotificationFn = KeyNotificationFunction; | |
CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA)); | |
InsertTailList (&Dev->NotifyList, &NewNotify->NotifyEntry); | |
*NotifyHandle = NewNotify; | |
Status = EFI_SUCCESS; | |
Exit: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
// ----------------------------------------------------------------------------- | |
// EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL API | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardUnregisterKeyNotify ( | |
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, | |
IN VOID *NotificationHandle | |
) | |
{ | |
EFI_STATUS Status; | |
VIRTIO_KBD_DEV *Dev; | |
EFI_TPL OldTpl; | |
LIST_ENTRY *Link; | |
VIRTIO_KBD_IN_EX_NOTIFY *CurrentNotify; | |
if (NotificationHandle == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (((VIRTIO_KBD_IN_EX_NOTIFY *)NotificationHandle)->Signature != VIRTIO_KBD_SIG) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Dev = VIRTIO_KEYBOARD_EX_FROM_THIS (This); | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
for (Link = Dev->NotifyList.ForwardLink; | |
Link != &Dev->NotifyList; | |
Link = Link->ForwardLink) | |
{ | |
CurrentNotify = CR ( | |
Link, | |
VIRTIO_KBD_IN_EX_NOTIFY, | |
NotifyEntry, | |
VIRTIO_KBD_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 | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardInit ( | |
IN OUT VIRTIO_KBD_DEV *Dev | |
) | |
{ | |
UINT8 NextDevStat; | |
EFI_STATUS Status; | |
UINT64 Features; | |
// | |
// Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence. | |
// | |
NextDevStat = 0; // step 1 -- reset device | |
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence | |
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it | |
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// Set Page Size - MMIO VirtIo Specific | |
// | |
Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// step 4a -- retrieve and validate features | |
// | |
Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
Features &= VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM; | |
// | |
// In virtio-1.0, feature negotiation is expected to complete before queue | |
// discovery, and the device can also reject the selected set of features. | |
// | |
if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) { | |
Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
} | |
Status = VirtioKeyboardInitRing (Dev, 0, KEYBOARD_RX_BUFSIZE); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// step 5 -- Report understood features and guest-tuneables. | |
// | |
if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) { | |
Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM); | |
Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
} | |
// | |
// step 6 -- initialization complete | |
// | |
NextDevStat |= VSTAT_DRIVER_OK; | |
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// populate the exported interface's attributes | |
// | |
// struct _EFI_SIMPLE_TEXT_INPUT_PROTOCOL { | |
// EFI_INPUT_RESET Reset; | |
// EFI_INPUT_READ_KEY ReadKeyStroke; | |
// EFI_EVENT WaitForKey; | |
// }; | |
Dev->Txt.Reset = (EFI_INPUT_RESET)VirtioKeyboardSimpleTextInputReset; | |
Dev->Txt.ReadKeyStroke = VirtioKeyboardSimpleTextInputReadKeyStroke; | |
Dev->Txt.WaitForKey = (EFI_EVENT)VirtioKeyboardWaitForKey; | |
// 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; | |
// } | |
Dev->TxtEx.Reset = (EFI_INPUT_RESET_EX)VirtioKeyboardResetEx; | |
Dev->TxtEx.ReadKeyStrokeEx = VirtioKeyboardReadKeyStrokeEx; | |
Dev->TxtEx.SetState = VirtioKeyboardSetState; | |
Dev->TxtEx.RegisterKeyNotify = VirtioKeyboardRegisterKeyNotify; | |
Dev->TxtEx.UnregisterKeyNotify = VirtioKeyboardUnregisterKeyNotify; | |
InitializeListHead (&Dev->NotifyList); | |
// | |
// Setup the WaitForKey event | |
// | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_WAIT, | |
TPL_NOTIFY, | |
VirtioKeyboardWaitForKey, | |
&(Dev->Txt), | |
&((Dev->Txt).WaitForKey) | |
); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// Setup the WaitForKeyEx event | |
// | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_WAIT, | |
TPL_NOTIFY, | |
VirtioKeyboardWaitForKeyEx, | |
&(Dev->TxtEx), | |
&((Dev->TxtEx).WaitForKeyEx) | |
); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
VirtioKeyboardRingFillRx (Dev, 0); | |
// | |
// Event for reading key in time intervals | |
// | |
Status = gBS->CreateEvent ( | |
EVT_TIMER | EVT_NOTIFY_SIGNAL, | |
TPL_NOTIFY, | |
VirtioKeyboardTimer, | |
Dev, | |
&Dev->KeyReadTimer | |
); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
Status = gBS->SetTimer ( | |
Dev->KeyReadTimer, | |
TimerPeriodic, | |
EFI_TIMER_PERIOD_MILLISECONDS (KEYBOARD_PROBE_TIME_MS) | |
); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
return EFI_SUCCESS; | |
Failed: | |
VirtioKeyboardUninitAllRings (Dev); | |
// VirtualKeyboardFreeNotifyList (&VirtualKeyboardPrivate->NotifyList); | |
// | |
// Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device | |
// Status. VirtIo access failure here should not mask the original error. | |
// | |
NextDevStat |= VSTAT_FAILED; | |
Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
return Status; // reached only via Failed above | |
} | |
// ----------------------------------------------------------------------------- | |
// Deinitialize driver | |
STATIC | |
VOID | |
EFIAPI | |
VirtioKeyboardUninit ( | |
IN OUT VIRTIO_KBD_DEV *Dev | |
) | |
{ | |
gBS->CloseEvent (Dev->KeyReadTimer); | |
// | |
// Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When | |
// VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from | |
// the old comms area. | |
// | |
Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0); | |
VirtioKeyboardUninitAllRings (Dev); | |
} | |
// ----------------------------------------------------------------------------- | |
// Handle device exit before switch to boot | |
STATIC | |
VOID | |
EFIAPI | |
VirtioKeyboardExitBoot ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
VIRTIO_KBD_DEV *Dev; | |
DEBUG ((DEBUG_INFO, "%a: Context=0x%p\n", __func__, Context)); | |
// | |
// Reset the device. This causes the hypervisor to forget about the virtio | |
// ring. | |
// | |
// We allocated said ring in EfiBootServicesData type memory, and code | |
// executing after ExitBootServices() is permitted to overwrite it. | |
// | |
Dev = Context; | |
Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0); | |
} | |
// ----------------------------------------------------------------------------- | |
// Binding validation function | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardBindingSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE DeviceHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_STATUS Status; | |
VIRTIO_DEVICE_PROTOCOL *VirtIo; | |
// | |
// Attempt to open the device with the VirtIo set of interfaces. On success, | |
// the protocol is "instantiated" for the VirtIo device. Covers duplicate | |
// open attempts (EFI_ALREADY_STARTED). | |
// | |
Status = gBS->OpenProtocol ( | |
DeviceHandle, // candidate device | |
&gVirtioDeviceProtocolGuid, // for generic VirtIo access | |
(VOID **)&VirtIo, // handle to instantiate | |
This->DriverBindingHandle, // requestor driver identity | |
DeviceHandle, // ControllerHandle, according to | |
// the UEFI Driver Model | |
EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to | |
// the device; to be released | |
); | |
if (EFI_ERROR (Status)) { | |
if (Status != EFI_UNSUPPORTED) { | |
DEBUG ((DEBUG_INFO, "%a:%d: %r\n", __func__, __LINE__, Status)); | |
} | |
return Status; | |
} | |
DEBUG ((DEBUG_INFO, "%a:%d: 0x%x\n", __func__, __LINE__, VirtIo->SubSystemDeviceId)); | |
if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_INPUT) { | |
Status = EFI_UNSUPPORTED; | |
} | |
// | |
// We needed VirtIo access only transitorily, to see whether we support the | |
// device or not. | |
// | |
gBS->CloseProtocol ( | |
DeviceHandle, | |
&gVirtioDeviceProtocolGuid, | |
This->DriverBindingHandle, | |
DeviceHandle | |
); | |
return Status; | |
} | |
// ----------------------------------------------------------------------------- | |
// Driver binding function API | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardBindingStart ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE DeviceHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
VIRTIO_KBD_DEV *Dev; | |
EFI_STATUS Status; | |
Dev = (VIRTIO_KBD_DEV *)AllocateZeroPool (sizeof *Dev); | |
if (Dev == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Status = gBS->OpenProtocol ( | |
DeviceHandle, | |
&gVirtioDeviceProtocolGuid, | |
(VOID **)&Dev->VirtIo, | |
This->DriverBindingHandle, | |
DeviceHandle, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
goto FreeVirtioKbd; | |
} | |
// | |
// VirtIo access granted, configure virtio keyboard device. | |
// | |
Status = VirtioKeyboardInit (Dev); | |
if (EFI_ERROR (Status)) { | |
goto CloseVirtIo; | |
} | |
Status = gBS->CreateEvent ( | |
EVT_SIGNAL_EXIT_BOOT_SERVICES, | |
TPL_CALLBACK, | |
&VirtioKeyboardExitBoot, | |
Dev, | |
&Dev->ExitBoot | |
); | |
if (EFI_ERROR (Status)) { | |
goto UninitDev; | |
} | |
// | |
// Setup complete, attempt to export the driver instance's EFI_SIMPLE_TEXT_INPUT_PROTOCOL | |
// interface. | |
// | |
Dev->Signature = VIRTIO_KBD_SIG; | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&DeviceHandle, | |
&gEfiSimpleTextInProtocolGuid, | |
&Dev->Txt, | |
&gEfiSimpleTextInputExProtocolGuid, | |
&Dev->TxtEx, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
goto CloseExitBoot; | |
} | |
return EFI_SUCCESS; | |
CloseExitBoot: | |
gBS->CloseEvent (Dev->ExitBoot); | |
UninitDev: | |
VirtioKeyboardUninit (Dev); | |
CloseVirtIo: | |
gBS->CloseProtocol ( | |
DeviceHandle, | |
&gVirtioDeviceProtocolGuid, | |
This->DriverBindingHandle, | |
DeviceHandle | |
); | |
FreeVirtioKbd: | |
FreePool (Dev); | |
return Status; | |
} | |
// ----------------------------------------------------------------------------- | |
// Driver unbinding function API | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardBindingStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE DeviceHandle, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Txt; | |
VIRTIO_KBD_DEV *Dev; | |
Status = gBS->OpenProtocol ( | |
DeviceHandle, // candidate device | |
&gEfiSimpleTextInProtocolGuid, // retrieve the RNG iface | |
(VOID **)&Txt, // target pointer | |
This->DriverBindingHandle, // requestor driver ident. | |
DeviceHandle, // lookup req. for dev. | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no new ref. | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Dev = VIRTIO_KEYBOARD_FROM_THIS (Txt); | |
// | |
// Handle Stop() requests for in-use driver instances gracefully. | |
// | |
Status = gBS->UninstallMultipleProtocolInterfaces ( | |
&DeviceHandle, | |
&gEfiSimpleTextInProtocolGuid, | |
&Dev->Txt, | |
&gEfiSimpleTextInputExProtocolGuid, | |
&Dev->TxtEx, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
gBS->CloseEvent (Dev->ExitBoot); | |
VirtioKeyboardUninit (Dev); | |
gBS->CloseProtocol ( | |
DeviceHandle, | |
&gVirtioDeviceProtocolGuid, | |
This->DriverBindingHandle, | |
DeviceHandle | |
); | |
FreePool (Dev); | |
return EFI_SUCCESS; | |
} | |
// ----------------------------------------------------------------------------- | |
// Forward declaration of global variable | |
STATIC | |
EFI_COMPONENT_NAME_PROTOCOL gComponentName; | |
// ----------------------------------------------------------------------------- | |
// Driver name to be displayed | |
STATIC | |
EFI_UNICODE_STRING_TABLE mDriverNameTable[] = { | |
{ "eng;en", L"Virtio Keyboard Driver" }, | |
{ NULL, NULL } | |
}; | |
// ----------------------------------------------------------------------------- | |
// Driver name lookup function | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardGetDriverName ( | |
IN EFI_COMPONENT_NAME_PROTOCOL *This, | |
IN CHAR8 *Language, | |
OUT CHAR16 **DriverName | |
) | |
{ | |
return LookupUnicodeString2 ( | |
Language, | |
This->SupportedLanguages, | |
mDriverNameTable, | |
DriverName, | |
(BOOLEAN)(This == &gComponentName) // Iso639Language | |
); | |
} | |
// ----------------------------------------------------------------------------- | |
// Device name to be displayed | |
STATIC | |
EFI_UNICODE_STRING_TABLE mDeviceNameTable[] = { | |
{ "eng;en", L"RHEL virtio virtual keyboard BOB (Basic Operation Board)" }, | |
{ NULL, NULL } | |
}; | |
// ----------------------------------------------------------------------------- | |
STATIC | |
EFI_COMPONENT_NAME_PROTOCOL gDeviceName; | |
// ----------------------------------------------------------------------------- | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardGetDeviceName ( | |
IN EFI_COMPONENT_NAME_PROTOCOL *This, | |
IN EFI_HANDLE DeviceHandle, | |
IN EFI_HANDLE ChildHandle, | |
IN CHAR8 *Language, | |
OUT CHAR16 **ControllerName | |
) | |
{ | |
return LookupUnicodeString2 ( | |
Language, | |
This->SupportedLanguages, | |
mDeviceNameTable, | |
ControllerName, | |
(BOOLEAN)(This == &gDeviceName) // Iso639Language | |
); | |
} | |
// ----------------------------------------------------------------------------- | |
// General driver UEFI interface for showing driver name | |
STATIC | |
EFI_COMPONENT_NAME_PROTOCOL gComponentName = { | |
&VirtioKeyboardGetDriverName, | |
&VirtioKeyboardGetDeviceName, | |
"eng" // SupportedLanguages, ISO 639-2 language codes | |
}; | |
// ----------------------------------------------------------------------------- | |
// General driver UEFI interface for showing driver name | |
STATIC | |
EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = { | |
(EFI_COMPONENT_NAME2_GET_DRIVER_NAME)&VirtioKeyboardGetDriverName, | |
(EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)&VirtioKeyboardGetDeviceName, | |
"en" // SupportedLanguages, RFC 4646 language codes | |
}; | |
// ----------------------------------------------------------------------------- | |
// General driver UEFI interface for loading / unloading driver | |
STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = { | |
&VirtioKeyboardBindingSupported, | |
&VirtioKeyboardBindingStart, | |
&VirtioKeyboardBindingStop, | |
0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers | |
NULL, // ImageHandle, to be overwritten by | |
// EfiLibInstallDriverBindingComponentName2() in VirtioKeyboardEntryPoint() | |
NULL // DriverBindingHandle, ditto | |
}; | |
// ----------------------------------------------------------------------------- | |
// Driver entry point set in INF file, registers all driver functions into UEFI | |
EFI_STATUS | |
EFIAPI | |
VirtioKeyboardEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
DEBUG ((DEBUG_INFO, "Virtio keyboard has been loaded.......................\n")); | |
return EfiLibInstallDriverBindingComponentName2 ( | |
ImageHandle, | |
SystemTable, | |
&gDriverBinding, | |
ImageHandle, | |
&gComponentName, | |
&gComponentName2 | |
); | |
} |