diff --git a/app/src/split/bluetooth/Kconfig b/app/src/split/bluetooth/Kconfig index 5f4a782f772..5760f4d5af2 100644 --- a/app/src/split/bluetooth/Kconfig +++ b/app/src/split/bluetooth/Kconfig @@ -102,6 +102,17 @@ config BT_GAP_AUTO_UPDATE_CONN_PARAMS endif # !ZMK_SPLIT_ROLE_CENTRAL +if ZMK_INPUT_SPLIT + +config ZMK_INPUT_SPLIT_MSG_QUEUE_SIZE + int "Max number of input split messages to queue for sending over BLE" + default 32 + help + Sets the maximum number of input split events that can be queued for BLE notifications. + Increasing this reduces drops during high-rate input bursts at the cost of additional RAM. + +endif # ZMK_INPUT_SPLIT + endmenu endif # ZMK_SPLIT_BLE diff --git a/app/src/split/bluetooth/service.c b/app/src/split/bluetooth/service.c index 5bbed1373e6..a51e8c30d9d 100644 --- a/app/src/split/bluetooth/service.c +++ b/app/src/split/bluetooth/service.c @@ -312,6 +312,27 @@ static int zmk_split_bt_sensor_triggered(uint8_t sensor_index, #if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT) +struct input_event_notify_item { + uint16_t attr_index; + struct zmk_split_input_event_payload payload; +}; + +K_MSGQ_DEFINE(input_event_msgq, sizeof(struct input_event_notify_item), + CONFIG_ZMK_INPUT_SPLIT_MSG_QUEUE_SIZE, 4); + +static void send_input_event_callback(struct k_work *work) { + struct input_event_notify_item item; + while (k_msgq_get(&input_event_msgq, &item, K_NO_WAIT) == 0) { + int err = bt_gatt_notify(NULL, &split_svc.attrs[item.attr_index], &item.payload, + sizeof(item.payload)); + if (err) { + LOG_ERR("Error notifying input event %d", err); + } + } +} + +static K_WORK_DEFINE(service_input_notify_work, send_input_event_callback); + static int zmk_split_bt_report_input(uint8_t reg, uint8_t type, uint16_t code, int32_t value, bool sync) { @@ -326,7 +347,24 @@ static int zmk_split_bt_report_input(uint8_t reg, uint8_t type, uint16_t code, i .sync = sync ? 1 : 0, }; - return bt_gatt_notify(NULL, &split_svc.attrs[i], &payload, sizeof(payload)); + struct input_event_notify_item item = {.attr_index = (uint16_t)i, .payload = payload}; + int err = k_msgq_put(&input_event_msgq, &item, K_NO_WAIT); + + if (err == 0) { + k_work_submit_to_queue(&service_work_q, &service_input_notify_work); + return 0; + } else { + LOG_WRN("Input event queue full, dropping one and retry"); + + struct input_event_notify_item discarded; + k_msgq_get(&input_event_msgq, &discarded, K_NO_WAIT); + err = k_msgq_put(&input_event_msgq, &item, K_NO_WAIT); + + if (err != 0) { + LOG_WRN("Failed to queue input event (%d)", err); + return err; + } + } } } return -ENODEV;