Skip to content

Commit

Permalink
refactor(behaviors): Create a list to lookup behaviors
Browse files Browse the repository at this point in the history
Added BEHAVIOR_DT_DEFINE() and BEHAVIOR_DT_INST_DEFINE(), which work
exactly like the DEVICE_*_DEFINE() macros, except they also register the
device as a behavior by adding a pointer to it to a memory section.

Added zmk_behavior_get_binding(), which works like device_get_binding()
except that it only searches the devices that have been registered as
behaviors. This ensures that behaviors cannot have name collisions with
other devices defined by the SoC, which will be important when we remove
the label property from behaviors so they are given their node names.

As an added benefit, this is faster since it searches a smaller list.
Some basic benchmark code I wrote indicates it takes 30-70% as long,
depending on where the behavior is in the list and whether the name
string is an exact pointer match.

From now on, behaviors should use BEHAVIOR_*_DEFINe() instead of
DEVICE_*_DEFINE(), and any code that looks up a behavior by name should
use zmk_behavior_get_binding() instead of device_get_binding().
  • Loading branch information
joelspadin committed Dec 5, 2023
1 parent 5ecd352 commit 36eda57
Show file tree
Hide file tree
Showing 31 changed files with 231 additions and 93 deletions.
2 changes: 2 additions & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ list(APPEND ZEPHYR_EXTRA_MODULES
find_package(Zephyr REQUIRED HINTS ../zephyr)
project(zmk)

zephyr_linker_sources(SECTIONS include/linker/zmk-behaviors.ld)
zephyr_linker_sources(RODATA include/linker/zmk-events.ld)

# Add your source file to the "app" target. This must come after
# find_package(Zephyr) which defines the target.
target_include_directories(app PRIVATE include)
target_sources(app PRIVATE src/stdlib.c)
target_sources(app PRIVATE src/activity.c)
target_sources(app PRIVATE src/behavior.c)
target_sources(app PRIVATE src/kscan.c)
target_sources(app PRIVATE src/matrix_transform.c)
target_sources(app PRIVATE src/sensors.c)
Expand Down
50 changes: 45 additions & 5 deletions app/include/drivers/behavior.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,46 @@ __subsystem struct behavior_driver_api {
* @endcond
*/

struct zmk_behavior_ref {
const struct device *device;
};

/**
* Registers @p node_id as a behavior.
*/
#define BEHAVIOR_DEFINE(node_id) \
static const STRUCT_SECTION_ITERABLE(zmk_behavior_ref, \
_CONCAT(zmk_behavior_, DEVICE_DT_NAME_GET(node_id))) = { \
.device = DEVICE_DT_GET(node_id), \
}

/**
* @brief Like DEVICE_DT_DEFINE(), but also registers the device as a behavior.
*
* @param node_id The devicetree node identifier.
* @param ... Other parameters as expected by DEVICE_DT_DEFINE.
*/
#define BEHAVIOR_DT_DEFINE(node_id, ...) \
DEVICE_DT_DEFINE(node_id, __VA_ARGS__); \
BEHAVIOR_DEFINE(node_id)

/**
* @brief Like DEVICE_DT_INST_DEFINE(), but also registers the device as a behavior.
*
* @param inst Instance number.
* @param ... Other parameters as expected by DEVICE_DT_DEFINE.
*/
#define BEHAVIOR_DT_INST_DEFINE(inst, ...) \
DEVICE_DT_INST_DEFINE(inst, __VA_ARGS__); \
BEHAVIOR_DEFINE(DT_DRV_INST(inst))

/**
* Syscall wrapper for zmk_behavior_get_binding().
*
* Use zmk_behavior_get_binding() in application code instead.
*/
__syscall const struct device *behavior_get_binding(const char *name);

/**
* @brief Handle the keymap binding which needs to be converted from relative "toggle" to absolute
* "turn on"
Expand All @@ -70,7 +110,7 @@ __syscall int behavior_keymap_binding_convert_central_state_dependent_params(

static inline int z_impl_behavior_keymap_binding_convert_central_state_dependent_params(
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;

if (api->binding_convert_central_state_dependent_params == NULL) {
Expand Down Expand Up @@ -116,7 +156,7 @@ __syscall int behavior_keymap_binding_pressed(struct zmk_behavior_binding *bindi

static inline int z_impl_behavior_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);

if (dev == NULL) {
return -EINVAL;
Expand Down Expand Up @@ -144,7 +184,7 @@ __syscall int behavior_keymap_binding_released(struct zmk_behavior_binding *bind

static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);

if (dev == NULL) {
return -EINVAL;
Expand Down Expand Up @@ -178,7 +218,7 @@ static inline int z_impl_behavior_sensor_keymap_binding_accept_data(
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
const struct zmk_sensor_config *sensor_config, size_t channel_data_size,
const struct zmk_sensor_channel_data *channel_data) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);

if (dev == NULL) {
return -EINVAL;
Expand Down Expand Up @@ -214,7 +254,7 @@ static inline int
z_impl_behavior_sensor_keymap_binding_process(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event,
enum behavior_sensor_binding_process_mode mode) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);

if (dev == NULL) {
return -EINVAL;
Expand Down
9 changes: 9 additions & 0 deletions app/include/linker/zmk-behaviors.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright (c) 2023 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#include <zephyr/linker/linker-defs.h>

ITERABLE_SECTION_ROM(zmk_behavior_ref, 4)
18 changes: 17 additions & 1 deletion app/include/zmk/behavior.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#pragma once

#include <zephyr/device.h>

#define ZMK_BEHAVIOR_OPAQUE 0
#define ZMK_BEHAVIOR_TRANSPARENT 1

Expand All @@ -19,4 +21,18 @@ struct zmk_behavior_binding_event {
int layer;
uint32_t position;
int64_t timestamp;
};
};

/**
* @brief Get a const struct device* for a behavior from its @p name field.
*
* @param name Behavior name to search for.
*
* @retval Pointer to the device structure for the behavior with the given name.
* @retval NULL if the behavior is not found or its initialization function failed.
*
* @note This is equivalent to device_get_binding(), except it only searches
* behavior devices, so it is faster and there is no chance of it returning an
* unrelated node which shares the same name as a behavior.
*/
const struct device *zmk_behavior_get_binding(const char *name);
69 changes: 69 additions & 0 deletions app/src/behavior.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2023 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/sys/util_macro.h>
#include <string.h>

#include <drivers/behavior.h>
#include <zmk/behavior.h>

#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);

const struct device *zmk_behavior_get_binding(const char *name) {
return behavior_get_binding(name);
}

const struct device *z_impl_behavior_get_binding(const char *name) {
if (name == NULL || name[0] == '\0') {
return NULL;
}

STRUCT_SECTION_FOREACH(zmk_behavior_ref, item) {
if (z_device_is_ready(item->device) && item->device->name == name) {
return item->device;
}
}

STRUCT_SECTION_FOREACH(zmk_behavior_ref, item) {
if (z_device_is_ready(item->device) && strcmp(item->device->name, name) == 0) {
return item->device;
}
}

return NULL;
}

#if IS_ENABLED(CONFIG_LOG)
static int check_behavior_names(const struct device *dev) {
ARG_UNUSED(dev);

// Behavior names must be unique, but we don't have a good way to enforce this
// at compile time, so log an error at runtime if they aren't unique.
ptrdiff_t count;
STRUCT_SECTION_COUNT(zmk_behavior_ref, &count);

for (ptrdiff_t i = 0; i < count; i++) {
const struct zmk_behavior_ref *current;
STRUCT_SECTION_GET(zmk_behavior_ref, i, &current);

for (ptrdiff_t j = i + 1; j < count; j++) {
const struct zmk_behavior_ref *other;
STRUCT_SECTION_GET(zmk_behavior_ref, j, &other);

if (strcmp(current->device->name, other->device->name) == 0) {
LOG_ERR("Multiple behaviors have the same name '%s'", current->device->name);
}
}
}

return 0;
}

SYS_INIT(check_behavior_names, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
#endif // IS_ENABLED(CONFIG_LOG)
4 changes: 2 additions & 2 deletions app/src/behaviors/behavior_backlight.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ static const struct behavior_driver_api behavior_backlight_driver_api = {
.locality = BEHAVIOR_LOCALITY_GLOBAL,
};

DEVICE_DT_INST_DEFINE(0, behavior_backlight_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_backlight_driver_api);
BEHAVIOR_DT_INST_DEFINE(0, behavior_backlight_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_backlight_driver_api);

#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
4 changes: 2 additions & 2 deletions app/src/behaviors/behavior_bt.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ static const struct behavior_driver_api behavior_bt_driver_api = {
.binding_released = on_keymap_binding_released,
};

DEVICE_DT_INST_DEFINE(0, behavior_bt_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_bt_driver_api);
BEHAVIOR_DT_INST_DEFINE(0, behavior_bt_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_bt_driver_api);

#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
8 changes: 4 additions & 4 deletions app/src/behaviors/behavior_caps_word.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ static void deactivate_caps_word(const struct device *dev) {

static int on_caps_word_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
struct behavior_caps_word_data *data = dev->data;

if (data->active) {
Expand Down Expand Up @@ -181,9 +181,9 @@ static int behavior_caps_word_init(const struct device *dev) {
.continuations = {LISTIFY(DT_INST_PROP_LEN(n, continue_list), BREAK_ITEM, (, ), n)}, \
.continuations_count = DT_INST_PROP_LEN(n, continue_list), \
}; \
DEVICE_DT_INST_DEFINE(n, behavior_caps_word_init, NULL, &behavior_caps_word_data_##n, \
&behavior_caps_word_config_##n, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_caps_word_driver_api);
BEHAVIOR_DT_INST_DEFINE(n, behavior_caps_word_init, NULL, &behavior_caps_word_data_##n, \
&behavior_caps_word_config_##n, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_caps_word_driver_api);

DT_INST_FOREACH_STATUS_OKAY(KP_INST)

Expand Down
4 changes: 2 additions & 2 deletions app/src/behaviors/behavior_ext_power.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ static const struct behavior_driver_api behavior_ext_power_driver_api = {
.locality = BEHAVIOR_LOCALITY_GLOBAL,
};

DEVICE_DT_INST_DEFINE(0, behavior_ext_power_init, NULL, NULL, NULL, APPLICATION,
CONFIG_APPLICATION_INIT_PRIORITY, &behavior_ext_power_driver_api);
BEHAVIOR_DT_INST_DEFINE(0, behavior_ext_power_init, NULL, NULL, NULL, APPLICATION,
CONFIG_APPLICATION_INIT_PRIORITY, &behavior_ext_power_driver_api);

#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
8 changes: 4 additions & 4 deletions app/src/behaviors/behavior_hold_tap.c
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ static void update_hold_status_for_retro_tap(uint32_t ignore_position) {

static int on_hold_tap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
const struct behavior_hold_tap_config *cfg = dev->config;

if (undecided_hold_tap != NULL) {
Expand Down Expand Up @@ -715,9 +715,9 @@ static int behavior_hold_tap_init(const struct device *dev) {
.hold_trigger_key_positions = DT_INST_PROP(n, hold_trigger_key_positions), \
.hold_trigger_key_positions_len = DT_INST_PROP_LEN(n, hold_trigger_key_positions), \
}; \
DEVICE_DT_INST_DEFINE(n, behavior_hold_tap_init, NULL, NULL, &behavior_hold_tap_config_##n, \
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_hold_tap_driver_api);
BEHAVIOR_DT_INST_DEFINE(n, behavior_hold_tap_init, NULL, NULL, &behavior_hold_tap_config_##n, \
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_hold_tap_driver_api);

DT_INST_FOREACH_STATUS_OKAY(KP_INST)

Expand Down
4 changes: 2 additions & 2 deletions app/src/behaviors/behavior_key_press.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ static const struct behavior_driver_api behavior_key_press_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};

#define KP_INST(n) \
DEVICE_DT_INST_DEFINE(n, behavior_key_press_init, NULL, NULL, NULL, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_key_press_driver_api);
BEHAVIOR_DT_INST_DEFINE(n, behavior_key_press_init, NULL, NULL, NULL, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_key_press_driver_api);

DT_INST_FOREACH_STATUS_OKAY(KP_INST)
10 changes: 5 additions & 5 deletions app/src/behaviors/behavior_key_repeat.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct behavior_key_repeat_data {

static int on_key_repeat_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
struct behavior_key_repeat_data *data = dev->data;

if (data->last_keycode_pressed.usage_page == 0) {
Expand All @@ -50,7 +50,7 @@ static int on_key_repeat_binding_pressed(struct zmk_behavior_binding *binding,

static int on_key_repeat_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
struct behavior_key_repeat_data *data = dev->data;

if (data->current_keycode_pressed.usage_page == 0) {
Expand Down Expand Up @@ -116,9 +116,9 @@ static int behavior_key_repeat_init(const struct device *dev) {
.usage_pages = DT_INST_PROP(n, usage_pages), \
.usage_pages_count = DT_INST_PROP_LEN(n, usage_pages), \
}; \
DEVICE_DT_INST_DEFINE(n, behavior_key_repeat_init, NULL, &behavior_key_repeat_data_##n, \
&behavior_key_repeat_config_##n, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_key_repeat_driver_api);
BEHAVIOR_DT_INST_DEFINE(n, behavior_key_repeat_init, NULL, &behavior_key_repeat_data_##n, \
&behavior_key_repeat_config_##n, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_key_repeat_driver_api);

DT_INST_FOREACH_STATUS_OKAY(KR_INST)

Expand Down
4 changes: 2 additions & 2 deletions app/src/behaviors/behavior_key_toggle.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ static const struct behavior_driver_api behavior_key_toggle_driver_api = {
};

#define KT_INST(n) \
DEVICE_DT_INST_DEFINE(n, behavior_key_toggle_init, NULL, NULL, NULL, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_key_toggle_driver_api);
BEHAVIOR_DT_INST_DEFINE(n, behavior_key_toggle_init, NULL, NULL, NULL, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_key_toggle_driver_api);

DT_INST_FOREACH_STATUS_OKAY(KT_INST)
10 changes: 5 additions & 5 deletions app/src/behaviors/behavior_macro.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ static void queue_macro(uint32_t position, const struct zmk_behavior_binding bin

static int on_macro_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
const struct behavior_macro_config *cfg = dev->config;
struct behavior_macro_state *state = dev->data;
struct behavior_macro_trigger_state trigger_state = {.mode = MACRO_MODE_TAP,
Expand All @@ -200,7 +200,7 @@ static int on_macro_binding_pressed(struct zmk_behavior_binding *binding,

static int on_macro_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
const struct behavior_macro_config *cfg = dev->config;
struct behavior_macro_state *state = dev->data;

Expand All @@ -224,9 +224,9 @@ static const struct behavior_driver_api behavior_macro_driver_api = {
.default_tap_ms = DT_PROP_OR(inst, tap_ms, CONFIG_ZMK_MACRO_DEFAULT_TAP_MS), \
.count = DT_PROP_LEN(inst, bindings), \
.bindings = TRANSFORMED_BEHAVIORS(inst)}; \
DEVICE_DT_DEFINE(inst, behavior_macro_init, NULL, &behavior_macro_state_##inst, \
&behavior_macro_config_##inst, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_macro_driver_api);
BEHAVIOR_DT_DEFINE(inst, behavior_macro_init, NULL, &behavior_macro_state_##inst, \
&behavior_macro_config_##inst, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_macro_driver_api);

DT_FOREACH_STATUS_OKAY(zmk_behavior_macro, MACRO_INST)
DT_FOREACH_STATUS_OKAY(zmk_behavior_macro_one_param, MACRO_INST)
Expand Down
Loading

0 comments on commit 36eda57

Please sign in to comment.