From 141dbd33a1b03818cd7efe8a5d79d3e792f840af Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Wed, 20 Dec 2023 18:08:40 -0800 Subject: [PATCH] refactor: Fixes for soft-off based on review. * Better naming for gpio-key behavior triggers. * Tweaks to scanned behavior trigger to avoid bad semaphore use, and reduce chance of issues with slowly scanned matrixes. * Various code cleanups of style issues. --- app/CMakeLists.txt | 6 +- app/Kconfig | 4 +- app/Kconfig.behaviors | 8 +- .../boards/nrf52840dk_nrf52840.overlay | 4 +- app/dts/behaviors/soft_off.dtsi | 1 - ...aml => zmk,gpio-key-behavior-trigger.yaml} | 4 +- ....yaml => zmk,gpio-key-wakeup-trigger.yaml} | 2 +- ...mk,gpio-scanned-key-behavior-trigger.yaml} | 4 +- .../bindings/zmk,soft-off-wakeup-sources.yaml | 4 +- app/module/drivers/kscan/kscan_gpio_direct.c | 7 +- app/module/drivers/kscan/kscan_gpio_matrix.c | 7 +- app/src/behaviors/behavior_soft_off.c | 2 +- ...vior_key.c => gpio_key_behavior_trigger.c} | 88 +++++++------ app/src/gpio_key_wakeup_trigger.c | 96 +++++++++++++++ ....c => gpio_scanned_key_behavior_trigger.c} | 116 ++++++++---------- app/src/pm.c | 4 +- app/src/wakeup_trigger_key.c | 87 ------------- docs/docs/config/kscan.md | 2 +- docs/docs/features/soft-off.md | 12 +- 19 files changed, 227 insertions(+), 231 deletions(-) rename app/dts/bindings/{zmk,behavior-key.yaml => zmk,gpio-key-behavior-trigger.yaml} (86%) rename app/dts/bindings/{zmk,wakeup-trigger-key.yaml => zmk,gpio-key-wakeup-trigger.yaml} (90%) rename app/dts/bindings/{zmk,behavior-key-scanned.yaml => zmk,gpio-scanned-key-behavior-trigger.yaml} (86%) rename app/src/{behavior_key.c => gpio_key_behavior_trigger.c} (61%) create mode 100644 app/src/gpio_key_wakeup_trigger.c rename app/src/{behavior_key_scanned.c => gpio_scanned_key_behavior_trigger.c} (57%) delete mode 100644 app/src/wakeup_trigger_key.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index dddb52268daf..23b0398f4073 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -22,11 +22,11 @@ target_sources(app PRIVATE src/matrix_transform.c) target_sources(app PRIVATE src/sensors.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c) target_sources(app PRIVATE src/event_manager.c) -target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY app PRIVATE src/behavior_key.c) -target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_SCANNED app PRIVATE src/behavior_key_scanned.c) +target_sources_ifdef(CONFIG_ZMK_GPIO_KEY_BEHAVIOR_TRIGGER app PRIVATE src/gpio_key_behavior_trigger.c) +target_sources_ifdef(CONFIG_ZMK_GPIO_SCANNED_KEY_BEHAVIOR_TRIGGER app PRIVATE src/gpio_scanned_key_behavior_trigger.c) target_sources_ifdef(CONFIG_ZMK_PM_SOFT_OFF app PRIVATE src/pm.c) target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c) -target_sources_ifdef(CONFIG_ZMK_WAKEUP_TRIGGER_KEY app PRIVATE src/wakeup_trigger_key.c) +target_sources_ifdef(CONFIG_ZMK_GPIO_KEY_WAKEUP_TRIGGER app PRIVATE src/gpio_key_wakeup_trigger.c) target_sources(app PRIVATE src/events/activity_state_changed.c) target_sources(app PRIVATE src/events/position_state_changed.c) target_sources(app PRIVATE src/events/sensor_event.c) diff --git a/app/Kconfig b/app/Kconfig index eda83a33ca90..5f3ccf45b8b1 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -410,10 +410,10 @@ config ZMK_PM_SOFT_OFF bool "Soft-off support" select PM_DEVICE -config ZMK_WAKEUP_TRIGGER_KEY +config ZMK_GPIO_KEY_WAKEUP_TRIGGER bool "Hardware supported wakeup (GPIO)" default y - depends on DT_HAS_ZMK_WAKEUP_TRIGGER_KEY_ENABLED && ZMK_PM_SOFT_OFF + depends on DT_HAS_ZMK_GPIO_KEY_WAKEUP_TRIGGER_ENABLED && ZMK_PM_SOFT_OFF #Power Management endmenu diff --git a/app/Kconfig.behaviors b/app/Kconfig.behaviors index 6c1981bea8c1..0b7d8fdc5748 100644 --- a/app/Kconfig.behaviors +++ b/app/Kconfig.behaviors @@ -1,15 +1,15 @@ # Copyright (c) 2023 The ZMK Contributors # SPDX-License-Identifier: MIT -config ZMK_BEHAVIOR_KEY +config ZMK_GPIO_KEY_BEHAVIOR_TRIGGER bool default y - depends on DT_HAS_ZMK_BEHAVIOR_KEY_ENABLED + depends on DT_HAS_ZMK_GPIO_KEY_BEHAVIOR_TRIGGER_ENABLED -config ZMK_BEHAVIOR_KEY_SCANNED +config ZMK_GPIO_SCANNED_KEY_BEHAVIOR_TRIGGER bool default y - depends on DT_HAS_ZMK_BEHAVIOR_KEY_SCANNED_ENABLED + depends on DT_HAS_ZMK_GPIO_SCANNED_KEY_BEHAVIOR_TRIGGER_ENABLED config ZMK_BEHAVIOR_KEY_TOGGLE bool diff --git a/app/boards/shields/zmk_uno/boards/nrf52840dk_nrf52840.overlay b/app/boards/shields/zmk_uno/boards/nrf52840dk_nrf52840.overlay index d08105c68891..b068b431e259 100644 --- a/app/boards/shields/zmk_uno/boards/nrf52840dk_nrf52840.overlay +++ b/app/boards/shields/zmk_uno/boards/nrf52840dk_nrf52840.overlay @@ -43,7 +43,7 @@ encoder: &qdec0 { }; wakeup_source: wakeup_source { - compatible = "zmk,wakeup-trigger-key"; + compatible = "zmk,gpio-key-wakeup-trigger"; status = "okay"; trigger = <&button0>; @@ -58,7 +58,7 @@ encoder: &qdec0 { }; soft_off_behavior_key { - compatible = "zmk,behavior-key"; + compatible = "zmk,gpio-key-behavior-trigger"; status = "okay"; bindings = <&soft_off>; key = <&button0>; diff --git a/app/dts/behaviors/soft_off.dtsi b/app/dts/behaviors/soft_off.dtsi index c88e1b51f125..1e58c7711d0d 100644 --- a/app/dts/behaviors/soft_off.dtsi +++ b/app/dts/behaviors/soft_off.dtsi @@ -8,7 +8,6 @@ behaviors { /omit-if-no-ref/ soft_off: soft_off { compatible = "zmk,behavior-soft-off"; - label = "SOFTOFF"; #binding-cells = <0>; }; }; diff --git a/app/dts/bindings/zmk,behavior-key.yaml b/app/dts/bindings/zmk,gpio-key-behavior-trigger.yaml similarity index 86% rename from app/dts/bindings/zmk,behavior-key.yaml rename to app/dts/bindings/zmk,gpio-key-behavior-trigger.yaml index ff7a585eafad..2a1387f0220b 100644 --- a/app/dts/bindings/zmk,behavior-key.yaml +++ b/app/dts/bindings/zmk,gpio-key-behavior-trigger.yaml @@ -4,7 +4,7 @@ description: | Driver for a dedicated key for invoking a connected behavior. -compatible: "zmk,behavior-key" +compatible: "zmk,gpio-key-behavior-trigger" include: base.yaml @@ -16,7 +16,7 @@ properties: bindings: type: phandle required: true - description: The GPIO key that triggers wake via interrupt + description: The behavior to invoke when the GPIO key is pressed debounce-press-ms: type: int default: 5 diff --git a/app/dts/bindings/zmk,wakeup-trigger-key.yaml b/app/dts/bindings/zmk,gpio-key-wakeup-trigger.yaml similarity index 90% rename from app/dts/bindings/zmk,wakeup-trigger-key.yaml rename to app/dts/bindings/zmk,gpio-key-wakeup-trigger.yaml index fa7636d1f326..4e16ff3307d7 100644 --- a/app/dts/bindings/zmk,wakeup-trigger-key.yaml +++ b/app/dts/bindings/zmk,gpio-key-wakeup-trigger.yaml @@ -4,7 +4,7 @@ description: | Driver for a dedicated key for waking the device from sleep -compatible: "zmk,wakeup-trigger-key" +compatible: "zmk,gpio-key-wakeup-trigger" include: base.yaml diff --git a/app/dts/bindings/zmk,behavior-key-scanned.yaml b/app/dts/bindings/zmk,gpio-scanned-key-behavior-trigger.yaml similarity index 86% rename from app/dts/bindings/zmk,behavior-key-scanned.yaml rename to app/dts/bindings/zmk,gpio-scanned-key-behavior-trigger.yaml index bdb3abaff0e3..860155dda729 100644 --- a/app/dts/bindings/zmk,behavior-key-scanned.yaml +++ b/app/dts/bindings/zmk,gpio-scanned-key-behavior-trigger.yaml @@ -4,7 +4,7 @@ description: | Driver for a dedicated key triggered by matrix scanning for invoking a connected behavior. -compatible: "zmk,behavior-key-scanned" +compatible: "zmk,gpio-scanned-key-behavior-trigger" include: base.yaml @@ -16,7 +16,7 @@ properties: bindings: type: phandle required: true - description: The GPIO key that triggers wake via interrupt + description: The behavior to invoke when the GPIO key is pressed debounce-press-ms: type: int default: 5 diff --git a/app/dts/bindings/zmk,soft-off-wakeup-sources.yaml b/app/dts/bindings/zmk,soft-off-wakeup-sources.yaml index f98039a0ca9c..6b55d5d265ab 100644 --- a/app/dts/bindings/zmk,soft-off-wakeup-sources.yaml +++ b/app/dts/bindings/zmk,soft-off-wakeup-sources.yaml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: MIT description: | - Description of all possible wakeup-sources from a forces + Description of all possible wakeup-sources from a forced soft-off state. compatible: "zmk,soft-off-wakeup-sources" @@ -11,4 +11,4 @@ properties: wakeup-sources: type: phandles required: true - description: List of wakeup-sources that should be enabled to wake the system from forces soft-off state. + description: List of wakeup-sources that should be enabled to wake the system from forced soft-off state. diff --git a/app/module/drivers/kscan/kscan_gpio_direct.c b/app/module/drivers/kscan/kscan_gpio_direct.c index c44debe54dec..87c8f716d501 100644 --- a/app/module/drivers/kscan/kscan_gpio_direct.c +++ b/app/module/drivers/kscan/kscan_gpio_direct.c @@ -322,8 +322,6 @@ static int kscan_direct_init(const struct device *dev) { #if IS_ENABLED(CONFIG_PM_DEVICE) static int kscan_direct_pm_action(const struct device *dev, enum pm_device_action action) { - int ret = 0; - switch (action) { case PM_DEVICE_ACTION_SUSPEND: kscan_direct_disable(dev); @@ -332,11 +330,8 @@ static int kscan_direct_pm_action(const struct device *dev, enum pm_device_actio kscan_direct_enable(dev); break; default: - ret = -ENOTSUP; - break; + return -ENOTSUP; } - - return ret; } #endif // IS_ENABLED(CONFIG_PM_DEVICE) diff --git a/app/module/drivers/kscan/kscan_gpio_matrix.c b/app/module/drivers/kscan/kscan_gpio_matrix.c index de2e11279205..d1d6a4969802 100644 --- a/app/module/drivers/kscan/kscan_gpio_matrix.c +++ b/app/module/drivers/kscan/kscan_gpio_matrix.c @@ -425,8 +425,6 @@ static int kscan_matrix_init(const struct device *dev) { #if IS_ENABLED(CONFIG_PM_DEVICE) static int kscan_matrix_pm_action(const struct device *dev, enum pm_device_action action) { - int ret = 0; - switch (action) { case PM_DEVICE_ACTION_SUSPEND: kscan_matrix_disable(dev); @@ -435,11 +433,8 @@ static int kscan_matrix_pm_action(const struct device *dev, enum pm_device_actio kscan_matrix_enable(dev); break; default: - ret = -ENOTSUP; - break; + return -ENOTSUP; } - - return ret; } #endif // IS_ENABLED(CONFIG_PM_DEVICE) diff --git a/app/src/behaviors/behavior_soft_off.c b/app/src/behaviors/behavior_soft_off.c index 0f24a644d9a4..e6096bb48411 100644 --- a/app/src/behaviors/behavior_soft_off.c +++ b/app/src/behaviors/behavior_soft_off.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 The ZMK Contributors + * Copyright (c) 2023 The ZMK Contributors * * SPDX-License-Identifier: MIT */ diff --git a/app/src/behavior_key.c b/app/src/gpio_key_behavior_trigger.c similarity index 61% rename from app/src/behavior_key.c rename to app/src/gpio_key_behavior_trigger.c index 3633ce39a406..a72f8e489253 100644 --- a/app/src/behavior_key.c +++ b/app/src/gpio_key_behavior_trigger.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: MIT */ -#define DT_DRV_COMPAT zmk_behavior_key +#define DT_DRV_COMPAT zmk_gpio_key_behavior_trigger #include #include @@ -19,13 +19,13 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); -struct behavior_key_config { +struct gkbt_config { struct zmk_debounce_config debounce_config; int32_t debounce_scan_period_ms; struct gpio_dt_spec key; }; -struct behavior_key_data { +struct gkbt_data { struct zmk_behavior_binding binding; struct zmk_debounce_state debounce_state; struct gpio_callback key_callback; @@ -34,21 +34,21 @@ struct behavior_key_data { uint32_t read_time; }; -static void bk_enable_interrupt(const struct device *dev) { - const struct behavior_key_config *config = dev->config; +static void gkbt_enable_interrupt(const struct device *dev) { + const struct gkbt_config *config = dev->config; gpio_pin_interrupt_configure_dt(&config->key, GPIO_INT_LEVEL_ACTIVE); } -static void bk_disable_interrupt(const struct device *dev) { - const struct behavior_key_config *config = dev->config; +static void gkbt_disable_interrupt(const struct device *dev) { + const struct gkbt_config *config = dev->config; gpio_pin_interrupt_configure_dt(&config->key, GPIO_INT_DISABLE); } -static void bk_read(const struct device *dev) { - const struct behavior_key_config *config = dev->config; - struct behavior_key_data *data = dev->data; +static void gkbt_read(const struct device *dev) { + const struct gkbt_config *config = dev->config; + struct gkbt_data *data = dev->data; zmk_debounce_update(&data->debounce_state, gpio_pin_get_dt(&config->key), config->debounce_scan_period_ms, &config->debounce_config); @@ -71,65 +71,72 @@ static void bk_read(const struct device *dev) { k_work_reschedule(&data->update_work, K_TIMEOUT_ABS_MS(data->read_time)); } else { - bk_enable_interrupt(dev); + gkbt_enable_interrupt(dev); } } -static void bk_update_work(struct k_work *work) { +static void gkbt_update_work(struct k_work *work) { struct k_work_delayable *dwork = CONTAINER_OF(work, struct k_work_delayable, work); - struct behavior_key_data *data = CONTAINER_OF(dwork, struct behavior_key_data, update_work); - bk_read(data->dev); + struct gkbt_data *data = CONTAINER_OF(dwork, struct gkbt_data, update_work); + gkbt_read(data->dev); } -static void bk_gpio_irq_callback(const struct device *port, struct gpio_callback *cb, - const gpio_port_pins_t pin) { - struct behavior_key_data *data = CONTAINER_OF(cb, struct behavior_key_data, key_callback); +static void gkbt_gpio_irq_callback(const struct device *port, struct gpio_callback *cb, + const gpio_port_pins_t pin) { + struct gkbt_data *data = CONTAINER_OF(cb, struct gkbt_data, key_callback); - bk_disable_interrupt(data->dev); + gkbt_disable_interrupt(data->dev); data->read_time = k_uptime_get(); k_work_reschedule(&data->update_work, K_NO_WAIT); } -static int behavior_key_init(const struct device *dev) { - const struct behavior_key_config *config = dev->config; - struct behavior_key_data *data = dev->data; +static void gkbt_wait_for_key_release(const struct device *dev) { + const struct gkbt_config *config = dev->config; + + while (gpio_pin_get_dt(&config->key)) { + k_sleep(K_MSEC(100)); + } +} + +static int gkbt_init(const struct device *dev) { + const struct gkbt_config *config = dev->config; + struct gkbt_data *data = dev->data; if (!device_is_ready(config->key.port)) { - LOG_ERR("GPIO port is not ready"); + LOG_ERR("GPIO port %s is not ready", config->key.port->name); return -ENODEV; } - k_work_init_delayable(&data->update_work, bk_update_work); + k_work_init_delayable(&data->update_work, gkbt_update_work); data->dev = dev; gpio_pin_configure_dt(&config->key, GPIO_INPUT); - gpio_init_callback(&data->key_callback, bk_gpio_irq_callback, BIT(config->key.pin)); + gpio_init_callback(&data->key_callback, gkbt_gpio_irq_callback, BIT(config->key.pin)); gpio_add_callback(config->key.port, &data->key_callback); - while (gpio_pin_get_dt(&config->key)) { - k_sleep(K_MSEC(100)); - } + // Be sure our wakeup key is released before startup continues to avoid wake/sleep loop. + gkbt_wait_for_key_release(dev); - bk_enable_interrupt(dev); + gkbt_enable_interrupt(dev); return 0; } -static int behavior_key_pm_action(const struct device *dev, enum pm_device_action action) { - const struct behavior_key_config *config = dev->config; - struct behavior_key_data *data = dev->data; +static int gkbt_pm_action(const struct device *dev, enum pm_device_action action) { + const struct gkbt_config *config = dev->config; + struct gkbt_data *data = dev->data; int ret; switch (action) { case PM_DEVICE_ACTION_SUSPEND: - bk_disable_interrupt(dev); + gkbt_disable_interrupt(dev); ret = gpio_remove_callback(config->key.port, &data->key_callback); break; case PM_DEVICE_ACTION_RESUME: ret = gpio_add_callback(config->key.port, &data->key_callback); - bk_enable_interrupt(dev); + gkbt_enable_interrupt(dev); break; default: ret = -ENOTSUP; @@ -139,8 +146,8 @@ static int behavior_key_pm_action(const struct device *dev, enum pm_device_actio return ret; } -#define BK_INST(n) \ - const struct behavior_key_config bk_config_##n = { \ +#define GKBT_INST(n) \ + const struct gkbt_config gkbt_config_##n = { \ .key = GPIO_DT_SPEC_GET(DT_INST_PHANDLE(n, key), gpios), \ .debounce_config = \ { \ @@ -149,11 +156,12 @@ static int behavior_key_pm_action(const struct device *dev, enum pm_device_actio }, \ .debounce_scan_period_ms = DT_INST_PROP(n, debounce_scan_period_ms), \ }; \ - struct behavior_key_data bk_data_##n = { \ + struct gkbt_data gkbt_data_##n = { \ .binding = ZMK_KEYMAP_EXTRACT_BINDING(0, DT_DRV_INST(n)), \ }; \ - PM_DEVICE_DT_INST_DEFINE(n, behavior_key_pm_action); \ - DEVICE_DT_INST_DEFINE(n, behavior_key_init, PM_DEVICE_DT_INST_GET(n), &bk_data_##n, \ - &bk_config_##n, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL); + PM_DEVICE_DT_INST_DEFINE(n, gkbt_pm_action); \ + DEVICE_DT_INST_DEFINE(n, gkbt_init, PM_DEVICE_DT_INST_GET(n), &gkbt_data_##n, \ + &gkbt_config_##n, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + NULL); -DT_INST_FOREACH_STATUS_OKAY(BK_INST) +DT_INST_FOREACH_STATUS_OKAY(GKBT_INST) diff --git a/app/src/gpio_key_wakeup_trigger.c b/app/src/gpio_key_wakeup_trigger.c new file mode 100644 index 000000000000..ac0c6b228db6 --- /dev/null +++ b/app/src/gpio_key_wakeup_trigger.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#define DT_DRV_COMPAT zmk_gpio_key_wakeup_trigger + +struct gpio_key_wakeup_trigger_config { + struct gpio_dt_spec trigger; + size_t extra_gpios_count; + struct gpio_dt_spec extra_gpios[]; +}; + +static int zmk_gpio_key_wakeup_trigger_init(const struct device *dev) { +#if IS_ENABLED(CONFIG_PM_DEVICE) + pm_device_init_suspended(dev); + pm_device_wakeup_enable(dev, true); +#endif + + return 0; +} + +#if IS_ENABLED(CONFIG_PM_DEVICE) + +static int gpio_key_wakeup_trigger_pm_resume(const struct device *dev) { + const struct gpio_key_wakeup_trigger_config *config = dev->config; + + int ret = gpio_pin_interrupt_configure_dt(&config->trigger, GPIO_INT_LEVEL_ACTIVE); + if (ret < 0) { + LOG_ERR("Failed to configure wakeup trigger key GPIO pin interrupt (%d)", ret); + goto exit; + } + + for (int i = 0; i < config->extra_gpios_count; i++) { + ret = gpio_pin_configure_dt(&config->extra_gpios[i], GPIO_OUTPUT_ACTIVE); + if (ret < 0) { + LOG_WRN("Failed to set extra GPIO pin active for waker (%d)", ret); + goto exit; + } + } + +exit: + return ret; +} + +static int gpio_key_wakeup_trigger_pm_suspend(const struct device *dev) { + const struct gpio_key_wakeup_trigger_config *config = dev->config; + + int ret = gpio_pin_interrupt_configure_dt(&config->trigger, GPIO_INT_DISABLE); + if (ret < 0) { + LOG_ERR("Failed to configure wakeup trigger key GPIO pin interrupt (%d)", ret); + } + + return ret; +} + +static int gpio_key_wakeup_trigger_pm_action(const struct device *dev, + enum pm_device_action action) { + switch (action) { + case PM_DEVICE_ACTION_RESUME: + return gpio_key_wakeup_trigger_pm_resume(dev); + case PM_DEVICE_ACTION_SUSPEND: + return gpio_key_wakeup_trigger_pm_suspend(dev); + default: + return -ENOTSUP; + } +} + +#endif // IS_ENABLED(CONFIG_PM_DEVICE) + +#define WAKEUP_TRIGGER_EXTRA_GPIO_SPEC(idx, n) \ + GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), extra_gpios, idx) + +#define GPIO_KEY_WAKEUP_TRIGGER_INST(n) \ + const struct gpio_key_wakeup_trigger_config wtk_cfg_##n = { \ + .trigger = GPIO_DT_SPEC_GET(DT_INST_PROP(n, trigger), gpios), \ + .extra_gpios = {LISTIFY(DT_PROP_LEN_OR(DT_DRV_INST(n), extra_gpios, 0), \ + WAKEUP_TRIGGER_EXTRA_GPIO_SPEC, (, ), n)}, \ + .extra_gpios_count = DT_PROP_LEN_OR(DT_DRV_INST(n), extra_gpios, 0), \ + }; \ + PM_DEVICE_DT_INST_DEFINE(n, gpio_key_wakeup_trigger_pm_action); \ + DEVICE_DT_INST_DEFINE(n, zmk_gpio_key_wakeup_trigger_init, PM_DEVICE_DT_INST_GET(n), NULL, \ + &wtk_cfg_##n, PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_KEY_WAKEUP_TRIGGER_INST) diff --git a/app/src/behavior_key_scanned.c b/app/src/gpio_scanned_key_behavior_trigger.c similarity index 57% rename from app/src/behavior_key_scanned.c rename to app/src/gpio_scanned_key_behavior_trigger.c index c961b292622f..d27b162b191d 100644 --- a/app/src/behavior_key_scanned.c +++ b/app/src/gpio_scanned_key_behavior_trigger.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: MIT */ -#define DT_DRV_COMPAT zmk_behavior_key_scanned +#define DT_DRV_COMPAT zmk_gpio_scanned_key_behavior_trigger #include #include @@ -19,45 +19,41 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); -struct behavior_key_scanned_config { +struct gskbt_config { struct zmk_debounce_config debounce_config; int32_t debounce_scan_period_ms; struct gpio_dt_spec key; }; -struct behavior_key_scanned_data { +struct gskbt_data { struct zmk_behavior_binding binding; struct zmk_debounce_state debounce_state; struct gpio_callback key_callback; const struct device *dev; struct k_work_delayable update_work; + struct k_work gpio_trigger_work; uint32_t read_time; + uint32_t trigger_time; bool pin_active; bool active_scan_detected; - struct k_sem sem; }; -static void bks_enable_interrupt(const struct device *dev, bool active_scanning) { - const struct behavior_key_scanned_config *config = dev->config; +static void gskbt_enable_interrupt(const struct device *dev, bool active_scanning) { + const struct gskbt_config *config = dev->config; gpio_pin_interrupt_configure_dt(&config->key, active_scanning ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_LEVEL_ACTIVE); } -static void bks_disable_interrupt(const struct device *dev) { - const struct behavior_key_scanned_config *config = dev->config; +static void gskbt_disable_interrupt(const struct device *dev) { + const struct gskbt_config *config = dev->config; gpio_pin_interrupt_configure_dt(&config->key, GPIO_INT_DISABLE); } -static void bks_read(const struct device *dev) { - const struct behavior_key_scanned_config *config = dev->config; - struct behavior_key_scanned_data *data = dev->data; - - if (k_sem_take(&data->sem, K_NO_WAIT) < 0) { - // k_work_reschedule(&data->update_work, K_NO_WAIT); - return; - } +static void gskbt_read(const struct device *dev) { + const struct gskbt_config *config = dev->config; + struct gskbt_data *data = dev->data; zmk_debounce_update(&data->debounce_state, data->active_scan_detected, config->debounce_scan_period_ms, &config->debounce_config); @@ -81,89 +77,83 @@ static void bks_read(const struct device *dev) { k_work_schedule(&data->update_work, K_TIMEOUT_ABS_MS(data->read_time)); } else { - bks_enable_interrupt(dev, false); + gskbt_enable_interrupt(dev, false); } - - k_sem_give(&data->sem); } -static void bks_update_work(struct k_work *work) { +static void gskbt_update_work(struct k_work *work) { struct k_work_delayable *dwork = CONTAINER_OF(work, struct k_work_delayable, work); - struct behavior_key_scanned_data *data = - CONTAINER_OF(dwork, struct behavior_key_scanned_data, update_work); - bks_read(data->dev); + struct gskbt_data *data = CONTAINER_OF(dwork, struct gskbt_data, update_work); + gskbt_read(data->dev); } -static void bks_gpio_irq_callback(const struct device *port, struct gpio_callback *cb, - const gpio_port_pins_t pin) { - struct behavior_key_scanned_data *data = - CONTAINER_OF(cb, struct behavior_key_scanned_data, key_callback); - const struct behavior_key_scanned_config *config = data->dev->config; - - uint32_t time = k_uptime_get(); - - if (k_sem_take(&data->sem, K_MSEC(10)) < 0) { - LOG_ERR("FAILED TO TAKE THE SEMAPHORE"); - // Do more? - return; - } +static void gskbt_gpio_interrupt_work(struct k_work *work) { + struct gskbt_data *data = CONTAINER_OF(work, struct gskbt_data, gpio_trigger_work); - data->active_scan_detected = true; - data->read_time = time; + const struct gskbt_config *config = data->dev->config; if (!zmk_debounce_is_active(&data->debounce_state)) { - // When we get that very first interrupt, we need to schedule the update checks to fall in - // between each of the real scans, so we can do our checks for state *after* each scan has + // When we get that very first interrupt, we need to schedule the update checks right before + // the next real scan, so we can do our checks for state *after* each scan has // occurred. + data->read_time = data->trigger_time; k_work_reschedule(&data->update_work, - K_TIMEOUT_ABS_MS(time + (config->debounce_scan_period_ms / 2))); - - bks_enable_interrupt(data->dev, true); + K_TIMEOUT_ABS_MS(data->read_time + config->debounce_scan_period_ms - 1)); } +} - k_sem_give(&data->sem); +static void gskbt_gpio_irq_callback(const struct device *port, struct gpio_callback *cb, + const gpio_port_pins_t pin) { + struct gskbt_data *data = CONTAINER_OF(cb, struct gskbt_data, key_callback); + + // LOG_DBG("IRQ"); + data->active_scan_detected = true; + data->trigger_time = k_uptime_get(); + gskbt_enable_interrupt(data->dev, true); + k_work_submit(&data->gpio_trigger_work); } -static int behavior_key_scanned_init(const struct device *dev) { - const struct behavior_key_scanned_config *config = dev->config; - struct behavior_key_scanned_data *data = dev->data; +static int gskbt_init(const struct device *dev) { + const struct gskbt_config *config = dev->config; + struct gskbt_data *data = dev->data; if (!device_is_ready(config->key.port)) { LOG_ERR("GPIO port is not ready"); return -ENODEV; } - k_work_init_delayable(&data->update_work, bks_update_work); - k_sem_init(&data->sem, 1, 1); + k_work_init_delayable(&data->update_work, gskbt_update_work); + k_work_init(&data->gpio_trigger_work, gskbt_gpio_interrupt_work); + data->dev = dev; gpio_pin_configure_dt(&config->key, GPIO_INPUT); - gpio_init_callback(&data->key_callback, bks_gpio_irq_callback, BIT(config->key.pin)); + gpio_init_callback(&data->key_callback, gskbt_gpio_irq_callback, BIT(config->key.pin)); gpio_add_callback(config->key.port, &data->key_callback); while (gpio_pin_get_dt(&config->key)) { k_sleep(K_MSEC(100)); } - bks_enable_interrupt(dev, false); + gskbt_enable_interrupt(dev, false); return 0; } -static int behavior_key_scanned_pm_action(const struct device *dev, enum pm_device_action action) { - const struct behavior_key_scanned_config *config = dev->config; - struct behavior_key_scanned_data *data = dev->data; +static int gskbt_pm_action(const struct device *dev, enum pm_device_action action) { + const struct gskbt_config *config = dev->config; + struct gskbt_data *data = dev->data; int ret; switch (action) { case PM_DEVICE_ACTION_SUSPEND: - bks_disable_interrupt(dev); + gskbt_disable_interrupt(dev); ret = gpio_remove_callback(config->key.port, &data->key_callback); break; case PM_DEVICE_ACTION_RESUME: ret = gpio_add_callback(config->key.port, &data->key_callback); - bks_enable_interrupt(dev, false); + gskbt_enable_interrupt(dev, false); break; default: ret = -ENOTSUP; @@ -173,8 +163,8 @@ static int behavior_key_scanned_pm_action(const struct device *dev, enum pm_devi return ret; } -#define BK_INST(n) \ - const struct behavior_key_scanned_config bks_config_##n = { \ +#define GSKBT_INST(n) \ + const struct gskbt_config gskbt_config_##n = { \ .key = GPIO_DT_SPEC_GET(DT_INST_PHANDLE(n, key), gpios), \ .debounce_config = \ { \ @@ -183,12 +173,12 @@ static int behavior_key_scanned_pm_action(const struct device *dev, enum pm_devi }, \ .debounce_scan_period_ms = DT_INST_PROP(n, debounce_scan_period_ms), \ }; \ - struct behavior_key_scanned_data bks_data_##n = { \ + struct gskbt_data gskbt_data_##n = { \ .binding = ZMK_KEYMAP_EXTRACT_BINDING(0, DT_DRV_INST(n)), \ }; \ - PM_DEVICE_DT_INST_DEFINE(n, behavior_key_scanned_pm_action); \ - DEVICE_DT_INST_DEFINE(n, behavior_key_scanned_init, PM_DEVICE_DT_INST_GET(n), &bks_data_##n, \ - &bks_config_##n, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + PM_DEVICE_DT_INST_DEFINE(n, gskbt_pm_action); \ + DEVICE_DT_INST_DEFINE(n, gskbt_init, PM_DEVICE_DT_INST_GET(n), &gskbt_data_##n, \ + &gskbt_config_##n, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ NULL); -DT_INST_FOREACH_STATUS_OKAY(BK_INST) +DT_INST_FOREACH_STATUS_OKAY(GSKBT_INST) diff --git a/app/src/pm.c b/app/src/pm.c index a78b6ae53cb4..41d72eeed300 100644 --- a/app/src/pm.c +++ b/app/src/pm.c @@ -41,10 +41,10 @@ int zmk_pm_soft_off(void) { // from normal "inactive goes to sleep" behavior, so disable them as wakeup devices // and then suspend them so we're ready to take over setting up our system // and then putting it into an off state. + LOG_DBG("soft-on-off pressed cb: suspend devices"); for (int i = 0; i < device_count; i++) { const struct device *dev = &devs[i]; - LOG_DBG("soft-on-off pressed cb: suspend device"); if (pm_device_wakeup_is_enabled(dev)) { pm_device_wakeup_enable(dev, false); } @@ -60,6 +60,6 @@ int zmk_pm_soft_off(void) { } #endif // HAS_WAKERS - LOG_DBG("soft-on-off interrupt: go to sleep"); + LOG_DBG("soft-off: go to sleep"); return pm_state_force(0U, &(struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0}); } diff --git a/app/src/wakeup_trigger_key.c b/app/src/wakeup_trigger_key.c deleted file mode 100644 index 0cc4f250707b..000000000000 --- a/app/src/wakeup_trigger_key.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2023 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include -#include -#include -#include - -#include - -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); - -#define DT_DRV_COMPAT zmk_wakeup_trigger_key - -struct wakeup_trigger_key_config { - struct gpio_dt_spec trigger; - size_t extra_gpios_count; - struct gpio_dt_spec extra_gpios[]; -}; - -static int zmk_wakeup_trigger_key_init(const struct device *dev) { -#if IS_ENABLED(CONFIG_PM_DEVICE) - pm_device_init_suspended(dev); - pm_device_wakeup_enable(dev, true); -#endif - - return 0; -} - -#if IS_ENABLED(CONFIG_PM_DEVICE) - -static int wakeup_trigger_key_pm_action(const struct device *dev, enum pm_device_action action) { - const struct wakeup_trigger_key_config *config = dev->config; - int ret = 0; - - switch (action) { - case PM_DEVICE_ACTION_RESUME: - ret = gpio_pin_interrupt_configure_dt(&config->trigger, GPIO_INT_LEVEL_ACTIVE); - if (ret < 0) { - LOG_ERR("Failed to configure wakeup trigger key GPIO pin interrupt (%d)", ret); - return ret; - } - - for (int i = 0; i < config->extra_gpios_count; i++) { - ret = gpio_pin_configure_dt(&config->extra_gpios[i], GPIO_OUTPUT_ACTIVE); - if (ret < 0) { - LOG_WRN("Failed to set extra GPIO pin active for waker (%d)", ret); - } - } - break; - case PM_DEVICE_ACTION_SUSPEND: - - ret = gpio_pin_interrupt_configure_dt(&config->trigger, GPIO_INT_DISABLE); - if (ret < 0) { - LOG_ERR("Failed to configure wakeup trigger key GPIO pin interrupt (%d)", ret); - return ret; - } - break; - default: - ret = -ENOTSUP; - break; - } - - return ret; -} - -#endif // IS_ENABLED(CONFIG_PM_DEVICE) - -#define WAKEUP_TRIGGER_EXTRA_GPIO_SPEC(idx, n) \ - GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), extra_gpios, idx) - -#define WAKEUP_TRIGGER_KEY_INST(n) \ - const struct wakeup_trigger_key_config wtk_cfg_##n = { \ - .trigger = GPIO_DT_SPEC_GET(DT_INST_PROP(n, trigger), gpios), \ - .extra_gpios = {LISTIFY(DT_PROP_LEN_OR(DT_DRV_INST(n), extra_gpios, 0), \ - WAKEUP_TRIGGER_EXTRA_GPIO_SPEC, (, ), n)}, \ - .extra_gpios_count = DT_PROP_LEN_OR(DT_DRV_INST(n), extra_gpios, 0), \ - }; \ - PM_DEVICE_DT_INST_DEFINE(n, wakeup_trigger_key_pm_action); \ - DEVICE_DT_INST_DEFINE(n, zmk_wakeup_trigger_key_init, PM_DEVICE_DT_INST_GET(n), NULL, \ - &wtk_cfg_##n, PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL); - -DT_INST_FOREACH_STATUS_OKAY(WAKEUP_TRIGGER_KEY_INST) diff --git a/docs/docs/config/kscan.md b/docs/docs/config/kscan.md index 052d64ce2700..051622495c27 100644 --- a/docs/docs/config/kscan.md +++ b/docs/docs/config/kscan.md @@ -455,7 +455,7 @@ Note that the entire addressable space does not need to be mapped. }; kscan0: kscan { - compatible = "zmk,kscan-gpio-charlieplex";k + compatible = "zmk,kscan-gpio-charlieplex"; wakeup-source; interrupt-gpios = <&pro_micro 21 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN) >; diff --git a/docs/docs/features/soft-off.md b/docs/docs/features/soft-off.md index a3a5d7a1cb74..6b6b5cf4b33d 100644 --- a/docs/docs/features/soft-off.md +++ b/docs/docs/features/soft-off.md @@ -79,7 +79,7 @@ In this case, we will be creating a dedicated instance of the [Soft Off Behavior }; soft_off_behavior_key { - compatible = "zmk,behavior-key"; + compatible = "zmk,gpio-key-behavior-trigger"; bindings = <&hw_soft_off>; key = <&wakeup_key>; }; @@ -88,7 +88,7 @@ In this case, we will be creating a dedicated instance of the [Soft Off Behavior Here are the properties for the behavior key node: -- The `compatible` property for the node must be `zmk,behavior-key`. +- The `compatible` property for the node must be `zmk,gpio-key-behavior-trigger`. - The `bindings` property is a phandle to the soft off behavior defined above. - The `key` property is a phandle to the GPIO key defined earlier. @@ -97,7 +97,7 @@ If you have set up your on/off to be controlled by a matrix-integrated combo, th ``` / { soft_off_behavior_key { - compatible = "zmk,behavior-key-scanned"; + compatible = "zmk,gpio-scanned-key-behavior-trigger"; status = "okay"; bindings = <&hw_soft_off>; key = <&wakeup_key>; @@ -105,7 +105,7 @@ If you have set up your on/off to be controlled by a matrix-integrated combo, th }; ``` -Note that the only difference from the `soft_off_behavior_key` definition for GPIO keys above is the `compatible` value of `zmk,behavior-key-scanned`. +Note that the only difference from the `soft_off_behavior_key` definition for GPIO keys above is the `compatible` value of `zmk,gpio-scanned-key-behavior-trigger`. #### Wakeup Sources @@ -131,7 +131,7 @@ Next, we need to add another device which will be enabled only when the keyboard ``` / { wakeup_source: wakeup_source { - compatible = "zmk,wakeup-trigger-key"; + compatible = "zmk,gpio-key-wakeup-trigger"; trigger = <&wakeup_key>; wakeup-source; @@ -141,7 +141,7 @@ Next, we need to add another device which will be enabled only when the keyboard Here are the properties for the node: -- The `compatible` property for the node must be `zmk,wakeup-trigger-key`. +- The `compatible` property for the node must be `zmk,gpio-key-wakeup-trigger`. - The `trigger` property is a phandle to the GPIO key defined earlier. - The `wakeup-source` property signals to Zephyr this device should not be suspended during the shutdown procedure. - An optional `output-gpios` property contains a list of GPIO pins (including the appropriate flags) to set active before going into power off, if needed to ensure the GPIO pin will trigger properly to wake the keyboard. This is only needed for matrix integrated combos. For those keyboards, the list should include the matrix output needs needed so the combo hardware is properly "driven" when the keyboard is off.