forked from kofal.net/zmk
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.
This commit is contained in:
committed by
Pete Johanson
parent
96968514e3
commit
fceb0351a5
@@ -29,11 +29,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)
|
||||
|
||||
@@ -427,10 +427,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,4 +20,7 @@ config ZMK_RGB_UNDERGLOW
|
||||
select WS2812_STRIP
|
||||
select SPI
|
||||
|
||||
config ZMK_PM_SOFT_OFF
|
||||
default y if BOARD_NRF52840DK_NRF52840
|
||||
|
||||
endif
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
CONFIG_ZMK_PM_SOFT_OFF=y
|
||||
@@ -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>;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
behaviors {
|
||||
/omit-if-no-ref/ soft_off: soft_off {
|
||||
compatible = "zmk,behavior-soft-off";
|
||||
label = "SOFTOFF";
|
||||
#binding-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
@@ -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 <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
@@ -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;
|
||||
|
||||
if (!device_is_ready(config->key.port)) {
|
||||
LOG_ERR("GPIO port is not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
k_work_init_delayable(&data->update_work, bk_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_add_callback(config->key.port, &data->key_callback);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
bk_enable_interrupt(dev);
|
||||
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 %s is not ready", config->key.port->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
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, gkbt_gpio_irq_callback, BIT(config->key.pin));
|
||||
gpio_add_callback(config->key.port, &data->key_callback);
|
||||
|
||||
// Be sure our wakeup key is released before startup continues to avoid wake/sleep loop.
|
||||
gkbt_wait_for_key_release(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)
|
||||
96
app/src/gpio_key_wakeup_trigger.c
Normal file
96
app/src/gpio_key_wakeup_trigger.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/pm/pm.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
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)
|
||||
@@ -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 <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
@@ -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;
|
||||
static void gskbt_gpio_interrupt_work(struct k_work *work) {
|
||||
struct gskbt_data *data = CONTAINER_OF(work, struct gskbt_data, gpio_trigger_work);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 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 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 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)
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/pm/pm.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
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)
|
||||
Reference in New Issue
Block a user