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/serial/serial.h b/app/include/zmk/split/serial/serial.h new file mode 100644 index 00000000000..30487d4410b --- /dev/null +++ b/app/include/zmk/split/serial/serial.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include + +// The serial protocol is defined for payloads of up to 254 bytes. This should +// be large enough to ensure that one message can be fully buffered. +#define SERIAL_BUF_SIZE 300 + +struct serial_device { + const struct device *dev; + uint8_t rx_buf[SERIAL_BUF_SIZE], tx_buf[SERIAL_BUF_SIZE]; + struct ring_buf rx_rb, tx_rb; + struct k_work rx_work; + +#ifdef CONFIG_ZMK_SPLIT_SERIAL_UART_POLL + bool poll; + struct k_work tx_work; + struct k_timer rx_timer; +#endif +}; + +void serial_handle_rx(uint32_t cmd, uint8_t *data, uint8_t len); 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..e73bb6ab29d 100644 --- a/app/src/split/CMakeLists.txt +++ b/app/src/split/CMakeLists.txt @@ -1,6 +1,23 @@ # Copyright (c) 2022 The ZMK Contributors # SPDX-License-Identifier: MIT +if (NOT CONFIG_ZMK_SPLIT_ROLE_CENTRAL) + if (CONFIG_ZMK_SPLIT_BLE OR CONFIG_ZMK_SPLIT_SERIAL) + target_sources(app PRIVATE listener.c) + target_sources(app PRIVATE service.c) + endif() +endif() + +if (CONFIG_ZMK_SPLIT_ROLE_CENTRAL) + if (CONFIG_ZMK_SPLIT_BLE OR CONFIG_ZMK_SPLIT_SERIAL) + target_sources(app PRIVATE central.c) + endif() +endif() + if (CONFIG_ZMK_SPLIT_BLE) add_subdirectory(bluetooth) -endif() \ No newline at end of file +endif() + +if (CONFIG_ZMK_SPLIT_SERIAL) + add_subdirectory(serial) +endif() diff --git a/app/src/split/Kconfig b/app/src/split/Kconfig index ce90037b1ea..800a4fa3fa2 100644 --- a/app/src/split/Kconfig +++ b/app/src/split/Kconfig @@ -6,19 +6,59 @@ 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" -choice ZMK_SPLIT_TRANSPORT - prompt "Split transport" +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 config ZMK_SPLIT_BLE bool "BLE" + default ZMK_SPLIT && ZMK_BLE depends on ZMK_BLE select BT_USER_PHY_UPDATE select BT_AUTO_PHY_UPDATE -endchoice +config ZMK_SPLIT_SERIAL + bool "Serial" + select RING_BUFFER config ZMK_SPLIT_PERIPHERAL_HID_INDICATORS bool "Peripheral HID Indicators" @@ -30,3 +70,4 @@ config ZMK_SPLIT_PERIPHERAL_HID_INDICATORS endif rsource "bluetooth/Kconfig" +rsource "serial/Kconfig" 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/serial/CMakeLists.txt b/app/src/split/serial/CMakeLists.txt new file mode 100644 index 00000000000..cec576ccbdc --- /dev/null +++ b/app/src/split/serial/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (c) 2023 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if (NOT CONFIG_ZMK_SPLIT_ROLE_CENTRAL) + target_sources(app PRIVATE peripheral.c) +endif() + +if (CONFIG_ZMK_SPLIT_ROLE_CENTRAL) + target_sources(app PRIVATE central.c) +endif() + +target_sources(app PRIVATE serial.c) diff --git a/app/src/split/serial/Kconfig b/app/src/split/serial/Kconfig new file mode 100644 index 00000000000..3751ab6ebce --- /dev/null +++ b/app/src/split/serial/Kconfig @@ -0,0 +1,20 @@ +if ZMK_SPLIT && ZMK_SPLIT_SERIAL + +menu "Serial Transport" + +config ZMK_SPLIT_SERIAL_UART + bool "Serial over UART" + select CRC + default y + +config ZMK_SPLIT_SERIAL_UART_POLL + bool "Serial over UART Polling API" + default DT_HAS_RASPBERRYPI_PICO_UART_PIO_ENABLED || BOARD_NATIVE_SIM + +config ZMK_SPLIT_SERIAL_CDC_ACM + bool "Serial over USB CDC ACM" + default n + +endmenu + +endif diff --git a/app/src/split/serial/central.c b/app/src/split/serial/central.c new file mode 100644 index 00000000000..42f372b4412 --- /dev/null +++ b/app/src/split/serial/central.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +#include +#include +#include + +// TODO TODO TODO +#include +LOG_MODULE_DECLARE(slicemk); + +#define POSITION_STATE_DATA_LEN 16 +static uint8_t position_state[POSITION_STATE_DATA_LEN]; +static uint8_t changed_positions[POSITION_STATE_DATA_LEN]; + +static void serial_handle_bitmap(uint8_t *data, uint8_t len) { + for (int i = 0; i < POSITION_STATE_DATA_LEN; i++) { + changed_positions[i] = ((uint8_t *)data)[i] ^ position_state[i]; + position_state[i] = ((uint8_t *)data)[i]; + LOG_DBG("TODO TODO TODO data: %d", position_state[i]); + } + + for (int i = 0; i < POSITION_STATE_DATA_LEN; i++) { + for (int j = 0; j < 8; j++) { + if (changed_positions[i] & BIT(j)) { + uint32_t position = (i * 8) + j; + bool pressed = position_state[i] & BIT(j); + + // TODO TODO TODO does zero make sense? check ble central. what + // slot is central itself? + int slot = 0; + + struct zmk_position_state_changed ev = {.source = slot, + .position = position, + .state = pressed, + .timestamp = k_uptime_get()}; + zmk_position_state_change_handle(&ev); + } + } + } +} + +void serial_handle_rx(uint32_t cmd, uint8_t *data, uint8_t len) { + switch (cmd) { + // Handle split bitmap transformed (sbt) version 0. + case 0x73627400: + serial_handle_bitmap(data, len); + break; + + default: + LOG_ERR("Received unexpected UART command 0x%08x", cmd); + break; + } +} diff --git a/app/src/split/serial/peripheral.c b/app/src/split/serial/peripheral.c new file mode 100644 index 00000000000..c6299f0884f --- /dev/null +++ b/app/src/split/serial/peripheral.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +#include + +// TODO TODO TODO +#include +LOG_MODULE_DECLARE(slicemk); + +// TODO TODO TODO these two should be in a header somewhere + +// TODO TODO TODO implement central to peripheral data transfer +void serial_handle_rx(uint32_t cmd, uint8_t *data, uint8_t len) { + LOG_HEXDUMP_ERR(data, len, "central to peripheral"); +} + +void send_position_state_impl(uint8_t *state, int len) { + serial_write_uart(0x73627400, state, len); +} diff --git a/app/src/split/serial/serial.c b/app/src/split/serial/serial.c new file mode 100644 index 00000000000..1775e3a2c33 --- /dev/null +++ b/app/src/split/serial/serial.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +#include + +// TODO TODO TODO +#include +LOG_MODULE_REGISTER(slicemk); + +#define SERIAL_MSG_PREFIX "UarT" + +K_THREAD_STACK_DEFINE(serial_wq_stack, 1024); +static struct k_work_q serial_wq; + +static struct serial_device serial_devs[] = { +#ifdef CONFIG_ZMK_SPLIT_SERIAL_UART + { + .dev = DEVICE_DT_GET(DT_CHOSEN(zmk_split_uart)), +#ifdef CONFIG_ZMK_SPLIT_SERIAL_UART_POLL + .poll = true, +#endif + }, +#endif +#ifdef CONFIG_ZMK_SPLIT_SERIAL_CDC_ACM + { + .dev = DEVICE_DT_GET(DT_CHOSEN(zmk_split_cdc_acm)), + }, +#endif +}; + +#define CONFIG_ZMK_SPLIT_SERIAL_COUNT ARRAY_SIZE(serial_devs) + +static bool serial_tx_callback(struct serial_device *ud) { + // Read data from buffer. Stop transmitting if buffer is empty. + uint8_t data[32]; + int len = ring_buf_peek(&ud->tx_rb, data, sizeof(data)); + if (len == 0) { + return true; + } + + // Write data to UART and remove number of bytes written from buffer. + int ret = uart_fifo_fill(ud->dev, data, len); + if (ret < 0) { + LOG_ERR("failed to fill UART FIFO (err %d)", ret); + return true; + } + ring_buf_get(&ud->tx_rb, data, ret); + return false; +} + +static void serial_rx_work_handler(struct k_work *work) { + struct serial_device *sd = CONTAINER_OF(work, struct serial_device, rx_work); + + // Continue processing data as long as the buffer exceeds the header length + // (13 bytes). + uint8_t data[280]; + while (ring_buf_peek(&sd->rx_rb, data, 13) >= 13) { + // Discard single byte if prefix does not match. + if (memcmp(data, SERIAL_MSG_PREFIX, strlen(SERIAL_MSG_PREFIX))) { + uint8_t discard; + ring_buf_get(&sd->rx_rb, &discard, 1); + continue; + } + + // Stop processing if message body is not completely buffered. + int len = data[12]; + int total = len + 13; + if (ring_buf_size_get(&sd->rx_rb) < total) { + return; + } + + // Check message checksum and handle message. + uint32_t cmd, crc; + ring_buf_get(&sd->rx_rb, data, total); + memcpy(&cmd, &data[4], sizeof(cmd)); + memcpy(&crc, &data[8], sizeof(crc)); + if (crc == crc32_ieee(&data[13], len)) { + serial_handle_rx(cmd, &data[13], len); + } else { + LOG_ERR("received UART message with invalid CRC32 checksum"); + } + } +} + +static void serial_rx_callback(struct serial_device *sd) { + uint8_t c; + while (uart_fifo_read(sd->dev, &c, 1) == 1) { + ring_buf_put(&sd->rx_rb, &c, 1); + } + k_work_submit_to_queue(&serial_wq, &sd->rx_work); +} + +static void serial_callback(const struct device *dev, void *data) { + if (uart_irq_update(dev)) { + struct serial_device *ud = data; + + if (uart_irq_tx_ready(dev)) { + // If transmission complete, disable IRQ until next transmission. + bool complete = serial_tx_callback(ud); + if (complete) { + uart_irq_tx_disable(dev); + } + } + + // TODO TODO TODO lookup index in serial_devs array for slot ID? + if (uart_irq_rx_ready(dev)) { + serial_rx_callback(ud); + } + } +} + +static void serial_write(struct serial_device *sd, uint32_t cmd, uint8_t *data, uint8_t len) { + // TODO TODO TODO use buf with size SERIAL_BUF_SIZE. do single + // ring_buf_put() to avoid potential race + uint8_t header[13] = SERIAL_MSG_PREFIX; + memcpy(&header[4], &cmd, sizeof(cmd)); + uint32_t crc = crc32_ieee(data, len); + memcpy(&header[8], &crc, sizeof(crc)); + header[12] = len; + ring_buf_put(&sd->tx_rb, header, sizeof(header)); + ring_buf_put(&sd->tx_rb, data, len); + +#ifdef CONFIG_ZMK_SPLIT_SERIAL_UART_POLL + if (sd->poll) { + k_work_submit_to_queue(&serial_wq, &sd->tx_work); + return; + } +#endif + + uart_irq_tx_enable(sd->dev); +} + +// TODO TODO TODO this should be abstracted a bit differently +#ifdef CONFIG_ZMK_SPLIT_SERIAL_UART +void serial_write_uart(uint32_t cmd, uint8_t *data, uint8_t len) { + serial_write(&serial_devs[0], cmd, data, len); +} +#endif + +#ifdef CONFIG_ZMK_SPLIT_SERIAL_UART_POLL + +static void serial_tx_work_handler(struct k_work *work) { + struct serial_device *sd = CONTAINER_OF(work, struct serial_device, tx_work); + uint8_t c; + while (ring_buf_get(&sd->tx_rb, &c, sizeof(c))) { + uart_poll_out(sd->dev, c); + } +} + +static void serial_rx_timer_handler(struct k_timer *timer) { + struct serial_device *sd = CONTAINER_OF(timer, struct serial_device, rx_timer); + uint8_t c; + while (uart_poll_in(sd->dev, &c) == 0) { + ring_buf_put(&sd->rx_rb, &c, sizeof(c)); + } + k_work_submit_to_queue(&serial_wq, &sd->rx_work); +} + +#endif + +static int serial_init(void) { + struct k_work_queue_config uart_tx_cfg = {.name = "serial_wq"}; + k_work_queue_start(&serial_wq, serial_wq_stack, K_THREAD_STACK_SIZEOF(serial_wq_stack), 14, + &uart_tx_cfg); + + for (int i = 0; i < CONFIG_ZMK_SPLIT_SERIAL_COUNT; i++) { + struct serial_device *sd = &serial_devs[i]; + if (!device_is_ready(sd->dev)) { + LOG_ERR("failed to get serial device %s", sd->dev->name); + return 1; + } + + // Initialize ring buffer. + ring_buf_init(&sd->rx_rb, sizeof(sd->rx_buf), sd->rx_buf); + ring_buf_init(&sd->tx_rb, sizeof(sd->tx_buf), sd->tx_buf); + + k_work_init(&sd->rx_work, serial_rx_work_handler); +#ifdef CONFIG_ZMK_SPLIT_SERIAL_UART_POLL + if (sd->poll) { + k_timer_init(&sd->rx_timer, serial_rx_timer_handler, NULL); + k_timer_start(&sd->rx_timer, K_NO_WAIT, K_TICKS(1)); + k_work_init(&sd->tx_work, serial_tx_work_handler); + continue; + } +#endif + + int err = uart_irq_callback_user_data_set(sd->dev, serial_callback, sd); + if (err) { + LOG_ERR("failed to set callback for %s (err %d)", sd->dev->name, err); + return err; + } + uart_irq_rx_enable(sd->dev); + } + + return 0; +} + +SYS_INIT(serial_init, APPLICATION, CONFIG_ZMK_SPLIT_INIT_PRIORITY); 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 |