From e8779962898e946687d4ef427188520f88e69152 Mon Sep 17 00:00:00 2001 From: Dzmitry Sankouski Date: Sun, 22 Jan 2023 18:21:25 +0300 Subject: [PATCH] dm: input: add button_kbd driver Bootmenu requires an input device with arrows and enter key. A common smartphone luckily has power, volume up/down buttons, which may be used for controlling bootmenu. To use driver, add 'button-kbd' to stdin. Signed-off-by: Dzmitry Sankouski Reviewed-by: Simon Glass --- drivers/input/Kconfig | 9 +++ drivers/input/Makefile | 1 + drivers/input/button_kbd.c | 126 +++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 drivers/input/button_kbd.c diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 1c534be005..32360d94c0 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -46,6 +46,15 @@ config APPLE_SPI_KEYB laptops based on Apple SoCs. These keyboards use an Apple-specific HID-over-SPI protocol. +config BUTTON_KEYBOARD + bool "Buttons as keyboard" + depends on BUTTON_GPIO + depends on DM_KEYBOARD + help + Enable support for mapping buttons to keycode events. Use linux,code button driver + dt node to define button-event mapping. + For example, an arrows and enter may be implemented to navigate boot menu. + config CROS_EC_KEYB bool "Enable Chrome OS EC keyboard support" depends on INPUT diff --git a/drivers/input/Makefile b/drivers/input/Makefile index ded76bddb2..14c0ea7325 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_$(SPL_TPL_)CROS_EC_KEYB) += cros_ec_keyb.o obj-$(CONFIG_$(SPL_TPL_)OF_CONTROL) += key_matrix.o obj-$(CONFIG_$(SPL_TPL_)DM_KEYBOARD) += input.o keyboard-uclass.o +obj-$(CONFIG_BUTTON_KEYBOARD) += button_kbd.o ifndef CONFIG_SPL_BUILD diff --git a/drivers/input/button_kbd.c b/drivers/input/button_kbd.c new file mode 100644 index 0000000000..99e65f12f0 --- /dev/null +++ b/drivers/input/button_kbd.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2023 Dzmitry Sankouski + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct button_kbd_priv - driver private data + * + * @input: input configuration + * @button_size: number of buttons found + * @old_state: a pointer to old button states array. Used to determine button state change. + */ +struct button_kbd_priv { + struct input_config *input; + u32 button_size; + u32 *old_state; +}; + +static int button_kbd_start(struct udevice *dev) +{ + struct button_kbd_priv *priv = dev_get_priv(dev); + int i = 0; + struct udevice *button_gpio_devp; + + uclass_foreach_dev_probe(UCLASS_BUTTON, button_gpio_devp) { + struct button_uc_plat *uc_plat = dev_get_uclass_plat(button_gpio_devp); + /* Ignore the top-level button node */ + if (!uc_plat->label) + continue; + debug("Found button %s #%d - %s, probing...\n", + uc_plat->label, i, button_gpio_devp->name); + i++; + } + + priv->button_size = i; + priv->old_state = calloc(i, sizeof(int)); + + return 0; +} + +int button_read_keys(struct input_config *input) +{ + struct button_kbd_priv *priv = dev_get_priv(input->dev); + struct udevice *button_gpio_devp; + struct uclass *uc; + int i = 0; + u32 code, state, state_changed = 0; + + uclass_id_foreach_dev(UCLASS_BUTTON, button_gpio_devp, uc) { + struct button_uc_plat *uc_plat = dev_get_uclass_plat(button_gpio_devp); + /* Ignore the top-level button node */ + if (!uc_plat->label) + continue; + code = button_get_code(button_gpio_devp); + if (!code) + continue; + + state = button_get_state(button_gpio_devp); + state_changed = state != priv->old_state[i]; + + if (state_changed) { + debug("%s: %d\n", uc_plat->label, code); + priv->old_state[i] = state; + input_add_keycode(input, code, state); + } + i++; + } + return 0; +} + +static const struct keyboard_ops button_kbd_ops = { + .start = button_kbd_start, +}; + +static int button_kbd_probe(struct udevice *dev) +{ + struct button_kbd_priv *priv = dev_get_priv(dev); + struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); + struct stdio_dev *sdev = &uc_priv->sdev; + struct input_config *input = &uc_priv->input; + int ret = 0; + + input_init(input, false); + input_add_tables(input, false); + + /* Register the device. */ + priv->input = input; + input->dev = dev; + input->read_keys = button_read_keys; + strcpy(sdev->name, "button-kbd"); + ret = input_stdio_register(sdev); + if (ret) { + debug("%s: input_stdio_register() failed\n", __func__); + return ret; + } + + return 0; +} + +static const struct udevice_id button_kbd_ids[] = { + { .compatible = "button-kbd" }, + { } +}; + +U_BOOT_DRIVER(button_kbd) = { + .name = "button_kbd", + .id = UCLASS_KEYBOARD, + .of_match = button_kbd_ids, + .ops = &button_kbd_ops, + .priv_auto = sizeof(struct button_kbd_priv), + .probe = button_kbd_probe, +}; -- 2.39.5