mirror of
https://github.com/tianocore/edk2
synced 2026-08-27 00:23:19 -04:00
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:
parent
efdbf3c355
commit
9e3e68218c
5 changed files with 413 additions and 5 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
VirtioKeyCodes.h
|
||||
VirtioKeyboard.c
|
||||
VirtioMouse.c
|
||||
VirtioTablet.c
|
||||
|
||||
[Packages]
|
||||
MdePkg/MdePkg.dec
|
||||
|
|
@ -42,3 +43,4 @@
|
|||
gEfiSimpleTextInputExProtocolGuid
|
||||
gVirtioDeviceProtocolGuid
|
||||
gEfiSimplePointerProtocolGuid
|
||||
gEfiAbsolutePointerProtocolGuid
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
293
OvmfPkg/VirtioInputDxe/VirtioTablet.c
Normal file
293
OvmfPkg/VirtioInputDxe/VirtioTablet.c
Normal 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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue