diff --git a/OvmfPkg/VirtioInputDxe/VirtioInput.c b/OvmfPkg/VirtioInputDxe/VirtioInput.c index dfa8e78091..1283a7f411 100644 --- a/OvmfPkg/VirtioInputDxe/VirtioInput.c +++ b/OvmfPkg/VirtioInputDxe/VirtioInput.c @@ -419,6 +419,10 @@ VirtioInputGetDeviceData ( if (Dev->HasMouse) { VirtioMouseHandleEvent (Dev, &Event); } + + if (Dev->HasTablet) { + VirtioTabletHandleEvent (Dev, &Event); + } } else if (Dev->HasKeyboard) { VirtioKeyboardHandleEvent (Dev, &Event); } @@ -433,6 +437,14 @@ VirtioInputGetDeviceData ( break; + case EV_ABS: + // Absolute pointer movement received + if (Dev->HasTablet) { + VirtioTabletHandleEvent (Dev, &Event); + } + + break; + default: DEBUG ((DEBUG_INFO, "%a: Unhandled VirtIo event\n", __func__)); break; @@ -549,7 +561,8 @@ VirtioInputInit ( // Dev->HasKeyboard = VirtioKeyboardProbe (Dev); Dev->HasMouse = VirtioMouseProbe (Dev); - if (!Dev->HasKeyboard && !Dev->HasMouse) { + Dev->HasTablet = VirtioTabletProbe (Dev); + if (!Dev->HasKeyboard && !Dev->HasMouse && !Dev->HasTablet) { Status = EFI_UNSUPPORTED; goto Failed; } @@ -571,6 +584,13 @@ VirtioInputInit ( } } + if (Dev->HasTablet) { + Status = VirtioTabletInit (Dev); + if (EFI_ERROR (Status)) { + goto Failed; + } + } + VirtioInputRingFillRx (Dev, 0); // @@ -630,6 +650,10 @@ VirtioInputUninit ( VirtioMouseUninit (Dev); } + if (Dev->HasTablet) { + VirtioTabletUninit (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 @@ -801,8 +825,30 @@ VirtioInputBindingStart ( } } + if (Dev->HasTablet) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &DeviceHandle, + &gEfiAbsolutePointerProtocolGuid, + &Dev->AbsolutePointer, + NULL + ); + if (EFI_ERROR (Status)) { + goto UninstallMouse; + } + } + return EFI_SUCCESS; +UninstallMouse: + if (Dev->HasMouse) { + gBS->UninstallMultipleProtocolInterfaces ( + DeviceHandle, + &gEfiSimplePointerProtocolGuid, + &Dev->SimplePointer, + NULL + ); + } + UninstallKeyboard: if (Dev->HasKeyboard) { gBS->UninstallMultipleProtocolInterfaces ( @@ -850,6 +896,7 @@ VirtioInputBindingStop ( EFI_STATUS Status; EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Txt; EFI_SIMPLE_POINTER_PROTOCOL *Pointer; + EFI_ABSOLUTE_POINTER_PROTOCOL *AbsPointer; VIRTIO_INPUT_DEV *Dev; Status = gBS->OpenProtocol ( @@ -871,11 +918,23 @@ VirtioInputBindingStop ( DeviceHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); - if (EFI_ERROR (Status)) { - return Status; - } + if (!EFI_ERROR (Status)) { + Dev = VIRTIO_INPUT_FROM_POINTER_THIS (Pointer); + } else { + Status = gBS->OpenProtocol ( + DeviceHandle, + &gEfiAbsolutePointerProtocolGuid, + (VOID **)&AbsPointer, + This->DriverBindingHandle, + DeviceHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } - Dev = VIRTIO_INPUT_FROM_POINTER_THIS (Pointer); + Dev = VIRTIO_INPUT_FROM_ABS_POINTER_THIS (AbsPointer); + } } // @@ -907,6 +966,18 @@ VirtioInputBindingStop ( } } + if (Dev->HasTablet) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + DeviceHandle, + &gEfiAbsolutePointerProtocolGuid, + &Dev->AbsolutePointer, + 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 98d20dad93..1b00cb3655 100644 --- a/OvmfPkg/VirtioInputDxe/VirtioInput.h +++ b/OvmfPkg/VirtioInputDxe/VirtioInput.h @@ -10,6 +10,7 @@ #pragma once +#include #include #include #include @@ -91,6 +92,13 @@ typedef struct { EFI_SIMPLE_POINTER_MODE PointerMode; EFI_SIMPLE_POINTER_STATE PointerState; BOOLEAN PointerReady; + + // Tablet implementation + BOOLEAN HasTablet; + EFI_ABSOLUTE_POINTER_PROTOCOL AbsolutePointer; + EFI_ABSOLUTE_POINTER_MODE AbsPointerMode; + EFI_ABSOLUTE_POINTER_STATE AbsPointerState; + BOOLEAN AbsPointerReady; } VIRTIO_INPUT_DEV; // Helper functions to extract VIRTIO_INPUT_DEV structure pointers @@ -100,6 +108,8 @@ typedef struct { 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) +#define VIRTIO_INPUT_FROM_ABS_POINTER_THIS(a) \ + CR (a, VIRTIO_INPUT_DEV, AbsolutePointer, VIRTIO_INPUT_SIG) // Bellow candidates to be included as Linux header #define KEY_PRESSED 1 @@ -169,3 +179,27 @@ VOID VirtioMouseUninit ( IN OUT VIRTIO_INPUT_DEV *Dev ); + +// +// VirtioTablet.c +// +BOOLEAN +VirtioTabletProbe ( + IN VIRTIO_INPUT_DEV *Dev + ); + +VOID +VirtioTabletHandleEvent ( + IN OUT VIRTIO_INPUT_DEV *Dev, + IN VIRTIO_INPUT_EVENT *Event + ); + +EFI_STATUS +VirtioTabletInit ( + IN OUT VIRTIO_INPUT_DEV *Dev + ); + +VOID +VirtioTabletUninit ( + IN OUT VIRTIO_INPUT_DEV *Dev + ); diff --git a/OvmfPkg/VirtioInputDxe/VirtioInput.inf b/OvmfPkg/VirtioInputDxe/VirtioInput.inf index 0c680b01fa..4a20f606fc 100644 --- a/OvmfPkg/VirtioInputDxe/VirtioInput.inf +++ b/OvmfPkg/VirtioInputDxe/VirtioInput.inf @@ -21,6 +21,7 @@ VirtioKeyCodes.h VirtioKeyboard.c VirtioMouse.c + VirtioTablet.c [Packages] MdePkg/MdePkg.dec @@ -42,3 +43,4 @@ gEfiSimpleTextInputExProtocolGuid gVirtioDeviceProtocolGuid gEfiSimplePointerProtocolGuid + gEfiAbsolutePointerProtocolGuid diff --git a/OvmfPkg/VirtioInputDxe/VirtioKeyCodes.h b/OvmfPkg/VirtioInputDxe/VirtioKeyCodes.h index 0cb9f49f3d..82f0db9b96 100644 --- a/OvmfPkg/VirtioInputDxe/VirtioKeyCodes.h +++ b/OvmfPkg/VirtioInputDxe/VirtioKeyCodes.h @@ -19,6 +19,7 @@ #define EV_SYN 0x00 #define EV_KEY 0x01 #define EV_REL 0x02 +#define EV_ABS 0x03 /* * Keys and buttons @@ -295,6 +296,7 @@ #define BTN_LEFT 0x110 #define BTN_RIGHT 0x111 +#define BTN_TOUCH 0x14a #define KEY_OK 0x160 @@ -303,3 +305,9 @@ */ #define REL_X 0x00 #define REL_Y 0x01 + +/* + * Absolute axes + */ +#define ABS_X 0x00 +#define ABS_Y 0x01 diff --git a/OvmfPkg/VirtioInputDxe/VirtioTablet.c b/OvmfPkg/VirtioInputDxe/VirtioTablet.c new file mode 100644 index 0000000000..71b7a13a17 --- /dev/null +++ b/OvmfPkg/VirtioInputDxe/VirtioTablet.c @@ -0,0 +1,293 @@ +/** @file + + EFI_ABSOLUTE_POINTER_PROTOCOL implementation for virtio tablet. + + 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 +VirtioTabletProbe ( + IN VIRTIO_INPUT_DEV *Dev + ) +{ + EFI_STATUS Status; + UINT8 Size; + UINT8 Bitmap; + + // A tablet (absolute pointer) reports ABS_X and ABS_Y in the EV_ABS bitmap + Status = VirtioInputConfigQuerySize (Dev, VirtioInputCfgEvBits, EV_ABS, &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 << ABS_X)) && (Bitmap & (1 << ABS_Y)); +} + +// ----------------------------------------------------------------------------- +// Function handling VirtIO tablet events +VOID +VirtioTabletHandleEvent ( + IN OUT VIRTIO_INPUT_DEV *Dev, + IN VIRTIO_INPUT_EVENT *Event + ) +{ + switch (Event->Type) { + case EV_KEY: + switch (Event->Code) { + case BTN_TOUCH: + case BTN_LEFT: + if (Event->Value == KEY_PRESSED) { + Dev->AbsPointerState.ActiveButtons |= (UINT32)EFI_ABSP_TouchActive; + } else { + Dev->AbsPointerState.ActiveButtons &= ~(UINT32)EFI_ABSP_TouchActive; + } + + break; + + case BTN_RIGHT: + if (Event->Value == KEY_PRESSED) { + Dev->AbsPointerState.ActiveButtons |= (UINT32)EFI_ABS_AltActive; + } else { + Dev->AbsPointerState.ActiveButtons &= ~(UINT32)EFI_ABS_AltActive; + } + + break; + + default: + break; + } + + Dev->AbsPointerReady = TRUE; + break; + + case EV_ABS: + switch (Event->Code) { + case ABS_X: + Dev->AbsPointerState.CurrentX = Event->Value; + break; + + case ABS_Y: + Dev->AbsPointerState.CurrentY = Event->Value; + break; + + default: + break; + } + + Dev->AbsPointerReady = TRUE; + break; + + default: + break; + } +} + +// ----------------------------------------------------------------------------- +// EFI_ABSOLUTE_POINTER_PROTOCOL API +STATIC +EFI_STATUS +EFIAPI +VirtioTabletReset ( + IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + VIRTIO_INPUT_DEV *Dev; + EFI_TPL OldTpl; + + Dev = VIRTIO_INPUT_FROM_ABS_POINTER_THIS (This); + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + ZeroMem (&Dev->AbsPointerState, sizeof (Dev->AbsPointerState)); + Dev->AbsPointerReady = FALSE; + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + +// ----------------------------------------------------------------------------- +// EFI_ABSOLUTE_POINTER_PROTOCOL API +STATIC +EFI_STATUS +EFIAPI +VirtioTabletGetState ( + IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, + OUT EFI_ABSOLUTE_POINTER_STATE *State + ) +{ + VIRTIO_INPUT_DEV *Dev; + EFI_TPL OldTpl; + + if (State == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = VIRTIO_INPUT_FROM_ABS_POINTER_THIS (This); + + if (!Dev->AbsPointerReady) { + return EFI_NOT_READY; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + CopyMem (State, &Dev->AbsPointerState, sizeof (*State)); + + // + // The reported position is absolute, so it persists; only clear the "new + // data" flag so the next GetState () returns EFI_NOT_READY until a fresh + // event arrives. + // + Dev->AbsPointerReady = FALSE; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + +// ----------------------------------------------------------------------------- +// EFI_ABSOLUTE_POINTER_PROTOCOL WaitForInput event handler +STATIC +VOID +EFIAPI +VirtioTabletWaitForInput ( + 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->AbsPointerReady) { + gBS->SignalEvent (Event); + } +} + +STATIC +EFI_STATUS +VirtioTabletGetAbsMinMax ( + IN VIRTIO_INPUT_DEV *Dev, + IN UINT8 Axis, + OUT UINT32 *Min, + OUT UINT32 *Max + ) +{ + EFI_STATUS Status; + UINT8 Size; + + Status = VirtioInputConfigQuerySize (Dev, VirtioInputCfgAbsInfo, Axis, &Size); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Size < sizeof (VIRTIO_INPUT_ABS_INFO)) { + return EFI_UNSUPPORTED; + } + + Status = Dev->VirtIo->ReadDevice ( + Dev->VirtIo, + OFFSET_OF_VINPUT (Data.Abs.Min), + SIZE_OF_VINPUT (Data.Abs.Min), + sizeof (*Min), + Min + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Dev->VirtIo->ReadDevice ( + Dev->VirtIo, + OFFSET_OF_VINPUT (Data.Abs.Max), + SIZE_OF_VINPUT (Data.Abs.Max), + sizeof (*Max), + Max + ); + return Status; +} + +EFI_STATUS +VirtioTabletInit ( + IN OUT VIRTIO_INPUT_DEV *Dev + ) +{ + EFI_STATUS Status; + UINT32 AbsMinX; + UINT32 AbsMaxX; + UINT32 AbsMinY; + UINT32 AbsMaxY; + + Dev->AbsolutePointer.Reset = VirtioTabletReset; + Dev->AbsolutePointer.GetState = VirtioTabletGetState; + Dev->AbsolutePointer.Mode = &Dev->AbsPointerMode; + + ZeroMem (&Dev->AbsPointerMode, sizeof (Dev->AbsPointerMode)); + + Status = VirtioTabletGetAbsMinMax (Dev, ABS_X, &AbsMinX, &AbsMaxX); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = VirtioTabletGetAbsMinMax (Dev, ABS_Y, &AbsMinY, &AbsMaxY); + if (EFI_ERROR (Status)) { + return Status; + } + + Dev->AbsPointerMode.AbsoluteMinX = AbsMinX; + Dev->AbsPointerMode.AbsoluteMaxX = AbsMaxX; + Dev->AbsPointerMode.AbsoluteMinY = AbsMinY; + Dev->AbsPointerMode.AbsoluteMaxY = AbsMaxY; + Dev->AbsPointerMode.Attributes = EFI_ABSP_SupportsAltActive; + + ZeroMem (&Dev->AbsPointerState, sizeof (Dev->AbsPointerState)); + Dev->AbsPointerReady = FALSE; + + // + // Setup the WaitForInput event + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + VirtioTabletWaitForInput, + Dev, + &Dev->AbsolutePointer.WaitForInput + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +VOID +VirtioTabletUninit ( + IN OUT VIRTIO_INPUT_DEV *Dev + ) +{ + gBS->CloseEvent (Dev->AbsolutePointer.WaitForInput); +}