From b576d52d58eade3d909e536acffac282d71651c8 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Wed, 27 Mar 2024 22:17:32 +0000 Subject: [PATCH 01/16] feat(core): Support adding subs to other listeners. * Used for ZMK Studio event remappers to be sure the one RPC event listener subscribes to their mapped events. --- app/include/zmk/event_manager.h | 1 + 1 file changed, 1 insertion(+) diff --git a/app/include/zmk/event_manager.h b/app/include/zmk/event_manager.h index e4420715..0eb63ad7 100644 --- a/app/include/zmk/event_manager.h +++ b/app/include/zmk/event_manager.h @@ -64,6 +64,7 @@ struct zmk_event_subscription { #define ZMK_LISTENER(mod, cb) const struct zmk_listener zmk_listener_##mod = {.callback = cb}; #define ZMK_SUBSCRIPTION(mod, ev_type) \ + extern const struct zmk_listener zmk_listener_##mod; \ const Z_DECL_ALIGN(struct zmk_event_subscription) \ _CONCAT(_CONCAT(zmk_event_sub_, mod), ev_type) __used \ __attribute__((__section__(".event_subscription"))) = { \ From f7c34c70bad6d09dbdb4bfdfad5a196179dbb8c8 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Fri, 5 Apr 2024 16:44:42 +0000 Subject: [PATCH 02/16] refactor(ble): Extract API to get active profile connection. * Add `struct bt_conn *zmk_ble_active_profile_conn(void)` function for fetching a connection for the current profile. --- app/include/zmk/ble.h | 3 +++ app/src/ble.c | 15 +++++++++++++++ app/src/hog.c | 19 ++----------------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/app/include/zmk/ble.h b/app/include/zmk/ble.h index 773323c1..cc55a6ce 100644 --- a/app/include/zmk/ble.h +++ b/app/include/zmk/ble.h @@ -29,7 +29,10 @@ int zmk_ble_prof_disconnect(uint8_t index); int zmk_ble_active_profile_index(void); int zmk_ble_profile_index(const bt_addr_le_t *addr); + bt_addr_le_t *zmk_ble_active_profile_addr(void); +struct bt_conn *zmk_ble_active_profile_conn(void); + bool zmk_ble_active_profile_is_open(void); bool zmk_ble_active_profile_is_connected(void); char *zmk_ble_active_profile_name(void); diff --git a/app/src/ble.c b/app/src/ble.c index 7e1ae7d4..b2dfbfa1 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -318,6 +318,21 @@ int zmk_ble_prof_disconnect(uint8_t index) { bt_addr_le_t *zmk_ble_active_profile_addr(void) { return &profiles[active_profile].peer; } +struct bt_conn *zmk_ble_active_profile_conn(void) { + struct bt_conn *conn; + bt_addr_le_t *addr = zmk_ble_active_profile_addr(); + + if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) { + LOG_WRN("Not sending, no active address for current profile"); + return NULL; + } else if ((conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr)) == NULL) { + LOG_WRN("Not sending, not connected to active profile"); + return NULL; + } + + return conn; +} + char *zmk_ble_active_profile_name(void) { return profiles[active_profile].name; } #if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) diff --git a/app/src/hog.c b/app/src/hog.c index f17f759c..77dde989 100644 --- a/app/src/hog.c +++ b/app/src/hog.c @@ -220,21 +220,6 @@ BT_GATT_SERVICE_DEFINE( BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT, BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE, NULL, write_ctrl_point, &ctrl_point)); -struct bt_conn *destination_connection(void) { - struct bt_conn *conn; - bt_addr_le_t *addr = zmk_ble_active_profile_addr(); - LOG_DBG("Address pointer %p", addr); - if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) { - LOG_WRN("Not sending, no active address for current profile"); - return NULL; - } else if ((conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr)) == NULL) { - LOG_WRN("Not sending, not connected to active profile"); - return NULL; - } - - return conn; -} - K_THREAD_STACK_DEFINE(hog_q_stack, CONFIG_ZMK_BLE_THREAD_STACK_SIZE); struct k_work_q hog_work_q; @@ -246,7 +231,7 @@ void send_keyboard_report_callback(struct k_work *work) { struct zmk_hid_keyboard_report_body report; while (k_msgq_get(&zmk_hog_keyboard_msgq, &report, K_NO_WAIT) == 0) { - struct bt_conn *conn = destination_connection(); + struct bt_conn *conn = zmk_ble_active_profile_conn(); if (conn == NULL) { return; } @@ -298,7 +283,7 @@ void send_consumer_report_callback(struct k_work *work) { struct zmk_hid_consumer_report_body report; while (k_msgq_get(&zmk_hog_consumer_msgq, &report, K_NO_WAIT) == 0) { - struct bt_conn *conn = destination_connection(); + struct bt_conn *conn = zmk_ble_active_profile_conn(); if (conn == NULL) { return; } From 483a4930e992a219d9fe941d1e7369194d34b15f Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Wed, 17 Apr 2024 16:44:22 -0700 Subject: [PATCH 03/16] feat(behaviors): Add local ID system for behaviors * Add a new feature for tracking a given behavior by a new concept of a "behavior local ID" which is a stable 16-bit identifier for a given behavior, that is resilient to new behaviors being added and requires no additional work on the part of the behavior authors. * Add implementations for either settings lookup table, or CRC16 hashing of behavior device names for generating behavior local IDs. --- app/CMakeLists.txt | 4 + app/Kconfig.behaviors | 29 ++++ app/include/drivers/behavior.h | 19 ++- .../linker/zmk-behavior-local-id-map.ld | 9 ++ app/include/zmk/behavior.h | 22 +++ app/src/behavior.c | 135 ++++++++++++++++++ 6 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 app/include/linker/zmk-behavior-local-id-map.ld diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 0b681ea9..2818e932 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -11,6 +11,10 @@ project(zmk) zephyr_linker_sources(SECTIONS include/linker/zmk-behaviors.ld) zephyr_linker_sources(RODATA include/linker/zmk-events.ld) +if(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS) + zephyr_linker_sources(DATA_SECTIONS include/linker/zmk-behavior-local-id-map.ld) +endif() + zephyr_syscall_header(${APPLICATION_SOURCE_DIR}/include/drivers/behavior.h) zephyr_syscall_header(${APPLICATION_SOURCE_DIR}/include/drivers/ext_power.h) diff --git a/app/Kconfig.behaviors b/app/Kconfig.behaviors index c6cc45f3..0fa34546 100644 --- a/app/Kconfig.behaviors +++ b/app/Kconfig.behaviors @@ -7,6 +7,35 @@ config ZMK_BEHAVIOR_METADATA Enabling this option adds APIs for documenting and fetching metadata describing a behaviors name, and supported parameters. +config ZMK_BEHAVIOR_LOCAL_IDS + bool "Local IDs" + +if ZMK_BEHAVIOR_LOCAL_IDS + +choice ZMK_BEHAVIOR_LOCAL_ID_TYPE + prompt "Local ID Type" + +config ZMK_BEHAVIOR_LOCAL_ID_TYPE_SETTINGS_TABLE + bool "Settings Table" + depends on SETTINGS + help + Use persistent entries in the settings subsystem to identify + behaviors by local ID, which uses the device name to generate + a new settings entry tying a presistant local ID to that name. + This guarantees stable, colllision-free local IDs at the expense + of settings storage used. + +config ZMK_BEHAVIOR_LOCAL_ID_TYPE_CRC16 + bool "CRC16 Hash" + help + Use the CRC16-ANSI hash of behavior device names to generate + stable behavior local IDs. This saves on settings storage at + the expense of (highly unlikely) risk of collisions. + +endchoice + +endif + config ZMK_BEHAVIOR_KEY_TOGGLE bool default y diff --git a/app/include/drivers/behavior.h b/app/include/drivers/behavior.h index 3dd6e062..0b814ff2 100644 --- a/app/include/drivers/behavior.h +++ b/app/include/drivers/behavior.h @@ -108,6 +108,15 @@ struct zmk_behavior_ref { const struct zmk_behavior_metadata metadata; }; +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS) + +struct zmk_behavior_local_id_map { + const struct device *device; + zmk_behavior_local_id_t local_id; +}; + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS) + #define ZMK_BEHAVIOR_REF_DT_NAME(node_id) _CONCAT(zmk_behavior_, DEVICE_DT_NAME_GET(node_id)) #if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) @@ -125,9 +134,17 @@ struct zmk_behavior_ref { #define ZMK_BEHAVIOR_REF_INITIALIZER(node_id, _dev) \ { .device = _dev, .metadata = ZMK_BEHAVIOR_METADATA_INITIALIZER(node_id), } +#define ZMK_BEHAVIOR_LOCAL_ID_MAP_INITIALIZER(node_id, _dev) \ + { .device = _dev, } + #define ZMK_BEHAVIOR_REF_DEFINE(name, node_id, _dev) \ static const STRUCT_SECTION_ITERABLE(zmk_behavior_ref, name) = \ - ZMK_BEHAVIOR_REF_INITIALIZER(node_id, _dev) + ZMK_BEHAVIOR_REF_INITIALIZER(node_id, _dev); \ + COND_CODE_1(IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS), \ + (static const STRUCT_SECTION_ITERABLE(zmk_behavior_local_id_map, \ + _CONCAT(_zmk_behavior_local_id_map, name)) = \ + ZMK_BEHAVIOR_LOCAL_ID_MAP_INITIALIZER(node_id, _dev)), \ + ()); #define ZMK_BEHAVIOR_REF_DT_DEFINE(node_id) \ ZMK_BEHAVIOR_REF_DEFINE(ZMK_BEHAVIOR_REF_DT_NAME(node_id), node_id, DEVICE_DT_GET(node_id)) diff --git a/app/include/linker/zmk-behavior-local-id-map.ld b/app/include/linker/zmk-behavior-local-id-map.ld new file mode 100644 index 00000000..c91e64c4 --- /dev/null +++ b/app/include/linker/zmk-behavior-local-id-map.ld @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +ITERABLE_SECTION_RAM(zmk_behavior_local_id_map, 4) diff --git a/app/include/zmk/behavior.h b/app/include/zmk/behavior.h index 016fa3bc..34a415ca 100644 --- a/app/include/zmk/behavior.h +++ b/app/include/zmk/behavior.h @@ -23,6 +23,8 @@ struct zmk_behavior_binding_event { int64_t timestamp; }; +typedef uint16_t zmk_behavior_local_id_t; + /** * @brief Get a const struct device* for a behavior from its @p name field. * @@ -36,3 +38,23 @@ struct zmk_behavior_binding_event { * unrelated node which shares the same name as a behavior. */ const struct device *zmk_behavior_get_binding(const char *name); + +/** + * @brief Get a local ID for a behavior from its @p name field. + * + * @param name Behavior name to search for. + * + * @retval The local ID value that can be used to reference the behavior later, across reboots. + * @retval UINT16_MAX if the behavior is not found or its initialization function failed. + */ +zmk_behavior_local_id_t zmk_behavior_get_local_id(const char *name); + +/** + * @brief Get a behavior name for a behavior from its @p local_id . + * + * @param local_id Behavior local ID used to search for the behavior + * + * @retval The name of the behavior that is associated with that local ID. + * @retval NULL if the behavior is not found or its initialization function failed. + */ +const char *zmk_behavior_find_behavior_name_from_local_id(zmk_behavior_local_id_t local_id); diff --git a/app/src/behavior.c b/app/src/behavior.c index 7777155f..7505aa7f 100644 --- a/app/src/behavior.c +++ b/app/src/behavior.c @@ -6,9 +6,17 @@ #include #include +#include #include #include +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS) && \ + IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_ID_TYPE_SETTINGS_TABLE) + +#include + +#endif + #include #include #include @@ -185,6 +193,133 @@ int zmk_behavior_validate_binding(const struct zmk_behavior_binding *binding) { #endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) } +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS) + +zmk_behavior_local_id_t zmk_behavior_get_local_id(const char *name) { + if (!name) { + return UINT16_MAX; + } + + STRUCT_SECTION_FOREACH(zmk_behavior_local_id_map, item) { + if (z_device_is_ready(item->device) && strcmp(item->device->name, name) == 0) { + return item->local_id; + } + } + + return UINT16_MAX; +} + +const char *zmk_behavior_find_behavior_name_from_local_id(zmk_behavior_local_id_t local_id) { + STRUCT_SECTION_FOREACH(zmk_behavior_local_id_map, item) { + if (z_device_is_ready(item->device) && item->local_id == local_id) { + return item->device->name; + } + } + + return NULL; +} + +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_ID_TYPE_CRC16) + +static int behavior_local_id_init(void) { + STRUCT_SECTION_FOREACH(zmk_behavior_local_id_map, item) { + item->local_id = crc16_ansi(item->device->name, strlen(item->device->name)); + } + + return 0; +} + +#elif IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_ID_TYPE_SETTINGS_TABLE) + +static zmk_behavior_local_id_t largest_local_id = 0; + +static int behavior_handle_set(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg) { + const char *next; + + if (settings_name_steq(name, "local_id", &next) && next) { + char *endptr; + uint8_t local_id = strtoul(next, &endptr, 10); + if (*endptr != '\0') { + LOG_WRN("Invalid behavior local ID: %s with endptr %s", next, endptr); + return -EINVAL; + } + + if (len >= 64) { + LOG_ERR("Too large binding setting size (got %d expected less than %d)", len, 64); + return -EINVAL; + } + + char name[len + 1]; + + int err = read_cb(cb_arg, name, len); + if (err <= 0) { + LOG_ERR("Failed to handle keymap binding from settings (err %d)", err); + return err; + } + + name[len] = '\0'; + STRUCT_SECTION_FOREACH(zmk_behavior_local_id_map, item) { + if (strcmp(name, item->device->name) == 0) { + item->local_id = local_id; + largest_local_id = MAX(largest_local_id, local_id); + return 0; + } + } + + return -EINVAL; + } + + return 0; +} + +static int behavior_handle_commit(void) { + STRUCT_SECTION_FOREACH(zmk_behavior_local_id_map, item) { + if (item->local_id != 0) { + continue; + } + + if (!item->device || !item->device->name || !device_is_ready(item->device)) { + LOG_WRN("Skipping ID for device that doesn't exist or isn't ready"); + continue; + } + + item->local_id = ++largest_local_id; + char setting_name[32]; + sprintf(setting_name, "behavior/local_id/%d", item->local_id); + + // If the `device->name` is readonly in flash, settings save can fail to copy/read it while + // persisting to flash, so copy the device name into memory first before saving. + char device_name[32]; + snprintf(device_name, ARRAY_SIZE(device_name), "%s", item->device->name); + + settings_save_one(setting_name, device_name, strlen(device_name)); + } + + return 0; +} + +SETTINGS_STATIC_HANDLER_DEFINE(behavior, "behavior", NULL, behavior_handle_set, + behavior_handle_commit, NULL); + +static int behavior_local_id_init(void) { + settings_subsys_init(); + + settings_load_subtree("behavior"); + + return 0; +} + +#else + +#error "A behavior local ID mechanism must be selected" + +#endif + +SYS_INIT(behavior_local_id_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); + +#endif + #if IS_ENABLED(CONFIG_LOG) static int check_behavior_names(void) { // Behavior names must be unique, but we don't have a good way to enforce this From 96e55c8be61e9a12cf06147ece38dd7596023bc6 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Fri, 28 Jun 2024 17:40:49 -0600 Subject: [PATCH 04/16] fix: BLE refactor mouse keys fix. --- app/core-coverage.yml | 4 ++++ app/src/hog.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/core-coverage.yml b/app/core-coverage.yml index 7eef35cf..1350044c 100644 --- a/app/core-coverage.yml +++ b/app/core-coverage.yml @@ -22,6 +22,10 @@ include: shield: kyria_left cmake-args: "-DCONFIG_ZMK_DISPLAY=y" nickname: "display" + - board: nice_nano_v2 + shield: kyria_left + cmake-args: "-DCONFIG_ZMK_MOUSE=y" + nickname: "mouse" - board: sparkfun_pro_micro_rp2040 shield: reviung41 cmake-args: "-DSNIPPET='zmk-usb-logging'" diff --git a/app/src/hog.c b/app/src/hog.c index 77dde989..82fafc29 100644 --- a/app/src/hog.c +++ b/app/src/hog.c @@ -336,7 +336,7 @@ K_MSGQ_DEFINE(zmk_hog_mouse_msgq, sizeof(struct zmk_hid_mouse_report_body), void send_mouse_report_callback(struct k_work *work) { struct zmk_hid_mouse_report_body report; while (k_msgq_get(&zmk_hog_mouse_msgq, &report, K_NO_WAIT) == 0) { - struct bt_conn *conn = destination_connection(); + struct bt_conn *conn = zmk_ble_active_profile_conn(); if (conn == NULL) { return; } From f2ea1da036f7c54199c3bcb79fcbda9753f91028 Mon Sep 17 00:00:00 2001 From: ReFil <31960031+ReFil@users.noreply.github.com> Date: Sun, 30 Jun 2024 17:59:46 +0100 Subject: [PATCH 05/16] fix(boards): Suppress devicetree warning nRF5x boards throw a spurious warning for duplicate unit-address when the devicetree gets processed Upstream issue: https://github.com/zephyrproject-rtos/zephyr/issues/29713 Warning suppressed as per: https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html --- app/boards/arm/adv360pro/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/bluemicro840/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/bt60/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/ckp/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/corneish_zen/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/glove80/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/mikoto/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/nice60/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/nice_nano/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/nrf52840_m2/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/nrfmicro/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/pillbug/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/puchi_ble/pre_dt_board.cmake | 9 +++++++++ app/boards/arm/s40nc/pre_dt_board.cmake | 9 +++++++++ 14 files changed, 126 insertions(+) create mode 100644 app/boards/arm/adv360pro/pre_dt_board.cmake create mode 100644 app/boards/arm/bluemicro840/pre_dt_board.cmake create mode 100644 app/boards/arm/bt60/pre_dt_board.cmake create mode 100644 app/boards/arm/ckp/pre_dt_board.cmake create mode 100644 app/boards/arm/corneish_zen/pre_dt_board.cmake create mode 100644 app/boards/arm/glove80/pre_dt_board.cmake create mode 100644 app/boards/arm/mikoto/pre_dt_board.cmake create mode 100644 app/boards/arm/nice60/pre_dt_board.cmake create mode 100644 app/boards/arm/nice_nano/pre_dt_board.cmake create mode 100644 app/boards/arm/nrf52840_m2/pre_dt_board.cmake create mode 100644 app/boards/arm/nrfmicro/pre_dt_board.cmake create mode 100644 app/boards/arm/pillbug/pre_dt_board.cmake create mode 100644 app/boards/arm/puchi_ble/pre_dt_board.cmake create mode 100644 app/boards/arm/s40nc/pre_dt_board.cmake diff --git a/app/boards/arm/adv360pro/pre_dt_board.cmake b/app/boards/arm/adv360pro/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/adv360pro/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/bluemicro840/pre_dt_board.cmake b/app/boards/arm/bluemicro840/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/bluemicro840/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/bt60/pre_dt_board.cmake b/app/boards/arm/bt60/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/bt60/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/ckp/pre_dt_board.cmake b/app/boards/arm/ckp/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/ckp/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/corneish_zen/pre_dt_board.cmake b/app/boards/arm/corneish_zen/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/corneish_zen/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/glove80/pre_dt_board.cmake b/app/boards/arm/glove80/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/glove80/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/mikoto/pre_dt_board.cmake b/app/boards/arm/mikoto/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/mikoto/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/nice60/pre_dt_board.cmake b/app/boards/arm/nice60/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/nice60/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/nice_nano/pre_dt_board.cmake b/app/boards/arm/nice_nano/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/nice_nano/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/nrf52840_m2/pre_dt_board.cmake b/app/boards/arm/nrf52840_m2/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/nrf52840_m2/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/nrfmicro/pre_dt_board.cmake b/app/boards/arm/nrfmicro/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/nrfmicro/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/pillbug/pre_dt_board.cmake b/app/boards/arm/pillbug/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/pillbug/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/puchi_ble/pre_dt_board.cmake b/app/boards/arm/puchi_ble/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/puchi_ble/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file diff --git a/app/boards/arm/s40nc/pre_dt_board.cmake b/app/boards/arm/s40nc/pre_dt_board.cmake new file mode 100644 index 00000000..05b0efe5 --- /dev/null +++ b/app/boards/arm/s40nc/pre_dt_board.cmake @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +# Suppresses duplicate unit-address warning at build time for power, clock, acl and flash-controller +# https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") \ No newline at end of file From f18974e8c401b139058549dcd95ab7a4cdc7a6e9 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Mon, 1 Jul 2024 18:26:43 +0000 Subject: [PATCH 06/16] fix: Adjust hid indicator listeners for event refactor * Avoid static listener to prevent subscription issue. --- app/src/hid_indicators.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/hid_indicators.c b/app/src/hid_indicators.c index 1b489068..a2220b1b 100644 --- a/app/src/hid_indicators.c +++ b/app/src/hid_indicators.c @@ -64,5 +64,5 @@ static int profile_listener(const zmk_event_t *eh) { return 0; } -static ZMK_LISTENER(profile_listener, profile_listener); -static ZMK_SUBSCRIPTION(profile_listener, zmk_endpoint_changed); +ZMK_LISTENER(profile_listener, profile_listener); +ZMK_SUBSCRIPTION(profile_listener, zmk_endpoint_changed); From 80173f8ea33e592bba2aa59ae08f036ec44f4e81 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Wed, 3 Jul 2024 02:33:26 -0600 Subject: [PATCH 07/16] fix: Improve startup time with proper settings loading. * Avoid doing duplicate calls to setings_load_subtree, which iterates NVS fully each time under the hood, and instead use on settings_load later in the lifecycle. --- app/Kconfig.behaviors | 4 +++ app/include/zmk/behavior.h | 7 ++-- app/src/backlight.c | 14 ++++---- app/src/behavior.c | 14 ++------ app/src/ble.c | 48 ++++++++++++++-------------- app/src/endpoints.c | 13 ++------ app/src/ext_power_generic.c | 39 +++++++++++----------- app/src/main.c | 5 +++ app/src/rgb_underglow.c | 16 +++------- app/src/split/bluetooth/central.c | 24 +++++++++++++- app/src/split/bluetooth/peripheral.c | 47 ++++++++++++++++++--------- 11 files changed, 129 insertions(+), 102 deletions(-) diff --git a/app/Kconfig.behaviors b/app/Kconfig.behaviors index 0fa34546..d3f4537e 100644 --- a/app/Kconfig.behaviors +++ b/app/Kconfig.behaviors @@ -12,12 +12,16 @@ config ZMK_BEHAVIOR_LOCAL_IDS if ZMK_BEHAVIOR_LOCAL_IDS +config ZMK_BEHAVIOR_LOCAL_IDS_IN_BINDINGS + bool "Track in behavior bindings" + choice ZMK_BEHAVIOR_LOCAL_ID_TYPE prompt "Local ID Type" config ZMK_BEHAVIOR_LOCAL_ID_TYPE_SETTINGS_TABLE bool "Settings Table" depends on SETTINGS + select ZMK_BEHAVIOR_LOCAL_IDS_IN_BINDINGS help Use persistent entries in the settings subsystem to identify behaviors by local ID, which uses the device name to generate diff --git a/app/include/zmk/behavior.h b/app/include/zmk/behavior.h index 34a415ca..d45bbfff 100644 --- a/app/include/zmk/behavior.h +++ b/app/include/zmk/behavior.h @@ -11,7 +11,12 @@ #define ZMK_BEHAVIOR_OPAQUE 0 #define ZMK_BEHAVIOR_TRANSPARENT 1 +typedef uint16_t zmk_behavior_local_id_t; + struct zmk_behavior_binding { +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS_IN_BINDINGS) + zmk_behavior_local_id_t local_id; +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS_IN_BINDINGS) const char *behavior_dev; uint32_t param1; uint32_t param2; @@ -23,8 +28,6 @@ struct zmk_behavior_binding_event { int64_t timestamp; }; -typedef uint16_t zmk_behavior_local_id_t; - /** * @brief Get a const struct device* for a behavior from its @p name field. * diff --git a/app/src/backlight.c b/app/src/backlight.c index f050978f..22b73066 100644 --- a/app/src/backlight.c +++ b/app/src/backlight.c @@ -58,7 +58,7 @@ static int zmk_backlight_update(void) { #if IS_ENABLED(CONFIG_SETTINGS) static int backlight_settings_load_cb(const char *name, size_t len, settings_read_cb read_cb, - void *cb_arg, void *param) { + void *cb_arg) { const char *next; if (settings_name_steq(name, "state", &next) && !next) { if (len != sizeof(state)) { @@ -66,11 +66,18 @@ static int backlight_settings_load_cb(const char *name, size_t len, settings_rea } int rc = read_cb(cb_arg, &state, sizeof(state)); + if (rc >= 0) { + rc = zmk_backlight_update(); + } + return MIN(rc, 0); } return -ENOENT; } +SETTINGS_STATIC_HANDLER_DEFINE(backlight, "backlight", NULL, backlight_settings_load_cb, NULL, + NULL); + static void backlight_save_work_handler(struct k_work *work) { settings_save_one("backlight/state", &state, sizeof(state)); } @@ -85,11 +92,6 @@ static int zmk_backlight_init(void) { } #if IS_ENABLED(CONFIG_SETTINGS) - settings_subsys_init(); - int rc = settings_load_subtree_direct("backlight", backlight_settings_load_cb, NULL); - if (rc != 0) { - LOG_ERR("Failed to load backlight settings: %d", rc); - } k_work_init_delayable(&backlight_save_work, backlight_save_work_handler); #endif #if IS_ENABLED(CONFIG_ZMK_BACKLIGHT_AUTO_OFF_USB) diff --git a/app/src/behavior.c b/app/src/behavior.c index 7505aa7f..0d9a4cdf 100644 --- a/app/src/behavior.c +++ b/app/src/behavior.c @@ -229,6 +229,8 @@ static int behavior_local_id_init(void) { return 0; } +SYS_INIT(behavior_local_id_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); + #elif IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_ID_TYPE_SETTINGS_TABLE) static zmk_behavior_local_id_t largest_local_id = 0; @@ -239,7 +241,7 @@ static int behavior_handle_set(const char *name, size_t len, settings_read_cb re if (settings_name_steq(name, "local_id", &next) && next) { char *endptr; - uint8_t local_id = strtoul(next, &endptr, 10); + zmk_behavior_local_id_t local_id = strtoul(next, &endptr, 10); if (*endptr != '\0') { LOG_WRN("Invalid behavior local ID: %s with endptr %s", next, endptr); return -EINVAL; @@ -302,22 +304,12 @@ static int behavior_handle_commit(void) { SETTINGS_STATIC_HANDLER_DEFINE(behavior, "behavior", NULL, behavior_handle_set, behavior_handle_commit, NULL); -static int behavior_local_id_init(void) { - settings_subsys_init(); - - settings_load_subtree("behavior"); - - return 0; -} - #else #error "A behavior local ID mechanism must be selected" #endif -SYS_INIT(behavior_local_id_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); - #endif #if IS_ENABLED(CONFIG_LOG) diff --git a/app/src/ble.c b/app/src/ble.c index b2dfbfa1..776730fe 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -445,7 +445,11 @@ static int ble_profiles_handle_set(const char *name, size_t len, settings_read_c return 0; }; -struct settings_handler profiles_handler = {.name = "ble", .h_set = ble_profiles_handle_set}; +static int zmk_ble_complete_startup(void); + +static struct settings_handler profiles_handler = { + .name = "ble", .h_set = ble_profiles_handle_set, .h_commit = zmk_ble_complete_startup}; + #endif /* IS_ENABLED(CONFIG_SETTINGS) */ static bool is_conn_active_profile(const struct bt_conn *conn) { @@ -644,29 +648,7 @@ static void zmk_ble_ready(int err) { update_advertising(); } -static int zmk_ble_init(void) { - int err = bt_enable(NULL); - - if (err) { - LOG_ERR("BLUETOOTH FAILED (%d)", err); - return err; - } - -#if IS_ENABLED(CONFIG_SETTINGS) - settings_subsys_init(); - - err = settings_register(&profiles_handler); - if (err) { - LOG_ERR("Failed to setup the profile settings handler (err %d)", err); - return err; - } - - k_work_init_delayable(&ble_save_work, ble_save_profile_work); - - settings_load_subtree("ble"); - settings_load_subtree("bt"); - -#endif +static int zmk_ble_complete_startup(void) { #if IS_ENABLED(CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START) LOG_WRN("Clearing all existing BLE bond information from the keyboard"); @@ -706,6 +688,24 @@ static int zmk_ble_init(void) { return 0; } +static int zmk_ble_init(void) { + int err = bt_enable(NULL); + + if (err < 0 && err != -EALREADY) { + LOG_ERR("BLUETOOTH FAILED (%d)", err); + return err; + } + +#if IS_ENABLED(CONFIG_SETTINGS) + settings_register(&profiles_handler); + k_work_init_delayable(&ble_save_work, ble_save_profile_work); +#else + zmk_ble_complete_startup(); +#endif + + return 0; +} + #if IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY) static bool zmk_ble_numeric_usage_to_value(const zmk_key_t key, const zmk_key_t one, diff --git a/app/src/endpoints.c b/app/src/endpoints.c index 7c9d15a3..65243853 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -263,7 +263,8 @@ static int endpoints_handle_set(const char *name, size_t len, settings_read_cb r return 0; } -struct settings_handler endpoints_handler = {.name = "endpoints", .h_set = endpoints_handle_set}; +SETTINGS_STATIC_HANDLER_DEFINE(endpoints, "endpoints", NULL, endpoints_handle_set, NULL, NULL); + #endif /* IS_ENABLED(CONFIG_SETTINGS) */ static bool is_usb_ready(void) { @@ -322,17 +323,7 @@ static struct zmk_endpoint_instance get_selected_instance(void) { static int zmk_endpoints_init(void) { #if IS_ENABLED(CONFIG_SETTINGS) - settings_subsys_init(); - - int err = settings_register(&endpoints_handler); - if (err) { - LOG_ERR("Failed to register the endpoints settings handler (err %d)", err); - return err; - } - k_work_init_delayable(&endpoints_save_work, endpoints_save_preferred_work); - - settings_load_subtree("endpoints"); #endif current_instance = get_selected_instance(); diff --git a/app/src/ext_power_generic.c b/app/src/ext_power_generic.c index 2586f436..5a9cc5b8 100644 --- a/app/src/ext_power_generic.c +++ b/app/src/ext_power_generic.c @@ -121,12 +121,27 @@ static int ext_power_settings_set(const char *name, size_t len, settings_read_cb return -ENOENT; } -struct settings_handler ext_power_conf = {.name = "ext_power/state", - .h_set = ext_power_settings_set}; +static int ext_power_settings_commit() { + const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); + struct ext_power_generic_data *data = dev->data; + + if (!data->settings_init) { + + data->status = true; + k_work_schedule(&ext_power_save_work, K_NO_WAIT); + + ext_power_enable(dev); + } + + return 0; +} + +SETTINGS_STATIC_HANDLER_DEFINE(ext_power, "ext_power/state", NULL, ext_power_settings_set, + ext_power_settings_commit, NULL); + #endif static int ext_power_generic_init(const struct device *dev) { - struct ext_power_generic_data *data = dev->data; const struct ext_power_generic_config *config = dev->config; if (gpio_pin_configure_dt(&config->control, GPIO_OUTPUT_INACTIVE)) { @@ -135,25 +150,7 @@ static int ext_power_generic_init(const struct device *dev) { } #if IS_ENABLED(CONFIG_SETTINGS) - settings_subsys_init(); - - int err = settings_register(&ext_power_conf); - if (err) { - LOG_ERR("Failed to register the ext_power settings handler (err %d)", err); - return err; - } - k_work_init_delayable(&ext_power_save_work, ext_power_save_state_work); - - // Set default value (on) if settings isn't set - settings_load_subtree("ext_power"); - if (!data->settings_init) { - - data->status = true; - k_work_schedule(&ext_power_save_work, K_NO_WAIT); - - ext_power_enable(dev); - } #else // Default to the ext_power being open when no settings ext_power_enable(dev); diff --git a/app/src/main.c b/app/src/main.c index 9bd7af32..0d9caf65 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -24,6 +24,11 @@ int main(void) { return -ENOTSUP; } +#if IS_ENABLED(CONFIG_SETTINGS) + settings_subsys_init(); + settings_load(); +#endif + #ifdef CONFIG_ZMK_DISPLAY zmk_display_init(); #endif /* CONFIG_ZMK_DISPLAY */ diff --git a/app/src/rgb_underglow.c b/app/src/rgb_underglow.c index 5bf1ef25..3453fb44 100644 --- a/app/src/rgb_underglow.c +++ b/app/src/rgb_underglow.c @@ -221,6 +221,10 @@ static int rgb_settings_set(const char *name, size_t len, settings_read_cb read_ rc = read_cb(cb_arg, &state, sizeof(state)); if (rc >= 0) { + if (state.on) { + k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50)); + } + return 0; } @@ -230,7 +234,7 @@ static int rgb_settings_set(const char *name, size_t len, settings_read_cb read_ return -ENOENT; } -struct settings_handler rgb_conf = {.name = "rgb/underglow", .h_set = rgb_settings_set}; +SETTINGS_STATIC_HANDLER_DEFINE(rgb_underglow, "rgb/underglow", NULL, rgb_settings_set, NULL, NULL); static void zmk_rgb_underglow_save_state_work(struct k_work *_work) { settings_save_one("rgb/underglow/state", &state, sizeof(state)); @@ -262,17 +266,7 @@ static int zmk_rgb_underglow_init(void) { }; #if IS_ENABLED(CONFIG_SETTINGS) - settings_subsys_init(); - - int err = settings_register(&rgb_conf); - if (err) { - LOG_ERR("Failed to register the ext_power settings handler (err %d)", err); - return err; - } - k_work_init_delayable(&underglow_save_work, zmk_rgb_underglow_save_state_work); - - settings_load_subtree("rgb/underglow"); #endif #if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_AUTO_OFF_USB) diff --git a/app/src/split/bluetooth/central.c b/app/src/split/bluetooth/central.c index ee21a12f..0f4cd78b 100644 --- a/app/src/split/bluetooth/central.c +++ b/app/src/split/bluetooth/central.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -865,13 +866,34 @@ int zmk_split_bt_update_hid_indicator(zmk_hid_indicators_t indicators) { #endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS) +static int finish_init() { + return IS_ENABLED(CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START) ? 0 : start_scanning(); +} + +#if IS_ENABLED(CONFIG_SETTINGS) + +static int central_ble_handle_set(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg) { + return 0; +} + +static struct settings_handler ble_central_settings_handler = { + .name = "ble_central", .h_set = central_ble_handle_set, .h_commit = finish_init}; + +#endif // IS_ENABLED(CONFIG_SETTINGS) + static int zmk_split_bt_central_init(void) { k_work_queue_start(&split_central_split_run_q, split_central_split_run_q_stack, K_THREAD_STACK_SIZEOF(split_central_split_run_q_stack), CONFIG_ZMK_BLE_THREAD_PRIORITY, NULL); bt_conn_cb_register(&conn_callbacks); - return IS_ENABLED(CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START) ? 0 : start_scanning(); +#if IS_ENABLED(CONFIG_SETTINGS) + settings_register(&ble_central_settings_handler); + return 0; +#else + return finish_init(); +#endif // IS_ENABLED(CONFIG_SETTINGS) } SYS_INIT(zmk_split_bt_central_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY); diff --git a/app/src/split/bluetooth/peripheral.c b/app/src/split/bluetooth/peripheral.c index 6ce82d0a..5a12e0fc 100644 --- a/app/src/split/bluetooth/peripheral.c +++ b/app/src/split/bluetooth/peripheral.c @@ -146,21 +146,7 @@ bool zmk_split_bt_peripheral_is_connected(void) { return is_connected; } bool zmk_split_bt_peripheral_is_bonded(void) { return is_bonded; } -static int zmk_peripheral_ble_init(void) { - int err = bt_enable(NULL); - - if (err) { - LOG_ERR("BLUETOOTH FAILED (%d)", err); - return err; - } - -#if IS_ENABLED(CONFIG_SETTINGS) - settings_subsys_init(); - - settings_load_subtree("ble"); - settings_load_subtree("bt"); -#endif - +static int zmk_peripheral_ble_complete_startup(void) { #if IS_ENABLED(CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START) LOG_WRN("Clearing all existing BLE bond information from the keyboard"); @@ -176,4 +162,35 @@ static int zmk_peripheral_ble_init(void) { return 0; } +#if IS_ENABLED(CONFIG_SETTINGS) + +static int peripheral_ble_handle_set(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg) { + return 0; +} + +static struct settings_handler ble_peripheral_settings_handler = { + .name = "ble_peripheral", + .h_set = peripheral_ble_handle_set, + .h_commit = zmk_peripheral_ble_complete_startup}; + +#endif // IS_ENABLED(CONFIG_SETTINGS) + +static int zmk_peripheral_ble_init(void) { + int err = bt_enable(NULL); + + if (err) { + LOG_ERR("BLUETOOTH FAILED (%d)", err); + return err; + } + +#if IS_ENABLED(CONFIG_SETTINGS) + settings_register(&ble_peripheral_settings_handler); +#else + zmk_peripheral_ble_complete_startup(); +#endif + + return 0; +} + SYS_INIT(zmk_peripheral_ble_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY); From c5cca5b34f53263892257460acf681ffa490a240 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Wed, 24 Apr 2024 18:14:02 -0700 Subject: [PATCH 08/16] feat: Add keyboard physical layout system. * Add bindings to allow creating multiple physical layouts that specify their key's physical attributes, and the matching matrix transform and dependant kscan to use. * Synthesize a basic physical layout if none specified, for backwards compatibility. * Update matrix transform API to explicitly pass in the selected transform to the API instead of using a fixed chosen transform. * Move kscan subscription and handling into the physical layout code, so that selecting a different physical layout at runtime can also use the correct kscan instance. * Add `physical_layouts.dtsi` file to include so you can use the pre-configured `&key_physical_attrs` for adding you layout keys. --- app/CMakeLists.txt | 2 +- app/dts/bindings/zmk,key-physical-attrs.yaml | 24 ++ .../zmk,physical-layout-position-map.yaml | 23 ++ app/dts/bindings/zmk,physical-layout.yaml | 26 ++ app/dts/physical_layouts.dtsi | 13 + app/include/zmk/kscan.h | 11 - app/include/zmk/matrix.h | 18 +- app/include/zmk/matrix_transform.h | 14 +- app/include/zmk/physical_layouts.h | 43 ++ app/src/kscan.c | 87 ---- app/src/main.c | 7 - app/src/matrix_transform.c | 91 +++-- app/src/physical_layouts.c | 386 ++++++++++++++++++ 13 files changed, 604 insertions(+), 141 deletions(-) create mode 100644 app/dts/bindings/zmk,key-physical-attrs.yaml create mode 100644 app/dts/bindings/zmk,physical-layout-position-map.yaml create mode 100644 app/dts/bindings/zmk,physical-layout.yaml create mode 100644 app/dts/physical_layouts.dtsi delete mode 100644 app/include/zmk/kscan.h create mode 100644 app/include/zmk/physical_layouts.h delete mode 100644 app/src/kscan.c create mode 100644 app/src/physical_layouts.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 2818e932..ab2e1502 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -24,9 +24,9 @@ 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_ifdef(CONFIG_ZMK_KSCAN_SIDEBAND_BEHAVIORS app PRIVATE src/kscan_sideband_behaviors.c) target_sources(app PRIVATE src/matrix_transform.c) +target_sources(app PRIVATE src/physical_layouts.c) 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) diff --git a/app/dts/bindings/zmk,key-physical-attrs.yaml b/app/dts/bindings/zmk,key-physical-attrs.yaml new file mode 100644 index 00000000..9ea070f8 --- /dev/null +++ b/app/dts/bindings/zmk,key-physical-attrs.yaml @@ -0,0 +1,24 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# +# SPDX-License-Identifier: MIT + +description: | + The physical attributes of a key, including size, location, and rotation + +compatible: "zmk,key-physical-attrs" + +properties: + "#key-cells": + type: int + required: true + const: 7 + +key-cells: + - width + - height + - x + - y + - r + - rx + - ry diff --git a/app/dts/bindings/zmk,physical-layout-position-map.yaml b/app/dts/bindings/zmk,physical-layout-position-map.yaml new file mode 100644 index 00000000..8647404b --- /dev/null +++ b/app/dts/bindings/zmk,physical-layout-position-map.yaml @@ -0,0 +1,23 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# +# SPDX-License-Identifier: MIT + +description: | + Describes how to correlate equivalent keys between layouts that don't have the exact same X,Y location. + +compatible: "zmk,physical-layout-position-map" + +properties: + complete: + type: boolean + description: If the mapping complete describes the key mapping, and no position based mapping should be used. + +child-binding: + properties: + physical-layout: + type: phandle + description: The physical layout that corresponds to this mapping entry. + positions: + type: array + description: Array of key positions that match the same array entry in the other sibling nodes. diff --git a/app/dts/bindings/zmk,physical-layout.yaml b/app/dts/bindings/zmk,physical-layout.yaml new file mode 100644 index 00000000..3f9b8c24 --- /dev/null +++ b/app/dts/bindings/zmk,physical-layout.yaml @@ -0,0 +1,26 @@ +# +# Copyright (c) 2024 The ZMK Contributors +# +# SPDX-License-Identifier: MIT + +description: | + Describe the physical layout of a keyboard, including deps like the transform and kscan + that are needed for that layout to work. + +compatible: "zmk,physical-layout" + +properties: + display-name: + type: string + required: true + description: The name of this layout to display in the UI + transform: + type: phandle + required: true + description: The matrix transform to use along with this layout. + kscan: + type: phandle + description: The kscan to use along with this layout. The `zmk,kscan` chosen will be used as a fallback if this property is omitted. + keys: + type: phandle-array + description: Array of key physical attributes. diff --git a/app/dts/physical_layouts.dtsi b/app/dts/physical_layouts.dtsi new file mode 100644 index 00000000..1c8703ec --- /dev/null +++ b/app/dts/physical_layouts.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + key_physical_attrs: key_physical_attrs { + compatible = "zmk,key-physical-attrs"; + + #key-cells = <7>; + }; +}; \ No newline at end of file diff --git a/app/include/zmk/kscan.h b/app/include/zmk/kscan.h deleted file mode 100644 index eebe41e7..00000000 --- a/app/include/zmk/kscan.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#include - -int zmk_kscan_init(const struct device *dev); diff --git a/app/include/zmk/matrix.h b/app/include/zmk/matrix.h index 5f8cd7d7..e38f5a49 100644 --- a/app/include/zmk/matrix.h +++ b/app/include/zmk/matrix.h @@ -9,15 +9,25 @@ #include #define ZMK_MATRIX_NODE_ID DT_CHOSEN(zmk_kscan) +#define ZMK_MATRIX_HAS_TRANSFORM DT_HAS_CHOSEN(zmk_matrix_transform) -#if DT_HAS_CHOSEN(zmk_matrix_transform) +#if DT_HAS_COMPAT_STATUS_OKAY(zmk_physical_layout) + +#if ZMK_MATRIX_HAS_TRANSFORM +#error "To use physical layouts, remove the chosen `zmk,matrix-transform` value." +#endif + +#define ZMK_PHYSICAL_LAYOUT_BYTE_ARRAY(node_id) \ + uint8_t _CONCAT(prop_, node_id)[DT_PROP_LEN(DT_PHANDLE(node_id, transform), map)]; + +#define ZMK_KEYMAP_LEN \ + sizeof(union {DT_FOREACH_STATUS_OKAY(zmk_physical_layout, ZMK_PHYSICAL_LAYOUT_BYTE_ARRAY)}) + +#elif ZMK_MATRIX_HAS_TRANSFORM #define ZMK_KEYMAP_TRANSFORM_NODE DT_CHOSEN(zmk_matrix_transform) #define ZMK_KEYMAP_LEN DT_PROP_LEN(ZMK_KEYMAP_TRANSFORM_NODE, map) -#define ZMK_MATRIX_ROWS DT_PROP(ZMK_KEYMAP_TRANSFORM_NODE, rows) -#define ZMK_MATRIX_COLS DT_PROP(ZMK_KEYMAP_TRANSFORM_NODE, columns) - #else /* DT_HAS_CHOSEN(zmk_matrix_transform) */ #if DT_NODE_HAS_PROP(ZMK_MATRIX_NODE_ID, row_gpios) diff --git a/app/include/zmk/matrix_transform.h b/app/include/zmk/matrix_transform.h index ffd3e3f1..42a98151 100644 --- a/app/include/zmk/matrix_transform.h +++ b/app/include/zmk/matrix_transform.h @@ -6,4 +6,16 @@ #pragma once -int32_t zmk_matrix_transform_row_column_to_position(uint32_t row, uint32_t column); \ No newline at end of file +#include + +typedef const struct zmk_matrix_transform *zmk_matrix_transform_t; + +#define ZMK_MATRIX_TRANSFORM_DEFAULT_EXTERN() \ + extern const struct zmk_matrix_transform zmk_matrix_transform_default +#define ZMK_MATRIX_TRANSFORM_EXTERN(node_id) \ + extern const struct zmk_matrix_transform _CONCAT(zmk_matrix_transform_, node_id) + +#define ZMK_MATRIX_TRANSFORM_T_FOR_NODE(node_id) &_CONCAT(zmk_matrix_transform_, node_id) + +int32_t zmk_matrix_transform_row_column_to_position(zmk_matrix_transform_t mt, uint32_t row, + uint32_t column); diff --git a/app/include/zmk/physical_layouts.h b/app/include/zmk/physical_layouts.h new file mode 100644 index 00000000..8d8188e3 --- /dev/null +++ b/app/include/zmk/physical_layouts.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include + +struct zmk_key_physical_attrs { + int16_t width; + int16_t height; + int16_t x; + int16_t y; + int16_t rx; + int16_t ry; + int16_t r; +}; + +struct zmk_physical_layout { + const char *display_name; + + zmk_matrix_transform_t matrix_transform; + const struct device *kscan; + + const struct zmk_key_physical_attrs *keys; + size_t keys_len; +}; + +#define ZMK_PHYS_LAYOUTS_FOREACH(_ref) STRUCT_SECTION_FOREACH(zmk_physical_layout, _ref) + +size_t zmk_physical_layouts_get_list(struct zmk_physical_layout const *const **phys_layouts); + +int zmk_physical_layouts_select(uint8_t index); +int zmk_physical_layouts_get_selected(void); + +int zmk_physical_layouts_check_unsaved_selection(void); +int zmk_physical_layouts_save_selected(void); +int zmk_physical_layouts_revert_selected(void); + +int zmk_physical_layouts_get_position_map(uint8_t source, uint8_t dest, uint32_t *map); diff --git a/app/src/kscan.c b/app/src/kscan.c deleted file mode 100644 index 5c7a5535..00000000 --- a/app/src/kscan.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include -#include -#include -#include -#include - -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); - -#include -#include -#include - -#define ZMK_KSCAN_EVENT_STATE_PRESSED 0 -#define ZMK_KSCAN_EVENT_STATE_RELEASED 1 - -struct zmk_kscan_event { - uint32_t row; - uint32_t column; - uint32_t state; -}; - -struct zmk_kscan_msg_processor { - struct k_work work; -} msg_processor; - -K_MSGQ_DEFINE(zmk_kscan_msgq, sizeof(struct zmk_kscan_event), CONFIG_ZMK_KSCAN_EVENT_QUEUE_SIZE, 4); - -static void zmk_kscan_callback(const struct device *dev, uint32_t row, uint32_t column, - bool pressed) { - struct zmk_kscan_event ev = { - .row = row, - .column = column, - .state = (pressed ? ZMK_KSCAN_EVENT_STATE_PRESSED : ZMK_KSCAN_EVENT_STATE_RELEASED)}; - - k_msgq_put(&zmk_kscan_msgq, &ev, K_NO_WAIT); - k_work_submit(&msg_processor.work); -} - -void zmk_kscan_process_msgq(struct k_work *item) { - struct zmk_kscan_event ev; - - while (k_msgq_get(&zmk_kscan_msgq, &ev, K_NO_WAIT) == 0) { - bool pressed = (ev.state == ZMK_KSCAN_EVENT_STATE_PRESSED); - int32_t position = zmk_matrix_transform_row_column_to_position(ev.row, ev.column); - - if (position < 0) { - LOG_WRN("Not found in transform: row: %d, col: %d, pressed: %s", ev.row, ev.column, - (pressed ? "true" : "false")); - continue; - } - - LOG_DBG("Row: %d, col: %d, position: %d, pressed: %s", ev.row, ev.column, position, - (pressed ? "true" : "false")); - raise_zmk_position_state_changed( - (struct zmk_position_state_changed){.source = ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL, - .state = pressed, - .position = position, - .timestamp = k_uptime_get()}); - } -} - -int zmk_kscan_init(const struct device *dev) { - if (dev == NULL) { - LOG_ERR("Failed to get the KSCAN device"); - return -EINVAL; - } - - k_work_init(&msg_processor.work, zmk_kscan_process_msgq); - -#if IS_ENABLED(CONFIG_PM_DEVICE) - if (pm_device_wakeup_is_capable(dev)) { - pm_device_wakeup_enable(dev, true); - } -#endif // IS_ENABLED(CONFIG_PM_DEVICE) - - kscan_config(dev, zmk_kscan_callback); - kscan_enable_callback(dev); - - return 0; -} diff --git a/app/src/main.c b/app/src/main.c index 0d9caf65..60df1a45 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -12,18 +12,11 @@ #include LOG_MODULE_REGISTER(zmk, CONFIG_ZMK_LOG_LEVEL); -#include -#include #include -#include int main(void) { LOG_INF("Welcome to ZMK!\n"); - if (zmk_kscan_init(DEVICE_DT_GET(ZMK_MATRIX_NODE_ID)) != 0) { - return -ENOTSUP; - } - #if IS_ENABLED(CONFIG_SETTINGS) settings_subsys_init(); settings_load(); diff --git a/app/src/matrix_transform.c b/app/src/matrix_transform.c index 6c616d5e..97ab0efe 100644 --- a/app/src/matrix_transform.c +++ b/app/src/matrix_transform.c @@ -4,12 +4,23 @@ * SPDX-License-Identifier: MIT */ +#include #include +#include #include #include #include -#ifdef ZMK_KEYMAP_TRANSFORM_NODE +#define DT_DRV_COMPAT zmk_matrix_transform + +struct zmk_matrix_transform { + uint32_t const *lookup_table; + size_t len; + uint8_t rows; + uint8_t columns; + uint8_t col_offset; + uint8_t row_offset; +}; /* the transform in the device tree is a list of (row,column) pairs that is * indexed by by the keymap position of that key. We want to invert this in @@ -28,38 +39,58 @@ #define INDEX_OFFSET 1 -#define TRANSFORM_ENTRY(i, _) \ - [(KT_ROW(DT_PROP_BY_IDX(ZMK_KEYMAP_TRANSFORM_NODE, map, i)) * ZMK_MATRIX_COLS) + \ - KT_COL(DT_PROP_BY_IDX(ZMK_KEYMAP_TRANSFORM_NODE, map, i))] = i + INDEX_OFFSET +#if DT_HAS_COMPAT_STATUS_OKAY(zmk_matrix_transform) -static uint32_t transform[] = {LISTIFY(ZMK_KEYMAP_LEN, TRANSFORM_ENTRY, (, ), 0)}; +#define TRANSFORM_LOOKUP_ENTRY(i, n) \ + [(KT_ROW(DT_INST_PROP_BY_IDX(n, map, i)) * DT_INST_PROP(n, columns)) + \ + KT_COL(DT_INST_PROP_BY_IDX(n, map, i))] = i + INDEX_OFFSET -#endif +#define MATRIX_TRANSFORM_INIT(n) \ + static const uint32_t _CONCAT(zmk_transform_lookup_table_, n)[] = { \ + LISTIFY(DT_INST_PROP_LEN(n, map), TRANSFORM_LOOKUP_ENTRY, (, ), n)}; \ + const struct zmk_matrix_transform _CONCAT(zmk_matrix_transform_, DT_DRV_INST(n)) = { \ + .rows = DT_INST_PROP(n, rows), \ + .columns = DT_INST_PROP(n, columns), \ + .col_offset = DT_INST_PROP(n, col_offset), \ + .row_offset = DT_INST_PROP(n, row_offset), \ + .lookup_table = _CONCAT(zmk_transform_lookup_table_, n), \ + .len = ARRAY_SIZE(_CONCAT(zmk_transform_lookup_table_, n)), \ + }; -int32_t zmk_matrix_transform_row_column_to_position(uint32_t row, uint32_t column) { -#if DT_NODE_HAS_PROP(ZMK_KEYMAP_TRANSFORM_NODE, col_offset) - column += DT_PROP(ZMK_KEYMAP_TRANSFORM_NODE, col_offset); -#endif +DT_INST_FOREACH_STATUS_OKAY(MATRIX_TRANSFORM_INIT); -#if DT_NODE_HAS_PROP(ZMK_KEYMAP_TRANSFORM_NODE, row_offset) - row += DT_PROP(ZMK_KEYMAP_TRANSFORM_NODE, row_offset); -#endif +#elif DT_HAS_CHOSEN(zmk_kscan) && defined(ZMK_MATRIX_COLS) && defined(ZMK_MATRIX_ROWS) - const uint32_t matrix_index = (row * ZMK_MATRIX_COLS) + column; - -#ifdef ZMK_KEYMAP_TRANSFORM_NODE - if (matrix_index >= ARRAY_SIZE(transform)) { - return -EINVAL; - } - - const uint32_t value = transform[matrix_index]; - - if (!value) { - return -EINVAL; - } - - return value - INDEX_OFFSET; -#else - return matrix_index; -#endif /* ZMK_KEYMAP_TRANSFORM_NODE */ +const struct zmk_matrix_transform zmk_matrix_transform_default = { + .rows = ZMK_MATRIX_ROWS, + .columns = ZMK_MATRIX_COLS, + .len = ZMK_KEYMAP_LEN, }; + +#else + +#error "Need a matrix transform or compatible kscan selected to determine keymap size!" +` +#endif // DT_HAS_COMPAT_STATUS_OKAY(zmk_matrix_transform) + +int32_t zmk_matrix_transform_row_column_to_position(zmk_matrix_transform_t mt, uint32_t row, + uint32_t column) { + column += mt->col_offset; + row += mt->row_offset; + + if (!mt->lookup_table) { + return (row * mt->columns) + column; + } + + uint16_t lookup_index = ((row * mt->columns) + column); + if (lookup_index >= mt->len) { + return -EINVAL; + } + + int32_t val = mt->lookup_table[lookup_index]; + if (val == 0) { + return -EINVAL; + } + + return val - INDEX_OFFSET; +}; \ No newline at end of file diff --git a/app/src/physical_layouts.c b/app/src/physical_layouts.c new file mode 100644 index 00000000..16b13e71 --- /dev/null +++ b/app/src/physical_layouts.c @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include + +#if IS_ENABLED(CONFIG_SETTINGS) +#include +#endif + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include + +#define DT_DRV_COMPAT zmk_physical_layout + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +#define ZKPA_INIT(i, n) \ + (const struct zmk_key_physical_attrs) { \ + .width = (int16_t)(int32_t)DT_INST_PHA_BY_IDX(n, keys, i, width), \ + .height = (int16_t)(int32_t)DT_INST_PHA_BY_IDX(n, keys, i, height), \ + .x = (int16_t)(int32_t)DT_INST_PHA_BY_IDX(n, keys, i, x), \ + .y = (int16_t)(int32_t)DT_INST_PHA_BY_IDX(n, keys, i, y), \ + .rx = (int16_t)(int32_t)DT_INST_PHA_BY_IDX(n, keys, i, rx), \ + .ry = (int16_t)(int32_t)DT_INST_PHA_BY_IDX(n, keys, i, ry), \ + .r = (int16_t)(int32_t)DT_INST_PHA_BY_IDX(n, keys, i, r), \ + } + +#define ZMK_LAYOUT_INST(n) \ + static const struct zmk_key_physical_attrs const _CONCAT( \ + _zmk_physical_layout_keys_, n)[DT_INST_PROP_LEN_OR(n, keys, 0)] = { \ + LISTIFY(DT_INST_PROP_LEN_OR(n, keys, 0), ZKPA_INIT, (, ), n)}; \ + ZMK_MATRIX_TRANSFORM_EXTERN(DT_INST_PHANDLE(n, transform)); \ + static const struct zmk_physical_layout const _CONCAT(_zmk_physical_layout_, \ + DT_DRV_INST(n)) = { \ + .display_name = DT_INST_PROP_OR(n, display_name, "Layout #" #n), \ + .matrix_transform = ZMK_MATRIX_TRANSFORM_T_FOR_NODE(DT_INST_PHANDLE(n, transform)), \ + .keys = _CONCAT(_zmk_physical_layout_keys_, n), \ + .keys_len = DT_INST_PROP_LEN_OR(n, keys, 0), \ + .kscan = DEVICE_DT_GET(COND_CODE_1(DT_INST_PROP_LEN(n, kscan), \ + (DT_INST_PHANDLE(n, kscan)), (DT_CHOSEN(zmk_kscan))))}; + +DT_INST_FOREACH_STATUS_OKAY(ZMK_LAYOUT_INST) + +#define POS_MAP_COMPAT zmk_physical_layout_position_map +#define HAVE_POS_MAP DT_HAS_COMPAT_STATUS_OKAY(POS_MAP_COMPAT) + +#define POS_MAP_COMPLETE (HAVE_POS_MAP && DT_PROP(DT_INST(0, POS_MAP_COMPAT), complete)) + +#if HAVE_POS_MAP + +// Using sizeof + union trick to calculate the "positions" length statically. +#define ZMK_POS_MAP_POSITIONS_ARRAY(node_id) \ + uint8_t _CONCAT(prop_, node_id)[DT_PROP_LEN(node_id, positions)]; + +#define ZMK_POS_MAP_LEN \ + sizeof(union {DT_FOREACH_CHILD(DT_INST(0, POS_MAP_COMPAT), ZMK_POS_MAP_POSITIONS_ARRAY)}) + +struct position_map_entry { + const struct zmk_physical_layout *layout; + const uint32_t positions[ZMK_POS_MAP_LEN]; +}; + +#define ZMK_POS_MAP_ENTRY(node_id) \ + { \ + .layout = &_CONCAT(_zmk_physical_layout_, DT_PHANDLE(node_id, physical_layout)), \ + .positions = DT_PROP(node_id, positions), \ + } + +static const struct position_map_entry positions_maps[] = { + DT_FOREACH_CHILD_SEP(DT_INST(0, POS_MAP_COMPAT), ZMK_POS_MAP_ENTRY, (, ))}; + +#endif + +#define ZMK_LAYOUT_REF(n) &_CONCAT(_zmk_physical_layout_, DT_DRV_INST(n)), + +static const struct zmk_physical_layout *const layouts[] = { + DT_INST_FOREACH_STATUS_OKAY(ZMK_LAYOUT_REF)}; + +#elif DT_HAS_CHOSEN(zmk_matrix_transform) + +ZMK_MATRIX_TRANSFORM_EXTERN(DT_CHOSEN(zmk_matrix_transform)); + +static const struct zmk_physical_layout _CONCAT(_zmk_physical_layout_, chosen) = { + .display_name = "Default", + .matrix_transform = ZMK_MATRIX_TRANSFORM_T_FOR_NODE(DT_CHOSEN(zmk_matrix_transform)), + COND_CODE_1(DT_HAS_CHOSEN(zmk_kscan), (.kscan = DEVICE_DT_GET(DT_CHOSEN(zmk_kscan)), ), ())}; + +static const struct zmk_physical_layout *const layouts[] = { + &_CONCAT(_zmk_physical_layout_, chosen)}; + +#elif DT_HAS_CHOSEN(zmk_kscan) + +ZMK_MATRIX_TRANSFORM_DEFAULT_EXTERN(); +static const struct zmk_physical_layout _CONCAT(_zmk_physical_layout_, chosen) = { + .display_name = "Default", + .matrix_transform = &zmk_matrix_transform_default, + .kscan = DEVICE_DT_GET(DT_CHOSEN(zmk_kscan)), +}; + +static const struct zmk_physical_layout *const layouts[] = { + &_CONCAT(_zmk_physical_layout_, chosen)}; + +#endif + +const struct zmk_physical_layout *active; + +size_t zmk_physical_layouts_get_list(struct zmk_physical_layout const *const **dest_layouts) { + *dest_layouts = &layouts[0]; + + return ARRAY_SIZE(layouts); +} + +#define ZMK_KSCAN_EVENT_STATE_PRESSED 0 +#define ZMK_KSCAN_EVENT_STATE_RELEASED 1 + +struct zmk_kscan_event { + uint32_t row; + uint32_t column; + uint32_t state; +}; + +static struct zmk_kscan_msg_processor { struct k_work work; } msg_processor; + +K_MSGQ_DEFINE(physical_layouts_kscan_msgq, sizeof(struct zmk_kscan_event), + CONFIG_ZMK_KSCAN_EVENT_QUEUE_SIZE, 4); + +static void zmk_physical_layout_kscan_callback(const struct device *dev, uint32_t row, + uint32_t column, bool pressed) { + if (dev != active->kscan) { + return; + } + + struct zmk_kscan_event ev = { + .row = row, + .column = column, + .state = (pressed ? ZMK_KSCAN_EVENT_STATE_PRESSED : ZMK_KSCAN_EVENT_STATE_RELEASED)}; + + k_msgq_put(&physical_layouts_kscan_msgq, &ev, K_NO_WAIT); + k_work_submit(&msg_processor.work); +} + +static void zmk_physical_layouts_kscan_process_msgq(struct k_work *item) { + struct zmk_kscan_event ev; + + while (k_msgq_get(&physical_layouts_kscan_msgq, &ev, K_NO_WAIT) == 0) { + bool pressed = (ev.state == ZMK_KSCAN_EVENT_STATE_PRESSED); + int32_t position = zmk_matrix_transform_row_column_to_position(active->matrix_transform, + ev.row, ev.column); + + if (position < 0) { + LOG_WRN("Not found in transform: row: %d, col: %d, pressed: %s", ev.row, ev.column, + (pressed ? "true" : "false")); + continue; + } + + LOG_DBG("Row: %d, col: %d, position: %d, pressed: %s", ev.row, ev.column, position, + (pressed ? "true" : "false")); + raise_zmk_position_state_changed( + (struct zmk_position_state_changed){.source = ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL, + .state = pressed, + .position = position, + .timestamp = k_uptime_get()}); + } +} + +int zmk_physical_layouts_select_layout(const struct zmk_physical_layout *dest_layout) { + if (!dest_layout) { + return -ENODEV; + } + + if (dest_layout == active) { + return 0; + } + + if (active) { + if (active->kscan) { + kscan_disable_callback(active->kscan); +#if IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) + pm_device_runtime_put(active->kscan); +#elif IS_ENABLED(CONFIG_PM_DEVICE) + pm_device_action_run(active->kscan, PM_DEVICE_ACTION_SUSPEND); +#endif + } + } + + active = dest_layout; + + if (active->kscan) { +#if IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) + int err = pm_device_runtime_get(active->kscan); + if (err < 0) { + LOG_WRN("PM runtime get of kscan device to enable it %d", err); + return err; + } +#elif IS_ENABLED(CONFIG_PM_DEVICE) + pm_device_action_run(active->kscan, PM_DEVICE_ACTION_RESUME); +#endif + kscan_config(active->kscan, zmk_physical_layout_kscan_callback); + kscan_enable_callback(active->kscan); + } + + return 0; +} + +int zmk_physical_layouts_select(uint8_t index) { + if (index >= ARRAY_SIZE(layouts)) { + return -EINVAL; + } + + return zmk_physical_layouts_select_layout(layouts[index]); +} + +int zmk_physical_layouts_get_selected(void) { + for (int i = 0; i < ARRAY_SIZE(layouts); i++) { + if (layouts[i] == active) { + return i; + } + } + + return -ENODEV; +} + +#if IS_ENABLED(CONFIG_SETTINGS) + +static int8_t saved_selected_index = -1; + +#endif + +int zmk_physical_layouts_select_initial(void) { + const struct zmk_physical_layout *initial; + +#if DT_HAS_CHOSEN(zmk_physical_layout) + initial = &_CONCAT(_zmk_physical_layout_, DT_CHOSEN(zmk_physical_layout)); +#else + initial = layouts[0]; +#endif + + int ret = zmk_physical_layouts_select_layout(initial); + + return ret; +} + +int zmk_physical_layouts_check_unsaved_selection(void) { +#if IS_ENABLED(CONFIG_SETTINGS) + return saved_selected_index < 0 || + saved_selected_index == (uint8_t)zmk_physical_layouts_get_selected() + ? 0 + : 1; +#else + return -ENOTSUP; +#endif +} + +int zmk_physical_layouts_save_selected(void) { +#if IS_ENABLED(CONFIG_SETTINGS) + uint8_t val = (uint8_t)zmk_physical_layouts_get_selected(); + + return settings_save_one("physical_layouts/selected", &val, sizeof(val)); +#else + return -ENOTSUP; +#endif +} + +int zmk_physical_layouts_revert_selected(void) { return zmk_physical_layouts_select_initial(); } + +int zmk_physical_layouts_get_position_map(uint8_t source, uint8_t dest, uint32_t *map) { + if (source >= ARRAY_SIZE(layouts) || dest >= ARRAY_SIZE(layouts)) { + return -EINVAL; + } + + const struct zmk_physical_layout *src_layout = layouts[source]; + const struct zmk_physical_layout *dest_layout = layouts[dest]; + +#if HAVE_POS_MAP + const struct position_map_entry *src_pos_map = NULL; + const struct position_map_entry *dest_pos_map = NULL; + + for (int pm = 0; pm < ARRAY_SIZE(positions_maps); pm++) { + if (positions_maps[pm].layout == src_layout) { + src_pos_map = &positions_maps[pm]; + } + + if (positions_maps[pm].layout == dest_layout) { + dest_pos_map = &positions_maps[pm]; + } + } +#endif + + memset(map, UINT32_MAX, dest_layout->keys_len); + + for (int b = 0; b < dest_layout->keys_len; b++) { + bool found = false; + +#if HAVE_POS_MAP + if (src_pos_map && dest_pos_map) { + for (int m = 0; m < ZMK_POS_MAP_LEN; m++) { + if (dest_pos_map->positions[m] == b) { + map[b] = src_pos_map->positions[m]; + found = true; + break; + } + } + } +#endif + +#if !POS_MAP_COMPLETE + if (!found) { + const struct zmk_key_physical_attrs *key = &dest_layout->keys[b]; + for (int old_b = 0; old_b < src_layout->keys_len; old_b++) { + const struct zmk_key_physical_attrs *candidate_key = &src_layout->keys[old_b]; + + if (candidate_key->x == key->x && candidate_key->y == key->y) { + map[b] = old_b; + found = true; + break; + } + } + } +#endif + + if (!found || map[b] >= src_layout->keys_len) { + map[b] = UINT32_MAX; + } + } + + return dest_layout->keys_len; +} + +#if IS_ENABLED(CONFIG_SETTINGS) + +static int physical_layouts_handle_set(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg) { + const char *next; + + if (settings_name_steq(name, "selected", &next) && !next) { + if (len != sizeof(saved_selected_index)) { + return -EINVAL; + } + + int err = read_cb(cb_arg, &saved_selected_index, len); + if (err <= 0) { + LOG_ERR("Failed to handle selected physical dest_layout from settings (err %d)", err); + return err; + } + + return zmk_physical_layouts_select(saved_selected_index); + } + + return 0; +}; + +SETTINGS_STATIC_HANDLER_DEFINE(physical_layouts, "physical_layouts", NULL, + physical_layouts_handle_set, NULL, NULL); + +#endif // IS_ENABLED(CONFIG_SETTINGS) + +static int zmk_physical_layouts_init(void) { + k_work_init(&msg_processor.work, zmk_physical_layouts_kscan_process_msgq); + +#if IS_ENABLED(CONFIG_PM_DEVICE) + for (int l = 0; l < ARRAY_SIZE(layouts); l++) { + const struct zmk_physical_layout *pl = layouts[l]; + if (pl->kscan) { + if (pm_device_wakeup_is_capable(pl->kscan)) { + pm_device_wakeup_enable(pl->kscan, true); + } + } + } +#endif // IS_ENABLED(CONFIG_PM_DEVICE) + + return zmk_physical_layouts_select_initial(); +} + +SYS_INIT(zmk_physical_layouts_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); From 0438cb0ee532a31291aee3dabe24362f16146fd2 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Wed, 24 Apr 2024 21:11:16 -0700 Subject: [PATCH 09/16] feat(kscan): More complete PM support to drivers. * Update our GPIO kscan drivers to more completely support PM device, by doing proper hardare init/deinit in the PM action hook. --- app/boards/shields/zmk_uno/zmk_uno.overlay | 2 + .../kscan/zmk,kscan-sideband-behaviors.yaml | 3 + .../drivers/kscan/kscan_gpio_charlieplex.c | 69 +++++++++++++++-- app/module/drivers/kscan/kscan_gpio_direct.c | 33 +++++++- app/module/drivers/kscan/kscan_gpio_matrix.c | 57 +++++++++++++- app/src/kscan_sideband_behaviors.c | 76 +++++++++++++------ docs/docs/features/soft-off.md | 1 + 7 files changed, 203 insertions(+), 38 deletions(-) diff --git a/app/boards/shields/zmk_uno/zmk_uno.overlay b/app/boards/shields/zmk_uno/zmk_uno.overlay index 3d105abf..6948112b 100644 --- a/app/boards/shields/zmk_uno/zmk_uno.overlay +++ b/app/boards/shields/zmk_uno/zmk_uno.overlay @@ -38,6 +38,8 @@ endpoint_sideband_behaviors { compatible = "zmk,kscan-sideband-behaviors"; + + auto-enable; kscan = <&kscan_sp3t_toggle>; first_toggle_sideband: first_toggle_sideband { diff --git a/app/dts/bindings/kscan/zmk,kscan-sideband-behaviors.yaml b/app/dts/bindings/kscan/zmk,kscan-sideband-behaviors.yaml index f3ed180d..e38beeb4 100644 --- a/app/dts/bindings/kscan/zmk,kscan-sideband-behaviors.yaml +++ b/app/dts/bindings/kscan/zmk,kscan-sideband-behaviors.yaml @@ -11,6 +11,9 @@ compatible: "zmk,kscan-sideband-behaviors" include: kscan.yaml properties: + auto-enable: + type: boolean + kscan: type: phandle required: true diff --git a/app/module/drivers/kscan/kscan_gpio_charlieplex.c b/app/module/drivers/kscan/kscan_gpio_charlieplex.c index 3ecbcd6a..f48a6a2f 100644 --- a/app/module/drivers/kscan/kscan_gpio_charlieplex.c +++ b/app/module/drivers/kscan/kscan_gpio_charlieplex.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -167,6 +168,21 @@ static int kscan_charlieplex_set_all_outputs(const struct device *dev, const int return 0; } +static int kscan_charlieplex_disconnect_all(const struct device *dev) { + const struct kscan_charlieplex_config *config = dev->config; + + for (int i = 0; i < config->cells.len; i++) { + const struct gpio_dt_spec *gpio = &config->cells.gpios[i]; + int err = gpio_pin_configure_dt(gpio, GPIO_DISCONNECTED); + if (err) { + LOG_ERR("Unable to configure pin %u on %s for input", gpio->pin, gpio->port->name); + return err; + } + } + + return 0; +} + static int kscan_charlieplex_interrupt_configure(const struct device *dev, const gpio_flags_t flags) { const struct kscan_charlieplex_config *config = dev->config; @@ -359,11 +375,7 @@ static int kscan_charlieplex_init_interrupt(const struct device *dev) { return err; } -static int kscan_charlieplex_init(const struct device *dev) { - struct kscan_charlieplex_data *data = dev->data; - - data->dev = dev; - +static void kscan_charlieplex_setup_pins(const struct device *dev) { kscan_charlieplex_init_inputs(dev); kscan_charlieplex_set_all_outputs(dev, 0); @@ -371,7 +383,46 @@ static int kscan_charlieplex_init(const struct device *dev) { if (config->use_interrupt) { kscan_charlieplex_init_interrupt(dev); } +} + +#if IS_ENABLED(CONFIG_PM_DEVICE) + +static int kscan_charlieplex_pm_action(const struct device *dev, enum pm_device_action action) { + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + kscan_charlieplex_interrupt_configure(dev, GPIO_INT_DISABLE); + kscan_charlieplex_disconnect_all(dev); + + return kscan_charlieplex_disable(dev); + case PM_DEVICE_ACTION_RESUME: + kscan_charlieplex_setup_pins(dev); + + return kscan_charlieplex_enable(dev); + default: + return -ENOTSUP; + } +} + +#endif // IS_ENABLED(CONFIG_PM_DEVICE) + +static int kscan_charlieplex_init(const struct device *dev) { + struct kscan_charlieplex_data *data = dev->data; + + data->dev = dev; + k_work_init_delayable(&data->work, kscan_charlieplex_work_handler); + +#if IS_ENABLED(CONFIG_PM_DEVICE) + pm_device_init_suspended(dev); + +#if IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) + pm_device_runtime_enable(dev); +#endif + +#else + kscan_charlieplex_setup_pins(dev); +#endif + return 0; } @@ -406,8 +457,10 @@ static const struct kscan_driver_api kscan_charlieplex_api = { COND_THIS_INTERRUPT(n, (.use_interrupt = INST_INTR_DEFINED(n), )) \ COND_THIS_INTERRUPT(n, (.interrupt = KSCAN_INTR_CFG_INIT(n), ))}; \ \ - DEVICE_DT_INST_DEFINE(n, &kscan_charlieplex_init, NULL, &kscan_charlieplex_data_##n, \ - &kscan_charlieplex_config_##n, POST_KERNEL, CONFIG_KSCAN_INIT_PRIORITY, \ - &kscan_charlieplex_api); + PM_DEVICE_DT_INST_DEFINE(n, kscan_charlieplex_pm_action); \ + \ + DEVICE_DT_INST_DEFINE(n, &kscan_charlieplex_init, PM_DEVICE_DT_INST_GET(n), \ + &kscan_charlieplex_data_##n, &kscan_charlieplex_config_##n, POST_KERNEL, \ + CONFIG_KSCAN_INIT_PRIORITY, &kscan_charlieplex_api); DT_INST_FOREACH_STATUS_OKAY(KSCAN_CHARLIEPLEX_INIT); diff --git a/app/module/drivers/kscan/kscan_gpio_direct.c b/app/module/drivers/kscan/kscan_gpio_direct.c index fa24e69e..245e78b5 100644 --- a/app/module/drivers/kscan/kscan_gpio_direct.c +++ b/app/module/drivers/kscan/kscan_gpio_direct.c @@ -294,6 +294,24 @@ static int kscan_direct_init_input_inst(const struct device *dev, const struct g return 0; } +#if IS_ENABLED(CONFIG_PM_DEVICE) + +static int kscan_direct_disconnect_inputs(const struct device *dev) { + const struct kscan_direct_data *data = dev->data; + + for (int i = 0; i < data->inputs.len; i++) { + const struct gpio_dt_spec *gpio = &data->inputs.gpios[i].spec; + int err = gpio_pin_configure_dt(gpio, GPIO_DISCONNECTED); + if (err) { + return err; + } + } + + return 0; +} + +#endif // IS_ENABLED(CONFIG_PM_DEVICE) + static int kscan_direct_init_inputs(const struct device *dev) { const struct kscan_direct_data *data = dev->data; const struct kscan_direct_config *config = dev->config; @@ -317,9 +335,20 @@ static int kscan_direct_init(const struct device *dev) { // Sort inputs by port so we can read each port just once per scan. kscan_gpio_list_sort_by_port(&data->inputs); + k_work_init_delayable(&data->work, kscan_direct_work_handler); + +#if IS_ENABLED(CONFIG_PM_DEVICE) + pm_device_init_suspended(dev); + +#if IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) + pm_device_runtime_enable(dev); +#endif + +#else + kscan_direct_init_inputs(dev); - k_work_init_delayable(&data->work, kscan_direct_work_handler); +#endif return 0; } @@ -329,8 +358,10 @@ static int kscan_direct_init(const struct device *dev) { static int kscan_direct_pm_action(const struct device *dev, enum pm_device_action action) { switch (action) { case PM_DEVICE_ACTION_SUSPEND: + kscan_direct_disconnect_inputs(dev); return kscan_direct_disable(dev); case PM_DEVICE_ACTION_RESUME: + kscan_direct_init_inputs(dev); return kscan_direct_enable(dev); default: return -ENOTSUP; diff --git a/app/module/drivers/kscan/kscan_gpio_matrix.c b/app/module/drivers/kscan/kscan_gpio_matrix.c index 8a3c39f2..e0c76395 100644 --- a/app/module/drivers/kscan/kscan_gpio_matrix.c +++ b/app/module/drivers/kscan/kscan_gpio_matrix.c @@ -405,6 +405,44 @@ static int kscan_matrix_init_outputs(const struct device *dev) { return 0; } +#if IS_ENABLED(CONFIG_PM_DEVICE) + +static int kscan_matrix_disconnect_inputs(const struct device *dev) { + const struct kscan_matrix_data *data = dev->data; + + for (int i = 0; i < data->inputs.len; i++) { + const struct gpio_dt_spec *gpio = &data->inputs.gpios[i].spec; + int err = gpio_pin_configure_dt(gpio, GPIO_DISCONNECTED); + if (err) { + return err; + } + } + + return 0; +} + +static int kscan_matrix_disconnect_outputs(const struct device *dev) { + const struct kscan_matrix_config *config = dev->config; + + for (int i = 0; i < config->outputs.len; i++) { + const struct gpio_dt_spec *gpio = &config->outputs.gpios[i].spec; + int err = gpio_pin_configure_dt(gpio, GPIO_DISCONNECTED); + if (err) { + return err; + } + } + + return 0; +} + +#endif // IS_ENABLED(CONFIG_PM_DEVICE) + +static void kscan_matrix_setup_pins(const struct device *dev) { + kscan_matrix_init_inputs(dev); + kscan_matrix_init_outputs(dev); + kscan_matrix_set_all_outputs(dev, 0); +} + static int kscan_matrix_init(const struct device *dev) { struct kscan_matrix_data *data = dev->data; @@ -413,12 +451,19 @@ static int kscan_matrix_init(const struct device *dev) { // Sort inputs by port so we can read each port just once per scan. kscan_gpio_list_sort_by_port(&data->inputs); - kscan_matrix_init_inputs(dev); - kscan_matrix_init_outputs(dev); - kscan_matrix_set_all_outputs(dev, 0); - k_work_init_delayable(&data->work, kscan_matrix_work_handler); +#if IS_ENABLED(CONFIG_PM_DEVICE) + pm_device_init_suspended(dev); + +#if IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) + pm_device_runtime_enable(dev); +#endif + +#else + kscan_matrix_setup_pins(dev); +#endif + return 0; } @@ -427,8 +472,12 @@ static int kscan_matrix_init(const struct device *dev) { static int kscan_matrix_pm_action(const struct device *dev, enum pm_device_action action) { switch (action) { case PM_DEVICE_ACTION_SUSPEND: + kscan_matrix_disconnect_inputs(dev); + kscan_matrix_disconnect_outputs(dev); + return kscan_matrix_disable(dev); case PM_DEVICE_ACTION_RESUME: + kscan_matrix_setup_pins(dev); return kscan_matrix_enable(dev); default: return -ENOTSUP; diff --git a/app/src/kscan_sideband_behaviors.c b/app/src/kscan_sideband_behaviors.c index f3992ebc..602cae12 100644 --- a/app/src/kscan_sideband_behaviors.c +++ b/app/src/kscan_sideband_behaviors.c @@ -26,6 +26,7 @@ struct ksbb_entry { struct ksbb_config { const struct device *kscan; + bool auto_enable; struct ksbb_entry *entries; size_t entries_len; }; @@ -93,34 +94,65 @@ void ksbb_inner_kscan_callback(const struct device *dev, uint32_t row, uint32_t } static int ksbb_configure(const struct device *dev, kscan_callback_t callback) { - const struct ksbb_config *cfg = dev->config; struct ksbb_data *data = dev->data; data->callback = callback; -#if IS_ENABLED(CONFIG_PM_DEVICE) - if (pm_device_wakeup_is_enabled(dev) && pm_device_wakeup_is_capable(cfg->kscan)) { - pm_device_wakeup_enable(cfg->kscan, true); - } -#endif // IS_ENABLED(CONFIG_PM_DEVICE) - return 0; } static int ksbb_enable(const struct device *dev) { struct ksbb_data *data = dev->data; + const struct ksbb_config *config = dev->config; data->enabled = true; +#if IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) + if (!pm_device_runtime_is_enabled(dev) && pm_device_runtime_is_enabled(config->kscan)) { + pm_device_runtime_get(config->kscan); + } +#elif IS_ENABLED(CONFIG_PM_DEVICE) + pm_device_action_run(config->kscan, PM_DEVICE_ACTION_RESUME); +#endif // IS_ENABLED(CONFIG_PM_DEVICE) + + kscan_config(config->kscan, &ksbb_inner_kscan_callback); + kscan_enable_callback(config->kscan); + return 0; } static int ksbb_disable(const struct device *dev) { struct ksbb_data *data = dev->data; + const struct ksbb_config *config = dev->config; data->enabled = false; + kscan_disable_callback(config->kscan); + +#if IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) + if (!pm_device_runtime_is_enabled(dev) && pm_device_runtime_is_enabled(config->kscan)) { + pm_device_runtime_put(config->kscan); + } +#elif IS_ENABLED(CONFIG_PM_DEVICE) + pm_device_action_run(config->kscan, PM_DEVICE_ACTION_SUSPEND); +#endif // IS_ENABLED(CONFIG_PM_DEVICE) + return 0; } +#if IS_ENABLED(CONFIG_PM_DEVICE) + +static int ksbb_pm_action(const struct device *dev, enum pm_device_action action) { + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + return ksbb_disable(dev); + case PM_DEVICE_ACTION_RESUME: + return ksbb_enable(dev); + default: + return -ENOTSUP; + } +} + +#endif // IS_ENABLED(CONFIG_PM_DEVICE) + static int ksbb_init(const struct device *dev) { const struct ksbb_config *config = dev->config; @@ -129,8 +161,16 @@ static int ksbb_init(const struct device *dev) { return -ENODEV; } - kscan_config(config->kscan, &ksbb_inner_kscan_callback); - kscan_enable_callback(config->kscan); + if (config->auto_enable) { +#if !IS_ENABLED(CONFIG_PM_DEVICE) + kscan_config(config->kscan, &ksbb_inner_kscan_callback); + kscan_enable_callback(config->kscan); +#else + ksbb_pm_action(dev, PM_DEVICE_ACTION_RESUME); + } else { + pm_device_init_suspended(dev); +#endif + } return 0; } @@ -141,21 +181,6 @@ static const struct kscan_driver_api ksbb_api = { .disable_callback = ksbb_disable, }; -#if IS_ENABLED(CONFIG_PM_DEVICE) - -static int ksbb_pm_action(const struct device *dev, enum pm_device_action action) { - switch (action) { - case PM_DEVICE_ACTION_SUSPEND: - return ksbb_disable(dev); - case PM_DEVICE_ACTION_RESUME: - return ksbb_disable(dev); - default: - return -ENOTSUP; - } -} - -#endif // IS_ENABLED(CONFIG_PM_DEVICE) - #define ENTRY(e) \ { \ .row = DT_PROP(e, row), .column = DT_PROP(e, column), \ @@ -167,13 +192,14 @@ static int ksbb_pm_action(const struct device *dev, enum pm_device_action action DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(n, ENTRY, (, ))}; \ const struct ksbb_config ksbb_config_##n = { \ .kscan = DEVICE_DT_GET(DT_INST_PHANDLE(n, kscan)), \ + .auto_enable = DT_INST_PROP_OR(n, auto_enable, false), \ .entries = entries_##n, \ .entries_len = ARRAY_SIZE(entries_##n), \ }; \ struct ksbb_data ksbb_data_##n = {}; \ PM_DEVICE_DT_INST_DEFINE(n, ksbb_pm_action); \ DEVICE_DT_INST_DEFINE(n, ksbb_init, PM_DEVICE_DT_INST_GET(n), &ksbb_data_##n, \ - &ksbb_config_##n, APPLICATION, \ + &ksbb_config_##n, POST_KERNEL, \ CONFIG_ZMK_KSCAN_SIDEBAND_BEHAVIORS_INIT_PRIORITY, &ksbb_api); DT_INST_FOREACH_STATUS_OKAY(KSBB_INST) diff --git a/docs/docs/features/soft-off.md b/docs/docs/features/soft-off.md index 7018afa0..207bb13f 100644 --- a/docs/docs/features/soft-off.md +++ b/docs/docs/features/soft-off.md @@ -121,6 +121,7 @@ With that in place, the kscan sideband behavior will wrap the new driver: compatible = "zmk,kscan-sideband-behaviors"; kscan = <&soft_off_direct_scan>; + auto-enable; wakeup-source; soft_off { From 74f7fe921ba71b2da7c2177473e4bb5d4e3dd386 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Wed, 1 May 2024 14:06:28 -0700 Subject: [PATCH 10/16] fix(splits): Increase split notify stack size. --- app/src/split/bluetooth/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/split/bluetooth/Kconfig b/app/src/split/bluetooth/Kconfig index 4da50528..7f362fde 100644 --- a/app/src/split/bluetooth/Kconfig +++ b/app/src/split/bluetooth/Kconfig @@ -76,7 +76,7 @@ if !ZMK_SPLIT_ROLE_CENTRAL config ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE int "BLE split peripheral notify thread stack size" - default 650 + default 756 config ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY int "BLE split peripheral notify thread priority" From 5fcf09290177843db58f870bec29f0db5b1b8fec Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Wed, 1 May 2024 14:07:48 -0700 Subject: [PATCH 11/16] refactor(shields): Set ZMK Uno physical layouts. * Add physical layout definitions for uno and split uno shields. --- app/boards/shields/zmk_uno/zmk_uno.dtsi | 3 -- app/boards/shields/zmk_uno/zmk_uno.keymap | 6 +-- app/boards/shields/zmk_uno/zmk_uno.overlay | 35 ++++++++++++- app/boards/shields/zmk_uno/zmk_uno_split.dtsi | 49 +++++++++++++++++-- .../shields/zmk_uno/zmk_uno_split.keymap | 7 +-- .../zmk_uno/zmk_uno_split_right.overlay | 2 +- 6 files changed, 82 insertions(+), 20 deletions(-) diff --git a/app/boards/shields/zmk_uno/zmk_uno.dtsi b/app/boards/shields/zmk_uno/zmk_uno.dtsi index ba1d3b5d..9ea625a4 100644 --- a/app/boards/shields/zmk_uno/zmk_uno.dtsi +++ b/app/boards/shields/zmk_uno/zmk_uno.dtsi @@ -40,10 +40,8 @@ nice_view_spi: &arduino_spi { / { chosen { - zmk,kscan = &kscan_matrix; zmk,backlight = &backlight; zmk,underglow = &led_strip; - zmk,matrix-transform = &matrix_transform; }; // Commented out until we add more powerful power domain support @@ -109,7 +107,6 @@ nice_view_spi: &arduino_spi { kscan_direct: kscan_direct { compatible = "zmk,kscan-gpio-direct"; wakeup-source; - status = "disabled"; input-gpios = <&arduino_header 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> diff --git a/app/boards/shields/zmk_uno/zmk_uno.keymap b/app/boards/shields/zmk_uno/zmk_uno.keymap index a7f6a267..186cdb60 100644 --- a/app/boards/shields/zmk_uno/zmk_uno.keymap +++ b/app/boards/shields/zmk_uno/zmk_uno.keymap @@ -13,13 +13,9 @@ // Uncomment the following lines if using the "Direct Wire" jumper to switch the matrix to a direct wire. -// &kscan_direct { status = "okay"; }; -// &kscan_matrix { status = "disabled"; }; - // / { // chosen { -// zmk,matrix-transform = &direct_matrix_transform; -// zmk,kscan = &kscan_direct; +// zmk,physical-layout = &direct_physical_layout; // }; // }; diff --git a/app/boards/shields/zmk_uno/zmk_uno.overlay b/app/boards/shields/zmk_uno/zmk_uno.overlay index 6948112b..2a8eb266 100644 --- a/app/boards/shields/zmk_uno/zmk_uno.overlay +++ b/app/boards/shields/zmk_uno/zmk_uno.overlay @@ -7,13 +7,15 @@ #include "zmk_uno.dtsi" #include +#include #include #include / { chosen { - zmk,matrix-transform = &matrix_transform; + zmk,physical-layout = &matrix_physical_layout; }; + sensors: sensors { compatible = "zmk,keymap-sensors"; sensors = <&encoder>; @@ -58,4 +60,35 @@ }; }; + matrix_physical_layout: matrix_physical_layout { + compatible = "zmk,physical-layout"; + display-name = "Matrix Layout"; + + kscan = <&kscan_matrix>; + transform = <&matrix_transform>; + + keys + = <&key_physical_attrs 100 100 0 0 0 0 0> + , <&key_physical_attrs 100 100 100 0 0 0 0> + , <&key_physical_attrs 100 100 0 100 0 0 0> + , <&key_physical_attrs 100 100 100 100 0 0 0> + ; + }; + + direct_physical_layout: direct_physical_layout { + compatible = "zmk,physical-layout"; + + display-name = "Direct Wire Layout"; + + kscan = <&kscan_direct>; + transform = <&direct_matrix_transform>; + + keys + = <&key_physical_attrs 100 100 0 0 0 0 0> + , <&key_physical_attrs 100 100 100 0 0 0 0> + , <&key_physical_attrs 100 100 0 100 0 0 0> + , <&key_physical_attrs 100 100 100 100 0 0 0> + ; + }; + }; diff --git a/app/boards/shields/zmk_uno/zmk_uno_split.dtsi b/app/boards/shields/zmk_uno/zmk_uno_split.dtsi index dac6fc3e..9afbf79a 100644 --- a/app/boards/shields/zmk_uno/zmk_uno_split.dtsi +++ b/app/boards/shields/zmk_uno/zmk_uno_split.dtsi @@ -6,13 +6,15 @@ #include "zmk_uno.dtsi" + #include + left_encoder: &encoder { status = "disabled"; }; / { chosen { - zmk,matrix-transform = &split_matrix_transform; + zmk,physical-layout = &matrix_physical_layout; }; split_matrix_transform: split_matrix_transform { @@ -31,18 +33,57 @@ split_direct_matrix_transform: split_direct_matrix_transform { compatible = "zmk,matrix-transform"; - rows = <3>; + rows = <2>; columns = <4>; map = < RC(0,0) RC(0,1) RC(0,2) RC(0,3) - RC(2,0) RC(2,1) - RC(2,2) RC(2,3) + RC(1,0) RC(1,1) + RC(1,2) RC(1,3) >; }; + matrix_physical_layout: matrix_physical_layout { + compatible = "zmk,physical-layout"; + display-name = "Matrix Layout"; + + kscan = <&kscan_matrix>; + transform = <&split_matrix_transform>; + + keys + = <&key_physical_attrs 100 100 0 0 0 0 0> + , <&key_physical_attrs 100 100 100 0 0 0 0> + , <&key_physical_attrs 100 100 0 100 0 0 0> + , <&key_physical_attrs 100 100 100 100 0 0 0> + , <&key_physical_attrs 100 100 0 200 0 0 0> + , <&key_physical_attrs 100 100 100 200 0 0 0> + , <&key_physical_attrs 100 100 0 300 0 0 0> + , <&key_physical_attrs 100 100 100 300 0 0 0> + ; + }; + + direct_physical_layout: direct_physical_layout { + compatible = "zmk,physical-layout"; + + display-name = "Direct Wire Layout"; + + kscan = <&kscan_direct>; + transform = <&split_direct_matrix_transform>; + + keys + = <&key_physical_attrs 100 100 0 0 0 0 0> + , <&key_physical_attrs 100 100 100 0 0 0 0> + , <&key_physical_attrs 100 100 0 100 0 0 0> + , <&key_physical_attrs 100 100 100 100 0 0 0> + , <&key_physical_attrs 100 100 0 200 0 0 0> + , <&key_physical_attrs 100 100 100 200 0 0 0> + , <&key_physical_attrs 100 100 0 300 0 0 0> + , <&key_physical_attrs 100 100 100 300 0 0 0> + ; + }; + right_encoder: right_encoder { steps = <80>; status = "disabled"; diff --git a/app/boards/shields/zmk_uno/zmk_uno_split.keymap b/app/boards/shields/zmk_uno/zmk_uno_split.keymap index 0e50a283..d2daa6ea 100644 --- a/app/boards/shields/zmk_uno/zmk_uno_split.keymap +++ b/app/boards/shields/zmk_uno/zmk_uno_split.keymap @@ -14,14 +14,9 @@ // Uncomment the following lines if using the "Direct Wire" jumper to switch the matrix to a direct wire. - -// &kscan_direct { status = "okay"; }; -// &kscan_matrix { status = "disabled"; }; - // / { // chosen { -// zmk,matrix-transform = &split_direct_matrix_transform; -// zmk,kscan = &kscan_direct; +// zmk,physical-layout = &direct_physical_layout; // }; // }; diff --git a/app/boards/shields/zmk_uno/zmk_uno_split_right.overlay b/app/boards/shields/zmk_uno/zmk_uno_split_right.overlay index 9c2e7d7f..acfad5a2 100644 --- a/app/boards/shields/zmk_uno/zmk_uno_split_right.overlay +++ b/app/boards/shields/zmk_uno/zmk_uno_split_right.overlay @@ -11,7 +11,7 @@ }; &split_direct_matrix_transform { - row-offset = <2>; + row-offset = <1>; }; &right_encoder { From b4f9081b090ac8116476e078439b177eb9487e62 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Wed, 3 Jul 2024 13:58:08 -0600 Subject: [PATCH 12/16] docs: Updated new shield documentation for physical layouts. * Document how to define one or more physical layouts and assign the chosen one. Co-authored-by: Cem Aksoylar --- docs/docs/development/new-shield.mdx | 70 +++++++++++++++++++++------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/docs/docs/development/new-shield.mdx b/docs/docs/development/new-shield.mdx index d48e0d1d..3d0c45c1 100644 --- a/docs/docs/development/new-shield.mdx +++ b/docs/docs/development/new-shield.mdx @@ -19,7 +19,8 @@ The high level steps are: - Create a new shield directory. - Add the base Kconfig files. - Add the shield overlay file to define the KSCAN driver for detecting key press/release. -- (Optional) Add the matrix transform for mapping KSCAN row/column values to sane key positions. This is needed for non-rectangular keyboards, or where the underlying row/column pin arrangement does not map one to one with logical locations on the keyboard. +- Add the matrix transform for mapping KSCAN row/column values to key positions in the keymap. +- Add a physical layout definition to select the matrix transform and KSCAN instance. - Add a default keymap, which users can override in their own configs as needed. - Add a `.zmk.yml` metadata file to document the high level details of your shield, and the features it supports. - Update the `build.yaml` file from the repository template to have some sample builds of the firmware to test. @@ -318,7 +319,7 @@ The shared configuration in `my_awesome_split_board.conf` is only applied when y -## (Optional) Matrix Transform +## Matrix Transform Internally ZMK translates all row/column events into "key position" events to maintain a consistent model that works no matter what any possible GPIO matrix may look like for a certain keyboard. This is particularly helpful when: @@ -328,15 +329,7 @@ Internally ZMK translates all row/column events into "key position" events to ma A "key position" is the numeric index (zero-based) of a given key, which identifies the logical key location as perceived by the end user. All _keymap_ mappings actually bind behaviors to _key positions_, not to row/column values. -_Without_ a matrix transform, that intentionally map each key position to the row/column pair that position corresponds to, the default equation to determine that is: - -```c -($row * NUMBER_OF_COLUMNS) + $column -``` - -Which effectively amounts to numbering the key positions by traversing each row from top to bottom and assigning numerically incrementing key positions. - -Whenever that default key position mapping is insufficient, the `.overlay` file should _also_ include a matrix transform. +The `.overlay` must include a matrix transform that defines this mapping from row/column values to key positions. Here is an example for the [nice60](https://github.com/Nicell/nice60), which uses an efficient 8x8 GPIO matrix, and uses a transform: @@ -344,11 +337,6 @@ Here is an example for the [nice60](https://github.com/Nicell/nice60), which use #include / { - chosen { - zmk,kscan = &kscan0; - zmk,matrix-transform = &default_transform; - }; - /* define kscan node with label `kscan0`... */ default_transform: keymap_transform_0 { @@ -377,10 +365,58 @@ Some important things to note: - The `#include ` is critical. The `RC` macro is used to generate the internal storage in the matrix transform, and is actually replaced by a C preprocessor before the final devicetree is compiled into ZMK. - `RC(row, column)` is placed sequentially to define what row and column values that position corresponds to. -- If you have a keyboard with options for `2u` keys in certain positions, or break away portions, it is a good idea to set the chosen `zmk,matrix-transform` to the default arrangement, and include _other_ possible matrix transform nodes in the devicetree that users can select in their user config by overriding the chosen node. +- If you have a keyboard with options for `2u` keys in certain positions, ANSI vs. ISO layouts, or break away portions, define one matrix transform for each possible arrangement to be used in the physical layouts. This will allow the users to select the right layout in their keymap files. See the [matrix transform section](../config/kscan.md#matrix-transform) in the Keyboard Scan configuration documentation for details and more examples of matrix transforms. +## Physical Layout + +The physical layout is the top level entity that aggregates all details about a certain possible layout for a keyboard: the matrix transform that defines the set of key positions and what row/column they correspond to, what kscan driver is used for that layout, etc. +For keyboards that support multiple layouts, setting a `chosen` node to a defined physical layout in your keymap will allow selecting the specific layout that you've built. + +A physical layout is very basic, e.g.: + +``` +/ { + default_layout: default_layout { + compatible = "zmk,physical-layout"; + transform = <&default_transform>; + kscan = <&kscan0>; + }; +}; +``` + +When supporting multiple layouts, define the multiple layout nodes and then set a `chosen` for the default: + +``` +/ { + chosen { + zmk,physical-layout = &default_layout; + ... + }; + + default_layout: default_layout { + compatible = "zmk,physical-layout"; + transform = <&default_transform>; + kscan = <&kscan0>; + }; + + alt_layout: alt_layout { + compatible = "zmk,physical-layout"; + transform = <&alt_transform>; + kscan = <&alt_kscan0>; + }; +}; +``` + +This way, users can select a different layout by overriding the `zmk,physical-layout` chosen node in their keymap file. + +:::note +Some keyboards use different GPIO pins for different layouts, and need different kscan nodes created for each layout. +However, if all of your physical layouts use the same `kscan` node under the hood, you can skip setting the `kscan` property on each +layout and instead assign the `zmk,kscan` chosen node to your single kscan instance. +::: + ## Default Keymap Each keyboard should provide a default keymap to be used when building the firmware, which can be overridden and customized by user configs. For "shield keyboards", this should be placed in the `boards/shields//.keymap` file. The keymap is configured as an additional devicetree overlay that includes the following: From 82eed0f5ddb05fc244385e0ac05704fbdec0c3ee Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Wed, 5 Jun 2024 01:21:34 -0700 Subject: [PATCH 13/16] feat(boards): Add flash/settings to XIAO RP2040. * Add storage partition. * Default necessacy Kconfig symbols for flash/settings. --- app/boards/seeeduino_xiao_rp2040.conf | 7 +++++++ app/boards/seeeduino_xiao_rp2040.overlay | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/app/boards/seeeduino_xiao_rp2040.conf b/app/boards/seeeduino_xiao_rp2040.conf index 21c1893d..714e715c 100644 --- a/app/boards/seeeduino_xiao_rp2040.conf +++ b/app/boards/seeeduino_xiao_rp2040.conf @@ -2,3 +2,10 @@ CONFIG_CONSOLE=n CONFIG_SERIAL=n CONFIG_UART_CONSOLE=n CONFIG_ZMK_USB=y + +CONFIG_MPU_ALLOW_FLASH_WRITE=y +CONFIG_NVS=y +CONFIG_SETTINGS_NVS=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y diff --git a/app/boards/seeeduino_xiao_rp2040.overlay b/app/boards/seeeduino_xiao_rp2040.overlay index b5d2cdb2..e6ba8136 100644 --- a/app/boards/seeeduino_xiao_rp2040.overlay +++ b/app/boards/seeeduino_xiao_rp2040.overlay @@ -5,3 +5,18 @@ */ &xiao_serial { status = "disabled"; }; + +&code_partition { + reg = <0x100 (DT_SIZE_M(2) - 0x100 - DT_SIZE_K(512))>; +}; + +&flash0 { + reg = <0x10000000 DT_SIZE_M(2)>; + + partitions { + storage_partition: partition@180000 { + reg = <0x180000 DT_SIZE_K(512)>; + read-only; + }; + }; +}; From e01f13f9f00c0646bbadffd89dae4c3cd7d00a58 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Thu, 4 Jul 2024 18:26:27 +0000 Subject: [PATCH 14/16] fix: Fix external power settings load issue. * Because settings load is delayed now, enable external power on init, and let it be disabled on settings load later, to ensure power is on early for other peripheral initialization. Fixes: #2361 --- app/src/ext_power_generic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/ext_power_generic.c b/app/src/ext_power_generic.c index 5a9cc5b8..17b3ba64 100644 --- a/app/src/ext_power_generic.c +++ b/app/src/ext_power_generic.c @@ -151,11 +151,11 @@ static int ext_power_generic_init(const struct device *dev) { #if IS_ENABLED(CONFIG_SETTINGS) k_work_init_delayable(&ext_power_save_work, ext_power_save_state_work); -#else - // Default to the ext_power being open when no settings - ext_power_enable(dev); #endif + // Enable by default. We may get disabled again once settings load. + ext_power_enable(dev); + if (config->init_delay_ms) { k_msleep(config->init_delay_ms); } From 3694ff85a039fa49722655b0f656219623a01d3a Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Fri, 5 Jul 2024 12:56:33 -0600 Subject: [PATCH 15/16] fix: Fix up layer metadata, move to layer IDs. * Studio will use stable layer IDs to refer to layers, so that layer reordering doesn't affect behavior bindings, so update to match. * Fix a few layer metadata entries that missed being refactored. --- app/include/drivers/behavior.h | 2 +- app/src/behavior.c | 2 +- app/src/behaviors/behavior_momentary_layer.c | 2 +- app/src/behaviors/behavior_to_layer.c | 3 +-- app/src/behaviors/behavior_toggle_layer.c | 3 +-- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/include/drivers/behavior.h b/app/include/drivers/behavior.h index 0b814ff2..7c99f04e 100644 --- a/app/include/drivers/behavior.h +++ b/app/include/drivers/behavior.h @@ -39,7 +39,7 @@ struct behavior_parameter_value_metadata { BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE = 1, BEHAVIOR_PARAMETER_VALUE_TYPE_RANGE = 2, BEHAVIOR_PARAMETER_VALUE_TYPE_HID_USAGE = 3, - BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_INDEX = 4, + BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_ID = 4, } type; }; diff --git a/app/src/behavior.c b/app/src/behavior.c index 0d9a4cdf..e69cdf88 100644 --- a/app/src/behavior.c +++ b/app/src/behavior.c @@ -99,7 +99,7 @@ static int check_param_matches_value(const struct behavior_parameter_value_metad } break; - case BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_INDEX: + case BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_ID: if (param >= 0 && param < ZMK_KEYMAP_LEN) { return PARAM_MATCHES; } diff --git a/app/src/behaviors/behavior_momentary_layer.c b/app/src/behaviors/behavior_momentary_layer.c index e27889df..b781a953 100644 --- a/app/src/behaviors/behavior_momentary_layer.c +++ b/app/src/behaviors/behavior_momentary_layer.c @@ -20,7 +20,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); static const struct behavior_parameter_value_metadata param_values[] = { { .display_name = "Layer", - .type = BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_INDEX, + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_ID, }, }; diff --git a/app/src/behaviors/behavior_to_layer.c b/app/src/behaviors/behavior_to_layer.c index d260087e..f739ec8d 100644 --- a/app/src/behaviors/behavior_to_layer.c +++ b/app/src/behaviors/behavior_to_layer.c @@ -37,8 +37,7 @@ static int to_keymap_binding_released(struct zmk_behavior_binding *binding, static const struct behavior_parameter_value_metadata param_values[] = { { .display_name = "Layer", - .type = BEHAVIOR_PARAMETER_VALUE_TYPE_STANDARD, - .standard = BEHAVIOR_PARAMETER_STANDARD_DOMAIN_LAYER_INDEX, + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_ID, }, }; diff --git a/app/src/behaviors/behavior_toggle_layer.c b/app/src/behaviors/behavior_toggle_layer.c index df261ed3..ea46c79f 100644 --- a/app/src/behaviors/behavior_toggle_layer.c +++ b/app/src/behaviors/behavior_toggle_layer.c @@ -39,8 +39,7 @@ static int tog_keymap_binding_released(struct zmk_behavior_binding *binding, static const struct behavior_parameter_value_metadata param_values[] = { { .display_name = "Layer", - .type = BEHAVIOR_PARAMETER_VALUE_TYPE_STANDARD, - .standard = BEHAVIOR_PARAMETER_STANDARD_DOMAIN_LAYER_INDEX, + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_ID, }, }; From 8c6bda260ace119b3c22a21bdcdd6d17a83fc5eb Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Fri, 5 Jul 2024 15:30:44 -0600 Subject: [PATCH 16/16] fix: Proper behavior metadata for key repeat/toggle --- app/src/behaviors/behavior_key_repeat.c | 23 +---------------------- app/src/behaviors/behavior_key_toggle.c | 3 +-- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/app/src/behaviors/behavior_key_repeat.c b/app/src/behaviors/behavior_key_repeat.c index f2cd569f..21343ae8 100644 --- a/app/src/behaviors/behavior_key_repeat.c +++ b/app/src/behaviors/behavior_key_repeat.c @@ -19,27 +19,6 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) -#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) - -static const struct behavior_parameter_value_metadata param_values[] = { - { - .display_name = "Key", - .type = BEHAVIOR_PARAMETER_VALUE_TYPE_HID_USAGE, - }, -}; - -static const struct behavior_parameter_metadata_set param_metadata_set[] = {{ - .param1_values = param_values, - .param1_values_len = ARRAY_SIZE(param_values), -}}; - -static const struct behavior_parameter_metadata metadata = { - .sets_len = ARRAY_SIZE(param_metadata_set), - .sets = param_metadata_set, -}; - -#endif - struct behavior_key_repeat_config { uint8_t index; uint8_t usage_pages_count; @@ -89,7 +68,7 @@ static const struct behavior_driver_api behavior_key_repeat_driver_api = { .binding_pressed = on_key_repeat_binding_pressed, .binding_released = on_key_repeat_binding_released, #if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) - .parameter_metadata = &metadata, + .get_parameter_metadata = zmk_behavior_get_empty_param_metadata, #endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; diff --git a/app/src/behaviors/behavior_key_toggle.c b/app/src/behaviors/behavior_key_toggle.c index d967af01..72f2570b 100644 --- a/app/src/behaviors/behavior_key_toggle.c +++ b/app/src/behaviors/behavior_key_toggle.c @@ -36,8 +36,7 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding, static const struct behavior_parameter_value_metadata param_values[] = { { .display_name = "Key", - .type = BEHAVIOR_PARAMETER_VALUE_TYPE_STANDARD, - .standard = BEHAVIOR_PARAMETER_STANDARD_DOMAIN_HID_USAGE, + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_HID_USAGE, }, };