diff --git a/OvmfPkg/VirtioInputDxe/VirtioInput.c b/OvmfPkg/VirtioInputDxe/VirtioInput.c index 9ca8fc8720..dfa8e78091 100644 --- a/OvmfPkg/VirtioInputDxe/VirtioInput.c +++ b/OvmfPkg/VirtioInputDxe/VirtioInput.c @@ -415,8 +415,22 @@ VirtioInputGetDeviceData ( // Key press event received // DEBUG ((DEBUG_INFO, "%a: ---------------------- \nType: %x Code: %x Value: %x\n", // __func__, Event.Type, Event.Code, Event.Value)); + if (IS_BUTTON_CODE (Event.Code)) { + if (Dev->HasMouse) { + VirtioMouseHandleEvent (Dev, &Event); + } + } else if (Dev->HasKeyboard) { + VirtioKeyboardHandleEvent (Dev, &Event); + } + + break; + + case EV_REL: + // Relative pointer movement received + if (Dev->HasMouse) { + VirtioMouseHandleEvent (Dev, &Event); + } - VirtioKeyboardHandleEvent (Dev, &Event); break; default: @@ -534,7 +548,8 @@ VirtioInputInit ( // Check input device capabilities // Dev->HasKeyboard = VirtioKeyboardProbe (Dev); - if (!Dev->HasKeyboard) { + Dev->HasMouse = VirtioMouseProbe (Dev); + if (!Dev->HasKeyboard && !Dev->HasMouse) { Status = EFI_UNSUPPORTED; goto Failed; } @@ -549,6 +564,13 @@ VirtioInputInit ( } } + if (Dev->HasMouse) { + Status = VirtioMouseInit (Dev); + if (EFI_ERROR (Status)) { + goto Failed; + } + } + VirtioInputRingFillRx (Dev, 0); // @@ -604,6 +626,10 @@ VirtioInputUninit ( VirtioKeyboardUninit (Dev); } + if (Dev->HasMouse) { + VirtioMouseUninit (Dev); + } + // // 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 @@ -763,8 +789,32 @@ VirtioInputBindingStart ( } } + if (Dev->HasMouse) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &DeviceHandle, + &gEfiSimplePointerProtocolGuid, + &Dev->SimplePointer, + NULL + ); + if (EFI_ERROR (Status)) { + goto UninstallKeyboard; + } + } + return EFI_SUCCESS; +UninstallKeyboard: + if (Dev->HasKeyboard) { + gBS->UninstallMultipleProtocolInterfaces ( + DeviceHandle, + &gEfiSimpleTextInProtocolGuid, + &Dev->Txt, + &gEfiSimpleTextInputExProtocolGuid, + &Dev->TxtEx, + NULL + ); + } + CloseExitBoot: gBS->CloseEvent (Dev->ExitBoot); @@ -799,21 +849,34 @@ VirtioInputBindingStop ( { EFI_STATUS Status; EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Txt; + EFI_SIMPLE_POINTER_PROTOCOL *Pointer; VIRTIO_INPUT_DEV *Dev; Status = gBS->OpenProtocol ( DeviceHandle, // candidate device - &gEfiSimpleTextInProtocolGuid, // retrieve the RNG iface + &gEfiSimpleTextInProtocolGuid, // retrieve the keyboard 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; - } + if (!EFI_ERROR (Status)) { + Dev = VIRTIO_INPUT_FROM_THIS (Txt); + } else { + Status = gBS->OpenProtocol ( + DeviceHandle, + &gEfiSimplePointerProtocolGuid, + (VOID **)&Pointer, + This->DriverBindingHandle, + DeviceHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } - Dev = VIRTIO_INPUT_FROM_THIS (Txt); + Dev = VIRTIO_INPUT_FROM_POINTER_THIS (Pointer); + } // // Handle Stop() requests for in-use driver instances gracefully. @@ -832,6 +895,18 @@ VirtioInputBindingStop ( } } + if (Dev->HasMouse) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + DeviceHandle, + &gEfiSimplePointerProtocolGuid, + &Dev->SimplePointer, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + gBS->CloseEvent (Dev->ExitBoot); VirtioInputUninit (Dev); diff --git a/OvmfPkg/VirtioInputDxe/VirtioInput.h b/OvmfPkg/VirtioInputDxe/VirtioInput.h index fd7362824f..98d20dad93 100644 --- a/OvmfPkg/VirtioInputDxe/VirtioInput.h +++ b/OvmfPkg/VirtioInputDxe/VirtioInput.h @@ -12,11 +12,14 @@ #include #include +#include #include #include #include +#include "VirtioKeyCodes.h" + #define VIRTIO_INPUT_SIG SIGNATURE_32 ('V', 'I', 'N', 'P') #define MAX_RINGS 2 @@ -26,7 +29,10 @@ #define PROBE_TIME_MS 50 // Max range of recognized keyboard codes -#define MAX_KEYBOARD_CODE 255 +#define MAX_KEYBOARD_CODE (BTN_MISC - 1) + +// Key code 0x100 ~ 0x15f range is for all kinds of button events. +#define IS_BUTTON_CODE(Code) (((Code) >= BTN_MISC) && ((Code) < KEY_OK)) typedef struct { UINTN Signature; @@ -78,6 +84,13 @@ typedef struct { LIST_ENTRY KeyNotifyList; BOOLEAN KeyActive[MAX_KEYBOARD_CODE + 1]; // Key modifiers BOOLEAN KeyReady; + + // Mouse implementation + BOOLEAN HasMouse; + EFI_SIMPLE_POINTER_PROTOCOL SimplePointer; + EFI_SIMPLE_POINTER_MODE PointerMode; + EFI_SIMPLE_POINTER_STATE PointerState; + BOOLEAN PointerReady; } VIRTIO_INPUT_DEV; // Helper functions to extract VIRTIO_INPUT_DEV structure pointers @@ -85,6 +98,8 @@ typedef struct { CR (KbrPointer, VIRTIO_INPUT_DEV, Txt, VIRTIO_INPUT_SIG) #define VIRTIO_INPUT_EX_FROM_THIS(KbrPointer) \ CR (KbrPointer, VIRTIO_INPUT_DEV, TxtEx, VIRTIO_INPUT_SIG) +#define VIRTIO_INPUT_FROM_POINTER_THIS(a) \ + CR (a, VIRTIO_INPUT_DEV, SimplePointer, VIRTIO_INPUT_SIG) // Bellow candidates to be included as Linux header #define KEY_PRESSED 1 @@ -130,3 +145,27 @@ VOID VirtioKeyboardUninit ( IN OUT VIRTIO_INPUT_DEV *Dev ); + +// +// VirtioMouse.c +// +BOOLEAN +VirtioMouseProbe ( + IN VIRTIO_INPUT_DEV *Dev + ); + +VOID +VirtioMouseHandleEvent ( + IN OUT VIRTIO_INPUT_DEV *Dev, + IN VIRTIO_INPUT_EVENT *Event + ); + +EFI_STATUS +VirtioMouseInit ( + IN OUT VIRTIO_INPUT_DEV *Dev + ); + +VOID +VirtioMouseUninit ( + IN OUT VIRTIO_INPUT_DEV *Dev + ); diff --git a/OvmfPkg/VirtioInputDxe/VirtioInput.inf b/OvmfPkg/VirtioInputDxe/VirtioInput.inf index 4fc2730ac9..0c680b01fa 100644 --- a/OvmfPkg/VirtioInputDxe/VirtioInput.inf +++ b/OvmfPkg/VirtioInputDxe/VirtioInput.inf @@ -20,6 +20,7 @@ VirtioInput.h VirtioKeyCodes.h VirtioKeyboard.c + VirtioMouse.c [Packages] MdePkg/MdePkg.dec @@ -40,3 +41,4 @@ gEfiSimpleTextInProtocolGuid gEfiSimpleTextInputExProtocolGuid gVirtioDeviceProtocolGuid + gEfiSimplePointerProtocolGuid diff --git a/OvmfPkg/VirtioInputDxe/VirtioKeyCodes.h b/OvmfPkg/VirtioInputDxe/VirtioKeyCodes.h index 70d8c454ac..0cb9f49f3d 100644 --- a/OvmfPkg/VirtioInputDxe/VirtioKeyCodes.h +++ b/OvmfPkg/VirtioInputDxe/VirtioKeyCodes.h @@ -18,6 +18,7 @@ */ #define EV_SYN 0x00 #define EV_KEY 0x01 +#define EV_REL 0x02 /* * Keys and buttons @@ -289,3 +290,16 @@ #define KEY_MICMUTE 248 /* Mute / unmute the microphone */ /* Code 255 is reserved for special needs of AT keyboard driver */ + +#define BTN_MISC 0x100 + +#define BTN_LEFT 0x110 +#define BTN_RIGHT 0x111 + +#define KEY_OK 0x160 + +/* + * Relative axes + */ +#define REL_X 0x00 +#define REL_Y 0x01 diff --git a/OvmfPkg/VirtioInputDxe/VirtioMouse.c b/OvmfPkg/VirtioInputDxe/VirtioMouse.c new file mode 100644 index 0000000000..fcfcee6b9f --- /dev/null +++ b/OvmfPkg/VirtioInputDxe/VirtioMouse.c @@ -0,0 +1,223 @@ +/** @file + + EFI_SIMPLE_POINTER_PROTOCOL implementation for virtio mouse. + + Copyright (C) 2026, Advanced Micro Devices, Inc. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include + +#include "VirtioInput.h" +#include "VirtioKeyCodes.h" + +BOOLEAN +VirtioMouseProbe ( + IN VIRTIO_INPUT_DEV *Dev + ) +{ + EFI_STATUS Status; + UINT8 Size; + UINT8 Bitmap; + + // Check if REL_X and REL_Y are set in the EV_REL bitmap + Status = VirtioInputConfigQuerySize (Dev, VirtioInputCfgEvBits, EV_REL, &Size); + if (EFI_ERROR (Status) || (Size == 0)) { + return FALSE; + } + + Status = Dev->VirtIo->ReadDevice (Dev->VirtIo, OFFSET_OF_VINPUT (Data), 1, 1, &Bitmap); + if (EFI_ERROR (Status)) { + return FALSE; + } + + return (Bitmap & (1 << REL_X)) && (Bitmap & (1 << REL_Y)); +} + +// ----------------------------------------------------------------------------- +// Function handling VirtIO mouse events +VOID +VirtioMouseHandleEvent ( + IN OUT VIRTIO_INPUT_DEV *Dev, + IN VIRTIO_INPUT_EVENT *Event + ) +{ + switch (Event->Type) { + case EV_KEY: + switch (Event->Code) { + case BTN_LEFT: + Dev->PointerState.LeftButton = (BOOLEAN)(Event->Value == KEY_PRESSED); + break; + + case BTN_RIGHT: + Dev->PointerState.RightButton = (BOOLEAN)(Event->Value == KEY_PRESSED); + break; + + default: + break; + } + + Dev->PointerReady = TRUE; + break; + + case EV_REL: + switch (Event->Code) { + case REL_X: + Dev->PointerState.RelativeMovementX += (INT32)Event->Value; + break; + + case REL_Y: + Dev->PointerState.RelativeMovementY += (INT32)Event->Value; + break; + + default: + break; + } + + Dev->PointerReady = TRUE; + break; + + default: + break; + } +} + +// ----------------------------------------------------------------------------- +// EFI_SIMPLE_POINTER_PROTOCOL API +STATIC +EFI_STATUS +EFIAPI +VirtioMouseReset ( + IN EFI_SIMPLE_POINTER_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + VIRTIO_INPUT_DEV *Dev; + EFI_TPL OldTpl; + + Dev = VIRTIO_INPUT_FROM_POINTER_THIS (This); + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + ZeroMem (&Dev->PointerState, sizeof (Dev->PointerState)); + Dev->PointerReady = FALSE; + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + +// ----------------------------------------------------------------------------- +// EFI_SIMPLE_POINTER_PROTOCOL API +STATIC +EFI_STATUS +EFIAPI +VirtioMouseGetState ( + IN EFI_SIMPLE_POINTER_PROTOCOL *This, + OUT EFI_SIMPLE_POINTER_STATE *State + ) +{ + VIRTIO_INPUT_DEV *Dev; + EFI_TPL OldTpl; + + if (State == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = VIRTIO_INPUT_FROM_POINTER_THIS (This); + + if (!Dev->PointerReady) { + return EFI_NOT_READY; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + CopyMem (State, &Dev->PointerState, sizeof (*State)); + + // + // Clear mouse state + // + Dev->PointerState.RelativeMovementX = 0; + Dev->PointerState.RelativeMovementY = 0; + Dev->PointerReady = FALSE; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + +// ----------------------------------------------------------------------------- +// EFI_SIMPLE_POINTER_PROTOCOL WaitForInput event handler +STATIC +VOID +EFIAPI +VirtioMouseWaitForPointer ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + VIRTIO_INPUT_DEV *Dev = Context; + + // + // Stall 1ms to give other timer-driven drivers a chance to run while this + // routine is recursively invoked from WaitForEvent (). + // + gBS->Stall (1000); + + // Drain pending events from the device. + VirtioInputTimer (NULL, Dev); + + // If there is new pointer activity - send signal + if (Dev->PointerReady) { + gBS->SignalEvent (Event); + } +} + +EFI_STATUS +VirtioMouseInit ( + IN OUT VIRTIO_INPUT_DEV *Dev + ) +{ + EFI_STATUS Status; + + Dev->SimplePointer.Reset = VirtioMouseReset; + Dev->SimplePointer.GetState = VirtioMouseGetState; + Dev->SimplePointer.Mode = &Dev->PointerMode; + + Dev->PointerMode.ResolutionX = 8; + Dev->PointerMode.ResolutionY = 8; + Dev->PointerMode.LeftButton = TRUE; + Dev->PointerMode.RightButton = TRUE; + + ZeroMem (&Dev->PointerState, sizeof (Dev->PointerState)); + Dev->PointerReady = FALSE; + + // + // Setup the WaitForInput event + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + VirtioMouseWaitForPointer, + Dev, + &Dev->SimplePointer.WaitForInput + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +VOID +VirtioMouseUninit ( + IN OUT VIRTIO_INPUT_DEV *Dev + ) +{ + gBS->CloseEvent (Dev->SimplePointer.WaitForInput); +}