refactor(pm): Remove scanned behavior trigger.

* Remove the painful scanned behavior trigger for now, future enhancement
  will restore this high level functionality using kscan directly.
This commit is contained in:
Pete Johanson 2023-12-29 16:28:22 -08:00
parent 5ebe924e94
commit 933fdcd364
5 changed files with 2 additions and 236 deletions

View File

@ -30,7 +30,6 @@ 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_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 app PRIVATE src/pm.c)
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c)
target_sources_ifdef(CONFIG_ZMK_GPIO_KEY_WAKEUP_TRIGGER app PRIVATE src/gpio_key_wakeup_trigger.c)

View File

@ -6,11 +6,6 @@ config ZMK_GPIO_KEY_BEHAVIOR_TRIGGER
default y
depends on DT_HAS_ZMK_GPIO_KEY_BEHAVIOR_TRIGGER_ENABLED
config ZMK_GPIO_SCANNED_KEY_BEHAVIOR_TRIGGER
bool
default y
depends on DT_HAS_ZMK_GPIO_SCANNED_KEY_BEHAVIOR_TRIGGER_ENABLED
config ZMK_BEHAVIOR_KEY_TOGGLE
bool
default y

View File

@ -1,31 +0,0 @@
# Copyright (c) 2023 The ZMK Contributors
# SPDX-License-Identifier: MIT
description: |
Driver for a dedicated key triggered by matrix scanning for invoking a connected behavior.
compatible: "zmk,gpio-scanned-key-behavior-trigger"
include: base.yaml
properties:
key:
type: phandle
required: true
description: The GPIO key that triggers wake via interrupt
bindings:
type: phandle
required: true
description: The behavior to invoke when the GPIO key is pressed
debounce-press-ms:
type: int
default: 5
description: Debounce time for key press in milliseconds. Use 0 for eager debouncing.
debounce-release-ms:
type: int
default: 5
description: Debounce time for key release in milliseconds.
debounce-scan-period-ms:
type: int
default: 1
description: Time between reads in milliseconds when any key is pressed.

View File

@ -1,184 +0,0 @@
/*
* Copyright (c) 2023 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_gpio_scanned_key_behavior_trigger
#include <zephyr/device.h>
#include <drivers/behavior.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/device.h>
#include <zmk/event_manager.h>
#include <zmk/behavior.h>
#include <zmk/debounce.h>
#include <zmk/keymap.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct gskbt_config {
struct zmk_debounce_config debounce_config;
int32_t debounce_scan_period_ms;
struct gpio_dt_spec key;
};
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;
};
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 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 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);
if (zmk_debounce_get_changed(&data->debounce_state)) {
const bool pressed = zmk_debounce_is_pressed(&data->debounce_state);
struct zmk_behavior_binding_event event = {.position = INT32_MAX,
.timestamp = k_uptime_get()};
if (pressed) {
behavior_keymap_binding_pressed(&data->binding, event);
} else {
behavior_keymap_binding_released(&data->binding, event);
}
}
if (zmk_debounce_is_active(&data->debounce_state)) {
data->active_scan_detected = false;
data->read_time += config->debounce_scan_period_ms;
k_work_schedule(&data->update_work, K_TIMEOUT_ABS_MS(data->read_time));
} else {
gskbt_enable_interrupt(dev, false);
}
}
static void gskbt_update_work(struct k_work *work) {
struct k_work_delayable *dwork = CONTAINER_OF(work, struct k_work_delayable, work);
struct gskbt_data *data = CONTAINER_OF(dwork, struct gskbt_data, update_work);
gskbt_read(data->dev);
}
static void gskbt_gpio_interrupt_work(struct k_work *work) {
struct gskbt_data *data = CONTAINER_OF(work, struct gskbt_data, gpio_trigger_work);
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 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(data->read_time + config->debounce_scan_period_ms - 1));
}
}
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, 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, 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));
}
gskbt_enable_interrupt(dev, false);
return 0;
}
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:
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);
gskbt_enable_interrupt(dev, false);
break;
default:
ret = -ENOTSUP;
break;
}
return ret;
}
#define GSKBT_INST(n) \
const struct gskbt_config gskbt_config_##n = { \
.key = GPIO_DT_SPEC_GET(DT_INST_PHANDLE(n, key), gpios), \
.debounce_config = \
{ \
.debounce_press_ms = DT_INST_PROP(n, debounce_press_ms), \
.debounce_release_ms = DT_INST_PROP(n, debounce_release_ms), \
}, \
.debounce_scan_period_ms = DT_INST_PROP(n, debounce_scan_period_ms), \
}; \
struct gskbt_data gskbt_data_##n = { \
.binding = ZMK_KEYMAP_EXTRACT_BINDING(0, DT_DRV_INST(n)), \
}; \
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(GSKBT_INST)

View File

@ -37,7 +37,7 @@ The simplest way to achieve this is with a push button between a GPIO pin and gr
#### Matrix-Integrated Hardware Combo
Another, more complicated option is to tie two of the switch outputs in the matrix together through an AND gate and connect that to the dedicated GPIO pin. This way you can use a key combination in your existing keyboard matrix to trigger soft on/off. To make this work best, the two switches used should both be driven by the same matrix input pin so that both will be active simultaneously on the AND gate inputs. The alternative is to use a combination of diodes and capacitors to ensure both pins are active/high at the same time even if scanning sets them high at different times.
Another, more complicated option is to tie two of the switch outputs in the matrix together through an AND gate and connect that to the dedicated GPIO pin. This way you can use a key combination in your existing keyboard matrix to trigger soft on/off. To make this work best, the two switches used should both be driven by the same matrix input pin so that both will be active simultaneously on the AND gate inputs. The alternative is to use a combination of diodes and capacitors to ensure both pins are active/high at the same time even if scanning sets them high at different times. Support for this mode will be coming soon.
### Firmware Changes
@ -92,20 +92,7 @@ Here are the properties for the behavior key node:
- 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.
If you have set up your on/off to be controlled by a matrix-integrated combo, the behavior key needs use a different driver that will handle detecting the pressed state when the pin is toggled by the other matrix kscan driver:
```
/ {
soft_off_behavior_key {
compatible = "zmk,gpio-scanned-key-behavior-trigger";
status = "okay";
bindings = <&hw_soft_off>;
key = <&wakeup_key>;
};
};
```
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`.
If you have set up your on/off to be controlled by a matrix-integrated combo, the behavior key will need to be integrated into your existing kscan setup. Full details to come when this is supported.
#### Wakeup Sources