diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index eb228bb51b4..ba81f597d89 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -38,6 +38,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_LPC11U6X gpio_lpc11u6x.c) zephyr_library_sources_ifdef(CONFIG_GPIO_XLNX_AXI gpio_xlnx_axi.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NPCX gpio_npcx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_EMUL gpio_emul.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_EMUL_SDL gpio_emul_sdl.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PSOC6 gpio_psoc6.c) zephyr_library_sources_ifdef(CONFIG_GPIO_PCAL6408A gpio_pcal6408a.c) zephyr_library_sources_ifdef(CONFIG_GPIO_EOS_S3 gpio_eos_s3.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d8b8404f571..06a78c2b631 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -110,6 +110,8 @@ source "drivers/gpio/Kconfig.npcx" source "drivers/gpio/Kconfig.emul" +source "drivers/gpio/Kconfig.emul_sdl" + source "drivers/gpio/Kconfig.psoc6" source "drivers/gpio/Kconfig.pcal6408a" diff --git a/drivers/gpio/Kconfig.emul_sdl b/drivers/gpio/Kconfig.emul_sdl new file mode 100644 index 00000000000..fb57cb9f1c6 --- /dev/null +++ b/drivers/gpio/Kconfig.emul_sdl @@ -0,0 +1,13 @@ +# GPIO emulation using SDL keyboard events +# +# Copyright (c) 2022, Basalte bv +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_EMUL_SDL + bool "SDL GPIO emulation" + default y + depends on DT_HAS_ZEPHYR_GPIO_EMUL_SDL_ENABLED + depends on GPIO_EMUL + depends on HAS_SDL + help + Enable GPIO emulation using SDL keyboard events. diff --git a/drivers/gpio/gpio_emul_sdl.c b/drivers/gpio/gpio_emul_sdl.c new file mode 100644 index 00000000000..015d0f352c9 --- /dev/null +++ b/drivers/gpio/gpio_emul_sdl.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022, Basalte bv + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT zephyr_gpio_emul_sdl + +#include +#include + +#include + +LOG_MODULE_REGISTER(gpio_emul_sdl, CONFIG_GPIO_LOG_LEVEL); + +struct gpio_sdl_config { + const struct device *emul; + + const SDL_Scancode *codes; + uint8_t num_codes; +}; + +static int sdl_filter(void *arg, SDL_Event *event) +{ + const struct device *port = arg; + const struct gpio_sdl_config *config = port->config; + int ret; + + gpio_pin_t pin = 0; + + /* Only handle keyboard events */ + switch (event->type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + break; + default: + return 1; + } + + /* Search for the corresponding scancode */ + while (pin < config->num_codes) { + if (config->codes[pin] == event->key.keysym.scancode) { + break; + } + pin++; + } + + if (pin == config->num_codes) { + /* Not tracked */ + return 1; + } + + /* Lock the scheduler so we can't be preempted, + * as the gpio_emul driver keeps a mutex locked + * for as long as there are pending interrupts + */ + k_sched_lock(); + + /* Update the pin state */ + ret = gpio_emul_input_set(config->emul, pin, event->type == SDL_KEYDOWN ? 1 : 0); + + k_sched_unlock(); + if (ret < 0) { + LOG_WRN("Failed to emulate input (%d)", ret); + } + + return 0; +} + +static int gpio_sdl_init(const struct device *dev) +{ + const struct gpio_sdl_config *config = dev->config; + + for (uint8_t pin = 0; pin < config->num_codes; ++pin) { + if (config->codes[pin] != SDL_SCANCODE_UNKNOWN) { + LOG_INF("GPIO %s:%u = %u", dev->name, pin, config->codes[pin]); + } + } + + SDL_AddEventWatch(sdl_filter, (void *)dev); + + return 0; +} + +#define GPIO_SDL_DEFINE(inst) \ + BUILD_ASSERT(DT_NODE_HAS_COMPAT_STATUS(DT_INST_PARENT(inst), \ + zephyr_gpio_emul, okay), \ + "Enabled parent zephyr,gpio-emul node is required"); \ + \ + static const SDL_Scancode gpio_sdl_##inst##_codes[] \ + = DT_INST_PROP(inst, scancodes); \ + \ + static const struct gpio_sdl_config gpio_sdl_##inst##_config = { \ + .emul = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ + .codes = gpio_sdl_##inst##_codes, \ + .num_codes = DT_INST_PROP_LEN(inst, scancodes), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, gpio_sdl_init, NULL, NULL, \ + &gpio_sdl_##inst##_config, POST_KERNEL, \ + CONFIG_GPIO_INIT_PRIORITY, NULL); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_SDL_DEFINE) diff --git a/dts/bindings/gpio/zephyr,gpio-emul-sdl.yaml b/dts/bindings/gpio/zephyr,gpio-emul-sdl.yaml new file mode 100644 index 00000000000..28937403197 --- /dev/null +++ b/dts/bindings/gpio/zephyr,gpio-emul-sdl.yaml @@ -0,0 +1,53 @@ +# Copyright 2022, Basalte bv +# SPDX-License-Identifier: Apache-2.0 + +description: | + SDL keyboard GPIO input Emulator + + Simulate GPIO state/interrupts using SDL keyboard events. This node has + to be a child of a `zephyr,gpio-emul` compatible. + Add a list of scancodes for the desired keys to be mapped. + + Refer to https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf + section Keyboard/Keypad (p53) for a list of scancode values. + + The following example maps the first 3 numeric keys to GPIO pins: + + /* gpio0 has to be a zephyr,gpio-emul device */ + &gpio0 { + ngpios = <3>; + + sdl_gpio { + compatible = "zephyr,gpio-emul-sdl"; + scancodes = <30 31 32>; + }; + }; + + keypad: keypad { + compatible = "gpio-keys"; + key1: key1 { + gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + }; + key1: key2 { + gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; + }; + key3: key3 { + gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>; + }; + }; + + The limitations of usage are: + - Only active high as we don't get events for keys that aren't pressed + - Pressing multiple keys is best effort, state will be kept but no events + are generated once the last key is released + +compatible: "zephyr,gpio-emul-sdl" + +include: base.yaml + +properties: + scancodes: + type: array + required: true + description: | + An array of SDL scancodes mapped to its GPIO index