[PATCH v2 00/20] expo: Complete mouse operation in the EFI app
From: Simon Glass <sjg@chromium.org> This series includes various improvements which allow the mouse to be used when running as an EFI app. In particular: - support for the absolute-pointer protocol, since this provides better integration when running under QEMU - input tweaks to improve performance under QEMU It also includes some x86-specific fixes for i8042 and MTRRs. Finally, a new --bootcmd option is added to the build-qemu script to allow passing a boot command to U-Boot. This series is part F Changes in v2: - Add new patch to replace mouse_press_state_t enum with bool - Add new patch to replace press_state with bool pressed - Add new patch to move click detection into mouse_get_event() Simon Glass (20): qfw: Export qfw_locate_file() for external use event: Add EVT_BOOTCMD event for custom boot commands x86: qemu: Add EVT_BOOTCMD handler to get bootcmd from fw_cfg scripts: build-qemu: Add --bootcmd option to pass bootcmd via fw_cfg mouse: Replace mouse_press_state_t enum with bool mouse: Replace press_state with bool pressed mouse: Move click detection into mouse_get_event() expo: Speed up polling the keyboard x86: efi: Avoid setting MTRRs in the app x86: efi: Disable i8042 in the x86 app efi: app: Support reading SMBIOS3 tables efi: app: Detect running under QEMU efi: serial: Speed up reading from input efi: video: Add some more debugging for the modes efi: mouse: Move simple-pointer code into a function efi: mouse: Move button handling into a function efi: mouse: Split out event handling further efi: Add definitions for the absolute-pointer protocol efi: mouse: Add support for an absolute pointer efi: mouse: Scale the pointer to the display arch/sandbox/cpu/sdl.c | 4 +- arch/x86/cpu/qemu/qemu.c | 31 +++ arch/x86/lib/init_helpers.c | 3 +- boot/expo.c | 16 +- cmd/mouse.c | 2 +- common/event.c | 1 + common/main.c | 32 ++- doc/board/emulation/script.rst | 15 ++ drivers/input/Kconfig | 2 +- drivers/input/efi_mouse.c | 426 +++++++++++++++++++++++------- drivers/input/mouse-uclass.c | 50 ++-- drivers/input/usb_mouse.c | 2 +- drivers/qfw/qfw.c | 4 +- drivers/serial/serial_efi.c | 45 ++-- drivers/video/efi.c | 15 +- include/asm-generic/global_data.h | 6 + include/efi.h | 1 + include/efi_api.h | 30 +++ include/event.h | 22 ++ include/expo.h | 2 + include/mouse.h | 17 +- include/qfw.h | 12 + lib/efi/device_path.c | 1 + lib/efi_client/efi_app.c | 33 ++- scripts/build-qemu | 7 + test/dm/mouse.c | 16 +- 26 files changed, 615 insertions(+), 180 deletions(-) -- 2.43.0 base-commit: 036ed95857fe5e3c0f8ff7d71fdc1653ae4d5069 branch: prof2
From: Simon Glass <sjg@chromium.org> Export the qfw_locate_file() function to allow other modules to locate files in the QEMU firmware config without loading them into memory. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) drivers/qfw/qfw.c | 4 ++-- include/qfw.h | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/qfw/qfw.c b/drivers/qfw/qfw.c index 6ac5b1cd625..2e260d60449 100644 --- a/drivers/qfw/qfw.c +++ b/drivers/qfw/qfw.c @@ -212,8 +212,8 @@ int qemu_fwcfg_setup_kernel(struct udevice *qfw_dev, ulong load_addr, return 0; } -static int qfw_locate_file(struct udevice *dev, const char *fname, - enum fw_cfg_selector *selectp, ulong *sizep) +int qfw_locate_file(struct udevice *dev, const char *fname, + enum fw_cfg_selector *selectp, ulong *sizep) { struct fw_file *file; int ret; diff --git a/include/qfw.h b/include/qfw.h index c1f558b6f87..75aada09206 100644 --- a/include/qfw.h +++ b/include/qfw.h @@ -464,6 +464,18 @@ int qfw_load_file(struct udevice *dev, const char *fname, ulong addr); */ int qfw_get_file(struct udevice *dev, const char *fname, struct abuf *loader); +/** + * qfw_locate_file() - Locate a file in the QEMU firmware config + * + * @dev: UCLASS_QFW device + * @fname: Filename to locate + * @selectp: Returns the selector for the file + * @sizep: Returns the size of the file + * Return: 0 on success, -EINVAL if firmware list cannot be read or file not found + */ +int qfw_locate_file(struct udevice *dev, const char *fname, + enum fw_cfg_selector *selectp, ulong *sizep); + /** * cmd_qfw_e820() - Execute the 'qfw e820' command for x86 * -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a new event that is triggered in main_loop() before autoboot_command(). This allows platform code to provide a custom bootcmd string via the event system. The event uses struct event_bootcmd which provides a buffer for the bootcmd string and its size. Platform event handlers can write their custom bootcmd to this buffer. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) common/event.c | 1 + common/main.c | 32 +++++++++++++++++++++++++++++++- include/event.h | 22 ++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/common/event.c b/common/event.c index a48ca6c549d..44f32035775 100644 --- a/common/event.c +++ b/common/event.c @@ -49,6 +49,7 @@ const char *const type_name[] = { /* main loop events */ "main_loop", + "bootcmd", /* booting */ "boot_os_addr", diff --git a/common/main.c b/common/main.c index b0b6e74f5d3..06e5193ecb9 100644 --- a/common/main.c +++ b/common/main.c @@ -14,6 +14,7 @@ #include <command.h> #include <console.h> #include <env.h> +#include <event.h> #include <fdtdec.h> #include <init.h> #include <net.h> @@ -38,10 +39,35 @@ static void run_preboot_environment_command(void) } } +static const char *get_autoboot_cmd(char *buf, int size) +{ + const char *s = NULL; + + if (IS_ENABLED(CONFIG_EVENT)) { + struct event_bootcmd event_bootcmd; + int ret; + + event_bootcmd.bootcmd = buf; + event_bootcmd.size = size; + buf[0] = '\0'; + + ret = event_notify(EVT_BOOTCMD, &event_bootcmd, + sizeof(event_bootcmd)); + if (ret) + return NULL; + + if (buf[0] != '\0') + s = buf; + } + + return s; +} + /* We come here after U-Boot is initialised and ready to process commands */ void main_loop(void) { const char *s; + char bootcmd_buf[CONFIG_SYS_CBSIZE]; bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); @@ -64,7 +90,11 @@ void main_loop(void) process_button_cmds(); - s = bootdelay_process(); + /* Allow platform code to provide bootcmd via event */ + s = get_autoboot_cmd(bootcmd_buf, sizeof(bootcmd_buf)); + if (!s) + s = bootdelay_process(); + if (cli_process_fdt(&s)) cli_secure_boot_cmd(s); diff --git a/include/event.h b/include/event.h index 5fecaa66e80..2f0eac61633 100644 --- a/include/event.h +++ b/include/event.h @@ -172,6 +172,16 @@ enum event_t { */ EVT_MAIN_LOOP, + /** + * @EVT_BOOTCMD: + * This event is triggered in main_loop() before autoboot_command(). + * It allows platform code to provide a custom bootcmd string. + * Its parameter is of type struct event_bootcmd. + * The event handler can write the bootcmd to the provided buffer. + * A non-zero return value causes the boot to fail. + */ + EVT_BOOTCMD, + /** * @EVT_BOOT_OS_ADDR: * Triggered immediately before the OS is loaded into its final address @@ -270,6 +280,18 @@ union event_data { struct event_bootm_final { enum bootm_final_t flags; } bootm_final; + + /** + * struct event_bootcmd - bootcmd override + * + * @bootcmd: Buffer for bootcmd string (provided by caller, must be an + * empty string on entry) + * @size: Size of bootcmd buffer + */ + struct event_bootcmd { + char *bootcmd; + int size; + } bootcmd; }; /** -- 2.43.0
From: Simon Glass <sjg@chromium.org> Implement an EVT_BOOTCMD event handler that reads the bootcmd from QEMU's fw_cfg interface using the "opt/u-boot/bootcmd" file. This allows QEMU to provide a custom boot command to U-Boot at runtime. Uses the newly exported qfw_locate_file() function to locate and read the bootcmd file. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) arch/x86/cpu/qemu/qemu.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/arch/x86/cpu/qemu/qemu.c b/arch/x86/cpu/qemu/qemu.c index e846ccd44aa..24916d867ee 100644 --- a/arch/x86/cpu/qemu/qemu.c +++ b/arch/x86/cpu/qemu/qemu.c @@ -4,6 +4,9 @@ */ #include <cpu_func.h> +#include <env.h> +#include <errno.h> +#include <event.h> #include <init.h> #include <pci.h> #include <qfw.h> @@ -150,3 +153,31 @@ int mp_determine_pci_dstirq(int bus, int dev, int func, int pirq) return irq; } #endif + +#if CONFIG_IS_ENABLED(EVENT) +static int qemu_get_bootcmd(void *ctx, struct event *event) +{ + struct event_bootcmd *bc = &event->data.bootcmd; + enum fw_cfg_selector select; + struct udevice *qfw_dev; + ulong size; + + if (qfw_get_dev(&qfw_dev)) + return 0; + + if (qfw_locate_file(qfw_dev, "opt/u-boot/bootcmd", &select, &size)) + return 0; + if (!size) + return 0; + + /* Check if the command fits in the provided buffer with terminator */ + if (size >= bc->size) + return -ENOSPC; + + qfw_read_entry(qfw_dev, select, size, bc->bootcmd); + bc->bootcmd[size] = '\0'; + + return 0; +} +EVENT_SPY_FULL(EVT_BOOTCMD, qemu_get_bootcmd); +#endif -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a --bootcmd option to the build-qemu script that allows passing a boot command to QEMU via fw_cfg. The bootcmd is written to the "opt/u-boot/bootcmd" fw_cfg entry, which can be read by U-Boot's EVT_BOOTCMD handler. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) doc/board/emulation/script.rst | 15 +++++++++++++++ scripts/build-qemu | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/doc/board/emulation/script.rst b/doc/board/emulation/script.rst index cf718ad41a7..239b1e1718d 100644 --- a/doc/board/emulation/script.rst +++ b/doc/board/emulation/script.rst @@ -64,6 +64,14 @@ Once configured, you can build and run QEMU for arm64 like this:: scripts/build-qemu -rsw +To pass a custom boot command to U-Boot via fw_cfg, use the `--bootcmd` +option:: + + scripts/build-qemu -rsw --bootcmd "echo Hello from QEMU; bootflow scan -lb" + +This will cause U-Boot to execute the specified command instead of the default +autoboot behavior. + Options ~~~~~~~ @@ -107,3 +115,10 @@ Options are available to control the script: -w Use word version (32-bit). By default, 64-bit is used + +--bootcmd BOOTCMD + U-Boot bootcmd to pass via fw_cfg. This allows passing a custom boot + command to U-Boot at runtime through QEMU's firmware configuration + interface. The bootcmd is written to the 'opt/u-boot/bootcmd' fw_cfg + entry and is read by U-Boot's EVT_BOOTCMD handler before the default + autoboot process runs. diff --git a/scripts/build-qemu b/scripts/build-qemu index 522325a8d57..ddaafc7587f 100755 --- a/scripts/build-qemu +++ b/scripts/build-qemu @@ -41,6 +41,8 @@ def parse_args(): description='Build and/or run U-Boot with QEMU', formatter_class=argparse.RawTextHelpFormatter) build_helper.add_common_args(parser) + parser.add_argument('--bootcmd', type=str, + help='U-Boot bootcmd to pass via fw_cfg') parser.add_argument('-e', '--sct-run', action='store_true', help='Run UEFI Self-Certification Test (SCT)') parser.add_argument('-E', '--use-tianocore', action='store_true', @@ -290,6 +292,11 @@ class BuildQemu: # Add other parameters gathered from options qemu_cmd.extend(self.qemu_extra) + # Add bootcmd via fw_cfg if specified + if self.args.bootcmd: + qemu_cmd.extend(['-fw_cfg', + f'name=opt/u-boot/bootcmd,string={self.args.bootcmd}']) + self.helper.setup_share(qemu_cmd) self.helper.run(qemu_cmd) -- 2.43.0
From: Simon Glass <sjg@chromium.org> Replace the mouse_press_state_t enum with a simpler bool left_pressed field in mouse_uc_priv. Convert BUTTON_PRESSED/BUTTON_RELEASED to defines since they're still used in the mouse_event button structure. This simplifies the code by directly tracking whether the left button is pressed rather than using an enum to represent a binary state. Clarify the comment to struct mouse_uc_priv while here. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- Changes in v2: - Add new patch to replace mouse_press_state_t enum with bool drivers/input/mouse-uclass.c | 7 +++---- include/mouse.h | 12 +++++------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/input/mouse-uclass.c b/drivers/input/mouse-uclass.c index 4ade394d68a..43b6514f926 100644 --- a/drivers/input/mouse-uclass.c +++ b/drivers/input/mouse-uclass.c @@ -51,19 +51,18 @@ int mouse_get_click(struct udevice *dev, struct vid_pos *pos) /* Only process button events for left button */ if (event.type == MOUSE_EV_BUTTON && event.button.button == BUTTON_LEFT) { - enum mouse_press_state_t new_state = event.button.press_state; + bool pressed = event.button.press_state == BUTTON_PRESSED; bool pending = false; /* Detect press->release transition (click) */ - if (uc_priv->left_button_state == BUTTON_PRESSED && - new_state == BUTTON_RELEASED) { + if (uc_priv->left_pressed && !pressed) { pending = true; uc_priv->click_pos.x = event.button.x; uc_priv->click_pos.y = event.button.y; } /* Update button state */ - uc_priv->left_button_state = new_state; + uc_priv->left_pressed = pressed; /* If we just detected a click, return it */ if (pending) { diff --git a/include/mouse.h b/include/mouse.h index 560c7d14587..96b75ae1fa6 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -27,15 +27,13 @@ enum mouse_state_t { BUTTON_SCROLL_MINUS = 1 << 4, }; -enum mouse_press_state_t { - BUTTON_RELEASED = 0, - BUTTON_PRESSED, -}; +#define BUTTON_RELEASED 0 +#define BUTTON_PRESSED 1 /** - * struct mouse_uc_priv - private data for mouse uclass + * struct mouse_uc_priv - pre-device private data for mouse uclass * - * @left_button_state: Current state of left button (BUTTON_PRESSED/BUTTON_RELEASED) + * @left_pressed: True if left button is currently pressed * @click_pos: Position where the click occurred * @last_pos: Last position received from mouse * @video_dev: Video device for coordinate scaling @@ -43,7 +41,7 @@ enum mouse_press_state_t { * @video_height: Height of video display */ struct mouse_uc_priv { - enum mouse_press_state_t left_button_state; + bool left_pressed; struct vid_pos click_pos; struct vid_pos last_pos; struct udevice *video_dev; -- 2.43.0
From: Simon Glass <sjg@chromium.org> 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. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- Changes in v2: - Add new patch to replace press_state with bool pressed arch/sandbox/cpu/sdl.c | 4 +--- cmd/mouse.c | 2 +- drivers/input/efi_mouse.c | 2 +- drivers/input/mouse-uclass.c | 5 ++--- drivers/input/usb_mouse.c | 2 +- include/mouse.h | 7 ++----- test/dm/mouse.c | 16 ++++++++-------- 7 files changed, 16 insertions(+), 22 deletions(-) diff --git a/arch/sandbox/cpu/sdl.c b/arch/sandbox/cpu/sdl.c index c3745fa660e..dd2589c64e2 100644 --- a/arch/sandbox/cpu/sdl.c +++ b/arch/sandbox/cpu/sdl.c @@ -110,9 +110,7 @@ static void sandbox_sdl_poll_events(void) m->button.button = BUTTON_MIDDLE; else if (event.button.button == SDL_BUTTON_RIGHT) m->button.button = BUTTON_RIGHT; - m->button.press_state = event.type == - SDL_MOUSEBUTTONDOWN ? - BUTTON_PRESSED : BUTTON_RELEASED; + m->button.pressed = event.type == SDL_MOUSEBUTTONDOWN; m->button.x = event.button.x; m->button.y = event.motion.y; break; diff --git a/cmd/mouse.c b/cmd/mouse.c index 60210b9f868..ebf496307b9 100644 --- a/cmd/mouse.c +++ b/cmd/mouse.c @@ -35,7 +35,7 @@ static int do_mouse_dump(struct cmd_tbl *cmdtp, int flag, int argc, struct mouse_button *but = &evt.button; printf("button: button==%d, press=%d, clicks=%d, X=%d, Y=%d\n", - but->button, but->press_state, + but->button, but->pressed, but->clicks, but->x, but->y); break; } diff --git a/drivers/input/efi_mouse.c b/drivers/input/efi_mouse.c index 84347951f62..077e2148da9 100644 --- a/drivers/input/efi_mouse.c +++ b/drivers/input/efi_mouse.c @@ -78,7 +78,7 @@ static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event) u8 mask = 1 << i; if (diff & mask) { but->button = i; - but->press_state = (new_buttons & mask) ? 1 : 0; + but->pressed = (new_buttons & mask) ? true : false; but->clicks = 1; but->x = priv->x; but->y = priv->y; diff --git a/drivers/input/mouse-uclass.c b/drivers/input/mouse-uclass.c index 43b6514f926..6bfce915e2b 100644 --- a/drivers/input/mouse-uclass.c +++ b/drivers/input/mouse-uclass.c @@ -51,18 +51,17 @@ int mouse_get_click(struct udevice *dev, struct vid_pos *pos) /* Only process button events for left button */ if (event.type == MOUSE_EV_BUTTON && event.button.button == BUTTON_LEFT) { - bool pressed = event.button.press_state == BUTTON_PRESSED; bool pending = false; /* Detect press->release transition (click) */ - if (uc_priv->left_pressed && !pressed) { + if (uc_priv->left_pressed && !event.button.pressed) { pending = true; uc_priv->click_pos.x = event.button.x; uc_priv->click_pos.y = event.button.y; } /* Update button state */ - uc_priv->left_pressed = pressed; + uc_priv->left_pressed = event.button.pressed; /* If we just detected a click, return it */ if (pending) { diff --git a/drivers/input/usb_mouse.c b/drivers/input/usb_mouse.c index 74f093a4b5a..456d84d5e39 100644 --- a/drivers/input/usb_mouse.c +++ b/drivers/input/usb_mouse.c @@ -142,7 +142,7 @@ static int usb_mouse_get_event(struct udevice *dev, struct mouse_event *event) if (diff && mask) { but->button = i; - but->press_state = priv->buttons & mask; + but->pressed = priv->buttons & mask; but->clicks = 1; but->x = priv->x; but->y = priv->y; diff --git a/include/mouse.h b/include/mouse.h index 96b75ae1fa6..62cabf24769 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -27,9 +27,6 @@ enum mouse_state_t { BUTTON_SCROLL_MINUS = 1 << 4, }; -#define BUTTON_RELEASED 0 -#define BUTTON_PRESSED 1 - /** * struct mouse_uc_priv - pre-device private data for mouse uclass * @@ -74,14 +71,14 @@ struct mouse_event { /** * @button: Button number that was pressed/released (BUTTON_...) - * @state: BUTTON_PRESSED / BUTTON_RELEASED + * @pressed: True if button was pressed, false if released * @clicks: number of clicks (normally 1; 2 = double-click) * @x: X position of mouse * @y: Y position of mouse */ struct mouse_button { unsigned char button; - unsigned char press_state; + bool pressed; unsigned char clicks; unsigned short x; unsigned short y; diff --git a/test/dm/mouse.c b/test/dm/mouse.c index c22cd026cb2..893644005cc 100644 --- a/test/dm/mouse.c +++ b/test/dm/mouse.c @@ -75,7 +75,7 @@ static int dm_test_mouse_button(struct unit_test_state *uts) /* inject a button press event */ inject.type = MOUSE_EV_BUTTON; inject.button.button = BUTTON_LEFT; - inject.button.press_state = BUTTON_PRESSED; + inject.button.pressed = true; inject.button.clicks = 1; inject.button.x = 150; inject.button.y = 250; @@ -86,7 +86,7 @@ static int dm_test_mouse_button(struct unit_test_state *uts) ut_assertok(mouse_get_event(dev, &event)); ut_asserteq(MOUSE_EV_BUTTON, event.type); ut_asserteq(BUTTON_LEFT, event.button.button); - ut_asserteq(BUTTON_PRESSED, event.button.press_state); + ut_asserteq(true, event.button.pressed); ut_asserteq(1, event.button.clicks); ut_asserteq(150, event.button.x); ut_asserteq(250, event.button.y); @@ -112,7 +112,7 @@ static int dm_test_mouse_click(struct unit_test_state *uts) /* inject a left button press */ inject.type = MOUSE_EV_BUTTON; inject.button.button = BUTTON_LEFT; - inject.button.press_state = BUTTON_PRESSED; + inject.button.pressed = true; inject.button.clicks = 1; inject.button.x = 300; inject.button.y = 400; @@ -128,7 +128,7 @@ static int dm_test_mouse_click(struct unit_test_state *uts) /* inject a left button release */ inject.type = MOUSE_EV_BUTTON; inject.button.button = BUTTON_LEFT; - inject.button.press_state = BUTTON_RELEASED; + inject.button.pressed = false; inject.button.clicks = 1; inject.button.x = 300; inject.button.y = 400; @@ -161,7 +161,7 @@ static int dm_test_mouse_click_no_coordinates(struct unit_test_state *uts) /* inject press and release to create a click */ inject.type = MOUSE_EV_BUTTON; inject.button.button = BUTTON_LEFT; - inject.button.press_state = BUTTON_PRESSED; + inject.button.pressed = true; inject.button.clicks = 1; inject.button.x = 500; inject.button.y = 600; @@ -170,7 +170,7 @@ static int dm_test_mouse_click_no_coordinates(struct unit_test_state *uts) /* process the press event */ ut_asserteq(-EAGAIN, mouse_get_click(dev, &pos)); - inject.button.press_state = BUTTON_RELEASED; + inject.button.pressed = false; sandbox_mouse_inject(dev, &inject); /* now test that click is detected (coordinates are ignored) */ @@ -197,7 +197,7 @@ static int dm_test_mouse_right_button(struct unit_test_state *uts) */ inject.type = MOUSE_EV_BUTTON; inject.button.button = BUTTON_RIGHT; - inject.button.press_state = BUTTON_PRESSED; + inject.button.pressed = true; inject.button.clicks = 1; inject.button.x = 100; inject.button.y = 200; @@ -205,7 +205,7 @@ static int dm_test_mouse_right_button(struct unit_test_state *uts) ut_asserteq(-EAGAIN, mouse_get_click(dev, &pos)); - inject.button.press_state = BUTTON_RELEASED; + inject.button.pressed = false; sandbox_mouse_inject(dev, &inject); /* still no click detected since it was right button */ -- 2.43.0
From: Simon Glass <sjg@chromium.org> Currently mouse_get_click() processes events by calling mouse_get_event() and tracking the press->release transition. But if other code calls mouse_get_event() directly, those button events are consumed and mouse_get_click() never sees them. Fix this by moving the click detection logic into mouse_get_event() itself. Add a click_pending flag to track when a click has been detected, and simplify mouse_get_click() to just check and clear this flag. This ensures clicks are properly registered regardless of whether callers use mouse_get_event() or mouse_get_click(). Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- Changes in v2: - Add new patch to move click detection into mouse_get_event() drivers/input/mouse-uclass.c | 48 +++++++++++++++++------------------- include/mouse.h | 2 ++ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/input/mouse-uclass.c b/drivers/input/mouse-uclass.c index 6bfce915e2b..7cbe961af35 100644 --- a/drivers/input/mouse-uclass.c +++ b/drivers/input/mouse-uclass.c @@ -28,10 +28,23 @@ int mouse_get_event(struct udevice *dev, struct mouse_event *evt) uc_priv->last_pos.y = evt->motion.y; } - /* Update last position for button events */ + /* Update last position for button events and detect clicks */ if (evt->type == MOUSE_EV_BUTTON) { uc_priv->last_pos.x = evt->button.x; uc_priv->last_pos.y = evt->button.y; + + /* Process left-button clicks */ + if (evt->button.button == BUTTON_LEFT) { + /* Detect press->release transition (click) */ + if (uc_priv->left_pressed && !evt->button.pressed) { + uc_priv->click_pending = true; + uc_priv->click_pos.x = evt->button.x; + uc_priv->click_pos.y = evt->button.y; + } + + /* Update button state */ + uc_priv->left_pressed = evt->button.pressed; + } } return 0; @@ -41,36 +54,21 @@ int mouse_get_click(struct udevice *dev, struct vid_pos *pos) { struct mouse_uc_priv *uc_priv = dev_get_uclass_priv(dev); struct mouse_event event; - int ret; - /* Get one mouse event */ - ret = mouse_get_event(dev, &event); - if (ret) - return -EAGAIN; /* No event available */ - - /* Only process button events for left button */ - if (event.type == MOUSE_EV_BUTTON && - event.button.button == BUTTON_LEFT) { - bool pending = false; - - /* Detect press->release transition (click) */ - if (uc_priv->left_pressed && !event.button.pressed) { - pending = true; - uc_priv->click_pos.x = event.button.x; - uc_priv->click_pos.y = event.button.y; - } - - /* Update button state */ - uc_priv->left_pressed = event.button.pressed; + /* Process all available events until we find a click */ + while (true) { + if (mouse_get_event(dev, &event)) + return -EAGAIN; /* No more events */ - /* If we just detected a click, return it */ - if (pending) { + /* Check if this event resulted in a click */ + if (uc_priv->click_pending) { *pos = uc_priv->click_pos; - return 0; + uc_priv->click_pending = false; + break; } } - return -EAGAIN; + return 0; } int mouse_get_pos(struct udevice *dev, struct vid_pos *pos) diff --git a/include/mouse.h b/include/mouse.h index 62cabf24769..1a3a93801e2 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -31,6 +31,7 @@ enum mouse_state_t { * struct mouse_uc_priv - pre-device private data for mouse uclass * * @left_pressed: True if left button is currently pressed + * @click_pending: True if a click has occurred but not yet retrieved * @click_pos: Position where the click occurred * @last_pos: Last position received from mouse * @video_dev: Video device for coordinate scaling @@ -39,6 +40,7 @@ enum mouse_state_t { */ struct mouse_uc_priv { bool left_pressed; + bool click_pending; struct vid_pos click_pos; struct vid_pos last_pos; struct udevice *video_dev; -- 2.43.0
From: Simon Glass <sjg@chromium.org> The current algorithm spends 10ms or more looking for keyboard input. Since we expect the frame rate to be 50 or more, it should be OK to only check once. Handle the timeout by recording the timestamp of the last key and timing out the CLI entry after 10ms. This allows an 'Escape' key to work, rather than it just waiting forever for a terminal sequence that starts with escape. Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) boot/expo.c | 16 ++++++++-------- include/expo.h | 2 ++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/boot/expo.c b/boot/expo.c index ebe31059e87..5704dd9fecc 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -16,6 +16,7 @@ #include <mapmem.h> #include <menu.h> #include <mouse.h> +#include <time.h> #include <video.h> #include <watchdog.h> #include <linux/delay.h> @@ -45,6 +46,7 @@ int expo_new(const char *name, void *priv, struct expo **expp) INIT_LIST_HEAD(&exp->str_head); exp->next_id = EXPOID_BASE_ID; cli_ch_init(&exp->cch); + exp->last_key_ms = get_timer(0); *expp = exp; @@ -488,21 +490,19 @@ static int poll_keys(struct expo *exp) ichar = cli_ch_process(&exp->cch, 0); if (!ichar) { - int i; - - for (i = 0; i < 10 && !ichar && !tstc(); i++) { - schedule(); - mdelay(2); - ichar = cli_ch_process(&exp->cch, -ETIMEDOUT); - } - while (!ichar && tstc()) { + /* Check once for available input */ + if (tstc()) { ichar = getchar(); ichar = cli_ch_process(&exp->cch, ichar); } + + if (!ichar && get_timer(exp->last_key_ms) >= 10) + ichar = cli_ch_process(&exp->cch, -ETIMEDOUT); } key = 0; if (ichar) { + exp->last_key_ms = get_timer(0); key = bootmenu_conv_key(ichar); if (key == BKEY_NONE || key >= BKEY_FIRST_EXTRA) key = ichar; diff --git a/include/expo.h b/include/expo.h index e9e71f4fe36..b51d946f367 100644 --- a/include/expo.h +++ b/include/expo.h @@ -146,6 +146,7 @@ struct expo_theme { * @scene_head: List of scenes * @str_head: list of strings * @cch: Keyboard context for input + * @last_key_ms: timestamp of the last key received */ struct expo { char *name; @@ -173,6 +174,7 @@ struct expo { struct list_head scene_head; struct list_head str_head; struct cli_ch_state cch; + ulong last_key_ms; }; /** -- 2.43.0
From: Simon Glass <sjg@chromium.org> The MTRRs have already been set by the previous phase so we should not set them when the U-Boot app starts. Add a condition to prevent it. Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) arch/x86/lib/init_helpers.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/lib/init_helpers.c b/arch/x86/lib/init_helpers.c index bd0efde00c1..4384c10f697 100644 --- a/arch/x86/lib/init_helpers.c +++ b/arch/x86/lib/init_helpers.c @@ -29,7 +29,8 @@ int init_cache_f_r(void) * the MTRRs here */ do_mtrr &= !IS_ENABLED(CONFIG_FSP_VERSION1) && - !IS_ENABLED(CONFIG_SYS_SLIMBOOTLOADER); + !IS_ENABLED(CONFIG_SYS_SLIMBOOTLOADER) && + !IS_ENABLED(CONFIG_EFI_APP); if (do_mtrr) { ret = mtrr_commit(false); -- 2.43.0
From: Simon Glass <sjg@chromium.org> We should not try to directly access the keyboard when running as an EFI app. Disable CONFIG_I8042_KEYB option to prevent that. Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) drivers/input/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 6e19f6f7b3d..34eec07aac7 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -96,7 +96,7 @@ config TPL_CROS_EC_KEYB config I8042_KEYB bool "Enable Intel i8042 keyboard support" depends on DM_KEYBOARD - default X86 + default X86 && !EFI_APP help This adds a driver for the i8042 keyboard controller, allowing the keyboard to be used on devices which support this controller. The -- 2.43.0
From: Simon Glass <sjg@chromium.org> Check for both GUIDs when looking for the SMBIOS tables. This allows both table versions to be detected when running from OVMF. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) lib/efi_client/efi_app.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/efi_client/efi_app.c b/lib/efi_client/efi_app.c index c851173f1ae..00003c27ec5 100644 --- a/lib/efi_client/efi_app.c +++ b/lib/efi_client/efi_app.c @@ -164,7 +164,8 @@ static void free_memory(struct efi_priv *priv) static void scan_tables(struct efi_system_table *sys_table) { efi_guid_t acpi = EFI_ACPI_TABLE_GUID; - efi_guid_t smbios = SMBIOS3_TABLE_GUID; + efi_guid_t smbios = SMBIOS_TABLE_GUID; + efi_guid_t smbios3 = SMBIOS3_TABLE_GUID; uint i; for (i = 0; i < sys_table->nr_tables; i++) { @@ -172,8 +173,9 @@ static void scan_tables(struct efi_system_table *sys_table) if (!memcmp(&tab->guid, &acpi, sizeof(efi_guid_t))) gd_set_acpi_start(map_to_sysmem(tab->table)); - else if (!memcmp(&tab->guid, &smbios, sizeof(efi_guid_t))) - gd->arch.smbios_start = map_to_sysmem(tab->table); + else if (!memcmp(&tab->guid, &smbios, sizeof(efi_guid_t)) || + !memcmp(&tab->guid, &smbios3, sizeof(efi_guid_t))) + gd_set_smbios_start(map_to_sysmem(tab->table)); } } -- 2.43.0
From: Simon Glass <sjg@chromium.org> When the app is running under QEMU we may wish to do some things differently. Add a flag for this and use the SMBIOS tables to detect it. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) include/asm-generic/global_data.h | 6 ++++++ lib/efi_client/efi_app.c | 25 ++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 8b59df66ec5..cff9066de53 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -764,6 +764,12 @@ enum gd_flags { * For now, this just avoids console output on startup */ GD_FLG_ULIB = 0x10000000, + /** + * @GD_FLG_EMUL: Running in an emulator (e.g. QEMU) + * + * Detected from SMBIOS or other platform information + */ + GD_FLG_EMUL = 0x20000000, }; #if CONFIG_IS_ENABLED(ULIB) diff --git a/lib/efi_client/efi_app.c b/lib/efi_client/efi_app.c index 00003c27ec5..1d5a88b4827 100644 --- a/lib/efi_client/efi_app.c +++ b/lib/efi_client/efi_app.c @@ -22,6 +22,7 @@ #include <image.h> #include <init.h> #include <malloc.h> +#include <smbios.h> #include <sysreset.h> #include <u-boot/uuid.h> #include <asm/global_data.h> @@ -179,6 +180,27 @@ static void scan_tables(struct efi_system_table *sys_table) } } +static bool detect_emulator(void) +{ + struct smbios_info info; + struct smbios_type1 *t1; + const char *manufacturer; + + /* Check if running in QEMU by looking at SMBIOS manufacturer */ + if (!smbios_locate(gd_smbios_start(), &info)) { + t1 = (void *)smbios_get_header(&info, + SMBIOS_SYSTEM_INFORMATION); + if (t1) { + manufacturer = smbios_get_string(&t1->hdr, + t1->manufacturer); + if (manufacturer && !strcmp(manufacturer, "QEMU")) + return true; + } + } + + return false; +} + static void find_protocols(struct efi_priv *priv) { efi_guid_t guid = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; @@ -415,7 +437,8 @@ efi_status_t EFIAPI efi_main(efi_handle_t image, printf("starting\n"); - board_init_f(GD_FLG_SKIP_RELOC); + board_init_f(GD_FLG_SKIP_RELOC | + (detect_emulator() ? GD_FLG_EMUL : 0)); gd = gd->new_gd; board_init_r(NULL, 0); free_memory(priv); -- 2.43.0
From: Simon Glass <sjg@chromium.org> The serial driver is currently quite slow since it sets an event and waits for at least 1ms for a keypress, which may never come. On real hardware, the keyboard does not work correctly wihout waiting for events. Reduce the time to 10us which should be enough to provide an opportunity for event processing. QEMU does not actually have this problem, so detect that and skip the processing altogether. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) drivers/serial/serial_efi.c | 45 ++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/drivers/serial/serial_efi.c b/drivers/serial/serial_efi.c index 6645520e2ca..89df6a9dbb4 100644 --- a/drivers/serial/serial_efi.c +++ b/drivers/serial/serial_efi.c @@ -12,9 +12,12 @@ #include <fdtdec.h> #include <log.h> #include <linux/compiler.h> +#include <asm/global_data.h> #include <asm/io.h> #include <serial.h> +DECLARE_GLOBAL_DATA_PTR; + /* Information about the efi console */ struct serial_efi_priv { struct efi_simple_text_input_protocol *con_in; @@ -44,26 +47,38 @@ int serial_efi_setbrg(struct udevice *dev, int baudrate) static int serial_efi_get_key(struct serial_efi_priv *priv) { struct efi_simple_text_input_protocol *cin = priv->con_in; - struct efi_event *events[2] = {cin->wait_for_key, priv->timer}; - efi_uintn_t index; efi_status_t ret; if (priv->have_key) return 0; - ret = priv->boot->wait_for_event(2, events, &index); - if (ret) { - log_err("wait_for_event() failed\n"); - return -EAGAIN; + /* For emulators like QEMU, skip the timer wait since USB event + * processing is not needed + */ + if (gd->flags & GD_FLG_EMUL) { + ret = priv->con_in->read_key_stroke(priv->con_in, &priv->key); + if (ret == EFI_NOT_READY) + return -EAGAIN; + else if (ret != EFI_SUCCESS) + return -EIO; + } else { + struct efi_event *events[2] = {cin->wait_for_key, priv->timer}; + efi_uintn_t index; + + ret = priv->boot->wait_for_event(2, events, &index); + if (ret) { + log_err("wait_for_event() failed\n"); + return -EAGAIN; + } + if (index) + return -EAGAIN; + + ret = priv->con_in->read_key_stroke(priv->con_in, &priv->key); + if (ret == EFI_NOT_READY) + return -EAGAIN; + else if (ret != EFI_SUCCESS) + return -EIO; } - if (index) - return -EAGAIN; - - ret = priv->con_in->read_key_stroke(priv->con_in, &priv->key); - if (ret == EFI_NOT_READY) - return -EAGAIN; - else if (ret != EFI_SUCCESS) - return -EIO; priv->have_key = true; @@ -155,7 +170,7 @@ static int serial_efi_probe(struct udevice *dev) if (ret) return -ECOMM; ret = boot->set_timer(priv->timer, EFI_TIMER_PERIODIC, - 1000 * 1000 / 100 /* 1ms in 100ns units */); + 100 /* 10μs in 100ns units */); if (ret) return -ECOMM; -- 2.43.0
From: Simon Glass <sjg@chromium.org> This driver can operate in various modes, e.g. with VIDEO_COPY and with different colour depths or in blit mode. Add some debugging to help debug problems on particular boards. Fix an errant blank line while we are here. Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) drivers/video/efi.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/video/efi.c b/drivers/video/efi.c index b5e472395e9..bfa367cc7ea 100644 --- a/drivers/video/efi.c +++ b/drivers/video/efi.c @@ -11,6 +11,7 @@ #include <efi_api.h> #include <efi_stub.h> #include <log.h> +#include <mapmem.h> #include <vesa.h> #include <video.h> @@ -60,6 +61,10 @@ static int efi_video_sync(struct udevice *dev, uint flags) return -EIO; } } + /* + * When use_blit is false and CONFIG_VIDEO_COPY is enabled, + * video_manual_sync() handles copying to copy_fb + */ return 0; } @@ -252,7 +257,9 @@ static int efi_video_probe(struct udevice *dev) goto err; printf("Video: %dx%dx%d @ %lx\n", uc_priv->xsize, uc_priv->ysize, - vesa->bits_per_pixel, (ulong)priv->fb); + vesa->bits_per_pixel, (ulong)uc_priv->fb); + log_debug("use_blit %d, copy_base %lx, copy_fb %p\n", priv->use_blit, + (ulong)plat->copy_base, uc_priv->copy_fb); return 0; @@ -267,7 +274,6 @@ static int efi_video_bind(struct udevice *dev) { struct video_uc_plat *plat = dev_get_uclass_plat(dev); struct efi_video_priv tmp_priv; - struct vesa_mode_info vesa; int ret; @@ -282,10 +288,13 @@ static int efi_video_bind(struct udevice *dev) /* this is not reached if the EFI call failed */ plat->copy_size = vesa.bytes_per_scanline * vesa.y_resolution; + log_debug("copy size %lx\n", plat->copy_size); } } - if (tmp_priv.use_blit) + if (tmp_priv.use_blit) { plat->size = vesa.bytes_per_scanline * vesa.y_resolution; + log_debug("plat size %x\n", plat->size); + } return 0; } -- 2.43.0
From: Simon Glass <sjg@chromium.org> In preparation for supporting an absolute pointer, move the setup of the simple pointer into its own function. Rename 'pointer' to simple and last_state to simple_last. Take this opportunity to add comments for struct efi_mouse_priv since it will be less obvious once the new support is added. Clean up some unnecessary cruft in the remove() path. Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) drivers/input/efi_mouse.c | 103 +++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 41 deletions(-) diff --git a/drivers/input/efi_mouse.c b/drivers/input/efi_mouse.c index 077e2148da9..5fdf6979b8f 100644 --- a/drivers/input/efi_mouse.c +++ b/drivers/input/efi_mouse.c @@ -14,9 +14,24 @@ #include <log.h> #include <mouse.h> +/* Maximum coordinate value for mouse position */ +#define MOUSE_MAX_COORD 0xffff + +/** + * struct efi_mouse_priv - Private data for EFI mouse driver + * + * @simple: Simple pointer protocol (relative movement) + * @simple_last: Last simple pointer state + * @has_last_state: True if we have a previous state for delta calculation + * @x: Current X position + * @y: Current Y position + * @buttons: Current button state + * @old_buttons: Previous button state for detecting changes + * @timer_event: EFI timer event for periodic polling + */ struct efi_mouse_priv { - struct efi_simple_pointer_protocol *pointer; - struct efi_simple_pointer_state last_state; + struct efi_simple_pointer_protocol *simple; + struct efi_simple_pointer_state simple_last; bool has_last_state; int x, y; int buttons; @@ -30,7 +45,7 @@ static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event) struct efi_simple_pointer_state state; efi_status_t ret; int new_buttons; - if (!priv->pointer) + if (!priv->simple) return -ENODEV; /* Use timer-based polling approach like EFI keyboard */ @@ -41,8 +56,8 @@ static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event) efi_uintn_t num_events = 1; events[0] = priv->timer_event; - if (priv->pointer->wait_for_input) { - events[1] = priv->pointer->wait_for_input; + if (priv->simple->wait_for_input) { + events[1] = priv->simple->wait_for_input; num_events = 2; } @@ -52,7 +67,7 @@ static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event) } /* Get current pointer state */ - ret = priv->pointer->get_state(priv->pointer, &state); + ret = priv->simple->get_state(priv->simple, &state); if (ret != EFI_SUCCESS) { if (ret == EFI_NOT_READY) return -EAGAIN; @@ -117,63 +132,74 @@ static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event) return -EAGAIN; } -static int efi_mouse_probe(struct udevice *dev) +/** + * setup_simple_pointer() - Set up simple pointer protocol + * + * @priv: Private data + * Return: 0 if OK, -ve on error + */ +static int setup_simple_pointer(struct efi_mouse_priv *priv) { - 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; + efi_status_t ret; - log_debug("EFI mouse probe\n"); - - /* Find Simple Pointer Protocol handles */ + log_debug("EFI simple-pointer mouse probe\n"); 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); + if (ret) return -ENODEV; - } - log_debug("Found %zu EFI pointer device(s)\n", num_handles); + log_debug("Found %zu simple pointer device(s)\n", num_handles); - /* Use the first pointer device */ + /* Use the first simple 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); + (void **)&priv->simple, + efi_get_parent_image(), NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret) { + log_debug("Cannot open simple pointer protocol (ret=0x%lx)\n", + ret); efi_free_pool(handles); - return -ENODEV; + return -EIO; } + log_debug("Using simple pointer protocol\n"); efi_free_pool(handles); + return 0; +} + +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; + + if (setup_simple_pointer(priv)) + return -ENODEV; + /* Reset the pointer device */ - ret = priv->pointer->reset(priv->pointer, false); + ret = priv->simple->reset(priv->simple, 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); + if (ret) { + log_debug("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); + ret = boot->set_timer(priv->timer_event, EFI_TIMER_PERIODIC, + 10000); + if (ret) { + log_debug("Failed to set timer (ret=0x%lx)\n", ret); boot->close_event(priv->timer_event); priv->timer_event = NULL; } @@ -188,15 +214,10 @@ 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) { + 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; - } + /* Protocol will be automatically closed when the image is unloaded */ return 0; } -- 2.43.0
From: Simon Glass <sjg@chromium.org> Split out the code that handles button presses into its own function, since efi_mouse_get_event() is already quite long. Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) drivers/input/efi_mouse.c | 62 ++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/drivers/input/efi_mouse.c b/drivers/input/efi_mouse.c index 5fdf6979b8f..5badbfab066 100644 --- a/drivers/input/efi_mouse.c +++ b/drivers/input/efi_mouse.c @@ -39,6 +39,44 @@ struct efi_mouse_priv { struct efi_event *timer_event; }; +/** + * get_button_event() - Check for button-change events + * + * @priv: Private data + * @new_buttons: New button state + * @event: Event to populate if button changed + * Return: 0 if button event found, -EAGAIN if no button change + */ +static int get_button_event(struct efi_mouse_priv *priv, int new_buttons, + struct mouse_event *event) +{ + struct mouse_button *but = &event->button; + int diff = new_buttons ^ priv->old_buttons; + int i; + + if (new_buttons == priv->old_buttons) + return -EAGAIN; + + event->type = MOUSE_EV_BUTTON; + /* Find first changed button */ + for (i = 0; i < 2; i++) { + u8 mask = 1 << i; + + if (!(diff & mask)) + continue; + + 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; + } + + return -EAGAIN; +} + static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event) { struct efi_mouse_priv *priv = dev_get_priv(dev); @@ -81,27 +119,9 @@ static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event) 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; - } - } - } + ret = get_button_event(priv, new_buttons, event); + if (!ret) + return 0; /* Check for movement */ if (state.relative_movement_x || state.relative_movement_y) { -- 2.43.0
From: Simon Glass <sjg@chromium.org> Create a function which handles getting the pointer position using the simple-pointer protocol. Call this from efi_mouse_get_event() Return immediately if there is a button event. Otherwise return a motion event if motion is detected. Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) drivers/input/efi_mouse.c | 162 +++++++++++++++++++++++++------------- 1 file changed, 106 insertions(+), 56 deletions(-) diff --git a/drivers/input/efi_mouse.c b/drivers/input/efi_mouse.c index 5badbfab066..ced8672085c 100644 --- a/drivers/input/efi_mouse.c +++ b/drivers/input/efi_mouse.c @@ -39,6 +39,93 @@ struct efi_mouse_priv { struct efi_event *timer_event; }; +/** + * get_rel_pointer() - Handle relative pointer input + * + * @priv: Private data + * @rel_x: Returns relative X movement + * @rel_y: Returns relative Y movement + * @new_buttons: Returns button state + * Return: 0 if OK, -EAGAIN if no event, -ve on error + */ +static int get_rel_pointer(struct efi_mouse_priv *priv, int *rel_x, + int *rel_y, int *new_buttons) +{ + struct efi_simple_pointer_state state; + efi_status_t ret; + + /* 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->simple->wait_for_input) { + events[1] = priv->simple->wait_for_input; + num_events = 2; + } + + ret = boot->wait_for_event(num_events, events, &index); + if (ret != EFI_SUCCESS) + return -EAGAIN; + } + + log_debug("rel: calling get_state\n"); + ret = priv->simple->get_state(priv->simple, &state); + log_debug("rel: get_state returned 0x%lx\n", ret); + if (ret == EFI_NOT_READY) + return -EAGAIN; + if (ret) { + log_debug("rel: get_state failed (ret=0x%lx)\n", ret); + return -EIO; + } + + log_debug("rel: RelX=%d RelY=%d LeftBtn=%d RightBtn=%d\n", + state.relative_movement_x, state.relative_movement_y, + state.left_button, state.right_button); + + /* + * Scale down large movement values that seem to be incorrectly + * reported + */ + *rel_x = state.relative_movement_x; + *rel_y = state.relative_movement_y; + + /* If movement values are very large, scale them down */ + if (abs(*rel_x) > 1000) { + *rel_x = *rel_x / 1000; + if (*rel_x == 0 && state.relative_movement_x != 0) + *rel_x = (state.relative_movement_x > 0) ? 1 : -1; + } + if (abs(*rel_y) > 1000) { + *rel_y = *rel_y / 1000; + if (*rel_y == 0 && state.relative_movement_y != 0) + *rel_y = (state.relative_movement_y > 0) ? 1 : -1; + } + + log_debug("rel: scaled RelX=%d RelY=%d\n", *rel_x, *rel_y); + + /* Update absolute position */ + priv->x += *rel_x; + priv->x = max(priv->x, 0); + priv->x = min(priv->x, MOUSE_MAX_COORD); + + priv->y += *rel_y; + priv->y = max(priv->y, 0); + priv->y = min(priv->y, MOUSE_MAX_COORD); + + /* Extract button state */ + *new_buttons = 0; + if (state.left_button) + *new_buttons |= 1 << 0; + if (state.right_button) + *new_buttons |= 1 << 1; + + return 0; +} + /** * get_button_event() - Check for button-change events * @@ -80,76 +167,39 @@ static int get_button_event(struct efi_mouse_priv *priv, int new_buttons, 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; + struct mouse_motion *motion; int new_buttons; - if (!priv->simple) - 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->simple->wait_for_input) { - events[1] = priv->simple->wait_for_input; - num_events = 2; - } - - ret = boot->wait_for_event(num_events, events, &index); - if (ret != EFI_SUCCESS) - return -EAGAIN; - } + int rel_x, rel_y; + int ret; /* Get current pointer state */ - ret = priv->simple->get_state(priv->simple, &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; - } + ret = get_rel_pointer(priv, &rel_x, &rel_y, &new_buttons); + if (ret) + return ret; + + priv->has_last_state = true; /* Check for button changes */ - new_buttons = 0; - if (state.left_button) - new_buttons |= 1 << 0; - if (state.right_button) - new_buttons |= 1 << 1; ret = get_button_event(priv, new_buttons, event); if (!ret) 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; - + /* If there's no movement, nothing to do */ + if (!rel_x && !rel_y) { priv->buttons = new_buttons; - return 0; + return -EAGAIN; } + motion = &event->motion; + event->type = MOUSE_EV_MOTION; + motion->state = new_buttons; + motion->x = priv->x; + motion->y = priv->y; + motion->xrel = rel_x; + motion->yrel = rel_y; priv->buttons = new_buttons; - return -EAGAIN; + return 0; } /** -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add these definitions to the API so that we can make use of this feature in drivers, etc. Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) include/efi.h | 1 + include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi/device_path.c | 1 + 3 files changed, 32 insertions(+) diff --git a/include/efi.h b/include/efi.h index 3d983bd69a4..0bc7e0581f7 100644 --- a/include/efi.h +++ b/include/efi.h @@ -179,6 +179,7 @@ extern const efi_guid_t efi_guid_device_path; extern const efi_guid_t efi_simple_file_system_protocol_guid; extern const efi_guid_t efi_guid_simple_pointer; +extern const efi_guid_t efi_guid_absolute_pointer; /* Generic EFI table header */ struct efi_table_hdr { diff --git a/include/efi_api.h b/include/efi_api.h index 70e24cb5fc9..9e661913ecc 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -1006,6 +1006,36 @@ struct efi_simple_pointer_protocol { struct efi_simple_pointer_mode *mode; }; +#define EFI_ABSOLUTE_POINTER_PROTOCOL_GUID \ + EFI_GUID(0x8d59d32b, 0xc655, 0x4ae9, \ + 0x9b, 0x15, 0xf2, 0x59, 0x04, 0x99, 0x2a, 0x43) + +struct efi_absolute_pointer_state { + u64 current_x; + u64 current_y; + u64 current_z; + u32 active_buttons; +}; + +struct efi_absolute_pointer_mode { + u64 abs_min_x; + u64 abs_min_y; + u64 abs_min_z; + u64 abs_max_x; + u64 abs_max_y; + u64 abs_max_z; + u32 attributes; +}; + +struct efi_absolute_pointer_protocol { + efi_status_t (EFIAPI *reset)(struct efi_absolute_pointer_protocol *this, + bool extended_verification); + efi_status_t (EFIAPI *get_state)(struct efi_absolute_pointer_protocol *this, + struct efi_absolute_pointer_state *state); + struct efi_event *wait_for_input; + struct efi_absolute_pointer_mode *mode; +}; + #define EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID \ EFI_GUID(0x8b843e20, 0x8132, 0x4852, \ 0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c) diff --git a/lib/efi/device_path.c b/lib/efi/device_path.c index 939bf5a0679..de7647e13d0 100644 --- a/lib/efi/device_path.c +++ b/lib/efi/device_path.c @@ -28,6 +28,7 @@ const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID; const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID; const efi_guid_t efi_guid_loaded_image = EFI_LOADED_IMAGE_PROTOCOL_GUID; const efi_guid_t efi_guid_simple_pointer = EFI_SIMPLE_POINTER_PROTOCOL_GUID; +const efi_guid_t efi_guid_absolute_pointer = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID; const efi_guid_t efi_guid_loaded_image_device_path = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; const efi_guid_t efi_simple_file_system_protocol_guid = -- 2.43.0
From: Simon Glass <sjg@chromium.org> The absolute-pointer protocol is useful particularly with QEMU since it allows the position to be consistent between the host and the guest. Add support for this new protocol and use it in preference to the simple one, when both are available. Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) drivers/input/efi_mouse.c | 134 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 127 insertions(+), 7 deletions(-) diff --git a/drivers/input/efi_mouse.c b/drivers/input/efi_mouse.c index ced8672085c..b82a26c9885 100644 --- a/drivers/input/efi_mouse.c +++ b/drivers/input/efi_mouse.c @@ -21,8 +21,11 @@ * struct efi_mouse_priv - Private data for EFI mouse driver * * @simple: Simple pointer protocol (relative movement) + * @abs: Absolute pointer protocol (absolute position) * @simple_last: Last simple pointer state + * @abs_last: Last absolute pointer state * @has_last_state: True if we have a previous state for delta calculation + * @use_absolute: True to use absolute pointer, false for simple/relative * @x: Current X position * @y: Current Y position * @buttons: Current button state @@ -31,14 +34,64 @@ */ struct efi_mouse_priv { struct efi_simple_pointer_protocol *simple; + struct efi_absolute_pointer_protocol *abs; struct efi_simple_pointer_state simple_last; + struct efi_absolute_pointer_state abs_last; bool has_last_state; + bool use_absolute; int x, y; int buttons; int old_buttons; struct efi_event *timer_event; }; +/** + * get_abs_pointer() - Handle absolute pointer input + * + * @priv: Private data + * @rel_x: Returns relative X movement + * @rel_y: Returns relative Y movement + * @new_buttons: Returns button state + * Return: 0 if OK, -EAGAIN if no event, -ve on error + */ +static int get_abs_pointer(struct efi_mouse_priv *priv, int *rel_x, + int *rel_y, int *new_buttons) +{ + struct efi_absolute_pointer_state state; + efi_status_t ret; + + /* Debug: check structure size and alignment */ + log_debug("State struct size: %zu, address: %p\n", sizeof(state), + &state); + + ret = priv->abs->get_state(priv->abs, &state); + if (ret == EFI_NOT_READY) + return -EAGAIN; + if (ret) { + log_debug("abs: get_state failed (ret=0x%lx)\n", ret); + return -EIO; + } + + /* Always log the state values to see what we're getting */ + log_debug("abs: X=%llu Y=%llu Buttons=0x%x\n", state.current_x, + state.current_y, state.active_buttons); + + /* Calculate relative movement */ + if (priv->has_last_state) { + *rel_x = (int)(state.current_x - priv->abs_last.current_x); + *rel_y = (int)(state.current_y - priv->abs_last.current_y); + log_debug("abs: rel_x=%d, rel_y=%d\n", *rel_x, *rel_y); + } + priv->abs_last = state; + priv->x = state.current_x; + priv->y = state.current_y; + + /* Extract button state */ + *new_buttons = state.active_buttons & 0x3; /* Left and right buttons */ + + return 0; +} + /** * get_rel_pointer() - Handle relative pointer input * @@ -172,8 +225,14 @@ static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event) int rel_x, rel_y; int ret; - /* Get current pointer state */ - ret = get_rel_pointer(priv, &rel_x, &rel_y, &new_buttons); + /* + * Get current pointer state. Under QEMU, EFI pointer-events are broken + * so we poll directly + */ + if (priv->use_absolute) + ret = get_abs_pointer(priv, &rel_x, &rel_y, &new_buttons); + else + ret = get_rel_pointer(priv, &rel_x, &rel_y, &new_buttons); if (ret) return ret; @@ -202,6 +261,47 @@ static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event) return 0; } +/** + * setup_abs_pointer() - Set up absolute pointer protocol + * + * @priv: Private data + * Return: 0 if OK, -ve on error + */ +static int setup_abs_pointer(struct efi_mouse_priv *priv) +{ + struct efi_boot_services *boot = efi_get_boot(); + efi_handle_t *handles; + efi_uintn_t num_handles; + efi_status_t ret; + + ret = boot->locate_handle_buffer(BY_PROTOCOL, + &efi_guid_absolute_pointer, + NULL, &num_handles, &handles); + if (ret) + return -ENODEV; + + log_debug("Found %zu absolute pointer device(s) guid %pU\n", + num_handles, &efi_guid_absolute_pointer); + + /* Use the first absolute pointer device */ + ret = boot->open_protocol(handles[0], &efi_guid_absolute_pointer, + (void **)&priv->abs, + efi_get_parent_image(), NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret) { + log_debug("Cannot open absolute pointer protocol (ret=0x%lx)\n", + ret); + efi_free_pool(handles); + return -EIO; + } + + priv->use_absolute = true; + log_debug("Using absolute pointer protocol\n"); + efi_free_pool(handles); + + return 0; +} + /** * setup_simple_pointer() - Set up simple pointer protocol * @@ -235,6 +335,7 @@ static int setup_simple_pointer(struct efi_mouse_priv *priv) return -EIO; } + priv->use_absolute = false; log_debug("Using simple pointer protocol\n"); efi_free_pool(handles); @@ -247,13 +348,17 @@ static int efi_mouse_probe(struct udevice *dev) struct efi_boot_services *boot = efi_get_boot(); efi_status_t ret; - if (setup_simple_pointer(priv)) + /* Try absolute pointer first, then fall back to simple pointer */ + if (setup_abs_pointer(priv) && setup_simple_pointer(priv)) return -ENODEV; /* Reset the pointer device */ - ret = priv->simple->reset(priv->simple, false); - if (ret != EFI_SUCCESS) { - log_warning("Failed to reset EFI pointer device\n"); + if (priv->use_absolute) + ret = priv->abs->reset(priv->abs, true); + else + ret = priv->simple->reset(priv->simple, true); + if (ret) { + log_warning("Failed to reset device (err=0x%lx)\n", ret); /* Continue anyway - some devices might not support reset */ } @@ -275,7 +380,22 @@ static int efi_mouse_probe(struct udevice *dev) } } - log_info("EFI mouse initialized\n"); + /* Test protocol validity */ + if (priv->use_absolute && priv->abs->mode) { + struct efi_absolute_pointer_mode *mode = priv->abs->mode; + + log_debug("absolute mouse mode: x %llx-%llx y %llx-%llx\n", + mode->abs_min_x, mode->abs_max_x, + mode->abs_min_y, mode->abs_max_y); + log_debug("absolute mouse wait_for_input event: %p\n", + priv->abs->wait_for_input); + } else if (!priv->use_absolute && priv->simple) { + log_debug("simple mouse wait_for_input event: %p\n", + priv->simple->wait_for_input); + } + + log_debug("initialized (%s protocol)\n", + priv->use_absolute ? "absolute" : "simple"); return 0; } -- 2.43.0
From: Simon Glass <sjg@chromium.org> If a display is provided to the mouse uclass, use it to scale the coords returned by the absolute-pointer protocol. Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) drivers/input/efi_mouse.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/input/efi_mouse.c b/drivers/input/efi_mouse.c index b82a26c9885..087d135f675 100644 --- a/drivers/input/efi_mouse.c +++ b/drivers/input/efi_mouse.c @@ -13,6 +13,7 @@ #include <efi_api.h> #include <log.h> #include <mouse.h> +#include <video.h> /* Maximum coordinate value for mouse position */ #define MOUSE_MAX_COORD 0xffff @@ -49,12 +50,14 @@ struct efi_mouse_priv { * get_abs_pointer() - Handle absolute pointer input * * @priv: Private data + * @uc_priv: Uclass-private data * @rel_x: Returns relative X movement * @rel_y: Returns relative Y movement * @new_buttons: Returns button state * Return: 0 if OK, -EAGAIN if no event, -ve on error */ -static int get_abs_pointer(struct efi_mouse_priv *priv, int *rel_x, +static int get_abs_pointer(struct efi_mouse_priv *priv, + struct mouse_uc_priv *uc_priv, int *rel_x, int *rel_y, int *new_buttons) { struct efi_absolute_pointer_state state; @@ -83,8 +86,28 @@ static int get_abs_pointer(struct efi_mouse_priv *priv, int *rel_x, log_debug("abs: rel_x=%d, rel_y=%d\n", *rel_x, *rel_y); } priv->abs_last = state; - priv->x = state.current_x; - priv->y = state.current_y; + + /* Update absolute position - scale to video display if available */ + if (uc_priv->video_dev && priv->abs->mode) { + struct efi_absolute_pointer_mode *mode = priv->abs->mode; + u64 x_range = mode->abs_max_x - mode->abs_min_x; + u64 y_range = mode->abs_max_y - mode->abs_min_y; + + if (x_range > 0 && y_range > 0) { + log_debug("abs: unscaled x=%llx y=%llx\n", + state.current_x, state.current_y); + priv->x = ((state.current_x - mode->abs_min_x) * + uc_priv->video_width) / x_range; + priv->y = ((state.current_y - mode->abs_min_y) * + uc_priv->video_height) / y_range; + } else { + priv->x = state.current_x; + priv->y = state.current_y; + } + } else { + priv->x = state.current_x; + priv->y = state.current_y; + } /* Extract button state */ *new_buttons = state.active_buttons & 0x3; /* Left and right buttons */ @@ -219,6 +242,7 @@ static int get_button_event(struct efi_mouse_priv *priv, int new_buttons, static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event) { + struct mouse_uc_priv *uc_priv = dev_get_uclass_priv(dev); struct efi_mouse_priv *priv = dev_get_priv(dev); struct mouse_motion *motion; int new_buttons; @@ -230,7 +254,8 @@ static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event) * so we poll directly */ if (priv->use_absolute) - ret = get_abs_pointer(priv, &rel_x, &rel_y, &new_buttons); + ret = get_abs_pointer(priv, uc_priv, &rel_x, &rel_y, + &new_buttons); else ret = get_rel_pointer(priv, &rel_x, &rel_y, &new_buttons); if (ret) -- 2.43.0
participants (1)
- 
                
Simon Glass