[PATCH 00/17] 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 Simon Glass (17): 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 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/x86/cpu/qemu/qemu.c | 31 +++ arch/x86/lib/init_helpers.c | 3 +- boot/expo.c | 16 +- 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/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/qfw.h | 12 + lib/efi/device_path.c | 1 + lib/efi_client/efi_app.c | 33 ++- scripts/build-qemu | 7 + 20 files changed, 574 insertions(+), 130 deletions(-) -- 2.43.0 base-commit: 856580df1b89f25adf9054db39b06d2dade467a4 branch: prof
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> --- 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> --- 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> --- 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> --- 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> 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> --- 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> --- 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> --- 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> --- 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> --- 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> --- 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> --- 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> --- 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 84347951f62..974c7e9fe1c 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> --- 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 974c7e9fe1c..3e9ec370a50 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->press_state = (new_buttons & mask) ? 1 : 0; + 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->press_state = (new_buttons & mask) ? 1 : 0; - 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> --- 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 3e9ec370a50..81c3fcb862e 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> --- 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> --- 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 81c3fcb862e..53976bf9e22 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> --- 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 53976bf9e22..e9870e119a0 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