feat(split): Send layer state to peripherals

This commit is contained in:
darknao 2026-01-15 20:42:55 +01:00
parent 24487bd974
commit 9813105d1d
No known key found for this signature in database
14 changed files with 167 additions and 4 deletions

View File

@ -97,6 +97,7 @@ target_sources_ifdef(CONFIG_ZMK_BATTERY_REPORTING app PRIVATE src/battery.c)
target_sources_ifdef(CONFIG_ZMK_HID_INDICATORS app PRIVATE src/events/hid_indicators_changed.c)
target_sources_ifdef(CONFIG_ZMK_SPLIT app PRIVATE src/events/split_peripheral_status_changed.c)
target_sources_ifdef(CONFIG_ZMK_SPLIT app PRIVATE src/events/split_peripheral_layer_changed.c)
add_subdirectory_ifdef(CONFIG_ZMK_SPLIT src/split)
target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/usb.c)

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zephyr/kernel.h>
#include <zmk/event_manager.h>
struct zmk_split_peripheral_layer_changed {
uint32_t layers;
};
ZMK_EVENT_DECLARE(zmk_split_peripheral_layer_changed);

View File

@ -20,3 +20,4 @@
#define ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID ZMK_BT_SPLIT_UUID(0x00000004)
#define ZMK_SPLIT_BT_SELECT_PHYS_LAYOUT_UUID ZMK_BT_SPLIT_UUID(0x00000005)
#define ZMK_SPLIT_BT_INPUT_EVENT_UUID ZMK_BT_SPLIT_UUID(0x00000006)
#define ZMK_SPLIT_BT_UPDATE_LAYERS_UUID ZMK_BT_SPLIT_UUID(0x00000007)

View File

@ -46,3 +46,5 @@ int zmk_split_central_update_hid_indicator(zmk_hid_indicators_t indicators);
int zmk_split_central_get_peripheral_battery_level(uint8_t source, uint8_t *level);
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
int zmk_split_central_update_layers(uint32_t layers);

View File

@ -0,0 +1,5 @@
#pragma once
void set_peripheral_layers_state(uint32_t new_layers);
bool peripheral_layer_active(uint8_t layer);
uint8_t peripheral_highest_layer_active(void);

View File

@ -66,6 +66,7 @@ enum zmk_split_transport_central_command_type {
ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_INVOKE_BEHAVIOR,
ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_PHYSICAL_LAYOUT,
ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_HID_INDICATORS,
ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_RGB_LAYERS,
} __packed;
struct zmk_split_transport_central_command {
@ -87,5 +88,9 @@ struct zmk_split_transport_central_command {
struct {
zmk_hid_indicators_t indicators;
} set_hid_indicators;
struct {
uint32_t layers;
} set_rgb_layers;
} data;
} __packed;

View File

@ -0,0 +1,10 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/kernel.h>
#include <zmk/events/split_peripheral_layer_changed.h>
ZMK_EVENT_IMPL(zmk_split_peripheral_layer_changed);

View File

@ -14,5 +14,6 @@ if (CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
zephyr_linker_sources(SECTIONS ../../include/linker/zmk-split-transport-central.ld)
else()
target_sources(app PRIVATE peripheral.c)
target_sources(app PRIVATE peripheral_layers.c)
zephyr_linker_sources(SECTIONS ../../include/linker/zmk-split-transport-peripheral.ld)
endif()

View File

@ -33,6 +33,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/pointing/input_split.h>
#include <zmk/hid_indicators_types.h>
#include <zmk/physical_layouts.h>
#include <zmk/events/split_peripheral_layer_changed.h>
static int start_scanning(void);
@ -60,6 +61,8 @@ struct peripheral_slot {
uint16_t update_hid_indicators;
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
uint16_t selected_physical_layout_handle;
uint16_t update_layers_handle;
uint8_t position_state[POSITION_STATE_DATA_LEN];
uint8_t changed_positions[POSITION_STATE_DATA_LEN];
};
@ -219,6 +222,7 @@ int release_peripheral_slot(int index) {
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
slot->update_hid_indicators = 0;
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
slot->update_layers_handle = 0;
return 0;
}
@ -620,6 +624,10 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
LOG_DBG("Found update HID indicators handle");
slot->update_hid_indicators = bt_gatt_attr_value_handle(attr);
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_LAYERS_UUID))) {
LOG_DBG("Found update Layers handle");
slot->update_layers_handle = bt_gatt_attr_value_handle(attr);
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
BT_UUID_BAS_BATTERY_LEVEL)) {
@ -707,6 +715,8 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
}
#endif // IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
subscribed = subscribed && slot->update_layers_handle;
return subscribed ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE;
}
@ -1024,6 +1034,7 @@ K_MSGQ_DEFINE(zmk_split_central_split_run_msgq, sizeof(struct central_cmd_wrappe
void split_central_split_run_callback(struct k_work *work) {
struct central_cmd_wrapper payload_wrapper;
int err;
LOG_DBG("");
@ -1056,7 +1067,7 @@ void split_central_split_run_callback(struct k_work *work) {
payload.behavior_dev);
}
int err = bt_gatt_write_without_response(
err = bt_gatt_write_without_response(
peripherals[payload_wrapper.source].conn,
peripherals[payload_wrapper.source].run_behavior_handle, &payload,
sizeof(struct zmk_split_run_behavior_payload), true);
@ -1082,7 +1093,7 @@ void split_central_split_run_callback(struct k_work *work) {
break;
}
int err = bt_gatt_write_without_response(
err = bt_gatt_write_without_response(
peripherals[payload_wrapper.source].conn,
peripherals[payload_wrapper.source].update_hid_indicators,
&payload_wrapper.cmd.data.set_hid_indicators.indicators,
@ -1093,6 +1104,18 @@ void split_central_split_run_callback(struct k_work *work) {
}
break;
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
case ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_RGB_LAYERS:
err = bt_gatt_write_without_response(
peripherals[payload_wrapper.source].conn,
peripherals[payload_wrapper.source].update_layers_handle,
&payload_wrapper.cmd.data.set_rgb_layers.layers,
sizeof(payload_wrapper.cmd.data.set_rgb_layers.layers), true);
if (err) {
LOG_ERR("Failed to send layers to peripheral (err %d)", err);
}
break;
default:
LOG_WRN("Unsupported wrapped central command type %d", payload_wrapper.cmd.type);
return;
@ -1174,6 +1197,7 @@ static int split_central_bt_send_command(uint8_t source,
}
switch (cmd.type) {
case ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_RGB_LAYERS:
case ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_HID_INDICATORS:
case ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_PHYSICAL_LAYOUT:
case ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_INVOKE_BEHAVIOR: {

View File

@ -31,9 +31,11 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
#include <zmk/events/hid_indicators_changed.h>
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
#include <zmk/split/peripheral_layers.h>
#include <zmk/events/sensor_event.h>
#include <zmk/sensors.h>
#include <zmk/events/split_peripheral_layer_changed.h>
#if ZMK_KEYMAP_HAS_SENSORS
static struct sensor_event last_sensor_event;
@ -139,6 +141,31 @@ static ssize_t split_svc_get_selected_phys_layout(struct bt_conn *conn,
return bt_gatt_attr_read(conn, attrs, buf, len, offset, &selected, sizeof(selected));
}
static uint32_t layers = 0;
static void split_svc_update_layers_callback(struct k_work *work) {
LOG_DBG("Setting peripheral layers: %x", layers);
// set_peripheral_layers_state(layers);
raise_zmk_split_peripheral_layer_changed(
(struct zmk_split_peripheral_layer_changed){.layers = layers});
}
static K_WORK_DEFINE(split_svc_update_layers_work, split_svc_update_layers_callback);
static ssize_t split_svc_update_layers(struct bt_conn *conn, const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset,
uint8_t flags) {
if (offset + len > sizeof(uint32_t)) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
memcpy((uint8_t *)&layers + offset, buf, len);
k_work_submit(&split_svc_update_layers_work);
return len;
}
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
static void split_input_events_ccc(const struct bt_gatt_attr *attr, uint16_t value) {
@ -204,8 +231,11 @@ BT_GATT_SERVICE_DEFINE(
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_SELECT_PHYS_LAYOUT_UUID),
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_READ,
BT_GATT_PERM_WRITE_ENCRYPT | BT_GATT_PERM_READ_ENCRYPT,
split_svc_get_selected_phys_layout, split_svc_select_phys_layout,
NULL), );
split_svc_get_selected_phys_layout, split_svc_select_phys_layout, NULL),
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_LAYERS_UUID),
BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL,
split_svc_update_layers, NULL), );
K_THREAD_STACK_DEFINE(service_q_stack, CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE);

View File

@ -150,6 +150,42 @@ int zmk_split_central_update_hid_indicator(zmk_hid_indicators_t indicators) {
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
int zmk_split_central_update_layers(uint32_t new_layers) {
if (!active_transport || !active_transport->api ||
!active_transport->api->get_available_source_ids || !active_transport->api->send_command) {
return -ENODEV;
}
uint8_t source_ids[ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT];
int ret = active_transport->api->get_available_source_ids(source_ids);
if (ret < 0) {
return ret;
}
struct zmk_split_transport_central_command command =
(struct zmk_split_transport_central_command){
.type = ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_RGB_LAYERS,
.data =
{
.set_rgb_layers =
{
.layers = new_layers,
},
},
};
for (size_t i = 0; i < ret; i++) {
ret = active_transport->api->send_command(source_ids[i], command);
if (ret < 0) {
return ret;
}
}
return 0;
}
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
int zmk_split_central_get_peripheral_battery_level(uint8_t source, uint8_t *level) {

View File

@ -21,6 +21,7 @@
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
#include <zmk/events/hid_indicators_changed.h>
#endif
#include <zmk/events/split_peripheral_layer_changed.h>
#include <zephyr/init.h>
#include <zephyr/logging/log.h>
@ -66,6 +67,10 @@ int zmk_split_transport_peripheral_command_handler(
.indicators = cmd.data.set_hid_indicators.indicators});
}
#endif
case ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_RGB_LAYERS: {
return raise_zmk_split_peripheral_layer_changed(
(struct zmk_split_peripheral_layer_changed){.layers = cmd.data.set_rgb_layers.layers});
}
default:
LOG_WRN("Unhandled command type %d", cmd.type);
return -ENOTSUP;

View File

@ -0,0 +1,25 @@
#include <zephyr/types.h>
#include <zephyr/sys/util.h>
#include <zmk/split/peripheral_layers.h>
#include <zmk/keymap.h>
static uint32_t peripheral_layers = 0;
void set_peripheral_layers_state(uint32_t new_layers) { peripheral_layers = new_layers; }
bool peripheral_layer_active(uint8_t layer) {
return (peripheral_layers & (BIT(layer))) == (BIT(layer));
};
uint8_t peripheral_highest_layer_active(void) {
if (peripheral_layers > 0) {
for (uint8_t layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer > 0; layer--) {
if ((peripheral_layers & (BIT(layer))) == (BIT(layer)) || layer == 0) {
return layer;
}
}
}
return 0;
}

View File

@ -185,6 +185,8 @@ static ssize_t get_payload_data_size(const struct zmk_split_transport_central_co
return sizeof(cmd->data.set_physical_layout);
case ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_HID_INDICATORS:
return sizeof(cmd->data.set_hid_indicators);
case ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_RGB_LAYERS:
return sizeof(cmd->data.set_rgb_layers);
default:
return -ENOTSUP;
}