
From: Simon Glass <sjg@chromium.org> Add a keyboard driver which returns keys produced by EFI. This is basically the same as the serial driver but it doesn't combine input and output into one driver, allowing more control when using a separate screen. Add the required devicetree fragment for ARM (only). Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/arm/dts/efi-arm_app.dts | 4 + drivers/input/Kconfig | 14 +++ drivers/input/Makefile | 2 + drivers/input/efi_keyb.c | 173 +++++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 drivers/input/efi_keyb.c diff --git a/arch/arm/dts/efi-arm_app.dts b/arch/arm/dts/efi-arm_app.dts index d2a008fba6a..1a7ed77e6a8 100644 --- a/arch/arm/dts/efi-arm_app.dts +++ b/arch/arm/dts/efi-arm_app.dts @@ -29,6 +29,10 @@ bootph-some-ram; }; + keyboard { + compatible = "efi-keyboard"; + }; + mouse { compatible = "efi,mouse"; }; diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 7b34902dd7c..6e19f6f7b3d 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -14,6 +14,7 @@ config TPL_INPUT config DM_KEYBOARD bool "Enable driver model keyboard support" depends on DM + default y if EFI_APP help This adds a uclass for keyboards and implements keyboard support using driver model. The API is implemented by keyboard.h and @@ -63,6 +64,19 @@ config CROS_EC_KEYB Messages are used to request key scans from the EC and these are then decoded into keys by this driver. +config EFI_KEYB + bool "Keyboard on top of EFI" + depends on DM_KEYBOARD && EFI_APP + default y + help + Provides a keyboard driver for EFI. While this is often connected + to the serial driver, that can be confusing on a device which has + both serial and keyboard devices. Provide a separate keyboard + driver. + + For now this is not used, pending further work on teasing this + apart within the EFI subsystem. + config SPL_CROS_EC_KEYB bool "Enable Chrome OS EC keyboard support in SPL" depends on SPL_INPUT diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 4debad9e713..e8888079a1c 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -8,6 +8,8 @@ obj-$(CONFIG_$(PHASE_)OF_CONTROL) += key_matrix.o obj-$(CONFIG_$(PHASE_)DM_KEYBOARD) += input.o keyboard-uclass.o obj-$(CONFIG_BUTTON_KEYBOARD) += button_kbd.o +obj-$(CONFIG_EFI_KEYB) += efi_keyb.o + ifndef CONFIG_XPL_BUILD obj-$(CONFIG_APPLE_SPI_KEYB) += apple_spi_kbd.o diff --git a/drivers/input/efi_keyb.c b/drivers/input/efi_keyb.c new file mode 100644 index 00000000000..0cbb438c6de --- /dev/null +++ b/drivers/input/efi_keyb.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EFI-keyboard input driver + * + * Uses EFI's Simple Text Input Protocol, polling keystrokes and providing them + * to stdio + */ + +#define LOG_CATEGORY LOGC_EFI + +#include <dm.h> +#include <efi_loader.h> +#include <efi.h> +#include <keyboard.h> +#include <log.h> +#include <stdio_dev.h> +#include <linux/delay.h> + +/* + * struct efi_kbd_priv - private information for the keyboard + * + * @ex_con + */ +struct efi_kbd_priv { + struct efi_simple_text_input_ex_protocol *ex_con; + struct efi_simple_text_input_protocol *con_in; + struct efi_input_key key; + struct efi_key_data exkey; + + bool have_key; +}; + +/** + * efi_kbd_tstc() - Test for a character from EFI + * + * @dev: keyboard device + * Return: 1 if a character is available, 0 otherwise. + */ +static int efi_kbd_tstc(struct udevice *dev) +{ + struct efi_kbd_priv *priv = dev_get_priv(dev); + efi_status_t status; + + /* If we already have a key from a previous check, report it's available */ + if (priv->have_key) + return 1; + + /* wait until we don't see EFI_NOT_READY */ + if (priv->ex_con) { + status = priv->ex_con->read_key_stroke_ex(priv->ex_con, + &priv->exkey); + } else { + status = priv->con_in->read_key_stroke(priv->con_in, + &priv->key); + } + if (!status) { + priv->have_key = true; + return 1; + } + + return 0; +} + +/** + * efi_kbd_getc() - Get a character from EFI + * + * Waits until a key is available and returns the associated character + * + * @dev: stdio device pointer + * Return: character code, or 0 if none + */ +static int efi_kbd_getc(struct udevice *dev) +{ + struct efi_kbd_priv *priv = dev_get_priv(dev); + + if (!efi_kbd_tstc(dev)) + return 0; + + priv->have_key = false; + if (priv->ex_con) { + struct efi_input_key *exkey = &priv->exkey.key; + + log_debug("got exkey %x scan %x\n", exkey->unicode_char, + exkey->scan_code); + return efi_decode_key(exkey); + } else { + struct efi_input_key *key = &priv->key; + + log_debug("got key %x\n", key->unicode_char); + return efi_decode_key(key); + } + + return 0; +} + +/** + * efi_kbd_start() - Start the driver + * + * Reset the keyboard ready for use + * + * Return: 0 on success (always) + */ +static int efi_kbd_start(struct udevice *dev) +{ + struct efi_kbd_priv *priv = dev_get_priv(dev); + + log_debug("keyboard start\n"); + + /* reset keyboard to drop anything pressed during UEFI startup */ + priv->con_in->reset(priv->con_in, true); + if (priv->ex_con) + priv->ex_con->reset(priv->ex_con, true); + priv->have_key = false; + + return 0; +} + +static int efi_kbd_probe(struct udevice *dev) +{ + struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); + struct efi_system_table *systab = efi_get_sys_table(); + struct stdio_dev *sdev = &uc_priv->sdev; + struct efi_kbd_priv *priv = dev_get_priv(dev); + efi_status_t ret_efi; + int ret; + + log_debug("keyboard probe '%s'\n", dev->name); + priv->con_in = systab->con_in; + + /* Try to get the EFI Simple Text Input EX protocol from console handle */ + if (systab->con_in_handle) { + efi_guid_t ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; + + ret_efi = efi_get_boot()->open_protocol(systab->con_in_handle, + &ex_guid, + (void **)&priv->ex_con, + NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret_efi != EFI_SUCCESS) { + log_debug("Extended input protocol not available\n"); + priv->ex_con = NULL; + } + } + + strcpy(sdev->name, "efi-kbd"); + ret = input_stdio_register(sdev); + if (ret) { + log_err("Failed to register\n"); + return ret; + } + + return 0; +} + +static const struct keyboard_ops efi_kbd_ops = { + .start = efi_kbd_start, + .tstc = efi_kbd_tstc, + .getc = efi_kbd_getc, +}; + +static const struct udevice_id efi_kbd_ids[] = { + { .compatible = "efi-keyboard" }, + { } +}; + +U_BOOT_DRIVER(efi_kbd) = { + .name = "efi_kbd", + .id = UCLASS_KEYBOARD, + .of_match = efi_kbd_ids, + .ops = &efi_kbd_ops, + .priv_auto = sizeof(struct efi_kbd_priv), + .probe = efi_kbd_probe, +}; -- 2.43.0