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().
This commit is contained in:
Joel Spadin 2023-11-18 19:19:15 -06:00
parent 5ecd3521f5
commit 36eda571b7
31 changed files with 231 additions and 93 deletions

View File

@ -15,6 +15,7 @@ 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
@ -22,6 +23,7 @@ zephyr_linker_sources(RODATA include/linker/zmk-events.ld)
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)

View File

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

View File

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

View File

@ -6,6 +6,8 @@
#pragma once
#include <zephyr/device.h>
#define ZMK_BEHAVIOR_OPAQUE 0
#define ZMK_BEHAVIOR_TRANSPARENT 1
@ -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
app/src/behavior.c Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -36,7 +36,7 @@ struct behavior_mod_morph_data {
static int on_mod_morph_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_mod_morph_config *cfg = dev->config;
struct behavior_mod_morph_data *data = dev->data;
@ -56,7 +56,7 @@ static int on_mod_morph_binding_pressed(struct zmk_behavior_binding *binding,
static int on_mod_morph_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_mod_morph_data *data = dev->data;
if (data->pressed_binding == NULL) {
@ -97,9 +97,9 @@ static int behavior_mod_morph_init(const struct device *dev) { return 0; }
(DT_INST_PROP(n, mods) & ~DT_INST_PROP(n, keep_mods))), \
}; \
static struct behavior_mod_morph_data behavior_mod_morph_data_##n = {}; \
DEVICE_DT_INST_DEFINE(n, behavior_mod_morph_init, NULL, &behavior_mod_morph_data_##n, \
&behavior_mod_morph_config_##n, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mod_morph_driver_api);
BEHAVIOR_DT_INST_DEFINE(n, behavior_mod_morph_init, NULL, &behavior_mod_morph_data_##n, \
&behavior_mod_morph_config_##n, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mod_morph_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KP_INST)

View File

@ -39,5 +39,5 @@ static const struct behavior_mo_config behavior_mo_config = {};
static struct behavior_mo_data behavior_mo_data;
DEVICE_DT_INST_DEFINE(0, behavior_mo_init, NULL, &behavior_mo_data, &behavior_mo_config,
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mo_driver_api);
BEHAVIOR_DT_INST_DEFINE(0, behavior_mo_init, NULL, &behavior_mo_data, &behavior_mo_config,
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mo_driver_api);

View File

@ -39,9 +39,9 @@ static const struct behavior_driver_api behavior_mouse_key_press_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
#define MKP_INST(n) \
DEVICE_DT_INST_DEFINE(n, behavior_mouse_key_press_init, NULL, NULL, NULL, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_mouse_key_press_driver_api);
BEHAVIOR_DT_INST_DEFINE(n, behavior_mouse_key_press_init, NULL, NULL, NULL, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_mouse_key_press_driver_api);
DT_INST_FOREACH_STATUS_OKAY(MKP_INST)

View File

@ -33,7 +33,7 @@ static const struct behavior_driver_api behavior_none_driver_api = {
.binding_released = on_keymap_binding_released,
};
DEVICE_DT_INST_DEFINE(0, behavior_none_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_none_driver_api);
BEHAVIOR_DT_INST_DEFINE(0, behavior_none_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_none_driver_api);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View File

@ -42,7 +42,7 @@ static const struct behavior_driver_api behavior_outputs_driver_api = {
.binding_pressed = on_keymap_binding_pressed,
};
DEVICE_DT_INST_DEFINE(0, behavior_out_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_outputs_driver_api);
BEHAVIOR_DT_INST_DEFINE(0, behavior_out_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_outputs_driver_api);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View File

@ -25,7 +25,7 @@ static int behavior_reset_init(const struct device *dev) { return 0; };
static int on_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);
const struct behavior_reset_config *cfg = dev->config;
// TODO: Correct magic code for going into DFU?
@ -43,9 +43,9 @@ static const struct behavior_driver_api behavior_reset_driver_api = {
#define RST_INST(n) \
static const struct behavior_reset_config behavior_reset_config_##n = { \
.type = DT_INST_PROP(n, type)}; \
DEVICE_DT_INST_DEFINE(n, behavior_reset_init, NULL, NULL, &behavior_reset_config_##n, \
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_reset_driver_api);
BEHAVIOR_DT_INST_DEFINE(n, behavior_reset_init, NULL, NULL, &behavior_reset_config_##n, \
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_reset_driver_api);
DT_INST_FOREACH_STATUS_OKAY(RST_INST)

View File

@ -149,7 +149,7 @@ static const struct behavior_driver_api behavior_rgb_underglow_driver_api = {
.locality = BEHAVIOR_LOCALITY_GLOBAL,
};
DEVICE_DT_INST_DEFINE(0, behavior_rgb_underglow_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_rgb_underglow_driver_api);
BEHAVIOR_DT_INST_DEFINE(0, behavior_rgb_underglow_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_rgb_underglow_driver_api);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View File

@ -35,9 +35,9 @@ static int behavior_sensor_rotate_init(const struct device *dev) { return 0; };
.override_params = false, \
}; \
static struct behavior_sensor_rotate_data behavior_sensor_rotate_data_##n = {}; \
DEVICE_DT_INST_DEFINE(n, behavior_sensor_rotate_init, NULL, &behavior_sensor_rotate_data_##n, \
&behavior_sensor_rotate_config_##n, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_sensor_rotate_driver_api);
BEHAVIOR_DT_INST_DEFINE(n, behavior_sensor_rotate_init, NULL, \
&behavior_sensor_rotate_data_##n, &behavior_sensor_rotate_config_##n, \
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_sensor_rotate_driver_api);
DT_INST_FOREACH_STATUS_OKAY(SENSOR_ROTATE_INST)

View File

@ -15,7 +15,7 @@ int zmk_behavior_sensor_rotate_common_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);
struct behavior_sensor_rotate_data *data = dev->data;
const struct sensor_value value = channel_data[0].value;
@ -58,7 +58,7 @@ int zmk_behavior_sensor_rotate_common_accept_data(
int zmk_behavior_sensor_rotate_common_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);
const struct behavior_sensor_rotate_config *cfg = dev->config;
struct behavior_sensor_rotate_data *data = dev->data;

View File

@ -26,7 +26,7 @@ static int behavior_sensor_rotate_var_init(const struct device *dev) { return 0;
.override_params = true, \
}; \
static struct behavior_sensor_rotate_data behavior_sensor_rotate_var_data_##n = {}; \
DEVICE_DT_INST_DEFINE( \
BEHAVIOR_DT_INST_DEFINE( \
n, behavior_sensor_rotate_var_init, NULL, &behavior_sensor_rotate_var_data_##n, \
&behavior_sensor_rotate_var_config_##n, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_sensor_rotate_var_driver_api);

View File

@ -129,7 +129,7 @@ static int stop_timer(struct active_sticky_key *sticky_key) {
static int on_sticky_key_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_sticky_key_config *cfg = dev->config;
struct active_sticky_key *sticky_key;
sticky_key = find_sticky_key(event.position);
@ -293,9 +293,9 @@ static struct behavior_sticky_key_data behavior_sticky_key_data;
.ignore_modifiers = DT_INST_PROP(n, ignore_modifiers), \
.quick_release = DT_INST_PROP(n, quick_release), \
}; \
DEVICE_DT_INST_DEFINE(n, behavior_sticky_key_init, NULL, &behavior_sticky_key_data, \
&behavior_sticky_key_config_##n, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_sticky_key_driver_api);
BEHAVIOR_DT_INST_DEFINE(n, behavior_sticky_key_init, NULL, &behavior_sticky_key_data, \
&behavior_sticky_key_config_##n, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_sticky_key_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KP_INST)

View File

@ -125,7 +125,7 @@ static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance,
static int on_tap_dance_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_tap_dance_config *cfg = dev->config;
struct active_tap_dance *tap_dance;
tap_dance = find_tap_dance(event.position);
@ -250,9 +250,9 @@ static int behavior_tap_dance_init(const struct device *dev) {
.tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \
.behaviors = behavior_tap_dance_config_##n##_bindings, \
.behavior_count = DT_INST_PROP_LEN(n, bindings)}; \
DEVICE_DT_INST_DEFINE(n, behavior_tap_dance_init, NULL, NULL, &behavior_tap_dance_config_##n, \
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_tap_dance_driver_api);
BEHAVIOR_DT_INST_DEFINE(n, behavior_tap_dance_init, NULL, NULL, \
&behavior_tap_dance_config_##n, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_tap_dance_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KP_INST)

View File

@ -37,7 +37,7 @@ static const struct behavior_driver_api behavior_to_driver_api = {
.binding_released = to_keymap_binding_released,
};
DEVICE_DT_INST_DEFINE(0, behavior_to_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_to_driver_api);
BEHAVIOR_DT_INST_DEFINE(0, behavior_to_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_to_driver_api);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View File

@ -43,7 +43,7 @@ static const struct behavior_tog_config behavior_tog_config = {};
static struct behavior_tog_data behavior_tog_data;
DEVICE_DT_INST_DEFINE(0, behavior_tog_init, NULL, &behavior_tog_data, &behavior_tog_config,
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_tog_driver_api);
BEHAVIOR_DT_INST_DEFINE(0, behavior_tog_init, NULL, &behavior_tog_data, &behavior_tog_config,
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_tog_driver_api);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View File

@ -33,7 +33,7 @@ static const struct behavior_driver_api behavior_transparent_driver_api = {
.binding_released = on_keymap_binding_released,
};
DEVICE_DT_INST_DEFINE(0, behavior_transparent_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_transparent_driver_api);
BEHAVIOR_DT_INST_DEFINE(0, behavior_transparent_init, NULL, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_transparent_driver_api);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View File

@ -176,7 +176,7 @@ int zmk_keymap_apply_position_state(uint8_t source, int layer, uint32_t position
LOG_DBG("layer: %d position: %d, binding name: %s", layer, position, binding.behavior_dev);
behavior = device_get_binding(binding.behavior_dev);
behavior = zmk_behavior_get_binding(binding.behavior_dev);
if (!behavior) {
LOG_WRN("No behavior assigned to %d on layer %d", position, layer);
@ -256,7 +256,7 @@ int zmk_keymap_sensor_event(uint8_t sensor_index,
LOG_DBG("layer: %d sensor_index: %d, binding name: %s", layer, sensor_index,
binding->behavior_dev);
const struct device *behavior = device_get_binding(binding->behavior_dev);
const struct device *behavior = zmk_behavior_get_binding(binding->behavior_dev);
if (!behavior) {
LOG_DBG("No behavior assigned to %d on layer %d", sensor_index, layer);
continue;

View File

@ -171,12 +171,12 @@ static const struct behavior_driver_api <behavior_name>_driver_api = {
};
DEVICE_DT_INST_DEFINE(0, // Instance Number (Equal to 0 for behaviors that don't require multiple instances,
// Equal to n for behaviors that do make use of multiple instances)
<behavior_name>_init, NULL, // Initialization Function, Power Management Device Pointer
&<behavior_name>_data, &<behavior_name>_config, // Behavior Data Pointer, Behavior Configuration Pointer (Both Optional)
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, // Initialization Level, Device Priority
&<behavior_name>_driver_api); // API Structure
BEHAVIOR_DT_INST_DEFINE(0, // Instance Number (Equal to 0 for behaviors that don't require multiple instances,
// Equal to n for behaviors that do make use of multiple instances)
<behavior_name>_init, NULL, // Initialization Function, Power Management Device Pointer
&<behavior_name>_data, &<behavior_name>_config, // Behavior Data Pointer, Behavior Configuration Pointer (Both Optional)
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, // Initialization Level, Device Priority
&<behavior_name>_driver_api); // API Structure
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
@ -244,13 +244,15 @@ Listeners, defined by the `ZMK_LISTENER(mod, cb)` function, take in a listener n
- `ZMK_EVENT_RELEASE(ev)`: Continue handling this event (`ev`) at the next registered event listener.
- `ZMK_EVENT_FREE(ev)`: Free the memory associated with the event (`ev`).
#### `DEVICE_DT_INST_DEFINE`
#### `BEHAVIOR_DT_INST_DEFINE`
`BEHAVIOR_DT_INST_DEFINE` is a special ZMK macro. It forwards all the parameters to Zephyr's `DEVICE_DT_INST_DEFINE` macro to define the driver instance, then it adds the driver to a list of ZMK behaviors so they can be found by `zmk_behavior_get_binding()`.
:::info
For more information on this function, refer to [Zephyr's documentation on the Device Driver Model](https://docs.zephyrproject.org/latest/kernel/drivers/index.html#c.DEVICE_DT_INST_DEFINE).
:::
The example `DEVICE_DT_INST_DEFINE` call can be left as is with the first parameter, the instance number, equal to `0` for behaviors that only require a single instance (e.g. external power, backlighting, accessing layers). For behaviors that can have multiple instances (e.g. hold-taps, tap-dances, sticky-keys), `DEVICE_DT_INST_DEFINE` can be placed inside a `#define` statement, usually formatted as `#define <ABBREVIATED BEHAVIOR NAME>_INST(n)`, that sets up any [data pointers](#data-pointers-optional) and/or [configuration pointers](#configuration-pointers-optional) that are unique to each instance.
The example `BEHAVIOR_DT_INST_DEFINE` call can be left as is with the first parameter, the instance number, equal to `0` for behaviors that only require a single instance (e.g. external power, backlighting, accessing layers). For behaviors that can have multiple instances (e.g. hold-taps, tap-dances, sticky-keys), `BEHAVIOR_DT_INST_DEFINE` can be placed inside a `#define` statement, usually formatted as `#define <ABBREVIATED BEHAVIOR NAME>_INST(n)`, that sets up any [data pointers](#data-pointers-optional) and/or [configuration pointers](#configuration-pointers-optional) that are unique to each instance.
An example of this can be seen below, taking the `#define KP_INST(n)` from the hold-tap driver.
@ -266,16 +268,16 @@ An example of this can be seen below, taking the `#define KP_INST(n)` from the h
.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)
```
Note that in the hold-tap example, the instance number, `0`, has been replaced by `n`, signifying the unique `node_id` of each instance of a behavior. Furthermore, the DT_INST_FOREACH_STATUS_OKAY(KP_INST) macro iterates through each compatible, non-disabled devicetree node, creating and applying the proper values to any instance-specific configurations or data by invoking the KP_INST macro for each instance of the new behavior.
Behaviors also require the following parameters of `DEVICE_DT_INST_DEFINE` to be changed:
Behaviors also require the following parameters of `BEHAVIOR_DT_INST_DEFINE` to be changed:
##### Initialization Function
@ -300,19 +302,19 @@ Comes in the form `static const struct behavior_driver_api <behavior_name>_drive
The data `struct` stores additional data required for **each new instance** of the behavior. Regardless of the instance number, `n`, `behavior_<behavior_name>_data_##n` is typically initialized as an empty `struct`. The data respective to each instance of the behavior can be accessed in functions like [`on_<behavior_name>_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event)`](#dependencies) by extracting the behavior device from the keybind like so:
```c
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
struct behavior_<behavior_name>_data *data = dev->data;
```
The variables stored inside the data `struct`, `data`, can be then modified as necessary.
The fourth cell of `DEVICE_DT_INST_DEFINE` can be set to `NULL` instead if instance-specific data is not required.
The fourth cell of `BEHAVIOR_DT_INST_DEFINE` can be set to `NULL` instead if instance-specific data is not required.
##### Configuration Pointers (Optional)
The configuration `struct` stores the properties declared from the behavior's `.yaml` for **each new instance** of the behavior. As seen in the `#define KP_INST(n)` of the hold-tap example, the configuration `struct`, `behavior_<behavior_name>_config_##n`, for each instance number, `n`, can be initialized using the [Zephyr Devicetree Instance-based APIs](https://docs.zephyrproject.org/latest/build/dts/api/api.html#instance-based-apis), which extract the values from the `properties` of each instance of the [devicetree binding](#creating-the-devicetree-binding-yaml) from a user's keymap or [predefined use-case `.dtsi` files](#defining-common-use-cases-for-the-behavior-dtsi-optional) stored in `app/dts/behaviors/`. We illustrate this further by comparing the [`#define KP_INST(n)` from the hold-tap driver](#device_dt_inst_define) and the [`properties` of the hold-tap devicetree binding.](#creating-the-devicetree-binding-yaml)
The configuration `struct` stores the properties declared from the behavior's `.yaml` for **each new instance** of the behavior. As seen in the `#define KP_INST(n)` of the hold-tap example, the configuration `struct`, `behavior_<behavior_name>_config_##n`, for each instance number, `n`, can be initialized using the [Zephyr Devicetree Instance-based APIs](https://docs.zephyrproject.org/latest/build/dts/api/api.html#instance-based-apis), which extract the values from the `properties` of each instance of the [devicetree binding](#creating-the-devicetree-binding-yaml) from a user's keymap or [predefined use-case `.dtsi` files](#defining-common-use-cases-for-the-behavior-dtsi-optional) stored in `app/dts/behaviors/`. We illustrate this further by comparing the [`#define KP_INST(n)` from the hold-tap driver](#behavior_dt_inst_define) and the [`properties` of the hold-tap devicetree binding.](#creating-the-devicetree-binding-yaml)
The fifth cell of `DEVICE_DT_INST_DEFINE` can be set to `NULL` instead if instance-specific configurations are not required.
The fifth cell of `BEHAVIOR_DT_INST_DEFINE` can be set to `NULL` instead if instance-specific configurations are not required.
:::caution
Remember that `.c` files should be formatted according to `clang-format` to ensure that checks run smoothly once the pull request is submitted.