Skip to content

Commit c2070cf

Browse files
committed
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.
1 parent 0c6686f commit c2070cf

17 files changed

+237
-19
lines changed

app/CMakeLists.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ target_sources(app PRIVATE src/events/sensor_event.c)
3939
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c)
4040
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c)
4141
target_sources_ifdef(CONFIG_USB app PRIVATE src/events/usb_conn_state_changed.c)
42+
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
43+
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
4244
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
4345
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
44-
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
4546
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
4647
target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c)
4748
target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c)
@@ -51,7 +52,6 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
5152
target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
5253
target_sources(app PRIVATE src/behaviors/behavior_none.c)
5354
target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c)
54-
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
5555
target_sources(app PRIVATE src/keymap.c)
5656
endif()
5757
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c)

app/dts/behaviors/reset.dtsi

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
bootloader: behavior_reset_dfu {
1818
compatible = "zmk,behavior-reset";
19-
label = "BOOTLOADER_RESET";
19+
label = "BOOTLOAD";
2020
type = <RST_UF2>;
2121
#binding-cells = <0>;
2222
};

app/dts/behaviors/rgb_underglow.dtsi

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
behaviors {
99
rgb_ug: behavior_rgb_underglow {
1010
compatible = "zmk,behavior-rgb-underglow";
11-
label = "RGB_UNDERGLOW";
11+
label = "RGB_UG";
1212
#binding-cells = <2>;
1313
};
1414
};

app/include/drivers/behavior.h

+47-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#include <zephyr/types.h>
1010
#include <stddef.h>
11+
#include <sys/util.h>
12+
#include <string.h>
1113
#include <device.h>
1214
#include <zmk/keys.h>
1315
#include <zmk/behavior.h>
@@ -26,7 +28,14 @@ typedef int (*behavior_sensor_keymap_binding_callback_t)(struct zmk_behavior_bin
2628
const struct device *sensor,
2729
int64_t timestamp);
2830

31+
enum behavior_locality {
32+
BEHAVIOR_LOCALITY_CENTRAL,
33+
BEHAVIOR_LOCALITY_EVENT_SOURCE,
34+
BEHAVIOR_LOCALITY_GLOBAL
35+
};
36+
2937
__subsystem struct behavior_driver_api {
38+
enum behavior_locality locality;
3039
behavior_keymap_binding_callback_t binding_pressed;
3140
behavior_keymap_binding_callback_t binding_released;
3241
behavior_sensor_keymap_binding_callback_t sensor_binding_triggered;
@@ -35,6 +44,28 @@ __subsystem struct behavior_driver_api {
3544
* @endcond
3645
*/
3746

47+
/**
48+
* @brief Determine where the behavior should be run
49+
* @param behavior Pointer to the device structure for the driver instance.
50+
*
51+
* @retval Zero if successful.
52+
* @retval Negative errno code if failure.
53+
*/
54+
__syscall int behavior_get_locality(const struct device *behavior,
55+
enum behavior_locality *locality);
56+
57+
static inline int z_impl_behavior_get_locality(const struct device *behavior,
58+
enum behavior_locality *locality) {
59+
if (behavior == NULL) {
60+
return -EINVAL;
61+
}
62+
63+
const struct behavior_driver_api *api = (const struct behavior_driver_api *)behavior->api;
64+
memcpy(locality, &api->locality, sizeof(enum behavior_locality));
65+
66+
return 0;
67+
}
68+
3869
/**
3970
* @brief Handle the keymap binding being pressed
4071
* @param dev Pointer to the device structure for the driver instance.
@@ -50,6 +81,11 @@ __syscall int behavior_keymap_binding_pressed(struct zmk_behavior_binding *bindi
5081
static inline int z_impl_behavior_keymap_binding_pressed(struct zmk_behavior_binding *binding,
5182
struct zmk_behavior_binding_event event) {
5283
const struct device *dev = device_get_binding(binding->behavior_dev);
84+
85+
if (dev == NULL) {
86+
return -EINVAL;
87+
}
88+
5389
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
5490

5591
if (api->binding_pressed == NULL) {
@@ -73,6 +109,11 @@ __syscall int behavior_keymap_binding_released(struct zmk_behavior_binding *bind
73109
static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_binding *binding,
74110
struct zmk_behavior_binding_event event) {
75111
const struct device *dev = device_get_binding(binding->behavior_dev);
112+
113+
if (dev == NULL) {
114+
return -EINVAL;
115+
}
116+
76117
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
77118

78119
if (api->binding_released == NULL) {
@@ -86,7 +127,7 @@ static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_bi
86127
* @brief Handle the a sensor keymap binding being triggered
87128
* @param dev Pointer to the device structure for the driver instance.
88129
* @param sensor Pointer to the sensor device structure for the sensor driver instance.
89-
* @param param1 User parameter specified at time of behavior binding.
130+
* @param param1 User parameter specified at time of behavior binding. diligence
90131
* @param param2 User parameter specified at time of behavior binding.
91132
*
92133
* @retval 0 If successful.
@@ -100,6 +141,11 @@ static inline int
100141
z_impl_behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *binding,
101142
const struct device *sensor, int64_t timestamp) {
102143
const struct device *dev = device_get_binding(binding->behavior_dev);
144+
145+
if (dev == NULL) {
146+
return -EINVAL;
147+
}
148+
103149
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
104150

105151
if (api->sensor_binding_triggered == NULL) {

app/include/zmk/behavior.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ struct zmk_behavior_binding_event {
1919
int layer;
2020
uint32_t position;
2121
int64_t timestamp;
22-
};
22+
};

app/include/zmk/events/position_state_changed.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,20 @@
88

99
#include <zephyr.h>
1010
#include <zmk/event_manager.h>
11+
#include <bluetooth/addr.h>
12+
13+
#if IS_ENABLED(CONFIG_ZMK_BLE)
14+
typedef const bt_addr_le_t *zmk_position_state_changed_source_t;
15+
#else
16+
typedef void *zmk_position_state_changed_source_t;
17+
#endif
1118

1219
struct position_state_changed {
1320
struct zmk_event_header header;
21+
zmk_position_state_changed_source_t source;
1422
uint32_t position;
1523
bool state;
1624
int64_t timestamp;
1725
};
1826

19-
ZMK_EVENT_DECLARE(position_state_changed);
27+
ZMK_EVENT_DECLARE(position_state_changed);

app/include/zmk/keymap.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#pragma once
88

9+
#include <zmk/events/position_state_changed.h>
10+
911
typedef uint32_t zmk_keymap_layers_state_t;
1012

1113
uint8_t zmk_keymap_layer_default();
@@ -18,4 +20,5 @@ int zmk_keymap_layer_toggle(uint8_t layer);
1820
int zmk_keymap_layer_to(uint8_t layer);
1921
const char *zmk_keymap_layer_label(uint8_t layer);
2022

21-
int zmk_keymap_position_state_changed(uint32_t position, bool pressed, int64_t timestamp);
23+
int zmk_keymap_position_state_changed(zmk_position_state_changed_source_t source, uint32_t position,
24+
bool pressed, int64_t timestamp);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
#pragma once
3+
4+
#include <bluetooth/addr.h>
5+
#include <zmk/behavior.h>
6+
7+
int zmk_split_bt_invoke_behavior(const bt_addr_le_t *source, struct zmk_behavior_binding *binding,
8+
struct zmk_behavior_binding_event event, bool state);

app/include/zmk/split/bluetooth/service.h

+14
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,19 @@
66

77
#pragma once
88

9+
#define ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN 9
10+
11+
struct zmk_split_run_behavior_data {
12+
uint8_t position;
13+
uint8_t state;
14+
uint32_t param1;
15+
uint32_t param2;
16+
} __packed;
17+
18+
struct zmk_split_run_behavior_payload {
19+
struct zmk_split_run_behavior_data data;
20+
char behavior_dev[ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN];
21+
} __packed;
22+
923
int zmk_split_bt_position_pressed(uint8_t position);
1024
int zmk_split_bt_position_released(uint8_t position);

app/include/zmk/split/bluetooth/uuid.h

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@
1515
#define ZMK_BT_SPLIT_UUID(num) BT_UUID_128_ENCODE(num, 0x0096, 0x7107, 0xc967, 0xc5cfb1c2482a)
1616
#define ZMK_SPLIT_BT_SERVICE_UUID ZMK_BT_SPLIT_UUID(0x00000000)
1717
#define ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID ZMK_BT_SPLIT_UUID(0x00000001)
18+
#define ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID ZMK_BT_SPLIT_UUID(0x00000002)

app/src/behaviors/behavior_ext_power.c

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ static int behavior_ext_power_init(const struct device *dev) { return 0; };
5151
static const struct behavior_driver_api behavior_ext_power_driver_api = {
5252
.binding_pressed = on_keymap_binding_pressed,
5353
.binding_released = on_keymap_binding_released,
54+
.locality = BEHAVIOR_LOCALITY_GLOBAL,
5455
};
5556

5657
DEVICE_AND_API_INIT(behavior_ext_power, DT_INST_LABEL(0), behavior_ext_power_init, NULL, NULL,

app/src/behaviors/behavior_reset.c

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
3535

3636
static const struct behavior_driver_api behavior_reset_driver_api = {
3737
.binding_pressed = on_keymap_binding_pressed,
38+
.locality = BEHAVIOR_LOCALITY_EVENT_SOURCE,
3839
};
3940

4041
#define RST_INST(n) \

app/src/behaviors/behavior_rgb_underglow.c

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
5959
static const struct behavior_driver_api behavior_rgb_underglow_driver_api = {
6060
.binding_pressed = on_keymap_binding_pressed,
6161
.binding_released = on_keymap_binding_released,
62+
.locality = BEHAVIOR_LOCALITY_GLOBAL,
6263
};
6364

6465
DEVICE_AND_API_INIT(behavior_rgb_underglow, DT_INST_LABEL(0), behavior_rgb_underglow_init, NULL,

app/src/keymap.c

+53-9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44
* SPDX-License-Identifier: MIT
55
*/
66

7+
#define IS_BLE_CENTRAL \
8+
(IS_ENABLED(CONFIG_ZMK_SPLIT) && IS_ENABLED(CONFIG_ZMK_BLE) && \
9+
IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL))
10+
711
#include <sys/util.h>
12+
#include <bluetooth/bluetooth.h>
813
#include <logging/log.h>
914
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
1015

@@ -14,6 +19,10 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
1419
#include <drivers/behavior.h>
1520
#include <zmk/behavior.h>
1621

22+
#if IS_BLE_CENTRAL
23+
#include <zmk/split/bluetooth/central.h>
24+
#endif
25+
1726
#include <zmk/event_manager.h>
1827
#include <zmk/events/position_state_changed.h>
1928
#include <zmk/events/layer_state_changed.h>
@@ -160,7 +169,17 @@ const char *zmk_keymap_layer_label(uint8_t layer) {
160169
return zmk_keymap_layer_names[layer];
161170
}
162171

163-
int zmk_keymap_apply_position_state(int layer, uint32_t position, bool pressed, int64_t timestamp) {
172+
int invoke_locally(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
173+
bool pressed) {
174+
if (pressed) {
175+
return behavior_keymap_binding_pressed(binding, event);
176+
} else {
177+
return behavior_keymap_binding_released(binding, event);
178+
}
179+
}
180+
181+
int zmk_keymap_apply_position_state(zmk_position_state_changed_source_t source, int layer,
182+
uint32_t position, bool pressed, int64_t timestamp) {
164183
struct zmk_behavior_binding *binding = &zmk_keymap[layer][position];
165184
const struct device *behavior;
166185
struct zmk_behavior_binding_event event = {
@@ -175,24 +194,48 @@ int zmk_keymap_apply_position_state(int layer, uint32_t position, bool pressed,
175194
behavior = device_get_binding(binding->behavior_dev);
176195

177196
if (!behavior) {
178-
LOG_DBG("No behavior assigned to %d on layer %d", position, layer);
197+
LOG_WRN("No behavior assigned to %d on layer %d", position, layer);
179198
return 1;
180199
}
181200

182-
if (pressed) {
183-
return behavior_keymap_binding_pressed(binding, event);
184-
} else {
185-
return behavior_keymap_binding_released(binding, event);
201+
enum behavior_locality locality = BEHAVIOR_LOCALITY_CENTRAL;
202+
int err = behavior_get_locality(behavior, &locality);
203+
if (err) {
204+
LOG_ERR("Failed to get behavior locality %d", err);
205+
return err;
186206
}
207+
208+
switch (locality) {
209+
case BEHAVIOR_LOCALITY_CENTRAL:
210+
return invoke_locally(binding, event, pressed);
211+
case BEHAVIOR_LOCALITY_EVENT_SOURCE:
212+
#if IS_BLE_CENTRAL
213+
if (!bt_addr_le_cmp(source, BT_ADDR_LE_NONE)) {
214+
return invoke_locally(binding, event, pressed);
215+
} else {
216+
return zmk_split_bt_invoke_behavior(source, binding, event, pressed);
217+
}
218+
#else
219+
return invoke_locally(binding, event, pressed);
220+
#endif
221+
case BEHAVIOR_LOCALITY_GLOBAL:
222+
#if IS_BLE_CENTRAL
223+
zmk_split_bt_invoke_behavior(BT_ADDR_LE_ANY, binding, event, pressed);
224+
#endif
225+
return invoke_locally(binding, event, pressed);
226+
}
227+
228+
return -ENOTSUP;
187229
}
188230

189-
int zmk_keymap_position_state_changed(uint32_t position, bool pressed, int64_t timestamp) {
231+
int zmk_keymap_position_state_changed(zmk_position_state_changed_source_t source, uint32_t position,
232+
bool pressed, int64_t timestamp) {
190233
if (pressed) {
191234
zmk_keymap_active_behavior_layer[position] = _zmk_keymap_layer_state;
192235
}
193236
for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) {
194237
if (zmk_keymap_layer_active_with_state(layer, zmk_keymap_active_behavior_layer[position])) {
195-
int ret = zmk_keymap_apply_position_state(layer, position, pressed, timestamp);
238+
int ret = zmk_keymap_apply_position_state(source, layer, position, pressed, timestamp);
196239
if (ret > 0) {
197240
LOG_DBG("behavior processing to continue to next layer");
198241
continue;
@@ -249,7 +292,8 @@ int zmk_keymap_sensor_triggered(uint8_t sensor_number, const struct device *sens
249292
int keymap_listener(const struct zmk_event_header *eh) {
250293
if (is_position_state_changed(eh)) {
251294
const struct position_state_changed *ev = cast_position_state_changed(eh);
252-
return zmk_keymap_position_state_changed(ev->position, ev->state, ev->timestamp);
295+
return zmk_keymap_position_state_changed(ev->source, ev->position, ev->state,
296+
ev->timestamp);
253297
#if ZMK_KEYMAP_HAS_SENSORS
254298
} else if (is_sensor_event(eh)) {
255299
const struct sensor_event *ev = cast_sensor_event(eh);

app/src/kscan.c

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <zephyr.h>
88
#include <device.h>
9+
#include <bluetooth/addr.h>
910
#include <drivers/kscan.h>
1011
#include <logging/log.h>
1112

@@ -51,6 +52,9 @@ void zmk_kscan_process_msgq(struct k_work *item) {
5152
LOG_DBG("Row: %d, col: %d, position: %d, pressed: %s\n", ev.row, ev.column, position,
5253
(pressed ? "true" : "false"));
5354
pos_ev = new_position_state_changed();
55+
#if IS_ENABLED(CONFIG_ZMK_BLE)
56+
pos_ev->source = BT_ADDR_LE_NONE;
57+
#endif
5458
pos_ev->state = pressed;
5559
pos_ev->position = position;
5660
pos_ev->timestamp = k_uptime_get();

0 commit comments

Comments
 (0)