Skip to content

Commit 36eda57

Browse files
committed
refactor(behaviors): Create a list to lookup behaviors
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().
1 parent 5ecd352 commit 36eda57

31 files changed

+231
-93
lines changed

app/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ list(APPEND ZEPHYR_EXTRA_MODULES
1515
find_package(Zephyr REQUIRED HINTS ../zephyr)
1616
project(zmk)
1717

18+
zephyr_linker_sources(SECTIONS include/linker/zmk-behaviors.ld)
1819
zephyr_linker_sources(RODATA include/linker/zmk-events.ld)
1920

2021
# Add your source file to the "app" target. This must come after
2122
# find_package(Zephyr) which defines the target.
2223
target_include_directories(app PRIVATE include)
2324
target_sources(app PRIVATE src/stdlib.c)
2425
target_sources(app PRIVATE src/activity.c)
26+
target_sources(app PRIVATE src/behavior.c)
2527
target_sources(app PRIVATE src/kscan.c)
2628
target_sources(app PRIVATE src/matrix_transform.c)
2729
target_sources(app PRIVATE src/sensors.c)

app/include/drivers/behavior.h

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,46 @@ __subsystem struct behavior_driver_api {
5656
* @endcond
5757
*/
5858

59+
struct zmk_behavior_ref {
60+
const struct device *device;
61+
};
62+
63+
/**
64+
* Registers @p node_id as a behavior.
65+
*/
66+
#define BEHAVIOR_DEFINE(node_id) \
67+
static const STRUCT_SECTION_ITERABLE(zmk_behavior_ref, \
68+
_CONCAT(zmk_behavior_, DEVICE_DT_NAME_GET(node_id))) = { \
69+
.device = DEVICE_DT_GET(node_id), \
70+
}
71+
72+
/**
73+
* @brief Like DEVICE_DT_DEFINE(), but also registers the device as a behavior.
74+
*
75+
* @param node_id The devicetree node identifier.
76+
* @param ... Other parameters as expected by DEVICE_DT_DEFINE.
77+
*/
78+
#define BEHAVIOR_DT_DEFINE(node_id, ...) \
79+
DEVICE_DT_DEFINE(node_id, __VA_ARGS__); \
80+
BEHAVIOR_DEFINE(node_id)
81+
82+
/**
83+
* @brief Like DEVICE_DT_INST_DEFINE(), but also registers the device as a behavior.
84+
*
85+
* @param inst Instance number.
86+
* @param ... Other parameters as expected by DEVICE_DT_DEFINE.
87+
*/
88+
#define BEHAVIOR_DT_INST_DEFINE(inst, ...) \
89+
DEVICE_DT_INST_DEFINE(inst, __VA_ARGS__); \
90+
BEHAVIOR_DEFINE(DT_DRV_INST(inst))
91+
92+
/**
93+
* Syscall wrapper for zmk_behavior_get_binding().
94+
*
95+
* Use zmk_behavior_get_binding() in application code instead.
96+
*/
97+
__syscall const struct device *behavior_get_binding(const char *name);
98+
5999
/**
60100
* @brief Handle the keymap binding which needs to be converted from relative "toggle" to absolute
61101
* "turn on"
@@ -70,7 +110,7 @@ __syscall int behavior_keymap_binding_convert_central_state_dependent_params(
70110

71111
static inline int z_impl_behavior_keymap_binding_convert_central_state_dependent_params(
72112
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) {
73-
const struct device *dev = device_get_binding(binding->behavior_dev);
113+
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
74114
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
75115

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

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

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

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

149189
if (dev == NULL) {
150190
return -EINVAL;
@@ -178,7 +218,7 @@ static inline int z_impl_behavior_sensor_keymap_binding_accept_data(
178218
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
179219
const struct zmk_sensor_config *sensor_config, size_t channel_data_size,
180220
const struct zmk_sensor_channel_data *channel_data) {
181-
const struct device *dev = device_get_binding(binding->behavior_dev);
221+
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
182222

183223
if (dev == NULL) {
184224
return -EINVAL;
@@ -214,7 +254,7 @@ static inline int
214254
z_impl_behavior_sensor_keymap_binding_process(struct zmk_behavior_binding *binding,
215255
struct zmk_behavior_binding_event event,
216256
enum behavior_sensor_binding_process_mode mode) {
217-
const struct device *dev = device_get_binding(binding->behavior_dev);
257+
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
218258

219259
if (dev == NULL) {
220260
return -EINVAL;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* Copyright (c) 2023 The ZMK Contributors
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#include <zephyr/linker/linker-defs.h>
8+
9+
ITERABLE_SECTION_ROM(zmk_behavior_ref, 4)

app/include/zmk/behavior.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#pragma once
88

9+
#include <zephyr/device.h>
10+
911
#define ZMK_BEHAVIOR_OPAQUE 0
1012
#define ZMK_BEHAVIOR_TRANSPARENT 1
1113

@@ -19,4 +21,18 @@ struct zmk_behavior_binding_event {
1921
int layer;
2022
uint32_t position;
2123
int64_t timestamp;
22-
};
24+
};
25+
26+
/**
27+
* @brief Get a const struct device* for a behavior from its @p name field.
28+
*
29+
* @param name Behavior name to search for.
30+
*
31+
* @retval Pointer to the device structure for the behavior with the given name.
32+
* @retval NULL if the behavior is not found or its initialization function failed.
33+
*
34+
* @note This is equivalent to device_get_binding(), except it only searches
35+
* behavior devices, so it is faster and there is no chance of it returning an
36+
* unrelated node which shares the same name as a behavior.
37+
*/
38+
const struct device *zmk_behavior_get_binding(const char *name);

app/src/behavior.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) 2023 The ZMK Contributors
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#include <zephyr/device.h>
8+
#include <zephyr/init.h>
9+
#include <zephyr/sys/util_macro.h>
10+
#include <string.h>
11+
12+
#include <drivers/behavior.h>
13+
#include <zmk/behavior.h>
14+
15+
#include <zephyr/logging/log.h>
16+
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
17+
18+
const struct device *zmk_behavior_get_binding(const char *name) {
19+
return behavior_get_binding(name);
20+
}
21+
22+
const struct device *z_impl_behavior_get_binding(const char *name) {
23+
if (name == NULL || name[0] == '\0') {
24+
return NULL;
25+
}
26+
27+
STRUCT_SECTION_FOREACH(zmk_behavior_ref, item) {
28+
if (z_device_is_ready(item->device) && item->device->name == name) {
29+
return item->device;
30+
}
31+
}
32+
33+
STRUCT_SECTION_FOREACH(zmk_behavior_ref, item) {
34+
if (z_device_is_ready(item->device) && strcmp(item->device->name, name) == 0) {
35+
return item->device;
36+
}
37+
}
38+
39+
return NULL;
40+
}
41+
42+
#if IS_ENABLED(CONFIG_LOG)
43+
static int check_behavior_names(const struct device *dev) {
44+
ARG_UNUSED(dev);
45+
46+
// Behavior names must be unique, but we don't have a good way to enforce this
47+
// at compile time, so log an error at runtime if they aren't unique.
48+
ptrdiff_t count;
49+
STRUCT_SECTION_COUNT(zmk_behavior_ref, &count);
50+
51+
for (ptrdiff_t i = 0; i < count; i++) {
52+
const struct zmk_behavior_ref *current;
53+
STRUCT_SECTION_GET(zmk_behavior_ref, i, &current);
54+
55+
for (ptrdiff_t j = i + 1; j < count; j++) {
56+
const struct zmk_behavior_ref *other;
57+
STRUCT_SECTION_GET(zmk_behavior_ref, j, &other);
58+
59+
if (strcmp(current->device->name, other->device->name) == 0) {
60+
LOG_ERR("Multiple behaviors have the same name '%s'", current->device->name);
61+
}
62+
}
63+
}
64+
65+
return 0;
66+
}
67+
68+
SYS_INIT(check_behavior_names, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
69+
#endif // IS_ENABLED(CONFIG_LOG)

app/src/behaviors/behavior_backlight.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ static const struct behavior_driver_api behavior_backlight_driver_api = {
9191
.locality = BEHAVIOR_LOCALITY_GLOBAL,
9292
};
9393

94-
DEVICE_DT_INST_DEFINE(0, behavior_backlight_init, NULL, NULL, NULL, APPLICATION,
95-
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_backlight_driver_api);
94+
BEHAVIOR_DT_INST_DEFINE(0, behavior_backlight_init, NULL, NULL, NULL, APPLICATION,
95+
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_backlight_driver_api);
9696

9797
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

app/src/behaviors/behavior_bt.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ static const struct behavior_driver_api behavior_bt_driver_api = {
5252
.binding_released = on_keymap_binding_released,
5353
};
5454

55-
DEVICE_DT_INST_DEFINE(0, behavior_bt_init, NULL, NULL, NULL, APPLICATION,
56-
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_bt_driver_api);
55+
BEHAVIOR_DT_INST_DEFINE(0, behavior_bt_init, NULL, NULL, NULL, APPLICATION,
56+
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_bt_driver_api);
5757

5858
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

app/src/behaviors/behavior_caps_word.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ static void deactivate_caps_word(const struct device *dev) {
5555

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

6161
if (data->active) {
@@ -181,9 +181,9 @@ static int behavior_caps_word_init(const struct device *dev) {
181181
.continuations = {LISTIFY(DT_INST_PROP_LEN(n, continue_list), BREAK_ITEM, (, ), n)}, \
182182
.continuations_count = DT_INST_PROP_LEN(n, continue_list), \
183183
}; \
184-
DEVICE_DT_INST_DEFINE(n, behavior_caps_word_init, NULL, &behavior_caps_word_data_##n, \
185-
&behavior_caps_word_config_##n, APPLICATION, \
186-
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_caps_word_driver_api);
184+
BEHAVIOR_DT_INST_DEFINE(n, behavior_caps_word_init, NULL, &behavior_caps_word_data_##n, \
185+
&behavior_caps_word_config_##n, APPLICATION, \
186+
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_caps_word_driver_api);
187187

188188
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
189189

app/src/behaviors/behavior_ext_power.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ static const struct behavior_driver_api behavior_ext_power_driver_api = {
7474
.locality = BEHAVIOR_LOCALITY_GLOBAL,
7575
};
7676

77-
DEVICE_DT_INST_DEFINE(0, behavior_ext_power_init, NULL, NULL, NULL, APPLICATION,
78-
CONFIG_APPLICATION_INIT_PRIORITY, &behavior_ext_power_driver_api);
77+
BEHAVIOR_DT_INST_DEFINE(0, behavior_ext_power_init, NULL, NULL, NULL, APPLICATION,
78+
CONFIG_APPLICATION_INIT_PRIORITY, &behavior_ext_power_driver_api);
7979

8080
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

app/src/behaviors/behavior_hold_tap.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ static void update_hold_status_for_retro_tap(uint32_t ignore_position) {
511511

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

517517
if (undecided_hold_tap != NULL) {
@@ -715,9 +715,9 @@ static int behavior_hold_tap_init(const struct device *dev) {
715715
.hold_trigger_key_positions = DT_INST_PROP(n, hold_trigger_key_positions), \
716716
.hold_trigger_key_positions_len = DT_INST_PROP_LEN(n, hold_trigger_key_positions), \
717717
}; \
718-
DEVICE_DT_INST_DEFINE(n, behavior_hold_tap_init, NULL, NULL, &behavior_hold_tap_config_##n, \
719-
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
720-
&behavior_hold_tap_driver_api);
718+
BEHAVIOR_DT_INST_DEFINE(n, behavior_hold_tap_init, NULL, NULL, &behavior_hold_tap_config_##n, \
719+
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
720+
&behavior_hold_tap_driver_api);
721721

722722
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
723723

0 commit comments

Comments
 (0)