feature(split): behavior locality support.
* GATT characteristic allowing passng data + behavior label to invoke the behavior on the peripheral side. * Behaviors have a locality setting to specify where they run. * Build reset/power/RGB on peripheral.
This commit is contained in:
parent
c74ae45e17
commit
47f873b038
17 changed files with 247 additions and 20 deletions
|
@ -42,9 +42,10 @@ target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c)
|
||||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c)
|
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c)
|
||||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c)
|
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c)
|
||||||
target_sources_ifdef(CONFIG_USB app PRIVATE src/events/usb_conn_state_changed.c)
|
target_sources_ifdef(CONFIG_USB app PRIVATE src/events/usb_conn_state_changed.c)
|
||||||
|
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
|
||||||
|
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
|
||||||
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
|
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
|
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
|
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c)
|
target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c)
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_caps_word.c)
|
target_sources(app PRIVATE src/behaviors/behavior_caps_word.c)
|
||||||
|
@ -57,7 +58,6 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
|
target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_none.c)
|
target_sources(app PRIVATE src/behaviors/behavior_none.c)
|
||||||
target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c)
|
target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c)
|
||||||
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
|
|
||||||
target_sources(app PRIVATE src/combo.c)
|
target_sources(app PRIVATE src/combo.c)
|
||||||
target_sources(app PRIVATE src/conditional_layer.c)
|
target_sources(app PRIVATE src/conditional_layer.c)
|
||||||
target_sources(app PRIVATE src/keymap.c)
|
target_sources(app PRIVATE src/keymap.c)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
behaviors {
|
behaviors {
|
||||||
/omit-if-no-ref/ ext_power: behavior_ext_power {
|
/omit-if-no-ref/ ext_power: behavior_ext_power {
|
||||||
compatible = "zmk,behavior-ext-power";
|
compatible = "zmk,behavior-ext-power";
|
||||||
label = "EXT_POWER_BEHAVIOR";
|
label = "EXTPOWER";
|
||||||
#binding-cells = <1>;
|
#binding-cells = <1>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
/omit-if-no-ref/ bootloader: behavior_reset_dfu {
|
/omit-if-no-ref/ bootloader: behavior_reset_dfu {
|
||||||
compatible = "zmk,behavior-reset";
|
compatible = "zmk,behavior-reset";
|
||||||
label = "BOOTLOADER_RESET";
|
label = "BOOTLOAD";
|
||||||
type = <RST_UF2>;
|
type = <RST_UF2>;
|
||||||
#binding-cells = <0>;
|
#binding-cells = <0>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
behaviors {
|
behaviors {
|
||||||
/omit-if-no-ref/ rgb_ug: behavior_rgb_underglow {
|
/omit-if-no-ref/ rgb_ug: behavior_rgb_underglow {
|
||||||
compatible = "zmk,behavior-rgb-underglow";
|
compatible = "zmk,behavior-rgb-underglow";
|
||||||
label = "RGB_UNDERGLOW";
|
label = "RGB_UG";
|
||||||
#binding-cells = <2>;
|
#binding-cells = <2>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include <zephyr/types.h>
|
#include <zephyr/types.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
#include <string.h>
|
||||||
#include <device.h>
|
#include <device.h>
|
||||||
#include <zmk/keys.h>
|
#include <zmk/keys.h>
|
||||||
#include <zmk/behavior.h>
|
#include <zmk/behavior.h>
|
||||||
|
@ -26,7 +28,14 @@ typedef int (*behavior_sensor_keymap_binding_callback_t)(struct zmk_behavior_bin
|
||||||
const struct device *sensor,
|
const struct device *sensor,
|
||||||
int64_t timestamp);
|
int64_t timestamp);
|
||||||
|
|
||||||
|
enum behavior_locality {
|
||||||
|
BEHAVIOR_LOCALITY_CENTRAL,
|
||||||
|
BEHAVIOR_LOCALITY_EVENT_SOURCE,
|
||||||
|
BEHAVIOR_LOCALITY_GLOBAL
|
||||||
|
};
|
||||||
|
|
||||||
__subsystem struct behavior_driver_api {
|
__subsystem struct behavior_driver_api {
|
||||||
|
enum behavior_locality locality;
|
||||||
behavior_keymap_binding_callback_t binding_convert_central_state_dependent_params;
|
behavior_keymap_binding_callback_t binding_convert_central_state_dependent_params;
|
||||||
behavior_keymap_binding_callback_t binding_pressed;
|
behavior_keymap_binding_callback_t binding_pressed;
|
||||||
behavior_keymap_binding_callback_t binding_released;
|
behavior_keymap_binding_callback_t binding_released;
|
||||||
|
@ -60,6 +69,28 @@ static inline int z_impl_behavior_keymap_binding_convert_central_state_dependent
|
||||||
return api->binding_convert_central_state_dependent_params(binding, event);
|
return api->binding_convert_central_state_dependent_params(binding, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Determine where the behavior should be run
|
||||||
|
* @param behavior Pointer to the device structure for the driver instance.
|
||||||
|
*
|
||||||
|
* @retval Zero if successful.
|
||||||
|
* @retval Negative errno code if failure.
|
||||||
|
*/
|
||||||
|
__syscall int behavior_get_locality(const struct device *behavior,
|
||||||
|
enum behavior_locality *locality);
|
||||||
|
|
||||||
|
static inline int z_impl_behavior_get_locality(const struct device *behavior,
|
||||||
|
enum behavior_locality *locality) {
|
||||||
|
if (behavior == NULL) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct behavior_driver_api *api = (const struct behavior_driver_api *)behavior->api;
|
||||||
|
*locality = api->locality;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handle the keymap binding being pressed
|
* @brief Handle the keymap binding being pressed
|
||||||
* @param dev Pointer to the device structure for the driver instance.
|
* @param dev Pointer to the device structure for the driver instance.
|
||||||
|
@ -75,6 +106,11 @@ __syscall int behavior_keymap_binding_pressed(struct zmk_behavior_binding *bindi
|
||||||
static inline int z_impl_behavior_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
static inline int z_impl_behavior_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||||
struct zmk_behavior_binding_event event) {
|
struct zmk_behavior_binding_event event) {
|
||||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||||
|
|
||||||
|
if (dev == NULL) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
|
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
|
||||||
|
|
||||||
if (api->binding_pressed == NULL) {
|
if (api->binding_pressed == NULL) {
|
||||||
|
@ -98,6 +134,11 @@ __syscall int behavior_keymap_binding_released(struct zmk_behavior_binding *bind
|
||||||
static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_binding *binding,
|
static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_binding *binding,
|
||||||
struct zmk_behavior_binding_event event) {
|
struct zmk_behavior_binding_event event) {
|
||||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||||
|
|
||||||
|
if (dev == NULL) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
|
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
|
||||||
|
|
||||||
if (api->binding_released == NULL) {
|
if (api->binding_released == NULL) {
|
||||||
|
@ -125,6 +166,11 @@ static inline int
|
||||||
z_impl_behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *binding,
|
z_impl_behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *binding,
|
||||||
const struct device *sensor, int64_t timestamp) {
|
const struct device *sensor, int64_t timestamp) {
|
||||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||||
|
|
||||||
|
if (dev == NULL) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
|
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
|
||||||
|
|
||||||
if (api->sensor_binding_triggered == NULL) {
|
if (api->sensor_binding_triggered == NULL) {
|
||||||
|
|
|
@ -8,7 +8,16 @@
|
||||||
|
|
||||||
#include <zephyr.h>
|
#include <zephyr.h>
|
||||||
#include <zmk/event_manager.h>
|
#include <zmk/event_manager.h>
|
||||||
|
#include <bluetooth/addr.h>
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_ZMK_BLE)
|
||||||
|
typedef const bt_addr_le_t *zmk_position_state_changed_source_t;
|
||||||
|
#else
|
||||||
|
typedef void *zmk_position_state_changed_source_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct zmk_position_state_changed {
|
struct zmk_position_state_changed {
|
||||||
|
zmk_position_state_changed_source_t source;
|
||||||
uint32_t position;
|
uint32_t position;
|
||||||
bool state;
|
bool state;
|
||||||
int64_t timestamp;
|
int64_t timestamp;
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <zmk/events/position_state_changed.h>
|
||||||
|
|
||||||
typedef uint32_t zmk_keymap_layers_state_t;
|
typedef uint32_t zmk_keymap_layers_state_t;
|
||||||
|
|
||||||
uint8_t zmk_keymap_layer_default();
|
uint8_t zmk_keymap_layer_default();
|
||||||
|
@ -18,7 +20,8 @@ int zmk_keymap_layer_toggle(uint8_t layer);
|
||||||
int zmk_keymap_layer_to(uint8_t layer);
|
int zmk_keymap_layer_to(uint8_t layer);
|
||||||
const char *zmk_keymap_layer_label(uint8_t layer);
|
const char *zmk_keymap_layer_label(uint8_t layer);
|
||||||
|
|
||||||
int zmk_keymap_position_state_changed(uint32_t position, bool pressed, int64_t timestamp);
|
int zmk_keymap_position_state_changed(zmk_position_state_changed_source_t source, uint32_t position,
|
||||||
|
bool pressed, int64_t timestamp);
|
||||||
|
|
||||||
#define ZMK_KEYMAP_EXTRACT_BINDING(idx, drv_inst) \
|
#define ZMK_KEYMAP_EXTRACT_BINDING(idx, drv_inst) \
|
||||||
{ \
|
{ \
|
||||||
|
|
8
app/include/zmk/split/bluetooth/central.h
Normal file
8
app/include/zmk/split/bluetooth/central.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <bluetooth/addr.h>
|
||||||
|
#include <zmk/behavior.h>
|
||||||
|
|
||||||
|
int zmk_split_bt_invoke_behavior(const bt_addr_le_t *source, struct zmk_behavior_binding *binding,
|
||||||
|
struct zmk_behavior_binding_event event, bool state);
|
|
@ -6,5 +6,19 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN 9
|
||||||
|
|
||||||
|
struct zmk_split_run_behavior_data {
|
||||||
|
uint8_t position;
|
||||||
|
uint8_t state;
|
||||||
|
uint32_t param1;
|
||||||
|
uint32_t param2;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct zmk_split_run_behavior_payload {
|
||||||
|
struct zmk_split_run_behavior_data data;
|
||||||
|
char behavior_dev[ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
int zmk_split_bt_position_pressed(uint8_t position);
|
int zmk_split_bt_position_pressed(uint8_t position);
|
||||||
int zmk_split_bt_position_released(uint8_t position);
|
int zmk_split_bt_position_released(uint8_t position);
|
|
@ -15,3 +15,4 @@
|
||||||
#define ZMK_BT_SPLIT_UUID(num) BT_UUID_128_ENCODE(num, 0x0096, 0x7107, 0xc967, 0xc5cfb1c2482a)
|
#define ZMK_BT_SPLIT_UUID(num) BT_UUID_128_ENCODE(num, 0x0096, 0x7107, 0xc967, 0xc5cfb1c2482a)
|
||||||
#define ZMK_SPLIT_BT_SERVICE_UUID ZMK_BT_SPLIT_UUID(0x00000000)
|
#define ZMK_SPLIT_BT_SERVICE_UUID ZMK_BT_SPLIT_UUID(0x00000000)
|
||||||
#define ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID ZMK_BT_SPLIT_UUID(0x00000001)
|
#define ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID ZMK_BT_SPLIT_UUID(0x00000001)
|
||||||
|
#define ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID ZMK_BT_SPLIT_UUID(0x00000002)
|
||||||
|
|
|
@ -71,6 +71,7 @@ static const struct behavior_driver_api behavior_ext_power_driver_api = {
|
||||||
on_keymap_binding_convert_central_state_dependent_params,
|
on_keymap_binding_convert_central_state_dependent_params,
|
||||||
.binding_pressed = on_keymap_binding_pressed,
|
.binding_pressed = on_keymap_binding_pressed,
|
||||||
.binding_released = on_keymap_binding_released,
|
.binding_released = on_keymap_binding_released,
|
||||||
|
.locality = BEHAVIOR_LOCALITY_GLOBAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
DEVICE_DT_INST_DEFINE(0, behavior_ext_power_init, device_pm_control_nop, NULL, NULL, APPLICATION,
|
DEVICE_DT_INST_DEFINE(0, behavior_ext_power_init, device_pm_control_nop, NULL, NULL, APPLICATION,
|
||||||
|
|
|
@ -36,6 +36,7 @@ static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||||
|
|
||||||
static const struct behavior_driver_api behavior_reset_driver_api = {
|
static const struct behavior_driver_api behavior_reset_driver_api = {
|
||||||
.binding_pressed = on_keymap_binding_pressed,
|
.binding_pressed = on_keymap_binding_pressed,
|
||||||
|
.locality = BEHAVIOR_LOCALITY_EVENT_SOURCE,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define RST_INST(n) \
|
#define RST_INST(n) \
|
||||||
|
|
|
@ -134,6 +134,7 @@ static const struct behavior_driver_api behavior_rgb_underglow_driver_api = {
|
||||||
on_keymap_binding_convert_central_state_dependent_params,
|
on_keymap_binding_convert_central_state_dependent_params,
|
||||||
.binding_pressed = on_keymap_binding_pressed,
|
.binding_pressed = on_keymap_binding_pressed,
|
||||||
.binding_released = on_keymap_binding_released,
|
.binding_released = on_keymap_binding_released,
|
||||||
|
.locality = BEHAVIOR_LOCALITY_GLOBAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
DEVICE_DT_INST_DEFINE(0, behavior_rgb_underglow_init, device_pm_control_nop, NULL, NULL,
|
DEVICE_DT_INST_DEFINE(0, behavior_rgb_underglow_init, device_pm_control_nop, NULL, NULL,
|
||||||
|
|
|
@ -4,7 +4,12 @@
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define IS_BLE_CENTRAL \
|
||||||
|
(IS_ENABLED(CONFIG_ZMK_SPLIT) && IS_ENABLED(CONFIG_ZMK_BLE) && \
|
||||||
|
IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL))
|
||||||
|
|
||||||
#include <sys/util.h>
|
#include <sys/util.h>
|
||||||
|
#include <bluetooth/bluetooth.h>
|
||||||
#include <logging/log.h>
|
#include <logging/log.h>
|
||||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
|
@ -14,6 +19,10 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
#include <drivers/behavior.h>
|
#include <drivers/behavior.h>
|
||||||
#include <zmk/behavior.h>
|
#include <zmk/behavior.h>
|
||||||
|
|
||||||
|
#if IS_BLE_CENTRAL
|
||||||
|
#include <zmk/split/bluetooth/central.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <zmk/event_manager.h>
|
#include <zmk/event_manager.h>
|
||||||
#include <zmk/events/position_state_changed.h>
|
#include <zmk/events/position_state_changed.h>
|
||||||
#include <zmk/events/layer_state_changed.h>
|
#include <zmk/events/layer_state_changed.h>
|
||||||
|
@ -152,7 +161,17 @@ const char *zmk_keymap_layer_label(uint8_t layer) {
|
||||||
return zmk_keymap_layer_names[layer];
|
return zmk_keymap_layer_names[layer];
|
||||||
}
|
}
|
||||||
|
|
||||||
int zmk_keymap_apply_position_state(int layer, uint32_t position, bool pressed, int64_t timestamp) {
|
int invoke_locally(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
|
||||||
|
bool pressed) {
|
||||||
|
if (pressed) {
|
||||||
|
return behavior_keymap_binding_pressed(binding, event);
|
||||||
|
} else {
|
||||||
|
return behavior_keymap_binding_released(binding, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int zmk_keymap_apply_position_state(zmk_position_state_changed_source_t source, int layer,
|
||||||
|
uint32_t position, bool pressed, int64_t timestamp) {
|
||||||
// We want to make a copy of this, since it may be converted from
|
// We want to make a copy of this, since it may be converted from
|
||||||
// relative to absolute before being invoked
|
// relative to absolute before being invoked
|
||||||
struct zmk_behavior_binding binding = zmk_keymap[layer][position];
|
struct zmk_behavior_binding binding = zmk_keymap[layer][position];
|
||||||
|
@ -169,7 +188,7 @@ int zmk_keymap_apply_position_state(int layer, uint32_t position, bool pressed,
|
||||||
behavior = device_get_binding(binding.behavior_dev);
|
behavior = device_get_binding(binding.behavior_dev);
|
||||||
|
|
||||||
if (!behavior) {
|
if (!behavior) {
|
||||||
LOG_DBG("No behavior assigned to %d on layer %d", position, layer);
|
LOG_WRN("No behavior assigned to %d on layer %d", position, layer);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,20 +198,44 @@ int zmk_keymap_apply_position_state(int layer, uint32_t position, bool pressed,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pressed) {
|
enum behavior_locality locality = BEHAVIOR_LOCALITY_CENTRAL;
|
||||||
return behavior_keymap_binding_pressed(&binding, event);
|
err = behavior_get_locality(behavior, &locality);
|
||||||
} else {
|
if (err) {
|
||||||
return behavior_keymap_binding_released(&binding, event);
|
LOG_ERR("Failed to get behavior locality %d", err);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (locality) {
|
||||||
|
case BEHAVIOR_LOCALITY_CENTRAL:
|
||||||
|
return invoke_locally(&binding, event, pressed);
|
||||||
|
case BEHAVIOR_LOCALITY_EVENT_SOURCE:
|
||||||
|
#if IS_BLE_CENTRAL
|
||||||
|
if (!bt_addr_le_cmp(source, BT_ADDR_LE_NONE)) {
|
||||||
|
return invoke_locally(&binding, event, pressed);
|
||||||
|
} else {
|
||||||
|
return zmk_split_bt_invoke_behavior(source, &binding, event, pressed);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return invoke_locally(&binding, event, pressed);
|
||||||
|
#endif
|
||||||
|
case BEHAVIOR_LOCALITY_GLOBAL:
|
||||||
|
#if IS_BLE_CENTRAL
|
||||||
|
zmk_split_bt_invoke_behavior(BT_ADDR_LE_ANY, &binding, event, pressed);
|
||||||
|
#endif
|
||||||
|
return invoke_locally(&binding, event, pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
int zmk_keymap_position_state_changed(uint32_t position, bool pressed, int64_t timestamp) {
|
int zmk_keymap_position_state_changed(zmk_position_state_changed_source_t source, uint32_t position,
|
||||||
|
bool pressed, int64_t timestamp) {
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
zmk_keymap_active_behavior_layer[position] = _zmk_keymap_layer_state;
|
zmk_keymap_active_behavior_layer[position] = _zmk_keymap_layer_state;
|
||||||
}
|
}
|
||||||
for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) {
|
for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) {
|
||||||
if (zmk_keymap_layer_active_with_state(layer, zmk_keymap_active_behavior_layer[position])) {
|
if (zmk_keymap_layer_active_with_state(layer, zmk_keymap_active_behavior_layer[position])) {
|
||||||
int ret = zmk_keymap_apply_position_state(layer, position, pressed, timestamp);
|
int ret = zmk_keymap_apply_position_state(source, layer, position, pressed, timestamp);
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
LOG_DBG("behavior processing to continue to next layer");
|
LOG_DBG("behavior processing to continue to next layer");
|
||||||
continue;
|
continue;
|
||||||
|
@ -249,7 +292,7 @@ int zmk_keymap_sensor_triggered(uint8_t sensor_number, const struct device *sens
|
||||||
int keymap_listener(const zmk_event_t *eh) {
|
int keymap_listener(const zmk_event_t *eh) {
|
||||||
const struct zmk_position_state_changed *pos_ev;
|
const struct zmk_position_state_changed *pos_ev;
|
||||||
if ((pos_ev = as_zmk_position_state_changed(eh)) != NULL) {
|
if ((pos_ev = as_zmk_position_state_changed(eh)) != NULL) {
|
||||||
return zmk_keymap_position_state_changed(pos_ev->position, pos_ev->state,
|
return zmk_keymap_position_state_changed(pos_ev->source, pos_ev->position, pos_ev->state,
|
||||||
pos_ev->timestamp);
|
pos_ev->timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <zephyr.h>
|
#include <zephyr.h>
|
||||||
#include <device.h>
|
#include <device.h>
|
||||||
|
#include <bluetooth/addr.h>
|
||||||
#include <drivers/kscan.h>
|
#include <drivers/kscan.h>
|
||||||
#include <logging/log.h>
|
#include <logging/log.h>
|
||||||
|
|
||||||
|
@ -49,8 +50,12 @@ void zmk_kscan_process_msgq(struct k_work *item) {
|
||||||
uint32_t position = zmk_matrix_transform_row_column_to_position(ev.row, ev.column);
|
uint32_t position = zmk_matrix_transform_row_column_to_position(ev.row, ev.column);
|
||||||
LOG_DBG("Row: %d, col: %d, position: %d, pressed: %s", ev.row, ev.column, position,
|
LOG_DBG("Row: %d, col: %d, position: %d, pressed: %s", ev.row, ev.column, position,
|
||||||
(pressed ? "true" : "false"));
|
(pressed ? "true" : "false"));
|
||||||
ZMK_EVENT_RAISE(new_zmk_position_state_changed((struct zmk_position_state_changed){
|
ZMK_EVENT_RAISE(new_zmk_position_state_changed((struct zmk_position_state_changed) {
|
||||||
.state = pressed, .position = position, .timestamp = k_uptime_get()}));
|
#if IS_ENABLED(CONFIG_ZMK_BLE)
|
||||||
|
.source = BT_ADDR_LE_NONE,
|
||||||
|
#endif
|
||||||
|
.state = pressed, .position = position, .timestamp = k_uptime_get()
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
#include <zmk/ble.h>
|
#include <zmk/ble.h>
|
||||||
|
#include <zmk/behavior.h>
|
||||||
#include <zmk/split/bluetooth/uuid.h>
|
#include <zmk/split/bluetooth/uuid.h>
|
||||||
|
#include <zmk/split/bluetooth/service.h>
|
||||||
#include <zmk/event_manager.h>
|
#include <zmk/event_manager.h>
|
||||||
#include <zmk/events/position_state_changed.h>
|
#include <zmk/events/position_state_changed.h>
|
||||||
#include <init.h>
|
#include <init.h>
|
||||||
|
@ -33,6 +35,7 @@ static const struct bt_uuid_128 split_service_uuid = BT_UUID_INIT_128(ZMK_SPLIT_
|
||||||
static struct bt_gatt_discover_params discover_params;
|
static struct bt_gatt_discover_params discover_params;
|
||||||
static struct bt_gatt_subscribe_params subscribe_params;
|
static struct bt_gatt_subscribe_params subscribe_params;
|
||||||
static struct bt_gatt_discover_params sub_discover_params;
|
static struct bt_gatt_discover_params sub_discover_params;
|
||||||
|
static uint16_t run_behavior_handle;
|
||||||
|
|
||||||
K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed),
|
K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed),
|
||||||
CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE, 4);
|
CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE, 4);
|
||||||
|
@ -72,8 +75,10 @@ static uint8_t split_central_notify_func(struct bt_conn *conn,
|
||||||
if (changed_positions[i] & BIT(j)) {
|
if (changed_positions[i] & BIT(j)) {
|
||||||
uint32_t position = (i * 8) + j;
|
uint32_t position = (i * 8) + j;
|
||||||
bool pressed = position_state[i] & BIT(j);
|
bool pressed = position_state[i] & BIT(j);
|
||||||
struct zmk_position_state_changed ev = {
|
struct zmk_position_state_changed ev = {.source = bt_conn_get_dst(conn),
|
||||||
.position = position, .state = pressed, .timestamp = k_uptime_get()};
|
.position = position,
|
||||||
|
.state = pressed,
|
||||||
|
.timestamp = k_uptime_get()};
|
||||||
|
|
||||||
k_msgq_put(&peripheral_event_msgq, &ev, K_NO_WAIT);
|
k_msgq_put(&peripheral_event_msgq, &ev, K_NO_WAIT);
|
||||||
k_work_submit(&peripheral_event_work);
|
k_work_submit(&peripheral_event_work);
|
||||||
|
@ -124,9 +129,28 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
|
||||||
subscribe_params.disc_params = &sub_discover_params;
|
subscribe_params.disc_params = &sub_discover_params;
|
||||||
subscribe_params.end_handle = discover_params.end_handle;
|
subscribe_params.end_handle = discover_params.end_handle;
|
||||||
subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||||
|
|
||||||
|
err = bt_gatt_discover(conn, &discover_params);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Discover failed (err %d)", err);
|
||||||
|
}
|
||||||
|
} else if (!bt_uuid_cmp(discover_params.uuid,
|
||||||
|
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID))) {
|
||||||
|
run_behavior_handle = bt_gatt_attr_value_handle(attr);
|
||||||
|
} else {
|
||||||
subscribe_params.notify = split_central_notify_func;
|
subscribe_params.notify = split_central_notify_func;
|
||||||
subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||||
split_central_subscribe(conn);
|
split_central_subscribe(conn);
|
||||||
|
|
||||||
|
memcpy(&uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID), sizeof(uuid));
|
||||||
|
discover_params.uuid = &uuid.uuid;
|
||||||
|
discover_params.start_handle = attr->handle + 1;
|
||||||
|
discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||||
|
|
||||||
|
err = bt_gatt_discover(conn, &discover_params);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Discover failed (err %d)", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return subscribe_params.value_handle ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE;
|
return subscribe_params.value_handle ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE;
|
||||||
|
@ -340,6 +364,27 @@ static struct bt_conn_cb conn_callbacks = {
|
||||||
.disconnected = split_central_disconnected,
|
.disconnected = split_central_disconnected,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int zmk_split_bt_invoke_behavior(const bt_addr_le_t *source, struct zmk_behavior_binding *binding,
|
||||||
|
struct zmk_behavior_binding_event event, bool state) {
|
||||||
|
struct zmk_split_run_behavior_payload payload = {.data = {
|
||||||
|
.param1 = binding->param1,
|
||||||
|
.param2 = binding->param2,
|
||||||
|
.position = event.position,
|
||||||
|
.state = state,
|
||||||
|
}};
|
||||||
|
strncpy(payload.behavior_dev, binding->behavior_dev, ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN - 1);
|
||||||
|
payload.behavior_dev[ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN - 1] = '\0';
|
||||||
|
|
||||||
|
int err = bt_gatt_write_without_response(default_conn, run_behavior_handle, &payload,
|
||||||
|
sizeof(struct zmk_split_run_behavior_payload), true);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Failed to write the behavior characteristic (err %d)", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
|
||||||
int zmk_split_bt_central_init(const struct device *_arg) {
|
int zmk_split_bt_central_init(const struct device *_arg) {
|
||||||
bt_conn_cb_register(&conn_callbacks);
|
bt_conn_cb_register(&conn_callbacks);
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
#include <bluetooth/gatt.h>
|
#include <bluetooth/gatt.h>
|
||||||
#include <bluetooth/uuid.h>
|
#include <bluetooth/uuid.h>
|
||||||
|
|
||||||
|
#include <drivers/behavior.h>
|
||||||
|
#include <zmk/behavior.h>
|
||||||
#include <zmk/matrix.h>
|
#include <zmk/matrix.h>
|
||||||
#include <zmk/split/bluetooth/uuid.h>
|
#include <zmk/split/bluetooth/uuid.h>
|
||||||
#include <zmk/split/bluetooth/service.h>
|
#include <zmk/split/bluetooth/service.h>
|
||||||
|
@ -24,12 +26,57 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
static uint8_t num_of_positions = ZMK_KEYMAP_LEN;
|
static uint8_t num_of_positions = ZMK_KEYMAP_LEN;
|
||||||
static uint8_t position_state[POS_STATE_LEN];
|
static uint8_t position_state[POS_STATE_LEN];
|
||||||
|
|
||||||
|
static struct zmk_split_run_behavior_payload behavior_run_payload;
|
||||||
|
|
||||||
static ssize_t split_svc_pos_state(struct bt_conn *conn, const struct bt_gatt_attr *attrs,
|
static ssize_t split_svc_pos_state(struct bt_conn *conn, const struct bt_gatt_attr *attrs,
|
||||||
void *buf, uint16_t len, uint16_t offset) {
|
void *buf, uint16_t len, uint16_t offset) {
|
||||||
return bt_gatt_attr_read(conn, attrs, buf, len, offset, &position_state,
|
return bt_gatt_attr_read(conn, attrs, buf, len, offset, &position_state,
|
||||||
sizeof(position_state));
|
sizeof(position_state));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t split_svc_run_behavior(struct bt_conn *conn, const struct bt_gatt_attr *attrs,
|
||||||
|
const void *buf, uint16_t len, uint16_t offset,
|
||||||
|
uint8_t flags) {
|
||||||
|
struct zmk_split_run_behavior_payload *payload = attrs->user_data;
|
||||||
|
uint16_t end_addr = offset + len;
|
||||||
|
|
||||||
|
LOG_DBG("offset %d len %d", offset, len);
|
||||||
|
|
||||||
|
if (end_addr > sizeof(struct zmk_split_run_behavior_payload)) {
|
||||||
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(payload + offset, buf, len);
|
||||||
|
|
||||||
|
// We run if:
|
||||||
|
// 1: We've gotten all the position/state/param data.
|
||||||
|
// 2: We have a null terminated string for the behavior device label.
|
||||||
|
if ((end_addr > sizeof(struct zmk_split_run_behavior_data)) &&
|
||||||
|
payload->behavior_dev[end_addr - sizeof(struct zmk_split_run_behavior_data)] == '\0') {
|
||||||
|
struct zmk_behavior_binding binding = {
|
||||||
|
.param1 = payload->data.param1,
|
||||||
|
.param2 = payload->data.param2,
|
||||||
|
.behavior_dev = payload->behavior_dev,
|
||||||
|
};
|
||||||
|
LOG_DBG("INVOKE THE BEHAVIOR: %s with params %d %d", log_strdup(binding.behavior_dev),
|
||||||
|
binding.param1, binding.param2);
|
||||||
|
struct zmk_behavior_binding_event event = {.position = payload->data.position,
|
||||||
|
.timestamp = k_uptime_get()};
|
||||||
|
int err;
|
||||||
|
if (payload->data.state > 0) {
|
||||||
|
err = behavior_keymap_binding_pressed(&binding, event);
|
||||||
|
} else {
|
||||||
|
err = behavior_keymap_binding_released(&binding, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Failed to invoke behavior %s: %d", log_strdup(binding.behavior_dev), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t split_svc_num_of_positions(struct bt_conn *conn, const struct bt_gatt_attr *attrs,
|
static ssize_t split_svc_num_of_positions(struct bt_conn *conn, const struct bt_gatt_attr *attrs,
|
||||||
void *buf, uint16_t len, uint16_t offset) {
|
void *buf, uint16_t len, uint16_t offset) {
|
||||||
return bt_gatt_attr_read(conn, attrs, buf, len, offset, attrs->user_data, sizeof(uint8_t));
|
return bt_gatt_attr_read(conn, attrs, buf, len, offset, attrs->user_data, sizeof(uint8_t));
|
||||||
|
@ -45,6 +92,9 @@ BT_GATT_SERVICE_DEFINE(
|
||||||
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ_ENCRYPT,
|
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ_ENCRYPT,
|
||||||
split_svc_pos_state, NULL, &position_state),
|
split_svc_pos_state, NULL, &position_state),
|
||||||
BT_GATT_CCC(split_svc_pos_state_ccc, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
|
BT_GATT_CCC(split_svc_pos_state_ccc, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
|
||||||
|
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID),
|
||||||
|
BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL,
|
||||||
|
split_svc_run_behavior, &behavior_run_payload),
|
||||||
BT_GATT_DESCRIPTOR(BT_UUID_NUM_OF_DIGITALS, BT_GATT_PERM_READ, split_svc_num_of_positions, NULL,
|
BT_GATT_DESCRIPTOR(BT_UUID_NUM_OF_DIGITALS, BT_GATT_PERM_READ, split_svc_num_of_positions, NULL,
|
||||||
&num_of_positions), );
|
&num_of_positions), );
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue