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:
Peter Johanson 2023-12-20 18:08:40 -08:00 committed by Pete Johanson
parent 96968514e3
commit fceb0351a5
21 changed files with 237 additions and 239 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1 +0,0 @@
CONFIG_ZMK_PM_SOFT_OFF=y

View File

@ -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>;

View File

@ -8,7 +8,6 @@
behaviors {
/omit-if-no-ref/ soft_off: soft_off {
compatible = "zmk,behavior-soft-off";
label = "SOFTOFF";
#binding-cells = <0>;
};
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020 The ZMK Contributors
* Copyright (c) 2023 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

View File

@ -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)

View 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)

View File

@ -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)

View File

@ -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});
}

View File

@ -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)

View File

@ -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) >;

View File

@ -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.