Compare commits

...

5 Commits
proc ... ape

Author SHA1 Message Date
Simon Glass
a1e9f4782b 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>
2025-09-15 04:17:14 -06:00
Simon Glass
2666becf93 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:17:14 -06:00
Simon Glass
75e29b4d1c 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:17:14 -06:00
Simon Glass
4a7b9cf05b 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:17:14 -06:00
Simon Glass
6f2149d2c5 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.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-09-15 04:17:13 -06:00
9 changed files with 257 additions and 2 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

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

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

@@ -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,11 @@ 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', '-device', 'usb-tablet'])
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'])