diff --git a/app/include/zmk/split/bluetooth/central.h b/app/include/zmk/split/bluetooth/central.h index 5e9e09ff6a1..973a4e8b23b 100644 --- a/app/include/zmk/split/bluetooth/central.h +++ b/app/include/zmk/split/bluetooth/central.h @@ -8,8 +8,8 @@ #include #endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS) -int zmk_split_bt_invoke_behavior(uint8_t source, struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event, bool state); +int zmk_split_invoke_behavior(uint8_t source, struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event, bool state); #if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS) diff --git a/app/include/zmk/split/central.h b/app/include/zmk/split/central.h new file mode 100644 index 00000000000..45205d78f1c --- /dev/null +++ b/app/include/zmk/split/central.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include + +void zmk_position_state_change_handle(struct zmk_position_state_changed *ev); + +#if ZMK_KEYMAP_HAS_SENSORS +void zmk_sensor_event_handle(struct zmk_sensor_event *ev); +#endif + +void send_split_run_impl(struct zmk_split_run_behavior_payload_wrapper *payload_wrapper); diff --git a/app/include/zmk/split/bluetooth/service.h b/app/include/zmk/split/service.h similarity index 52% rename from app/include/zmk/split/bluetooth/service.h rename to app/include/zmk/split/service.h index 112cd552942..98ce16cbd3b 100644 --- a/app/include/zmk/split/bluetooth/service.h +++ b/app/include/zmk/split/service.h @@ -10,6 +10,7 @@ #include #define ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN 9 +#define ZMK_SPLIT_POS_STATE_LEN 16 struct sensor_event { uint8_t sensor_index; @@ -30,8 +31,18 @@ struct zmk_split_run_behavior_payload { char behavior_dev[ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN]; } __packed; -int zmk_split_bt_position_pressed(uint8_t position); -int zmk_split_bt_position_released(uint8_t position); -int zmk_split_bt_sensor_triggered(uint8_t sensor_index, - const struct zmk_sensor_channel_data channel_data[], - size_t channel_data_size); +struct zmk_split_run_behavior_payload_wrapper { + uint8_t source; + struct zmk_split_run_behavior_payload payload; +}; + +int zmk_split_position_pressed(uint8_t position); +int zmk_split_position_released(uint8_t position); +int zmk_split_sensor_triggered(uint8_t sensor_index, + const struct zmk_sensor_channel_data channel_data[], + size_t channel_data_size); + +void send_position_state_impl(uint8_t *state, int len); +#if ZMK_KEYMAP_HAS_SENSORS +void send_sensor_state_impl(struct sensor_event *event, int len); +#endif diff --git a/app/src/keymap.c b/app/src/keymap.c index 94bd12048cf..e7e752120cd 100644 --- a/app/src/keymap.c +++ b/app/src/keymap.c @@ -214,7 +214,7 @@ int zmk_keymap_apply_position_state(uint8_t source, int layer, uint32_t position if (source == ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL) { return invoke_locally(&binding, event, pressed); } else { - return zmk_split_bt_invoke_behavior(source, &binding, event, pressed); + return zmk_split_invoke_behavior(source, &binding, event, pressed); } #else return invoke_locally(&binding, event, pressed); @@ -222,7 +222,7 @@ int zmk_keymap_apply_position_state(uint8_t source, int layer, uint32_t position case BEHAVIOR_LOCALITY_GLOBAL: #if ZMK_BLE_IS_CENTRAL for (int i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT; i++) { - zmk_split_bt_invoke_behavior(i, &binding, event, pressed); + zmk_split_invoke_behavior(i, &binding, event, pressed); } #endif return invoke_locally(&binding, event, pressed); diff --git a/app/src/split/CMakeLists.txt b/app/src/split/CMakeLists.txt index 27abb82ad06..b59533d4d89 100644 --- a/app/src/split/CMakeLists.txt +++ b/app/src/split/CMakeLists.txt @@ -1,6 +1,15 @@ # Copyright (c) 2022 The ZMK Contributors # SPDX-License-Identifier: MIT +if (CONFIG_ZMK_SPLIT_BLE AND (NOT CONFIG_ZMK_SPLIT_ROLE_CENTRAL)) + target_sources(app PRIVATE listener.c) + target_sources(app PRIVATE service.c) +endif() + +if (CONFIG_ZMK_SPLIT_BLE AND CONFIG_ZMK_SPLIT_ROLE_CENTRAL) + target_sources(app PRIVATE central.c) +endif() + if (CONFIG_ZMK_SPLIT_BLE) add_subdirectory(bluetooth) -endif() \ No newline at end of file +endif() diff --git a/app/src/split/Kconfig b/app/src/split/Kconfig index ce90037b1ea..5a333eedd42 100644 --- a/app/src/split/Kconfig +++ b/app/src/split/Kconfig @@ -6,9 +6,49 @@ menuconfig ZMK_SPLIT if ZMK_SPLIT +config ZMK_SPLIT_INIT_PRIORITY + int "Split Init Priority" + default 50 + config ZMK_SPLIT_ROLE_CENTRAL bool "Split central device" +if ZMK_SPLIT_ROLE_CENTRAL + +config ZMK_SPLIT_CENTRAL_POSITION_QUEUE_SIZE + int "Max number of key position state events to queue when received from peripherals" + default 5 + +config ZMK_SPLIT_CENTRAL_SPLIT_RUN_STACK_SIZE + int "Split central write thread stack size" + default 512 + +config ZMK_SPLIT_CENTRAL_SPLIT_RUN_QUEUE_SIZE + int "Max number of behavior run events to queue to send to the peripheral(s)" + default 5 + +config ZMK_SPLIT_CENTRAL_PRIORITY + int "Split central thread priority" + default 5 + +endif # ZMK_SPLIT_ROLE_CENTRAL + +if !ZMK_SPLIT_ROLE_CENTRAL + +config ZMK_SPLIT_PERIPHERAL_STACK_SIZE + int "Split peripheral notify thread stack size" + default 756 + +config ZMK_SPLIT_PERIPHERAL_PRIORITY + int "Split peripheral notify thread priority" + default 5 + +config ZMK_SPLIT_PERIPHERAL_POSITION_QUEUE_SIZE + int "Max number of key position state events to queue to send to the central" + default 10 + +endif #!ZMK_SPLIT_ROLE_CENTRAL + choice ZMK_SPLIT_TRANSPORT prompt "Split transport" diff --git a/app/src/split/bluetooth/CMakeLists.txt b/app/src/split/bluetooth/CMakeLists.txt index 6e0ad617284..d25ca45dacd 100644 --- a/app/src/split/bluetooth/CMakeLists.txt +++ b/app/src/split/bluetooth/CMakeLists.txt @@ -2,14 +2,14 @@ # SPDX-License-Identifier: MIT if (NOT CONFIG_ZMK_SPLIT_ROLE_CENTRAL) - target_sources(app PRIVATE split_listener.c) target_sources(app PRIVATE service.c) target_sources(app PRIVATE peripheral.c) endif() + if (CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE central.c) endif() if (CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY) target_sources(app PRIVATE central_bas_proxy.c) -endif() \ No newline at end of file +endif() diff --git a/app/src/split/bluetooth/Kconfig b/app/src/split/bluetooth/Kconfig index 7f362fdedc1..706bd2dfb5a 100644 --- a/app/src/split/bluetooth/Kconfig +++ b/app/src/split/bluetooth/Kconfig @@ -46,18 +46,6 @@ config ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY endif -config ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE - int "Max number of key position state events to queue when received from peripherals" - default 5 - -config ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_STACK_SIZE - int "BLE split central write thread stack size" - default 512 - -config ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_QUEUE_SIZE - int "Max number of behavior run events to queue to send to the peripheral(s)" - default 5 - config ZMK_SPLIT_BLE_PREF_INT int "Connection interval to use for split central/peripheral connection" default 6 @@ -74,18 +62,6 @@ endif # ZMK_SPLIT_ROLE_CENTRAL if !ZMK_SPLIT_ROLE_CENTRAL -config ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE - int "BLE split peripheral notify thread stack size" - default 756 - -config ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY - int "BLE split peripheral notify thread priority" - default 5 - -config ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE - int "Max number of key position state events to queue to send to the central" - default 10 - config BT_MAX_PAIRED default 1 diff --git a/app/src/split/bluetooth/central.c b/app/src/split/bluetooth/central.c index 0f4cd78b531..c49138dca9f 100644 --- a/app/src/split/bluetooth/central.c +++ b/app/src/split/bluetooth/central.c @@ -24,7 +24,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include -#include +#include +#include #include #include #include @@ -66,19 +67,6 @@ static bool is_scanning = false; static const struct bt_uuid_128 split_service_uuid = BT_UUID_INIT_128(ZMK_SPLIT_BT_SERVICE_UUID); -K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed), - CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE, 4); - -void peripheral_event_work_callback(struct k_work *work) { - struct zmk_position_state_changed ev; - while (k_msgq_get(&peripheral_event_msgq, &ev, K_NO_WAIT) == 0) { - LOG_DBG("Trigger key position state change for %d", ev.position); - raise_zmk_position_state_changed(ev); - } -} - -K_WORK_DEFINE(peripheral_event_work, peripheral_event_work_callback); - int peripheral_slot_index_for_conn(struct bt_conn *conn) { for (int i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT; i++) { if (peripherals[i].conn == conn) { @@ -126,9 +114,7 @@ int release_peripheral_slot(int index) { .position = position, .state = false, .timestamp = k_uptime_get()}; - - k_msgq_put(&peripheral_event_msgq, &ev, K_NO_WAIT); - k_work_submit(&peripheral_event_work); + zmk_position_state_change_handle(&ev); } } } @@ -182,19 +168,6 @@ int confirm_peripheral_slot_conn(struct bt_conn *conn) { } #if ZMK_KEYMAP_HAS_SENSORS -K_MSGQ_DEFINE(peripheral_sensor_event_msgq, sizeof(struct zmk_sensor_event), - CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE, 4); - -void peripheral_sensor_event_work_callback(struct k_work *work) { - struct zmk_sensor_event ev; - while (k_msgq_get(&peripheral_sensor_event_msgq, &ev, K_NO_WAIT) == 0) { - LOG_DBG("Trigger sensor change for %d", ev.sensor_index); - raise_zmk_sensor_event(ev); - } -} - -K_WORK_DEFINE(peripheral_sensor_event_work, peripheral_sensor_event_work_callback); - static uint8_t split_central_sensor_notify_func(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data, uint16_t length) { @@ -220,8 +193,7 @@ static uint8_t split_central_sensor_notify_func(struct bt_conn *conn, memcpy(ev.channel_data, sensor_event.channel_data, sizeof(struct zmk_sensor_channel_data) * sensor_event.channel_data_size); - k_msgq_put(&peripheral_sensor_event_msgq, &ev, K_NO_WAIT); - k_work_submit(&peripheral_sensor_event_work); + zmk_sensor_event_handle(&ev); return BT_GATT_ITER_CONTINUE; } @@ -261,9 +233,7 @@ static uint8_t split_central_notify_func(struct bt_conn *conn, .position = position, .state = pressed, .timestamp = k_uptime_get()}; - - k_msgq_put(&peripheral_event_msgq, &ev, K_NO_WAIT); - k_work_submit(&peripheral_event_work); + zmk_position_state_change_handle(&ev); } } } @@ -744,89 +714,24 @@ static struct bt_conn_cb conn_callbacks = { .disconnected = split_central_disconnected, }; -K_THREAD_STACK_DEFINE(split_central_split_run_q_stack, - CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_STACK_SIZE); - -struct k_work_q split_central_split_run_q; - -struct zmk_split_run_behavior_payload_wrapper { - uint8_t source; - struct zmk_split_run_behavior_payload payload; -}; - -K_MSGQ_DEFINE(zmk_split_central_split_run_msgq, - sizeof(struct zmk_split_run_behavior_payload_wrapper), - CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_QUEUE_SIZE, 4); - -void split_central_split_run_callback(struct k_work *work) { - struct zmk_split_run_behavior_payload_wrapper payload_wrapper; - - LOG_DBG(""); - - while (k_msgq_get(&zmk_split_central_split_run_msgq, &payload_wrapper, K_NO_WAIT) == 0) { - if (peripherals[payload_wrapper.source].state != PERIPHERAL_SLOT_STATE_CONNECTED) { - LOG_ERR("Source not connected"); - continue; - } - if (!peripherals[payload_wrapper.source].run_behavior_handle) { - LOG_ERR("Run behavior handle not found"); - continue; - } - - int err = bt_gatt_write_without_response( - peripherals[payload_wrapper.source].conn, - peripherals[payload_wrapper.source].run_behavior_handle, &payload_wrapper.payload, - sizeof(struct zmk_split_run_behavior_payload), true); - - if (err) { - LOG_ERR("Failed to write the behavior characteristic (err %d)", err); - } +void send_split_run_impl(struct zmk_split_run_behavior_payload_wrapper *payload_wrapper) { + if (peripherals[payload_wrapper->source].state != PERIPHERAL_SLOT_STATE_CONNECTED) { + LOG_ERR("Source not connected"); + return; + } + if (!peripherals[payload_wrapper->source].run_behavior_handle) { + LOG_ERR("Run behavior handle not found"); + return; } -} - -K_WORK_DEFINE(split_central_split_run_work, split_central_split_run_callback); -static int -split_bt_invoke_behavior_payload(struct zmk_split_run_behavior_payload_wrapper payload_wrapper) { - LOG_DBG(""); + int err = bt_gatt_write_without_response( + peripherals[payload_wrapper->source].conn, + peripherals[payload_wrapper->source].run_behavior_handle, &payload_wrapper->payload, + sizeof(struct zmk_split_run_behavior_payload), true); - int err = k_msgq_put(&zmk_split_central_split_run_msgq, &payload_wrapper, K_MSEC(100)); if (err) { - switch (err) { - case -EAGAIN: { - LOG_WRN("Consumer message queue full, popping first message and queueing again"); - struct zmk_split_run_behavior_payload_wrapper discarded_report; - k_msgq_get(&zmk_split_central_split_run_msgq, &discarded_report, K_NO_WAIT); - return split_bt_invoke_behavior_payload(payload_wrapper); - } - default: - LOG_WRN("Failed to queue behavior to send (%d)", err); - return err; - } + LOG_ERR("Failed to write the behavior characteristic (err %d)", err); } - - k_work_submit_to_queue(&split_central_split_run_q, &split_central_split_run_work); - - return 0; -}; - -int zmk_split_bt_invoke_behavior(uint8_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 ? 1 : 0, - }}; - const size_t payload_dev_size = sizeof(payload.behavior_dev); - if (strlcpy(payload.behavior_dev, binding->behavior_dev, payload_dev_size) >= - payload_dev_size) { - LOG_ERR("Truncated behavior label %s to %s before invoking peripheral behavior", - binding->behavior_dev, payload.behavior_dev); - } - - struct zmk_split_run_behavior_payload_wrapper wrapper = {.source = source, .payload = payload}; - return split_bt_invoke_behavior_payload(wrapper); } #if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS) @@ -883,9 +788,6 @@ static struct settings_handler ble_central_settings_handler = { #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); #if IS_ENABLED(CONFIG_SETTINGS) @@ -896,4 +798,4 @@ static int zmk_split_bt_central_init(void) { #endif // IS_ENABLED(CONFIG_SETTINGS) } -SYS_INIT(zmk_split_bt_central_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY); +SYS_INIT(zmk_split_bt_central_init, APPLICATION, CONFIG_ZMK_SPLIT_INIT_PRIORITY); diff --git a/app/src/split/bluetooth/peripheral.c b/app/src/split/bluetooth/peripheral.c index 5a12e0fc4dd..ed10c7088f5 100644 --- a/app/src/split/bluetooth/peripheral.c +++ b/app/src/split/bluetooth/peripheral.c @@ -193,4 +193,4 @@ static int zmk_peripheral_ble_init(void) { return 0; } -SYS_INIT(zmk_peripheral_ble_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY); +SYS_INIT(zmk_peripheral_ble_init, APPLICATION, CONFIG_ZMK_SPLIT_INIT_PRIORITY); diff --git a/app/src/split/bluetooth/service.c b/app/src/split/bluetooth/service.c index 505eb363cd8..1ce3c3efe47 100644 --- a/app/src/split/bluetooth/service.c +++ b/app/src/split/bluetooth/service.c @@ -20,7 +20,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include -#include +#include #if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS) #include @@ -43,10 +43,8 @@ static void split_svc_sensor_state_ccc(const struct bt_gatt_attr *attr, uint16_t } #endif /* ZMK_KEYMAP_HAS_SENSORS */ -#define POS_STATE_LEN 16 - static uint8_t num_of_positions = ZMK_KEYMAP_LEN; -static uint8_t position_state[POS_STATE_LEN]; +static uint8_t position_state[ZMK_SPLIT_POS_STATE_LEN]; static struct zmk_split_run_behavior_payload behavior_run_payload; @@ -162,116 +160,20 @@ BT_GATT_SERVICE_DEFINE( #endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS) ); -K_THREAD_STACK_DEFINE(service_q_stack, CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE); - -struct k_work_q service_work_q; - -K_MSGQ_DEFINE(position_state_msgq, sizeof(char[POS_STATE_LEN]), - CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE, 4); - -void send_position_state_callback(struct k_work *work) { - uint8_t state[POS_STATE_LEN]; - - while (k_msgq_get(&position_state_msgq, &state, K_NO_WAIT) == 0) { - int err = bt_gatt_notify(NULL, &split_svc.attrs[1], &state, sizeof(state)); - if (err) { - LOG_DBG("Error notifying %d", err); - } - } -}; - -K_WORK_DEFINE(service_position_notify_work, send_position_state_callback); - -int send_position_state() { - int err = k_msgq_put(&position_state_msgq, position_state, K_MSEC(100)); +void send_position_state_impl(uint8_t *state, int len) { + memcpy(position_state, state, MIN(len, sizeof(position_state))); + int err = bt_gatt_notify(NULL, &split_svc.attrs[1], state, len); if (err) { - switch (err) { - case -EAGAIN: { - LOG_WRN("Position state message queue full, popping first message and queueing again"); - uint8_t discarded_state[POS_STATE_LEN]; - k_msgq_get(&position_state_msgq, &discarded_state, K_NO_WAIT); - return send_position_state(); - } - default: - LOG_WRN("Failed to queue position state to send (%d)", err); - return err; - } + LOG_DBG("Error notifying %d", err); } - - k_work_submit_to_queue(&service_work_q, &service_position_notify_work); - - return 0; -} - -int zmk_split_bt_position_pressed(uint8_t position) { - WRITE_BIT(position_state[position / 8], position % 8, true); - return send_position_state(); -} - -int zmk_split_bt_position_released(uint8_t position) { - WRITE_BIT(position_state[position / 8], position % 8, false); - return send_position_state(); } #if ZMK_KEYMAP_HAS_SENSORS -K_MSGQ_DEFINE(sensor_state_msgq, sizeof(struct sensor_event), - CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE, 4); - -void send_sensor_state_callback(struct k_work *work) { - while (k_msgq_get(&sensor_state_msgq, &last_sensor_event, K_NO_WAIT) == 0) { - int err = bt_gatt_notify(NULL, &split_svc.attrs[8], &last_sensor_event, - sizeof(last_sensor_event)); - if (err) { - LOG_DBG("Error notifying %d", err); - } - } -}; - -K_WORK_DEFINE(service_sensor_notify_work, send_sensor_state_callback); - -int send_sensor_state(struct sensor_event ev) { - int err = k_msgq_put(&sensor_state_msgq, &ev, K_MSEC(100)); +void send_sensor_state_impl(struct sensor_event *event, int len) { + memcpy(&last_sensor_event, event, MIN(len, sizeof(last_sensor_event))); + int err = bt_gatt_notify(NULL, &split_svc.attrs[8], event, len); if (err) { - // retry... - switch (err) { - case -EAGAIN: { - LOG_WRN("Sensor state message queue full, popping first message and queueing again"); - struct sensor_event discarded_state; - k_msgq_get(&sensor_state_msgq, &discarded_state, K_NO_WAIT); - return send_sensor_state(ev); - } - default: - LOG_WRN("Failed to queue sensor state to send (%d)", err); - return err; - } + LOG_DBG("Error notifying %d", err); } - - k_work_submit_to_queue(&service_work_q, &service_sensor_notify_work); - return 0; -} - -int zmk_split_bt_sensor_triggered(uint8_t sensor_index, - const struct zmk_sensor_channel_data channel_data[], - size_t channel_data_size) { - if (channel_data_size > ZMK_SENSOR_EVENT_MAX_CHANNELS) { - return -EINVAL; - } - - struct sensor_event ev = - (struct sensor_event){.sensor_index = sensor_index, .channel_data_size = channel_data_size}; - memcpy(ev.channel_data, channel_data, - channel_data_size * sizeof(struct zmk_sensor_channel_data)); - return send_sensor_state(ev); } #endif /* ZMK_KEYMAP_HAS_SENSORS */ - -static int service_init(void) { - static const struct k_work_queue_config queue_config = { - .name = "Split Peripheral Notification Queue"}; - k_work_queue_start(&service_work_q, service_q_stack, K_THREAD_STACK_SIZEOF(service_q_stack), - CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY, &queue_config); - - return 0; -} - -SYS_INIT(service_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY); diff --git a/app/src/split/central.c b/app/src/split/central.c new file mode 100644 index 00000000000..15af4c70704 --- /dev/null +++ b/app/src/split/central.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed), + CONFIG_ZMK_SPLIT_CENTRAL_POSITION_QUEUE_SIZE, 4); + +void peripheral_event_work_callback(struct k_work *work) { + struct zmk_position_state_changed ev; + while (k_msgq_get(&peripheral_event_msgq, &ev, K_NO_WAIT) == 0) { + LOG_DBG("Trigger key position state change for %d", ev.position); + raise_zmk_position_state_changed(ev); + } +} + +K_WORK_DEFINE(peripheral_event_work, peripheral_event_work_callback); + +void zmk_position_state_change_handle(struct zmk_position_state_changed *ev) { + k_msgq_put(&peripheral_event_msgq, ev, K_NO_WAIT); + k_work_submit(&peripheral_event_work); +} + +#if ZMK_KEYMAP_HAS_SENSORS +K_MSGQ_DEFINE(peripheral_sensor_event_msgq, sizeof(struct zmk_sensor_event), + CONFIG_ZMK_SPLIT_CENTRAL_POSITION_QUEUE_SIZE, 4); + +void peripheral_sensor_event_work_callback(struct k_work *work) { + struct zmk_sensor_event ev; + while (k_msgq_get(&peripheral_sensor_event_msgq, &ev, K_NO_WAIT) == 0) { + LOG_DBG("Trigger sensor change for %d", ev.sensor_index); + raise_zmk_sensor_event(ev); + } +} + +K_WORK_DEFINE(peripheral_sensor_event_work, peripheral_sensor_event_work_callback); + +void zmk_sensor_event_handle(struct zmk_sensor_event *ev) { + k_msgq_put(&peripheral_sensor_event_msgq, ev, K_NO_WAIT); + k_work_submit(&peripheral_sensor_event_work); +} +#endif /* ZMK_KEYMAP_HAS_SENSORS */ + +K_THREAD_STACK_DEFINE(split_central_split_run_q_stack, + CONFIG_ZMK_SPLIT_CENTRAL_SPLIT_RUN_STACK_SIZE); + +struct k_work_q split_central_split_run_q; + +K_MSGQ_DEFINE(zmk_split_central_split_run_msgq, + sizeof(struct zmk_split_run_behavior_payload_wrapper), + CONFIG_ZMK_SPLIT_CENTRAL_SPLIT_RUN_QUEUE_SIZE, 4); + +void split_central_split_run_callback(struct k_work *work) { + struct zmk_split_run_behavior_payload_wrapper payload_wrapper; + + LOG_DBG(""); + + while (k_msgq_get(&zmk_split_central_split_run_msgq, &payload_wrapper, K_NO_WAIT) == 0) { + send_split_run_impl(&payload_wrapper); + } +} + +K_WORK_DEFINE(split_central_split_run_work, split_central_split_run_callback); + +static int +split_invoke_behavior_payload(struct zmk_split_run_behavior_payload_wrapper payload_wrapper) { + LOG_DBG(""); + + int err = k_msgq_put(&zmk_split_central_split_run_msgq, &payload_wrapper, K_MSEC(100)); + if (err) { + switch (err) { + case -EAGAIN: { + LOG_WRN("Consumer message queue full, popping first message and queueing again"); + struct zmk_split_run_behavior_payload_wrapper discarded_report; + k_msgq_get(&zmk_split_central_split_run_msgq, &discarded_report, K_NO_WAIT); + return split_invoke_behavior_payload(payload_wrapper); + } + default: + LOG_WRN("Failed to queue behavior to send (%d)", err); + return err; + } + } + + k_work_submit_to_queue(&split_central_split_run_q, &split_central_split_run_work); + + return 0; +}; + +int zmk_split_invoke_behavior(uint8_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 ? 1 : 0, + }}; + const size_t payload_dev_size = sizeof(payload.behavior_dev); + if (strlcpy(payload.behavior_dev, binding->behavior_dev, payload_dev_size) >= + payload_dev_size) { + LOG_ERR("Truncated behavior label %s to %s before invoking peripheral behavior", + binding->behavior_dev, payload.behavior_dev); + } + + struct zmk_split_run_behavior_payload_wrapper wrapper = {.source = source, .payload = payload}; + return split_invoke_behavior_payload(wrapper); +} + +static int zmk_split_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_SPLIT_CENTRAL_PRIORITY, NULL); + return 0; +} + +SYS_INIT(zmk_split_central_init, APPLICATION, CONFIG_ZMK_SPLIT_INIT_PRIORITY); diff --git a/app/src/split/bluetooth/split_listener.c b/app/src/split/listener.c similarity index 75% rename from app/src/split/bluetooth/split_listener.c rename to app/src/split/listener.c index 9b680d2c89c..d7a144de57f 100644 --- a/app/src/split/bluetooth/split_listener.c +++ b/app/src/split/listener.c @@ -7,7 +7,7 @@ #include #include -#include +#include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -23,17 +23,17 @@ int split_listener(const zmk_event_t *eh) { const struct zmk_position_state_changed *pos_ev; if ((pos_ev = as_zmk_position_state_changed(eh)) != NULL) { if (pos_ev->state) { - return zmk_split_bt_position_pressed(pos_ev->position); + return zmk_split_position_pressed(pos_ev->position); } else { - return zmk_split_bt_position_released(pos_ev->position); + return zmk_split_position_released(pos_ev->position); } } #if ZMK_KEYMAP_HAS_SENSORS const struct zmk_sensor_event *sensor_ev; if ((sensor_ev = as_zmk_sensor_event(eh)) != NULL) { - return zmk_split_bt_sensor_triggered(sensor_ev->sensor_index, sensor_ev->channel_data, - sensor_ev->channel_data_size); + return zmk_split_sensor_triggered(sensor_ev->sensor_index, sensor_ev->channel_data, + sensor_ev->channel_data_size); } #endif /* ZMK_KEYMAP_HAS_SENSORS */ return ZMK_EV_EVENT_BUBBLE; diff --git a/app/src/split/service.c b/app/src/split/service.c new file mode 100644 index 00000000000..d0b610a17e8 --- /dev/null +++ b/app/src/split/service.c @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include + +#include +#include +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +static uint8_t position_state[ZMK_SPLIT_POS_STATE_LEN]; +#if ZMK_KEYMAP_HAS_SENSORS +static struct sensor_event last_sensor_event; +#endif + +K_THREAD_STACK_DEFINE(service_q_stack, CONFIG_ZMK_SPLIT_PERIPHERAL_STACK_SIZE); + +struct k_work_q service_work_q; + +K_MSGQ_DEFINE(position_state_msgq, sizeof(char[ZMK_SPLIT_POS_STATE_LEN]), + CONFIG_ZMK_SPLIT_PERIPHERAL_POSITION_QUEUE_SIZE, 4); + +void send_position_state_callback(struct k_work *work) { + uint8_t state[ZMK_SPLIT_POS_STATE_LEN]; + + while (k_msgq_get(&position_state_msgq, &state, K_NO_WAIT) == 0) { + send_position_state_impl(state, sizeof(state)); + } +}; + +K_WORK_DEFINE(service_position_notify_work, send_position_state_callback); + +int send_position_state() { + int err = k_msgq_put(&position_state_msgq, position_state, K_MSEC(100)); + if (err) { + switch (err) { + case -EAGAIN: { + LOG_WRN("Position state message queue full, popping first message and queueing again"); + uint8_t discarded_state[ZMK_SPLIT_POS_STATE_LEN]; + k_msgq_get(&position_state_msgq, &discarded_state, K_NO_WAIT); + return send_position_state(); + } + default: + LOG_WRN("Failed to queue position state to send (%d)", err); + return err; + } + } + + k_work_submit_to_queue(&service_work_q, &service_position_notify_work); + + return 0; +} + +int zmk_split_position_pressed(uint8_t position) { + WRITE_BIT(position_state[position / 8], position % 8, true); + return send_position_state(); +} + +int zmk_split_position_released(uint8_t position) { + WRITE_BIT(position_state[position / 8], position % 8, false); + return send_position_state(); +} + +#if ZMK_KEYMAP_HAS_SENSORS +K_MSGQ_DEFINE(sensor_state_msgq, sizeof(struct sensor_event), + CONFIG_ZMK_SPLIT_PERIPHERAL_POSITION_QUEUE_SIZE, 4); + +void send_sensor_state_callback(struct k_work *work) { + while (k_msgq_get(&sensor_state_msgq, &last_sensor_event, K_NO_WAIT) == 0) { + send_sensor_state_impl(&last_sensor_event, sizeof(last_sensor_event)); + } +}; + +K_WORK_DEFINE(service_sensor_notify_work, send_sensor_state_callback); + +int send_sensor_state(struct sensor_event ev) { + int err = k_msgq_put(&sensor_state_msgq, &ev, K_MSEC(100)); + if (err) { + // retry... + switch (err) { + case -EAGAIN: { + LOG_WRN("Sensor state message queue full, popping first message and queueing again"); + struct sensor_event discarded_state; + k_msgq_get(&sensor_state_msgq, &discarded_state, K_NO_WAIT); + return send_sensor_state(ev); + } + default: + LOG_WRN("Failed to queue sensor state to send (%d)", err); + return err; + } + } + + k_work_submit_to_queue(&service_work_q, &service_sensor_notify_work); + return 0; +} + +int zmk_split_sensor_triggered(uint8_t sensor_index, + const struct zmk_sensor_channel_data channel_data[], + size_t channel_data_size) { + if (channel_data_size > ZMK_SENSOR_EVENT_MAX_CHANNELS) { + return -EINVAL; + } + + struct sensor_event ev = + (struct sensor_event){.sensor_index = sensor_index, .channel_data_size = channel_data_size}; + memcpy(ev.channel_data, channel_data, + channel_data_size * sizeof(struct zmk_sensor_channel_data)); + return send_sensor_state(ev); +} +#endif /* ZMK_KEYMAP_HAS_SENSORS */ + +static int service_init(void) { + static const struct k_work_queue_config queue_config = { + .name = "Split Peripheral Notification Queue"}; + k_work_queue_start(&service_work_q, service_q_stack, K_THREAD_STACK_SIZEOF(service_q_stack), + CONFIG_ZMK_SPLIT_PERIPHERAL_PRIORITY, &queue_config); + + return 0; +} + +SYS_INIT(service_init, APPLICATION, CONFIG_ZMK_SPLIT_INIT_PRIORITY); diff --git a/docs/docs/config/system.md b/docs/docs/config/system.md index 1a5306bda5f..0508ddde1d5 100644 --- a/docs/docs/config/system.md +++ b/docs/docs/config/system.md @@ -129,9 +129,11 @@ Following [split keyboard](../features/split-keyboards.md) settings are defined | `CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING` | bool | Enable fetching split peripheral battery levels to the central side | n | | `CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY` | bool | Enable central reporting of split battery levels to hosts | n | | `CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_QUEUE_SIZE` | int | Max number of battery level events to queue when received from peripherals | `CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS` | -| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue when received from peripherals | 5 | -| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_STACK_SIZE` | int | Stack size of the BLE split central write thread | 512 | -| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_QUEUE_SIZE` | int | Max number of behavior run events to queue to send to the peripheral(s) | 5 | -| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE` | int | Stack size of the BLE split peripheral notify thread | 650 | -| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY` | int | Priority of the BLE split peripheral notify thread | 5 | -| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue to send to the central | 10 | +| `CONFIG_ZMK_SPLIT_CENTRAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue when received from peripherals | 5 | +| `CONFIG_ZMK_SPLIT_CENTRAL_SPLIT_RUN_STACK_SIZE` | int | Stack size of the BLE split central write thread | 512 | +| `CONFIG_ZMK_SPLIT_CENTRAL_SPLIT_RUN_QUEUE_SIZE` | int | Max number of behavior run events to queue to send to the peripheral(s) | 5 | +| `CONFIG_ZMK_SPLIT_CENTRAL_PRIORITY` | int | Priority of the split central thread | 5 | +| `CONFIG_ZMK_SPLIT_PERIPHERAL_STACK_SIZE` | int | Stack size of the split peripheral notify thread | 756 | +| `CONFIG_ZMK_SPLIT_PERIPHERAL_PRIORITY` | int | Priority of the split peripheral notify thread | 5 | +| `CONFIG_ZMK_SPLIT_PERIPHERAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue to send to the central | 10 | +| `CONFIG_ZMK_SPLIT_INIT_PRIORITY` | int | Split init priority | 50 |