Compare commits

...

9 Commits
ape ... tab

Author SHA1 Message Date
Simon Glass
4fa9da5163 build-qemu: Use the virtio tablet instead of USB for x86
Switch the x86 QEMU configuration to use virtio-tablet-pci instead of
usb-tablet. This allows testing the new virtio input device driver and
does not need 'usb start' to work.

The virtio tablet provides the same functionality as USB tablet but
integrates with the VirtIO subsystem that U-Boot already supports.

Series-to: concept
Series-cc: heinrich

Signed-off-by: Simon Glass <sjg@chromium.org>
Series-links: 1:35
2025-09-15 13:35:13 -06:00
Simon Glass
4bdf99b6ce mouse: Allow selecting the device to use
Enhance the mouse dump command to support selecting a specific mouse
device by number. This allows testing multiple mouse devices when
available in the system.

Add some documentation while we are here.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-09-15 13:35:10 -06:00
Simon Glass
a8ca829f58 virtio: Add input-device driver
Add a virtio input device driver that supports tablets and mice using
the existing mouse uclass. The driver handles absolute and relative
coordinates.

Mouse buttons are supported (left, right, middle). EV_SYN events are
skipped.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-09-15 13:35:09 -06:00
Simon Glass
203f498cf5 Merge branch 'ape2' into 'master'
mouse: Provide some support for using a mouse

See merge request u-boot/u-boot!182
2025-09-15 18:56:16 +00:00
Simon Glass
2f22976bc9 qemu: Enable the mouse
Enable the mouse when running on QEMU, for ARM and x86.

This requires 'usb start' on x86. For ARM, the mouse does not work,
perhaps related to the fact that the display does not work either.

Series-to: concept
Series-cc: heinrich
Cover-letter:
mouse: Provide some support for using a mouse
This series resurects some old code that was never submitted, related to
using Nuklear with U-Boot.

It includes:
- a very simple mouse uclass
- sandbox mouse driver
- USB mouse driver, useful on x86
- EFI mouse driver, useful when running as an EFI app
- script updates to use the above with build-qemu and build-efi

It also includes a few small patches for sandbox, tests and membuf

Not everything is fully working at present:
- Mouse works on x86 QEMU (after 'usb start') but not ARM
- Mouse works on real hardware with EFI, but not with build-efi script
- Mouse times out with 'usb start', even though it does actually work

More work will be needed to tidy up these remaining issues.
END

Signed-off-by: Simon Glass <sjg@chromium.org>
Series-links: 1:33
Series-version: 2
2025-09-15 04:49:38 -06:00
Simon Glass
5a4a8278ee x86: emulation: Enable USB support
The mouse is typically connected via USB on x86, so enable this feature.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-09-15 04:49:38 -06:00
Simon Glass
ff37f4218a script: Support mouse with build-efi/qemu scripts
Provide arguments to QEMU to enable a mouse. Note that this does not
work with EFI at present.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-09-15 04:49:38 -06:00
Simon Glass
b05dd27c97 efi: arm: x86: Enable the mouse
Provide a devicetree fragment to enable the mouse for x86 and arm, when
running as an EFI app.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-09-15 04:49:38 -06:00
Simon Glass
6a078d8cb7 efi: Provide a mouse driver for EFI
In some cases the device may have a touchpad or mouse, so it is useful
to be able to use this in U-Boot. Add a simple driver for a mouse that
uses the EFI simple-pointer protocol.

Series-changes: 2
- Add missed Claude credit

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-09-15 04:49:15 -06:00
15 changed files with 720 additions and 6 deletions

View File

@@ -23,9 +23,14 @@
compatible = "efi,reset";
bootph-all;
};
efi-fb {
compatible = "efi-fb";
bootph-some-ram;
};
mouse {
compatible = "efi,mouse";
};
};

View File

@@ -30,4 +30,8 @@
bootph-some-ram;
};
mouse {
compatible = "efi,mouse";
};
};

View File

@@ -18,13 +18,21 @@ static int do_mouse_dump(struct cmd_tbl *cmdtp, int flag, int argc,
struct udevice *dev;
bool running;
int count;
int mouse_dev = 0;
int ret;
ret = uclass_first_device_err(UCLASS_MOUSE, &dev);
/* Parse optional device number */
if (argc > 1)
mouse_dev = dectoul(argv[1], NULL);
/* Get the specified mouse device */
ret = uclass_get_device(UCLASS_MOUSE, mouse_dev, &dev);
if (ret) {
printf("Mouse not found (err=%d)\n", ret);
printf("Mouse device %d not found (err=%d)\n", mouse_dev, ret);
return CMD_RET_FAILURE;
}
printf("Using mouse device %d: %s\n", mouse_dev, dev->name);
for (running = true, count = 0; running;) {
struct mouse_event evt;
@@ -63,7 +71,7 @@ static int do_mouse_dump(struct cmd_tbl *cmdtp, int flag, int argc,
}
static char mouse_help_text[] =
"dump - Dump input from a mouse";
"dump [dev] - Dump input from mouse device (default: 0)";
U_BOOT_CMD_WITH_SUBCMDS(mouse, "Mouse input", mouse_help_text,
U_BOOT_SUBCMD_MKENT(dump, 1, 1, do_mouse_dump));
U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_mouse_dump));

View File

@@ -54,6 +54,9 @@ CONFIG_CMD_MEM_SEARCH=y
CONFIG_CMD_IDE=y
CONFIG_CMD_SPI=y
CONFIG_CMD_USB=y
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_PCI=y
CONFIG_CMD_CAT=y
# CONFIG_CMD_SETEXPR is not set
CONFIG_BOOTP_BOOTFILESIZE=y

View File

@@ -33,6 +33,9 @@ CONFIG_CMD_MEM_SEARCH=y
CONFIG_CMD_IDE=y
CONFIG_CMD_SPI=y
CONFIG_CMD_USB=y
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_PCI=y
CONFIG_CMD_CAT=y
# CONFIG_CMD_SETEXPR is not set
CONFIG_BOOTP_BOOTFILESIZE=y

66
doc/usage/cmd/mouse.rst Normal file
View File

@@ -0,0 +1,66 @@
.. SPDX-License-Identifier: GPL-2.0+:
.. index::
single: mouse (command)
mouse command
=============
Synopsis
--------
::
mouse dump [dev]
The mouse command is used to access mouse input devices.
mouse dump
----------
Dump input events from a mouse device in real-time. Events are displayed
until the user presses Ctrl+C to stop.
dev
Optional device number (default: 0). Use this to select a specific mouse
device when multiple mouse devices are available.
The command displays:
- Motion events with absolute/relative coordinates and button state
- Button events (left, right, middle) with press/release state and position
- Event count when finished
Example
-------
::
=> mouse dump
Using mouse device 0: xhci_pci.p0.usb_hub.p6.usb_mo
motion: Xrel=-27, Yrel=84, X=0, Y=84, but=0
motion: Xrel=-78, Yrel=87, X=0, Y=171, but=0
motion: Xrel=-1, Yrel=87, X=0, Y=258, but=0
motion: Xrel=76, Yrel=88, X=76, Y=346, but=0
button: button==0, press=1, clicks=1, X=76, Y=346
motion: Xrel=76, Yrel=88, X=152, Y=434, but=1
motion: Xrel=76, Yrel=88, X=228, Y=522, but=1
motion: Xrel=76, Yrel=88, X=304, Y=610, but=1
motion: Xrel=76, Yrel=88, X=380, Y=698, but=1
button: button==0, press=0, clicks=1, X=380, Y=698
motion: Xrel=76, Yrel=88, X=456, Y=786, but=0
motion: Xrel=76, Yrel=88, X=532, Y=874, but=0
motion: Xrel=50, Yrel=88, X=582, Y=962, but=0
motion: Xrel=-1, Yrel=87, X=581, Y=1049, but=0
motion: Xrel=76, Yrel=87, X=657, Y=1136, but=0
motion: Xrel=-104, Yrel=86, X=553, Y=1222, but=0
motion: Xrel=24, Yrel=86, X=577, Y=1308, but=0
motion: Xrel=-104, Yrel=85, X=473, Y=1393, but=0
18 events received
=> mouse dump 1
Mouse device 1 not found (err=-19)
Configuration
-------------
The mouse command is available when CONFIG_CMD_MOUSE is enabled.

View File

@@ -94,6 +94,7 @@ Shell commands
cmd/mbr
cmd/md
cmd/mmc
cmd/mouse
cmd/msr
cmd/mtest
cmd/mtrr

View File

@@ -104,7 +104,7 @@ config TWL4030_INPUT
config MOUSE
bool "Support for mice and other pointing devices"
depends on INPUT
default y if SANDBOX
default y if SANDBOX || EFI_APP || MACH_QEMU
help
This allows U-Boot to access mouse input, typically needed for
graphics boot menus and the like. The driver can provide mouse
@@ -113,8 +113,19 @@ config MOUSE
config USB_MOUSE
bool "USB mouse support"
default y if MACH_QEMU
help
This enables using a USB mouse to control a feature in U-Boot,
typically a boot menu. The driver uses the USB boot interface of
the mouse and attempts to auto-configure itself for normal
operation.
config EFI_MOUSE
bool "EFI mouse support"
depends on EFI_APP
default y if EFI_APP
help
This enables mouse support when U-Boot is running as an EFI
application. It uses the EFI Simple Pointer Protocol to access
mouse input from the underlying EFI firmware. This is useful
when U-Boot is used as an EFI boot manager or application.

View File

@@ -21,3 +21,4 @@ ifdef CONFIG_MOUSE
obj-$(CONFIG_SANDBOX) += sandbox_mouse.o
endif
obj-$(CONFIG_USB_MOUSE) += usb_mouse.o
obj-$(CONFIG_EFI_MOUSE) += efi_mouse.o

221
drivers/input/efi_mouse.c Normal file
View File

@@ -0,0 +1,221 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* EFI mouse driver using Simple Pointer Protocol
*
* Copyright 2025 Google LLC
* Written by Claude <noreply@anthropic.com>
*/
#define LOG_CATEGORY UCLASS_MOUSE
#include <dm.h>
#include <efi.h>
#include <efi_api.h>
#include <log.h>
#include <mouse.h>
struct efi_mouse_priv {
struct efi_simple_pointer_protocol *pointer;
struct efi_simple_pointer_state last_state;
bool has_last_state;
int x, y;
int buttons;
int old_buttons;
struct efi_event *timer_event;
};
static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event)
{
struct efi_mouse_priv *priv = dev_get_priv(dev);
struct efi_simple_pointer_state state;
efi_status_t ret;
int new_buttons;
if (!priv->pointer)
return -ENODEV;
/* Use timer-based polling approach like EFI keyboard */
if (priv->timer_event) {
struct efi_boot_services *boot = efi_get_boot();
efi_uintn_t index;
struct efi_event *events[2];
efi_uintn_t num_events = 1;
events[0] = priv->timer_event;
if (priv->pointer->wait_for_input) {
events[1] = priv->pointer->wait_for_input;
num_events = 2;
}
ret = boot->wait_for_event(num_events, events, &index);
if (ret != EFI_SUCCESS)
return -EAGAIN;
}
/* Get current pointer state */
ret = priv->pointer->get_state(priv->pointer, &state);
if (ret != EFI_SUCCESS) {
if (ret == EFI_NOT_READY)
return -EAGAIN;
printf("EFI mouse: get_state failed (ret=0x%lx)\n", ret);
return -EIO;
}
/* Check for button changes */
new_buttons = 0;
if (state.left_button)
new_buttons |= 1 << 0;
if (state.right_button)
new_buttons |= 1 << 1;
if (new_buttons != priv->old_buttons) {
struct mouse_button *but = &event->button;
u8 diff = new_buttons ^ priv->old_buttons;
int i;
event->type = MOUSE_EV_BUTTON;
/* Find first changed button */
for (i = 0; i < 2; i++) {
u8 mask = 1 << i;
if (diff & mask) {
but->button = i;
but->press_state = (new_buttons & mask) ? 1 : 0;
but->clicks = 1;
but->x = priv->x;
but->y = priv->y;
priv->old_buttons ^= mask;
return 0;
}
}
}
/* Check for movement */
if (state.relative_movement_x || state.relative_movement_y) {
struct mouse_motion *motion = &event->motion;
/* Update absolute position */
priv->x += state.relative_movement_x;
priv->x = max(priv->x, 0);
priv->x = min(priv->x, 0xffff);
priv->y += state.relative_movement_y;
priv->y = max(priv->y, 0);
priv->y = min(priv->y, 0xffff);
event->type = MOUSE_EV_MOTION;
motion->state = new_buttons;
motion->x = priv->x;
motion->y = priv->y;
motion->xrel = state.relative_movement_x;
motion->yrel = state.relative_movement_y;
priv->buttons = new_buttons;
return 0;
}
priv->buttons = new_buttons;
return -EAGAIN;
}
static int efi_mouse_probe(struct udevice *dev)
{
struct efi_mouse_priv *priv = dev_get_priv(dev);
struct efi_boot_services *boot = efi_get_boot();
efi_status_t ret;
efi_handle_t *handles;
efi_uintn_t num_handles;
log_debug("EFI mouse probe\n");
/* Find Simple Pointer Protocol handles */
ret = boot->locate_handle_buffer(BY_PROTOCOL, &efi_guid_simple_pointer,
NULL, &num_handles, &handles);
if (ret != EFI_SUCCESS) {
printf("EFI mouse: No EFI pointer devices found (ret=0x%lx)\n", ret);
return -ENODEV;
}
log_debug("Found %zu EFI pointer device(s)\n", num_handles);
/* Use the first pointer device */
ret = boot->open_protocol(handles[0], &efi_guid_simple_pointer,
(void **)&priv->pointer, efi_get_parent_image(),
NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (ret != EFI_SUCCESS) {
printf("EFI mouse: Failed to open EFI pointer protocol (ret=0x%lx)\n", ret);
efi_free_pool(handles);
return -ENODEV;
}
efi_free_pool(handles);
/* Reset the pointer device */
ret = priv->pointer->reset(priv->pointer, false);
if (ret != EFI_SUCCESS) {
log_warning("Failed to reset EFI pointer device\n");
/* Continue anyway - some devices might not support reset */
}
priv->x = 0;
priv->y = 0;
priv->buttons = 0;
priv->old_buttons = 0;
priv->has_last_state = false;
/* Create a timer event for periodic checking */
ret = boot->create_event(EVT_TIMER, TPL_NOTIFY, NULL, NULL,
&priv->timer_event);
if (ret != EFI_SUCCESS) {
printf("EFI mouse: Failed to create timer event (ret=0x%lx)\n", ret);
/* Continue without timer - fallback to direct polling */
priv->timer_event = NULL;
} else {
/* Set timer to trigger every 10ms (100000 x 100ns = 10ms) */
ret = boot->set_timer(priv->timer_event, EFI_TIMER_PERIODIC, 10000);
if (ret != EFI_SUCCESS) {
printf("EFI mouse: Failed to set timer (ret=0x%lx)\n", ret);
boot->close_event(priv->timer_event);
priv->timer_event = NULL;
}
}
log_info("EFI mouse initialized\n");
return 0;
}
static int efi_mouse_remove(struct udevice *dev)
{
struct efi_mouse_priv *priv = dev_get_priv(dev);
struct efi_boot_services *boot = efi_get_boot();
if (priv->timer_event) {
boot->close_event(priv->timer_event);
priv->timer_event = NULL;
}
if (priv->pointer) {
/* Protocol will be automatically closed when the image is unloaded */
priv->pointer = NULL;
}
return 0;
}
static const struct mouse_ops efi_mouse_ops = {
.get_event = efi_mouse_get_event,
};
static const struct udevice_id efi_mouse_ids[] = {
{ .compatible = "efi,mouse" },
{ }
};
U_BOOT_DRIVER(efi_mouse) = {
.name = "efi_mouse",
.id = UCLASS_MOUSE,
.of_match = efi_mouse_ids,
.ops = &efi_mouse_ops,
.probe = efi_mouse_probe,
.remove = efi_mouse_remove,
.priv_auto = sizeof(struct efi_mouse_priv),
};

View File

@@ -110,4 +110,17 @@ config VIRTIO_SCSI
A specification for the protocol is available at
https://docs.oasis-open.org/virtio/virtio/v1.3/virtio-v1.3.html
config VIRTIO_INPUT
bool "Input device driver for virtio devices"
depends on VIRTIO
select MOUSE
default y
help
This driver provides support for virtio-based input devices such as
tablets, mice and keyboards. It implements the mouse uclass for
tablet and pointer devices.
A specification for the protocol is available at
https://docs.oasis-open.org/virtio/virtio/v1.3/virtio-v1.3.html
endmenu

View File

@@ -14,3 +14,4 @@ obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
obj-$(CONFIG_VIRTIO_FS) += fs.o fs_dir.o fs_file.o fs_compat.o
obj-$(CONFIG_VIRTIO_SCSI) += virtio_scsi.o
obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o

View File

@@ -0,0 +1,369 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* VirtIO Input (tablet) driver for U-Boot
*
* Copyright 2025 Simon Glass <sjg@chromium.org>
*/
#define LOG_CATEGORY UCLASS_VIRTIO
#include <dm.h>
#include <errno.h>
#include <mouse.h>
#include <virtio_types.h>
#include <virtio.h>
#include <virtio_ring.h>
#include <linux/byteorder/little_endian.h>
#include <dt-bindings/input/linux-event-codes.h>
#define VIRTIO_INPUT_CFG_UNSET 0x00
#define VIRTIO_INPUT_CFG_ID_NAME 0x01
#define VIRTIO_INPUT_CFG_ID_SERIAL 0x02
#define VIRTIO_INPUT_CFG_ID_DEVIDS 0x03
#define VIRTIO_INPUT_CFG_PROP_BITS 0x10
#define VIRTIO_INPUT_CFG_EV_BITS 0x11
#define VIRTIO_INPUT_CFG_ABS_INFO 0x12
/* absolute axis information for input devices */
struct virtio_input_absinfo {
__le32 min; /* minimum value */
__le32 max; /* maximum value */
__le32 fuzz; /* fuzz value for noise filtering */
__le32 flat; /* flat area around center */
__le32 res; /* resolution in units per mm */
};
/* device identification information */
struct virtio_input_devids {
__le16 bustype; /* bus type identifier */
__le16 vendor; /* vendor identifier */
__le16 product; /* product identifier */
__le16 version; /* version number */
};
/* configuration space for querying device capabilities */
struct vinp_config {
__u8 select; /* configuration item to select */
__u8 subsel; /* sub-selection within item */
__u8 size; /* size of returned data */
__u8 reserved[5]; /* padding */
union {
char string[128]; /* for name/serial strings */
__u8 bitmap[128]; /* for capability bitmaps */
struct virtio_input_absinfo abs; /* for absolute axis info */
struct virtio_input_devids ids; /* for device IDs */
} u;
};
/* input event structure (follows Linux input_event format) */
struct virtio_input_event {
__le16 type; /* event type (EV_KEY, EV_ABS, EV_SYN, etc.) */
__le16 code; /* event code (button/axis identifier) */
__le32 value; /* event value */
};
#define BUF_COUNT 8
#define BUF_SIZE (16 * sizeof(struct virtio_input_event))
#define COORD_MAX 0xffff
/* private data for virtio input device */
struct virtio_input_priv {
struct virtqueue *event_vq; /* event virtqueue */
struct virtqueue *status_vq; /* status virtqueue */
char event_bufs[BUF_COUNT][BUF_SIZE]; /* event buffers */
bool rx_running; /* true if buffers are set up */
int abs_x_max; /* maximum X coordinate */
int abs_y_max; /* maximum Y coordinate */
int button_state; /* current button state */
int last_x; /* last X coordinate */
int last_y; /* last Y coordinate */
};
static int virtio_input_free_buffer(struct udevice *dev, void *buf)
{
struct virtio_input_priv *priv = dev_get_priv(dev);
struct virtio_sg sg = { buf, BUF_SIZE };
struct virtio_sg *sgs[] = { &sg };
/* put the buffer back to the event ring */
virtqueue_add(priv->event_vq, sgs, 0, 1);
return 0;
}
static int process_event(struct virtio_input_priv *priv,
struct virtio_input_event *vio_event,
struct mouse_event *evt)
{
u16 type = le16_to_cpu(vio_event->type);
u16 code = le16_to_cpu(vio_event->code);
u32 value = le32_to_cpu(vio_event->value);
/* skip EV_SYN events immediately */
if (type == EV_SYN)
return -EAGAIN;
log_debug("processing event: type=%d code=%d value=%d\n", type, code,
value);
switch (type) {
case EV_ABS:
if (code == ABS_X)
priv->last_x = value;
else if (code == ABS_Y)
priv->last_y = value;
/* report motion event */
evt->type = MOUSE_EV_MOTION;
evt->motion.state = priv->button_state;
evt->motion.x = (priv->last_x * COORD_MAX) / priv->abs_x_max;
evt->motion.y = (priv->last_y * COORD_MAX) / priv->abs_y_max;
evt->motion.xrel = 0; /* Absolute mode */
evt->motion.yrel = 0;
return 0;
case EV_KEY:
switch (code) {
case BTN_LEFT:
evt->type = MOUSE_EV_BUTTON;
evt->button.button = BUTTON_LEFT;
evt->button.press_state = value ? BUTTON_PRESSED :
BUTTON_RELEASED;
evt->button.clicks = 1;
evt->button.x = (priv->last_x * COORD_MAX) /
priv->abs_x_max;
evt->button.y = (priv->last_y * COORD_MAX) /
priv->abs_y_max;
if (evt->button.press_state == BUTTON_PRESSED)
priv->button_state |= BUTTON_LEFT;
else
priv->button_state &= ~BUTTON_LEFT;
return 0;
case BTN_RIGHT:
evt->type = MOUSE_EV_BUTTON;
evt->button.button = BUTTON_RIGHT;
evt->button.press_state = value ? BUTTON_PRESSED :
BUTTON_RELEASED;
evt->button.clicks = 1;
evt->button.x = (priv->last_x * COORD_MAX) /
priv->abs_x_max;
evt->button.y = (priv->last_y * COORD_MAX) /
priv->abs_y_max;
if (evt->button.press_state == BUTTON_PRESSED)
priv->button_state |= BUTTON_RIGHT;
else
priv->button_state &= ~BUTTON_RIGHT;
return 0;
case BTN_MIDDLE:
evt->type = MOUSE_EV_BUTTON;
evt->button.button = BUTTON_MIDDLE;
evt->button.press_state = value ? BUTTON_PRESSED :
BUTTON_RELEASED;
evt->button.clicks = 1;
evt->button.x = (priv->last_x * COORD_MAX) /
priv->abs_x_max;
evt->button.y = (priv->last_y * COORD_MAX) /
priv->abs_y_max;
if (evt->button.press_state == BUTTON_PRESSED)
priv->button_state |= BUTTON_MIDDLE;
else
priv->button_state &= ~BUTTON_MIDDLE;
return 0;
}
break;
}
return -EAGAIN;
}
static int virtio_input_start(struct udevice *dev)
{
struct virtio_input_priv *priv = dev_get_priv(dev);
if (!priv->rx_running) {
struct virtio_sg sg;
struct virtio_sg *sgs[] = { &sg };
int i;
/* setup event buffers */
sg.length = BUF_SIZE;
/* add all buffers to the event queue */
for (i = 0; i < BUF_COUNT; i++) {
sg.addr = priv->event_bufs[i];
virtqueue_add(priv->event_vq, sgs, 0, 1);
}
virtqueue_kick(priv->event_vq);
/* setup the event queue only once */
priv->rx_running = true;
log_debug("VirtIO input buffers initialized\n");
}
return 0;
}
static int virtio_input_get_event(struct udevice *dev, struct mouse_event *evt)
{
struct virtio_input_priv *priv = dev_get_priv(dev);
struct virtio_input_event *vio_event;
int i, num_events, ret;
void *buf;
uint len;
/* ensure buffers are setup */
ret = virtio_input_start(dev);
if (ret)
return ret;
log_debug("starting\n");
/* check for ready buffer */
buf = virtqueue_get_buf(priv->event_vq, &len);
if (!buf) {
log_debug("No events available\n");
return -EAGAIN;
}
log_debug("got buffer back: addr=%p len=%d\n", buf, len);
if (len < sizeof(struct virtio_input_event)) {
log_debug("Invalid event length: %d\n", len);
/* put buffer back */
virtio_input_free_buffer(dev, buf);
return -EAGAIN;
}
/* process all events in the buffer */
num_events = len / sizeof(struct virtio_input_event);
vio_event = (struct virtio_input_event *)buf;
for (i = 0; i < num_events; i++) {
ret = process_event(priv, &vio_event[i], evt);
if (!ret) {
/*
* found a valid event to return, put buffer back
* Note: this loses any remaining events in the buffer,
* but for input devices this is acceptable
*/
virtio_input_free_buffer(dev, buf);
return 0;
}
/* -EAGAIN means continue to next event */
}
/* no useful events found, put buffer back */
virtio_input_free_buffer(dev, buf);
evt->type = MOUSE_EV_NULL;
return -EAGAIN;
}
static int virtio_input_probe(struct udevice *dev)
{
struct virtio_input_priv *priv = dev_get_priv(dev);
struct virtqueue *vqs[2];
struct vinp_config cfg;
int ret;
ret = virtio_find_vqs(dev, 2, vqs);
if (ret) {
log_debug("Failed to find virtqueues: %d\n", ret);
return ret;
}
priv->event_vq = vqs[0];
priv->status_vq = vqs[1];
/* check what event types this device supports */
cfg.select = VIRTIO_INPUT_CFG_EV_BITS;
cfg.subsel = EV_KEY;
virtio_cwrite8(dev, offsetof(struct vinp_config, select), cfg.select);
virtio_cwrite8(dev, offsetof(struct vinp_config, subsel), cfg.subsel);
virtio_cread_bytes(dev, offsetof(struct vinp_config, u.bitmap),
cfg.u.bitmap, 16);
log_debug("EV_KEY bitmap: %02x %02x %02x %02x\n", cfg.u.bitmap[0],
cfg.u.bitmap[1], cfg.u.bitmap[2], cfg.u.bitmap[3]);
cfg.select = VIRTIO_INPUT_CFG_EV_BITS;
cfg.subsel = EV_REL;
virtio_cwrite8(dev, offsetof(struct vinp_config, select), cfg.select);
virtio_cwrite8(dev, offsetof(struct vinp_config, subsel), cfg.subsel);
virtio_cread_bytes(dev, offsetof(struct vinp_config, u.bitmap),
cfg.u.bitmap, 16);
log_debug("EV_REL bitmap: %02x %02x %02x %02x\n", cfg.u.bitmap[0],
cfg.u.bitmap[1], cfg.u.bitmap[2], cfg.u.bitmap[3]);
/* check if this device supports absolute coordinates (tablet) */
cfg.select = VIRTIO_INPUT_CFG_EV_BITS;
cfg.subsel = EV_ABS;
virtio_cwrite8(dev, offsetof(struct vinp_config, select), cfg.select);
virtio_cwrite8(dev, offsetof(struct vinp_config, subsel), cfg.subsel);
/* read the bitmap to see if ABS_X and ABS_Y are supported */
virtio_cread_bytes(dev, offsetof(struct vinp_config, u.bitmap),
cfg.u.bitmap, 16);
log_debug("EV_ABS bitmap: %02x %02x %02x %02x\n", cfg.u.bitmap[0],
cfg.u.bitmap[1], cfg.u.bitmap[2], cfg.u.bitmap[3]);
/* check if ABS_X (0) and ABS_Y (1) bits are set */
if ((cfg.u.bitmap[0] & (1 << 0)) &&
(cfg.u.bitmap[0] & (1 << 1))) {
/* get ABS_X range */
cfg.select = VIRTIO_INPUT_CFG_ABS_INFO;
cfg.subsel = ABS_X;
virtio_cwrite8(dev, offsetof(struct vinp_config, select),
cfg.select);
virtio_cwrite8(dev, offsetof(struct vinp_config, subsel),
cfg.subsel);
virtio_cread_bytes(dev, offsetof(struct vinp_config, u.abs),
&cfg.u.abs, sizeof(cfg.u.abs));
priv->abs_x_max = le32_to_cpu(cfg.u.abs.max);
/* get ABS_Y range */
cfg.subsel = ABS_Y;
virtio_cwrite8(dev, offsetof(struct vinp_config, subsel),
cfg.subsel);
virtio_cread_bytes(dev, offsetof(struct vinp_config, u.abs),
&cfg.u.abs, sizeof(cfg.u.abs));
priv->abs_y_max = le32_to_cpu(cfg.u.abs.max);
log_debug("tablet: X range 0-%d, Y range 0-%d\n",
priv->abs_x_max, priv->abs_y_max);
} else {
/* no absolute coordinates, use default range */
priv->abs_x_max = 32767;
priv->abs_y_max = 32767;
log_debug("table: No absolute coords, using relative mode\n");
}
return 0;
}
static int virtio_input_bind(struct udevice *dev)
{
struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
/* indicate what driver features we support */
virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0);
return 0;
}
static const struct mouse_ops virtio_input_ops = {
.get_event = virtio_input_get_event,
};
U_BOOT_DRIVER(virtio_input) = {
.name = VIRTIO_INPUT_DRV_NAME,
.id = UCLASS_MOUSE,
.bind = virtio_input_bind,
.probe = virtio_input_probe,
.ops = &virtio_input_ops,
.priv_auto = sizeof(struct virtio_input_priv),
.flags = DM_FLAG_ACTIVE_DMA,
};

View File

@@ -112,6 +112,9 @@ class BuildEfi:
extra += ['-device', 'qemu-xhci', '-device', 'usb-kbd',
'-device', 'usb-tablet']
extra += ['-display', 'default,show-cursor=on']
else: # x86
extra += ['-device', 'qemu-xhci', '-device', 'usb-kbd',
'-device', 'usb-tablet']
extra += ['-serial', 'mon:stdio']
serial_msg = ''
if self.args.kvm:

View File

@@ -274,7 +274,12 @@ class BuildQemu:
elif self.args.arch == 'arm':
qemu_cmd.extend(['-device', 'virtio-gpu-pci'])
qemu_cmd.extend(['-device', 'qemu-xhci', '-device', 'usb-kbd',
'-device', 'usb-tablet'])
'-device', 'usb-tablet', '-device', 'usb-mouse'])
qemu_cmd.extend(['-display', 'default,show-cursor=on'])
elif self.args.arch == 'x86':
qemu_cmd.extend(['-device', 'qemu-xhci'])
qemu_cmd.extend(['-device', 'usb-kbd'])
qemu_cmd.extend(['-device', 'virtio-tablet-pci'])
qemu_cmd.extend(['-display', 'default,show-cursor=on'])
if not any(item.startswith('-serial') for item in self.qemu_extra):
qemu_cmd.extend(['-serial', 'mon:stdio'])