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 index afdc9cd22ee..45205d78f1c 100644 --- a/app/include/zmk/split/central.h +++ b/app/include/zmk/split/central.h @@ -8,9 +8,12 @@ #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/service.h b/app/include/zmk/split/service.h index ccb259faafb..995685937b2 100644 --- a/app/include/zmk/split/service.h +++ b/app/include/zmk/split/service.h @@ -32,6 +32,11 @@ struct zmk_split_run_behavior_payload { char behavior_dev[ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN]; } __packed; +struct zmk_split_run_behavior_payload_wrapper { + uint8_t source; + struct zmk_split_run_behavior_payload payload; +}; + struct zmk_split_input_event_payload { uint8_t type; uint16_t code; @@ -39,7 +44,6 @@ struct zmk_split_input_event_payload { uint8_t sync; } __packed; - 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, diff --git a/app/src/behavior.c b/app/src/behavior.c index 9b20c706265..3b3b3c31683 100644 --- a/app/src/behavior.c +++ b/app/src/behavior.c @@ -99,7 +99,7 @@ int zmk_behavior_invoke_binding(const struct zmk_behavior_binding *src_binding, if (event.source == ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL) { return invoke_locally(&binding, event, pressed); } else { - return zmk_split_bt_invoke_behavior(event.source, &binding, event, pressed); + return zmk_split_invoke_behavior(event.source, &binding, event, pressed); } #else return invoke_locally(&binding, event, pressed); @@ -107,7 +107,7 @@ int zmk_behavior_invoke_binding(const struct zmk_behavior_binding *src_binding, 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/bluetooth/central.c b/app/src/split/bluetooth/central.c index c8b3bcb25df..ffe13a424e0 100644 --- a/app/src/split/bluetooth/central.c +++ b/app/src/split/bluetooth/central.c @@ -991,90 +991,24 @@ static struct bt_conn_cb conn_callbacks = { .security_changed = split_central_security_changed, }; -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, - .source = event.source, - .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) @@ -1131,9 +1065,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) @@ -1154,4 +1085,4 @@ static int zmk_split_bt_central_listener_cb(const zmk_event_t *eh) { } ZMK_LISTENER(zmk_split_bt_central, zmk_split_bt_central_listener_cb); -ZMK_SUBSCRIPTION(zmk_split_bt_central, zmk_physical_layout_selection_changed); \ No newline at end of file +ZMK_SUBSCRIPTION(zmk_split_bt_central, zmk_physical_layout_selection_changed); diff --git a/app/src/split/central.c b/app/src/split/central.c index 96f379a2e5f..b6ee4e378e4 100644 --- a/app/src/split/central.c +++ b/app/src/split/central.c @@ -7,9 +7,13 @@ #include #include +#include +#include #include #include #include +#include +#include #include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -51,3 +55,77 @@ void zmk_sensor_event_handle(struct zmk_sensor_event *ev) { 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_BLE_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_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) { + 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, + .source = event.source, + .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_BLE_THREAD_PRIORITY, NULL); + return 0; +} + +SYS_INIT(zmk_split_central_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY);