feat(behaviors): add soft power-off

This commit is contained in:
hyx0329 2023-05-10 16:27:01 +08:00
parent 48be2eedd0
commit f3ae8eb5de
No known key found for this signature in database
GPG key ID: B8C58C7455264351
9 changed files with 205 additions and 6 deletions

View file

@ -51,6 +51,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE src/behaviors/behavior_to_layer.c)
target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
target_sources(app PRIVATE src/behaviors/behavior_none.c)
target_sources(app PRIVATE src/behaviors/behavior_soft_poweroff.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE app PRIVATE src/behaviors/behavior_sensor_rotate.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR app PRIVATE src/behaviors/behavior_sensor_rotate_var.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON app PRIVATE src/behaviors/behavior_sensor_rotate_common.c)

View file

@ -18,4 +18,5 @@
#include <behaviors/caps_word.dtsi>
#include <behaviors/key_repeat.dtsi>
#include <behaviors/backlight.dtsi>
#include <behaviors/macros.dtsi>
#include <behaviors/macros.dtsi>
#include <behaviors/soft_poweroff.dtsi>

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2023 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <dt-bindings/zmk/soft_poweroff.h>
/ {
behaviors {
/omit-if-no-ref/ soft_poweroff: behavior_soft_poweroff {
compatible = "zmk,behavior-soft-poweroff";
label = "SOFT_POWEROFF";
#binding-cells = <0>;
};
/omit-if-no-ref/ sleep: behavior_sleep {
compatible = "zmk,behavior-soft-poweroff";
label = "SLEEP";
type = <SLEEP>;
#binding-cells = <0>;
};
};
};

View file

@ -0,0 +1,13 @@
# Copyright (c) 2023 The ZMK Contributors
# SPDX-License-Identifier: MIT
description: Keyboard System Soft Off Behavior
compatible: "zmk,behavior-soft-poweroff"
include: zero_param.yaml
properties:
type:
type: int
default: 0

View file

@ -0,0 +1,8 @@
/*
* Copyright (c) 2023 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define LOCKED 0x00
#define SLEEP 0x01

View file

@ -8,4 +8,6 @@
enum zmk_activity_state { ZMK_ACTIVITY_ACTIVE, ZMK_ACTIVITY_IDLE, ZMK_ACTIVITY_SLEEP };
enum zmk_activity_state zmk_activity_get_state();
enum zmk_activity_state zmk_activity_get_state();
int zmk_activity_set_state(enum zmk_activity_state state);

View file

@ -47,7 +47,7 @@ int raise_event() {
(struct zmk_activity_state_changed){.state = activity_state}));
}
int set_state(enum zmk_activity_state state) {
int zmk_activity_set_state(enum zmk_activity_state state) {
if (activity_state == state)
return 0;
@ -60,7 +60,7 @@ enum zmk_activity_state zmk_activity_get_state() { return activity_state; }
int activity_event_listener(const zmk_event_t *eh) {
activity_last_uptime = k_uptime_get();
return set_state(ZMK_ACTIVITY_ACTIVE);
return zmk_activity_set_state(ZMK_ACTIVITY_ACTIVE);
}
void activity_work_handler(struct k_work *work) {
@ -69,12 +69,12 @@ void activity_work_handler(struct k_work *work) {
#if IS_ENABLED(CONFIG_ZMK_SLEEP)
if (inactive_time > MAX_SLEEP_MS && !is_usb_power_present()) {
// Put devices in suspend power mode before sleeping
set_state(ZMK_ACTIVITY_SLEEP);
zmk_activity_set_state(ZMK_ACTIVITY_SLEEP);
pm_state_force(0U, &(struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
} else
#endif /* IS_ENABLED(CONFIG_ZMK_SLEEP) */
if (inactive_time > MAX_IDLE_MS) {
set_state(ZMK_ACTIVITY_IDLE);
zmk_activity_set_state(ZMK_ACTIVITY_IDLE);
}
}

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2023 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_soft_poweroff
#define SLEEP_S 2u
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/pm.h>
#include <drivers/behavior.h>
#include <zmk/behavior.h>
#include <zmk/activity.h>
#include <dt-bindings/zmk/soft_poweroff.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
struct behavior_softoff_config {
int type;
};
static void enter_deep_sleep() {
// Turn off the system. See `zephyr/samples/boards/nrf/system_off` or `app/src/activity.c`.
// Equivalent to ACPI S5.
// Put devices in suspend power mode before sleeping.
zmk_activity_set_state(ZMK_ACTIVITY_SLEEP);
pm_state_force(0u, &(struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
k_sleep(K_SECONDS(SLEEP_S));
// Normally the code below will not be reached.
pm_state_force(0u, &(struct pm_state_info){PM_STATE_ACTIVE, 0, 0});
zmk_activity_set_state(ZMK_ACTIVITY_ACTIVE);
LOG_WRN("The keyboard is not powered off!");
}
static void enter_deep_sleep_process(struct k_work *item) { enter_deep_sleep(); }
#if IS_ENABLED(CONFIG_ZMK_SLEEP)
K_WORK_DELAYABLE_DEFINE(enter_deep_sleep_work, enter_deep_sleep_process);
#endif
static int behavior_softoff_init(const struct device *dev) { return 0; };
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
#if IS_ENABLED(CONFIG_ZMK_SLEEP)
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct behavior_softoff_config *cfg = dev->config;
switch (cfg->type) {
case LOCKED:
// sleep before kscan interrupts enabled, so keys are locked
enter_deep_sleep();
break;
case SLEEP:
// sleep after kscan interrupts enabled
// so the keyboard can be waked up by typing(on platforms with PORT events, gpiote,
// interrupts enabled) limitation: any type before the actual sleep will make it a poweroff
// behavior how to improve: introduce something to terminate the kscan(but keep the
// interrupts) after sleep requested
k_work_reschedule(&enter_deep_sleep_work, K_SECONDS(SLEEP_S));
break;
default:
break;
}
#endif
return ZMK_BEHAVIOR_OPAQUE;
}
static const struct behavior_driver_api behavior_softoff_driver_api = {
.binding_released = on_keymap_binding_released,
.locality = BEHAVIOR_LOCALITY_GLOBAL,
};
#define SOFTOFF_INST(n) \
static const struct behavior_softoff_config behavior_softoff_config_##n = { \
.type = DT_INST_PROP(n, type)}; \
DEVICE_DT_INST_DEFINE(n, behavior_softoff_init, NULL, NULL, &behavior_softoff_config_##n, \
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_softoff_driver_api);
DT_INST_FOREACH_STATUS_OKAY(SOFTOFF_INST)
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -0,0 +1,57 @@
---
title: Soft Power Off Behavior
sidebar_label: Soft Power Off
---
## Summary
There are two available behaviors that can be used to manually control the power state of the keyboard. Both options will put the keyboard into deep sleep mode, but they differ in timing.
The first behavior is a soft power-off, which will immediately lock and power off the keyboard.
The second behavior adds a delay before entering the deep sleep mode. This delay allows
the kscan driver to enable interrupts (if `CONFIG_ZMK_KSCAN_DIRECT_POLLING` set to `n`), so
the keyboard can be activated simply by typing. It's important to note that the wake-up feature may not be available on all platforms.
## Soft Power Off
The soft power-off behavior will immediately put the keyboard into deep sleep mode, disabling the RGB underglow, backlight, and display.
However, please note that this behavior may not function as expected if USB logging is enabled and the keyboard is not connected to a host via USB.
### Behavior Binding
- Reference: `&soft_poweroff`
- Parameters: None
Example:
```
&soft_poweroff
```
## Sleep
The sleep behavior functions similarly to the soft power-off, with the addition of a two-second delay before entering the deep sleep state. This delay is particularly useful for keyboards that support wake by PORT events/interrupts, such as those based on NRF52840, as it gives user time to release all keys and allows the kscan driver to configure the necessary interrupts for waking up the keyboard if polling is disabled.
When the sleep behavior is triggered, the keyboard will enter the same deep sleep mode
as it would if `CONFIG_ZMK_IDLE_SLEEP_TIMEOUT` was exceeded. For more information
on this mode, please refer to [Idle/Sleep](../config/power.md).
A known limitation is that if any key is pressed just before going into deep sleep mode,
the kscan driver may not switch to interrupt mode, preventing the keyboard from waking up by typing.
In such cases, the sleep behavior is identical to the soft power-off behavior.
### Behavior Binding
- Reference: `&sleep`
- Parameters: None
Example:
```
&sleep
```
## Split Keyboards
Soft power off behaviors are global: This means that when triggered, they affect both the central and peripheral side of split keyboards.