diff --git a/app/include/zmk/split/bluetooth/central.h b/app/include/zmk/split/bluetooth/central.h index 443d9b1b4894..3cce64717707 100644 --- a/app/include/zmk/split/bluetooth/central.h +++ b/app/include/zmk/split/bluetooth/central.h @@ -3,6 +3,9 @@ #include #include +#include int zmk_split_bt_invoke_behavior(uint8_t source, struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event, bool state); \ No newline at end of file + struct zmk_behavior_binding_event event, bool state); + +int zmk_split_bt_update_hid_indicator(zmk_hid_indicators indicators); diff --git a/app/include/zmk/split/bluetooth/uuid.h b/app/include/zmk/split/bluetooth/uuid.h index cbdb17723cd1..8738807b24b5 100644 --- a/app/include/zmk/split/bluetooth/uuid.h +++ b/app/include/zmk/split/bluetooth/uuid.h @@ -16,3 +16,4 @@ #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_RUN_BEHAVIOR_UUID ZMK_BT_SPLIT_UUID(0x00000002) +#define ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID ZMK_BT_SPLIT_UUID(0x00000003) diff --git a/app/src/hid_indicators.c b/app/src/hid_indicators.c index 3e56c59e6c9d..74ff470072a3 100644 --- a/app/src/hid_indicators.c +++ b/app/src/hid_indicators.c @@ -60,8 +60,14 @@ zmk_hid_indicators zmk_hid_indicators_get_profile(enum zmk_endpoint endpoint, ui } static void raise_led_changed_event(struct k_work *_work) { - ZMK_EVENT_RAISE(new_zmk_hid_indicators_changed((struct zmk_hid_indicators_changed){ - .indicators = zmk_hid_indicators_get_current_profile()})); + zmk_hid_indicators indicators = zmk_hid_indicators_get_current_profile(); + + ZMK_EVENT_RAISE(new_zmk_hid_indicators_changed( + (struct zmk_hid_indicators_changed){.indicators = indicators})); + +#if IS_ENABLED(CONFIG_ZMK_SPLIT) && IS_ENABLED(CONFIG_ZMK_BLE) + zmk_split_bt_update_hid_indicator(indicators); +#endif } static K_WORK_DEFINE(led_changed_work, raise_led_changed_event); diff --git a/app/src/split/bluetooth/central.c b/app/src/split/bluetooth/central.c index ac073c598695..579c835dae8e 100644 --- a/app/src/split/bluetooth/central.c +++ b/app/src/split/bluetooth/central.c @@ -25,6 +25,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include +#include static int start_scan(void); @@ -43,6 +44,7 @@ struct peripheral_slot { struct bt_gatt_subscribe_params subscribe_params; struct bt_gatt_discover_params sub_discover_params; uint16_t run_behavior_handle; + uint16_t update_hid_indicators; uint8_t position_state[POSITION_STATE_DATA_LEN]; uint8_t changed_positions[POSITION_STATE_DATA_LEN]; }; @@ -126,6 +128,7 @@ int release_peripheral_slot(int index) { // Clean up previously discovered handles; slot->subscribe_params.value_handle = 0; slot->run_behavior_handle = 0; + slot->update_hid_indicators = 0; return 0; } @@ -265,9 +268,14 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID))) { LOG_DBG("Found run behavior handle"); slot->run_behavior_handle = bt_gatt_attr_value_handle(attr); + } else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid, + BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID))) { + LOG_DBG("Found update HID indicators handle"); + slot->update_hid_indicators = bt_gatt_attr_value_handle(attr); } - bool subscribed = (slot->run_behavior_handle && slot->subscribe_params.value_handle); + bool subscribed = (slot->run_behavior_handle && slot->subscribe_params.value_handle && + slot->update_hid_indicators); return subscribed ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE; } @@ -584,6 +592,32 @@ int zmk_split_bt_invoke_behavior(uint8_t source, struct zmk_behavior_binding *bi return split_bt_invoke_behavior_payload(wrapper); } +static zmk_hid_indicators hid_indicators = 0; + +static void split_central_update_indicators_callback(struct k_work *work) { + zmk_hid_indicators indicators = hid_indicators; + for (int i = 0; i < ZMK_BLE_SPLIT_PERIPHERAL_COUNT; i++) { + if (peripherals[i].state != PERIPHERAL_SLOT_STATE_CONNECTED) { + continue; + } + + int err = bt_gatt_write_without_response(peripherals[i].conn, + peripherals[i].update_hid_indicators, &indicators, + sizeof(indicators), true); + + if (err) { + LOG_ERR("Failed to write HID indicator characteristic (err %d)", err); + } + } +} + +static K_WORK_DEFINE(split_central_update_indicators, split_central_update_indicators_callback); + +int zmk_split_bt_update_hid_indicator(zmk_hid_indicators indicators) { + hid_indicators = indicators; + return k_work_submit_to_queue(&split_central_split_run_q, &split_central_update_indicators); +} + int zmk_split_bt_central_init(const struct device *_arg) { 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), diff --git a/app/src/split/bluetooth/service.c b/app/src/split/bluetooth/service.c index f7b0d587b268..f2dbd4e15250 100644 --- a/app/src/split/bluetooth/service.c +++ b/app/src/split/bluetooth/service.c @@ -20,6 +20,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include +#include #define POS_STATE_LEN 16 @@ -88,6 +89,29 @@ static void split_svc_pos_state_ccc(const struct bt_gatt_attr *attr, uint16_t va LOG_DBG("value %d", value); } +static zmk_hid_indicators hid_indicators = 0; + +static void split_svc_update_indicators_callback(struct k_work *work) { + ZMK_EVENT_RAISE(new_zmk_hid_indicators_changed( + (struct zmk_hid_indicators_changed){.indicators = hid_indicators})); +} + +static K_WORK_DEFINE(split_svc_update_indicators_work, split_svc_update_indicators_callback); + +static ssize_t split_svc_update_indicators(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(zmk_hid_indicators)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + memcpy((uint8_t *)&hid_indicators + offset, buf, len); + + k_work_submit(&split_svc_update_indicators_work); + + return len; +} + BT_GATT_SERVICE_DEFINE( split_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_SERVICE_UUID)), BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID), @@ -97,6 +121,9 @@ BT_GATT_SERVICE_DEFINE( 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_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID), + BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL, + split_svc_update_indicators, NULL), BT_GATT_DESCRIPTOR(BT_UUID_NUM_OF_DIGITALS, BT_GATT_PERM_READ, split_svc_num_of_positions, NULL, &num_of_positions), );