Change the mouse_button structure to use a bool pressed field instead of an unsigned char press_state. This simplifies the API by using a natural boolean type for a binary state. Remove the BUTTON_PRESSED/BUTTON_RELEASED defines as they're no longer needed. Update all mouse drivers, tests, and the mouse command to use the new field name and type. Series-changes: 2 - Add new patch to replace press_state with bool pressed Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org>
222 lines
5.4 KiB
C
222 lines
5.4 KiB
C
// 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->pressed = (new_buttons & mask) ? true : false;
|
|
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),
|
|
};
|