From 65e476df3e4584aa744763b52a86930c67e2e288 Mon Sep 17 00:00:00 2001 From: Pete Johanson Date: Wed, 6 Jan 2021 09:32:08 -0500 Subject: [PATCH] fix(splits): Send pos notify from dedicated thread * Avoid deadlocks by using a deadicated workqueue for sending position state notifications from peripherals. --- app/Kconfig | 14 ++++++- app/src/split/bluetooth/service.c | 61 +++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/app/Kconfig b/app/Kconfig index 7602b9bd..df00f1db 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -102,7 +102,7 @@ config ZMK_SPLIT if ZMK_SPLIT -config ZMK_SPLIT_BLE +menuconfig ZMK_SPLIT_BLE bool "Split keyboard support via BLE transport" depends on ZMK_BLE default y @@ -125,6 +125,18 @@ endif if !ZMK_SPLIT_BLE_ROLE_CENTRAL +config ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE + int "BLE split peripheral notify thread stack size" + default 512 + +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 ZMK_USB default n diff --git a/app/src/split/bluetooth/service.c b/app/src/split/bluetooth/service.c index 48390849..fbac6446 100644 --- a/app/src/split/bluetooth/service.c +++ b/app/src/split/bluetooth/service.c @@ -6,6 +6,7 @@ #include #include +#include #include @@ -18,8 +19,10 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include +#define POS_STATE_LEN 16 + static uint8_t num_of_positions = ZMK_KEYMAP_LEN; -static uint8_t position_state[16]; +static uint8_t position_state[POS_STATE_LEN]; static ssize_t split_svc_pos_state(struct bt_conn *conn, const struct bt_gatt_attr *attrs, void *buf, uint16_t len, uint16_t offset) { @@ -45,12 +48,62 @@ BT_GATT_SERVICE_DEFINE( BT_GATT_DESCRIPTOR(BT_UUID_NUM_OF_DIGITALS, BT_GATT_PERM_READ, split_svc_num_of_positions, NULL, &num_of_positions), ); +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)); + 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; + } + } + + 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 bt_gatt_notify(NULL, &split_svc.attrs[1], &position_state, sizeof(position_state)); + return send_position_state(); } int zmk_split_bt_position_released(uint8_t position) { WRITE_BIT(position_state[position / 8], position % 8, false); - return bt_gatt_notify(NULL, &split_svc.attrs[1], &position_state, sizeof(position_state)); -} \ No newline at end of file + return send_position_state(); +} + +int service_init(const struct device *_arg) { + k_work_q_start(&service_work_q, service_q_stack, K_THREAD_STACK_SIZEOF(service_q_stack), + CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY); + + return 0; +} + +SYS_INIT(service_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY);