Compare commits
5 Commits
cherry-3e4
...
ape
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1e9f4782b | ||
|
|
2666becf93 | ||
|
|
75e29b4d1c | ||
|
|
4a7b9cf05b | ||
|
|
6f2149d2c5 |
@@ -23,9 +23,14 @@
|
|||||||
compatible = "efi,reset";
|
compatible = "efi,reset";
|
||||||
bootph-all;
|
bootph-all;
|
||||||
};
|
};
|
||||||
|
|
||||||
efi-fb {
|
efi-fb {
|
||||||
compatible = "efi-fb";
|
compatible = "efi-fb";
|
||||||
bootph-some-ram;
|
bootph-some-ram;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mouse {
|
||||||
|
compatible = "efi,mouse";
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,4 +30,8 @@
|
|||||||
bootph-some-ram;
|
bootph-some-ram;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mouse {
|
||||||
|
compatible = "efi,mouse";
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ CONFIG_CMD_MEM_SEARCH=y
|
|||||||
CONFIG_CMD_IDE=y
|
CONFIG_CMD_IDE=y
|
||||||
CONFIG_CMD_SPI=y
|
CONFIG_CMD_SPI=y
|
||||||
CONFIG_CMD_USB=y
|
CONFIG_CMD_USB=y
|
||||||
|
CONFIG_USB=y
|
||||||
|
CONFIG_USB_XHCI_HCD=y
|
||||||
|
CONFIG_USB_XHCI_PCI=y
|
||||||
CONFIG_CMD_CAT=y
|
CONFIG_CMD_CAT=y
|
||||||
# CONFIG_CMD_SETEXPR is not set
|
# CONFIG_CMD_SETEXPR is not set
|
||||||
CONFIG_BOOTP_BOOTFILESIZE=y
|
CONFIG_BOOTP_BOOTFILESIZE=y
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ CONFIG_CMD_MEM_SEARCH=y
|
|||||||
CONFIG_CMD_IDE=y
|
CONFIG_CMD_IDE=y
|
||||||
CONFIG_CMD_SPI=y
|
CONFIG_CMD_SPI=y
|
||||||
CONFIG_CMD_USB=y
|
CONFIG_CMD_USB=y
|
||||||
|
CONFIG_USB=y
|
||||||
|
CONFIG_USB_XHCI_HCD=y
|
||||||
|
CONFIG_USB_XHCI_PCI=y
|
||||||
CONFIG_CMD_CAT=y
|
CONFIG_CMD_CAT=y
|
||||||
# CONFIG_CMD_SETEXPR is not set
|
# CONFIG_CMD_SETEXPR is not set
|
||||||
CONFIG_BOOTP_BOOTFILESIZE=y
|
CONFIG_BOOTP_BOOTFILESIZE=y
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ config TWL4030_INPUT
|
|||||||
config MOUSE
|
config MOUSE
|
||||||
bool "Support for mice and other pointing devices"
|
bool "Support for mice and other pointing devices"
|
||||||
depends on INPUT
|
depends on INPUT
|
||||||
default y if SANDBOX
|
default y if SANDBOX || EFI_APP || MACH_QEMU
|
||||||
help
|
help
|
||||||
This allows U-Boot to access mouse input, typically needed for
|
This allows U-Boot to access mouse input, typically needed for
|
||||||
graphics boot menus and the like. The driver can provide mouse
|
graphics boot menus and the like. The driver can provide mouse
|
||||||
@@ -113,8 +113,19 @@ config MOUSE
|
|||||||
|
|
||||||
config USB_MOUSE
|
config USB_MOUSE
|
||||||
bool "USB mouse support"
|
bool "USB mouse support"
|
||||||
|
default y if MACH_QEMU
|
||||||
help
|
help
|
||||||
This enables using a USB mouse to control a feature in U-Boot,
|
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
|
typically a boot menu. The driver uses the USB boot interface of
|
||||||
the mouse and attempts to auto-configure itself for normal
|
the mouse and attempts to auto-configure itself for normal
|
||||||
operation.
|
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.
|
||||||
|
|||||||
@@ -21,3 +21,4 @@ ifdef CONFIG_MOUSE
|
|||||||
obj-$(CONFIG_SANDBOX) += sandbox_mouse.o
|
obj-$(CONFIG_SANDBOX) += sandbox_mouse.o
|
||||||
endif
|
endif
|
||||||
obj-$(CONFIG_USB_MOUSE) += usb_mouse.o
|
obj-$(CONFIG_USB_MOUSE) += usb_mouse.o
|
||||||
|
obj-$(CONFIG_EFI_MOUSE) += efi_mouse.o
|
||||||
|
|||||||
221
drivers/input/efi_mouse.c
Normal file
221
drivers/input/efi_mouse.c
Normal 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),
|
||||||
|
};
|
||||||
@@ -112,6 +112,9 @@ class BuildEfi:
|
|||||||
extra += ['-device', 'qemu-xhci', '-device', 'usb-kbd',
|
extra += ['-device', 'qemu-xhci', '-device', 'usb-kbd',
|
||||||
'-device', 'usb-tablet']
|
'-device', 'usb-tablet']
|
||||||
extra += ['-display', 'default,show-cursor=on']
|
extra += ['-display', 'default,show-cursor=on']
|
||||||
|
else: # x86
|
||||||
|
extra += ['-device', 'qemu-xhci', '-device', 'usb-kbd',
|
||||||
|
'-device', 'usb-tablet']
|
||||||
extra += ['-serial', 'mon:stdio']
|
extra += ['-serial', 'mon:stdio']
|
||||||
serial_msg = ''
|
serial_msg = ''
|
||||||
if self.args.kvm:
|
if self.args.kvm:
|
||||||
|
|||||||
@@ -274,7 +274,11 @@ class BuildQemu:
|
|||||||
elif self.args.arch == 'arm':
|
elif self.args.arch == 'arm':
|
||||||
qemu_cmd.extend(['-device', 'virtio-gpu-pci'])
|
qemu_cmd.extend(['-device', 'virtio-gpu-pci'])
|
||||||
qemu_cmd.extend(['-device', 'qemu-xhci', '-device', 'usb-kbd',
|
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'])
|
qemu_cmd.extend(['-display', 'default,show-cursor=on'])
|
||||||
if not any(item.startswith('-serial') for item in self.qemu_extra):
|
if not any(item.startswith('-serial') for item in self.qemu_extra):
|
||||||
qemu_cmd.extend(['-serial', 'mon:stdio'])
|
qemu_cmd.extend(['-serial', 'mon:stdio'])
|
||||||
|
|||||||
Reference in New Issue
Block a user