OvmfPkg/VirtioInputDxe: Add virtio tablet support

Virtio tablet devices report absolute coordinates (EV_ABS) and button
inputs. Add VirtioTablet.c implementing tablet capability probing,
EFI_ABSOLUTE_POINTER_PROTOCOL implementation and event handler that
converts VIRTIO_INPUT_EVENT into EFI_ABSOLUTE_POINTER_STATE.

The axis ranges advertised by EFI_ABSOLUTE_POINTER_MODE are read from
the device's abs_info for ABS_X and ABS_Y.

The shared event dispatcher in VirtioInput.c now routes EV_ABS events
and EV_KEY codes above MAX_KEYBOARD_CODE to the tablet handler.

Signed-off-by: Jiaqing Zhao <Zhao.Jiaqing@amd.com>
This commit is contained in:
Jiaqing Zhao 2026-06-25 17:02:31 +08:00 committed by mergify[bot]
parent efdbf3c355
commit 9e3e68218c
5 changed files with 413 additions and 5 deletions

View file

@ -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);

View file

@ -10,6 +10,7 @@
#pragma once
#include <Protocol/AbsolutePointer.h>
#include <Protocol/ComponentName.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/SimplePointer.h>
@ -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
);

View file

@ -21,6 +21,7 @@
VirtioKeyCodes.h
VirtioKeyboard.c
VirtioMouse.c
VirtioTablet.c
[Packages]
MdePkg/MdePkg.dec
@ -42,3 +43,4 @@
gEfiSimpleTextInputExProtocolGuid
gVirtioDeviceProtocolGuid
gEfiSimplePointerProtocolGuid
gEfiAbsolutePointerProtocolGuid

View file

@ -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

View file

@ -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 <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/VirtioLib.h>
#include <IndustryStandard/VirtioInput.h>
#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);
}