From e92a98625a6a0c9fa178ce16ffc1fffcc4a41a6e Mon Sep 17 00:00:00 2001 From: Megamind <68985133+megamind4089@users.noreply.github.com> Date: Thu, 3 Feb 2022 22:57:02 +0800 Subject: [PATCH 1/6] Add wired split support using UART communication --- app/CMakeLists.txt | 9 +- app/Kconfig | 59 +++++- app/boards/shields/a_dux/Kconfig.defconfig | 28 ++- .../shields/a_dux/boards/stemcell.overlay | 37 ++++ app/include/zmk/split/bluetooth/central.h | 2 +- app/include/zmk/split/bluetooth/service.h | 3 - app/include/zmk/split/common.h | 24 +++ app/src/split/bluetooth/service.c | 5 +- app/src/split/serial/central.c | 190 ++++++++++++++++++ app/src/split/serial/service.c | 106 ++++++++++ app/src/split_listener.c | 8 +- app/west.yml | 6 +- 12 files changed, 457 insertions(+), 20 deletions(-) create mode 100644 app/boards/shields/a_dux/boards/stemcell.overlay create mode 100644 app/include/zmk/split/common.h create mode 100644 app/src/split/serial/central.c create mode 100644 app/src/split/serial/service.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 25f6c6cd0ef..41a26d5cc4c 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -45,7 +45,7 @@ target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed target_sources_ifdef(CONFIG_USB app PRIVATE src/events/usb_conn_state_changed.c) target_sources(app PRIVATE src/behaviors/behavior_reset.c) target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c) -if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) +if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL OR CONFIG_ZMK_SPLIT_SERIAL_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_key_press.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c) @@ -75,6 +75,13 @@ endif() if (CONFIG_ZMK_SPLIT_BLE AND CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) target_sources(app PRIVATE src/split/bluetooth/central.c) endif() +if (CONFIG_ZMK_SPLIT_SERIAL AND (NOT CONFIG_ZMK_SPLIT_SERIAL_ROLE_CENTRAL)) + target_sources(app PRIVATE src/split_listener.c) + target_sources(app PRIVATE src/split/serial/service.c) +endif() +if (CONFIG_ZMK_SPLIT_SERIAL AND CONFIG_ZMK_SPLIT_SERIAL_ROLE_CENTRAL) + target_sources(app PRIVATE src/split/serial/central.c) +endif() target_sources_ifdef(CONFIG_USB app PRIVATE src/usb.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/hog.c) target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow.c) diff --git a/app/Kconfig b/app/Kconfig index 1c9f929db66..81c08b313de 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -155,10 +155,14 @@ config ZMK_SPLIT if ZMK_SPLIT +choice ZMK_SPLIT_TYPE + prompt "ZMK split type" + +if ZMK_BLE + menuconfig ZMK_SPLIT_BLE bool "Split keyboard support via BLE transport" depends on ZMK_BLE - default y select BT_USER_PHY_UPDATE if ZMK_SPLIT_BLE @@ -199,9 +203,6 @@ 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 - config BT_MAX_PAIRED default 1 @@ -217,6 +218,56 @@ endif #ZMK_SPLIT_BLE endif +# ZMK_BLE +endif + +if ZMK_USB + +menuconfig ZMK_SPLIT_SERIAL + bool "Split keyboard support via Serial transport" + depends on ZMK_USB + +if ZMK_SPLIT_SERIAL + +choice ZMK_SPLIT_SERIAL_ROLE + prompt "ZMK split serial role" + +menuconfig ZMK_SPLIT_SERIAL_ROLE_CENTRAL + bool "Central" + +menuconfig ZMK_SPLIT_SERIAL_ROLE_PERIPHERAL + bool "Peripheral" + +if ZMK_SPLIT_SERIAL_ROLE_PERIPHERAL + +config ZMK_SPLIT_SERIAL_PERIPHERAL_STACK_SIZE + int "Serial split peripheral notify thread stack size" + default 512 + +config ZMK_SPLIT_SERIAL_PERIPHERAL_PRIORITY + int "Serial split peripheral notify thread priority" + default 5 + +config ZMK_SPLIT_SERIAL_PERIPHERAL_POSITION_QUEUE_SIZE + int "Max number of key position state events to queue to send to the central" + default 10 + +# ZMK_SPLIT_SERIAL_ROLE_PERIPHERAL +endif + +# ZMK_SPLIT_SERIAL_ROLE +endchoice + +# ZMK_SPLIT_SERIAL +endif + +# ZMK_USB +endif + +# ZMK_SPLIT_TYPE +endchoice + + #ZMK_SPLIT endif diff --git a/app/boards/shields/a_dux/Kconfig.defconfig b/app/boards/shields/a_dux/Kconfig.defconfig index 3d77a5f8423..9e42da222c9 100644 --- a/app/boards/shields/a_dux/Kconfig.defconfig +++ b/app/boards/shields/a_dux/Kconfig.defconfig @@ -1,19 +1,41 @@ # Copyright (c) 2021 The ZMK Contributors # SPDX-License-Identifier: MIT +if SHIELD_A_DUX_LEFT || SHIELD_A_DUX_RIGHT + +if ZMK_BLE || ZMK_USB +config ZMK_SPLIT + default y +endif + if SHIELD_A_DUX_LEFT config ZMK_KEYBOARD_NAME default "A. Dux" +if ZMK_BLE config ZMK_SPLIT_BLE_ROLE_CENTRAL default y +endif +if ZMK_USB +choice ZMK_SPLIT_SERIAL_ROLE + default ZMK_SPLIT_SERIAL_ROLE_CENTRAL +endchoice endif -if SHIELD_A_DUX_LEFT || SHIELD_A_DUX_RIGHT +# SHIELD_A_DUX_LEFT +endif -config ZMK_SPLIT - default y +if SHIELD_A_DUX_RIGHT + +if ZMK_USB +choice ZMK_SPLIT_SERIAL_ROLE + default ZMK_SPLIT_SERIAL_ROLE_PERIPHERAL +endchoice +endif + +# SHIELD_A_DUX_RIGHT +endif endif diff --git a/app/boards/shields/a_dux/boards/stemcell.overlay b/app/boards/shields/a_dux/boards/stemcell.overlay new file mode 100644 index 00000000000..1bedcfebfce --- /dev/null +++ b/app/boards/shields/a_dux/boards/stemcell.overlay @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + chosen { + zmk,split-serial = &usart1; + }; +}; + +&usart1 { + status = "okay"; +}; + +&kscan0 { + input-gpios = + <&pro_micro_d 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_d 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_a 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_d 16 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_a 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_d 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_d 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, /* Changed since swapped pins */ + <&pro_micro_a 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_d 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_a 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_d 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_d 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, /* TODO: Use 0 if Console is needed */ + <&pro_micro_d 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_d 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_d 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&pro_micro_d 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + ; +}; diff --git a/app/include/zmk/split/bluetooth/central.h b/app/include/zmk/split/bluetooth/central.h index 072408605cf..4c112453ec9 100644 --- a/app/include/zmk/split/bluetooth/central.h +++ b/app/include/zmk/split/bluetooth/central.h @@ -5,4 +5,4 @@ #include int zmk_split_bt_invoke_behavior(uint8_t source, struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event, bool state); \ No newline at end of file + struct zmk_behavior_binding_event event, bool state); diff --git a/app/include/zmk/split/bluetooth/service.h b/app/include/zmk/split/bluetooth/service.h index f0c1d79ff7d..27744ebfacc 100644 --- a/app/include/zmk/split/bluetooth/service.h +++ b/app/include/zmk/split/bluetooth/service.h @@ -19,6 +19,3 @@ struct zmk_split_run_behavior_payload { struct zmk_split_run_behavior_data data; 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); \ No newline at end of file diff --git a/app/include/zmk/split/common.h b/app/include/zmk/split/common.h new file mode 100644 index 00000000000..821c0d86b5b --- /dev/null +++ b/app/include/zmk/split/common.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include + +#define SPLIT_DATA_LEN 16 + +#define SPLIT_TYPE_KEYPOSITION 0 + +typedef struct _split_data_t { + uint16_t type; + uint8_t data[SPLIT_DATA_LEN]; + uint16_t crc; +} split_data_t; + +int zmk_split_position_pressed(uint8_t position); + +int zmk_split_position_released(uint8_t position); + diff --git a/app/src/split/bluetooth/service.c b/app/src/split/bluetooth/service.c index 7de78506fce..d3fa4dfcd1e 100644 --- a/app/src/split/bluetooth/service.c +++ b/app/src/split/bluetooth/service.c @@ -19,6 +19,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include +#include #include #define POS_STATE_LEN 16 @@ -141,12 +142,12 @@ int send_position_state() { return 0; } -int zmk_split_bt_position_pressed(uint8_t position) { +int zmk_split_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) { +int zmk_split_position_released(uint8_t position) { WRITE_BIT(position_state[position / 8], position % 8, false); return send_position_state(); } diff --git a/app/src/split/serial/central.c b/app/src/split/serial/central.c new file mode 100644 index 00000000000..80186614533 --- /dev/null +++ b/app/src/split/serial/central.c @@ -0,0 +1,190 @@ +/* +* Copyright (c) 2022 The ZMK Contributors +* +* SPDX-License-Identifier: MIT +*/ + +#include +#include + +#include +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include + +#if DT_HAS_CHOSEN(zmk_split_serial) + +#define UART_NODE1 DT_CHOSEN(zmk_split_serial) +const struct device *serial_dev = DEVICE_DT_GET(UART_NODE1); +static int uart_ready = 0; + +#define CONFIG_ZMK_SPLIT_SERIAL_CENTRAL_POSITION_QUEUE_SIZE 5 + +#define CONFIG_SERIAL_THREAD_STACK_SIZE 128 +#define CONFIG_SERIAL_THREAD_PRIORITY 5 + +static void split_serial_receive_thread(void *unused, void *unused1, void *unused2); + +K_MEM_SLAB_DEFINE(split_memory_slab, sizeof(split_data_t), \ + CONFIG_ZMK_SPLIT_SERIAL_CENTRAL_POSITION_QUEUE_SIZE, 4); + +K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed), \ + CONFIG_ZMK_SPLIT_SERIAL_CENTRAL_POSITION_QUEUE_SIZE, 4); + +K_THREAD_DEFINE(split_central, CONFIG_SERIAL_THREAD_STACK_SIZE, + split_serial_receive_thread, NULL, NULL, NULL, + K_PRIO_PREEMPT(CONFIG_SERIAL_THREAD_PRIORITY), 0, 0); + + +static 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); + ZMK_EVENT_RAISE(new_zmk_position_state_changed(ev)); + } +} + +K_WORK_DEFINE(peripheral_event_work, peripheral_event_work_callback); + +static uint8_t split_central_notify_func(const void *data, uint16_t length) { + static uint8_t position_state[SPLIT_DATA_LEN]; + uint8_t changed_positions[SPLIT_DATA_LEN]; + const split_data_t *split_data = data; + uint16_t crc; + + LOG_DBG("[NOTIFICATION] data %p type:%u CRC:%u", data, split_data->type, split_data->crc); + + crc = crc16_ansi(split_data->data, sizeof(split_data->data)); + if (crc != split_data->crc) { + LOG_WRN("CRC mismatch (%x:%x), skipping data", crc, split_data->crc); + return 0; + } + + + for (int i = 0; i < SPLIT_DATA_LEN; i++) { + changed_positions[i] = split_data->data[i] ^ position_state[i]; + position_state[i] = split_data->data[i]; + } + + for (int i = 0; i < SPLIT_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); + struct zmk_position_state_changed ev = { + .position = position, .state = pressed, .timestamp = k_uptime_get()}; + + if (position > ZMK_KEYMAP_LEN) { + LOG_WRN("Invalid position: %u", position); + continue; + } + + k_msgq_put(&peripheral_event_msgq, &ev, K_NO_WAIT); + k_work_submit(&peripheral_event_work); + } + } + } + + return 0; +} + +static char *alloc_position_state_buffer() { + char *block_ptr = NULL; + if (k_mem_slab_alloc(&split_memory_slab, (void **)&block_ptr, K_NO_WAIT) == 0) { + memset(block_ptr, 0, SPLIT_DATA_LEN); + } else { + LOG_WRN("Memory allocation time-out"); + } + return block_ptr; +} + + +static void free_position_state_buffer(char *block_ptr) { + k_mem_slab_free(&split_memory_slab, (void **)&block_ptr); +} + + +static void uart_callback(const struct device *dev, struct uart_event *evt, void *user_data) { + char *buf = NULL; + + switch (evt->type) { + + case UART_RX_STOPPED: + LOG_DBG("UART device:%s rx stopped", serial_dev->name); + break; + + case UART_RX_BUF_REQUEST: + LOG_DBG("UART device:%s rx extra buf req", serial_dev->name); + buf = alloc_position_state_buffer(); + if (NULL != buf) { + int ret = uart_rx_buf_rsp(serial_dev, buf, sizeof(split_data_t)); + if (0 != ret) { + LOG_WRN("UART device:%s rx extra buf req add failed: %d", serial_dev->name, ret); + free_position_state_buffer(buf); + } + } + break; + + case UART_RX_RDY: + LOG_DBG("UART device:%s rx buf ready", serial_dev->name); + break; + + case UART_RX_BUF_RELEASED: + LOG_DBG("UART device:%s rx buf released", serial_dev->name); + split_central_notify_func(evt->data.rx_buf.buf, sizeof(split_data_t)); + free_position_state_buffer(evt->data.rx_buf.buf); + break; + + default: + LOG_DBG("UART device:%s unhandled event: %u", serial_dev->name, evt->type); + break; + }; + return; +} + + +static void split_serial_receive_thread(void *unused, void *unused1, void *unused2) { + if (!device_is_ready(serial_dev)) { + LOG_WRN("UART device:%s not ready", serial_dev->name); + return; + } + + int ret = uart_callback_set(serial_dev, uart_callback, NULL); + if (ret == -ENOTSUP || ret == -ENOSYS) { + LOG_WRN("UART device:%s ASYNC not supported", serial_dev->name); + return; + } + + uart_ready = 1; + LOG_DBG("UART device:%s ready", serial_dev->name); + + while(true) { + char *buf = alloc_position_state_buffer(); + if (NULL == buf) { + k_msleep(100); + continue; + } + + int ret = uart_rx_enable(serial_dev, buf, sizeof(split_data_t), SYS_FOREVER_MS); + if (ret == -ENOTSUP) { + LOG_WRN("UART device:%s not supporting DMA", serial_dev->name); + free_position_state_buffer(buf); + return; + } + if (ret != 0 && ret != -EBUSY) { + LOG_WRN("UART device:%s RX error:%d", serial_dev->name, ret); + free_position_state_buffer(buf); + continue; + } + }; +} + +#endif /* DT_HAS_CHOSEN(zmk_matrix_transform) */ + diff --git a/app/src/split/serial/service.c b/app/src/split/serial/service.c new file mode 100644 index 00000000000..d3b7e3253f6 --- /dev/null +++ b/app/src/split/serial/service.c @@ -0,0 +1,106 @@ +/* +* Copyright (c) 2022 The ZMK Contributors +* +* SPDX-License-Identifier: MIT +*/ + +#include +#include +#include +#include + +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include + +#if DT_HAS_CHOSEN(zmk_split_serial) + +#define UART_NODE1 DT_CHOSEN(zmk_split_serial) +const struct device *serial_dev = DEVICE_DT_GET(UART_NODE1); +static int uart_ready = 0; + +static uint8_t position_state[SPLIT_DATA_LEN]; + +K_THREAD_STACK_DEFINE(service_q_stack, CONFIG_ZMK_SPLIT_SERIAL_PERIPHERAL_STACK_SIZE); + +struct k_work_q service_work_q; + +K_MSGQ_DEFINE(position_state_msgq, sizeof(char[SPLIT_DATA_LEN]), + CONFIG_ZMK_SPLIT_SERIAL_PERIPHERAL_POSITION_QUEUE_SIZE, 4); + + +void send_data_via_uart(const struct device *dev, char *data, size_t len) { + if (!uart_ready) { + return; + } + + for (int i=0; i < len; i++) { + uart_poll_out(serial_dev, data[i]); + } +} + +void send_position_state_callback(struct k_work *work) { + split_data_t split_data = {.type = SPLIT_TYPE_KEYPOSITION}; + + while (k_msgq_get(&position_state_msgq, &split_data.data, K_NO_WAIT) == 0) { + split_data.crc = crc16_ansi(split_data.data, sizeof(split_data.data)); + send_data_via_uart(serial_dev, (void *)&split_data, sizeof(split_data)); + } +}; + +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[SPLIT_DATA_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(); +} + +int service_init(const struct device *_arg) { + + if (!device_is_ready(serial_dev)) { + LOG_WRN("UART device:%s not ready", serial_dev->name); + return 1; + } + + uart_ready = 1; + LOG_INF("UART device:%s ready", serial_dev->name); + k_work_q_start(&service_work_q, service_q_stack, K_THREAD_STACK_SIZEOF(service_q_stack), + CONFIG_ZMK_SPLIT_SERIAL_PERIPHERAL_PRIORITY); + + return 0; +} + +SYS_INIT(service_init, APPLICATION, CONFIG_ZMK_USB_INIT_PRIORITY); + +#endif /* DT_HAS_CHOSEN(zmk_matrix_transform) */ + diff --git a/app/src/split_listener.c b/app/src/split_listener.c index 01cd89d912f..1e13d25cc17 100644 --- a/app/src/split_listener.c +++ b/app/src/split_listener.c @@ -8,7 +8,7 @@ #include #include -#include +#include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -22,13 +22,13 @@ int split_listener(const zmk_event_t *eh) { const struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh); if (ev != NULL) { if (ev->state) { - return zmk_split_bt_position_pressed(ev->position); + return zmk_split_position_pressed(ev->position); } else { - return zmk_split_bt_position_released(ev->position); + return zmk_split_position_released(ev->position); } } return ZMK_EV_EVENT_BUBBLE; } ZMK_LISTENER(split_listener, split_listener); -ZMK_SUBSCRIPTION(split_listener, zmk_position_state_changed); \ No newline at end of file +ZMK_SUBSCRIPTION(split_listener, zmk_position_state_changed); diff --git a/app/west.yml b/app/west.yml index cab02a5a7f7..79a8ea7ec79 100644 --- a/app/west.yml +++ b/app/west.yml @@ -4,12 +4,14 @@ manifest: url-base: https://github.com/zephyrproject-rtos - name: zmkfirmware url-base: https://github.com/zmkfirmware + - name: megamind4089 + url-base: https://github.com/megamind4089 - name: microsoft url-base: https://github.com/microsoft projects: - name: zephyr - remote: zmkfirmware - revision: v2.5.0+zmk-fixes + remote: megamind4089 + revision: v2.5.0+zmk-fixes-stm32f4 clone-depth: 1 import: # TODO: Rename once upstream offers option like `exclude` or `denylist` From f357aeddc8c1f716c00621fe74a6d08883c183f1 Mon Sep 17 00:00:00 2001 From: Megamind <68985133+megamind4089@users.noreply.github.com> Date: Sun, 6 Feb 2022 16:14:36 +0800 Subject: [PATCH 2/6] Address comments --- app/Kconfig | 25 +++++++++------------- app/boards/shields/a_dux/Kconfig.defconfig | 2 -- app/src/split/serial/central.c | 19 ++++++---------- app/src/split/serial/service.c | 12 +++++------ 4 files changed, 23 insertions(+), 35 deletions(-) diff --git a/app/Kconfig b/app/Kconfig index 81c08b313de..d406864ec9b 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -238,25 +238,20 @@ menuconfig ZMK_SPLIT_SERIAL_ROLE_CENTRAL menuconfig ZMK_SPLIT_SERIAL_ROLE_PERIPHERAL bool "Peripheral" -if ZMK_SPLIT_SERIAL_ROLE_PERIPHERAL +# ZMK_SPLIT_SERIAL_ROLE +endchoice -config ZMK_SPLIT_SERIAL_PERIPHERAL_STACK_SIZE - int "Serial split peripheral notify thread stack size" - default 512 +config ZMK_SPLIT_SERIAL_THREAD_STACK_SIZE + int "Serial split thread stack size" + default 128 -config ZMK_SPLIT_SERIAL_PERIPHERAL_PRIORITY - int "Serial split peripheral notify thread priority" +config ZMK_SPLIT_SERIAL_THREAD_PRIORITY + int "Serial split thread priority" default 5 -config ZMK_SPLIT_SERIAL_PERIPHERAL_POSITION_QUEUE_SIZE - int "Max number of key position state events to queue to send to the central" - default 10 - -# ZMK_SPLIT_SERIAL_ROLE_PERIPHERAL -endif - -# ZMK_SPLIT_SERIAL_ROLE -endchoice +config ZMK_SPLIT_SERIAL_THREAD_QUEUE_SIZE + int "Max number of queue size for split data buffering" + default 5 # ZMK_SPLIT_SERIAL endif diff --git a/app/boards/shields/a_dux/Kconfig.defconfig b/app/boards/shields/a_dux/Kconfig.defconfig index 9e42da222c9..63612e56908 100644 --- a/app/boards/shields/a_dux/Kconfig.defconfig +++ b/app/boards/shields/a_dux/Kconfig.defconfig @@ -3,10 +3,8 @@ if SHIELD_A_DUX_LEFT || SHIELD_A_DUX_RIGHT -if ZMK_BLE || ZMK_USB config ZMK_SPLIT default y -endif if SHIELD_A_DUX_LEFT diff --git a/app/src/split/serial/central.c b/app/src/split/serial/central.c index 80186614533..54e8dd2b9a6 100644 --- a/app/src/split/serial/central.c +++ b/app/src/split/serial/central.c @@ -19,28 +19,25 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include -#if DT_HAS_CHOSEN(zmk_split_serial) +#if !DT_HAS_CHOSEN(zmk_split_serial) + #error "No zmk-split-serial node is chosen" +#endif #define UART_NODE1 DT_CHOSEN(zmk_split_serial) const struct device *serial_dev = DEVICE_DT_GET(UART_NODE1); static int uart_ready = 0; -#define CONFIG_ZMK_SPLIT_SERIAL_CENTRAL_POSITION_QUEUE_SIZE 5 - -#define CONFIG_SERIAL_THREAD_STACK_SIZE 128 -#define CONFIG_SERIAL_THREAD_PRIORITY 5 - static void split_serial_receive_thread(void *unused, void *unused1, void *unused2); K_MEM_SLAB_DEFINE(split_memory_slab, sizeof(split_data_t), \ - CONFIG_ZMK_SPLIT_SERIAL_CENTRAL_POSITION_QUEUE_SIZE, 4); + CONFIG_ZMK_SPLIT_SERIAL_THREAD_QUEUE_SIZE, 4); K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed), \ - CONFIG_ZMK_SPLIT_SERIAL_CENTRAL_POSITION_QUEUE_SIZE, 4); + CONFIG_ZMK_SPLIT_SERIAL_THREAD_QUEUE_SIZE, 4); -K_THREAD_DEFINE(split_central, CONFIG_SERIAL_THREAD_STACK_SIZE, +K_THREAD_DEFINE(split_central, CONFIG_ZMK_SPLIT_SERIAL_THREAD_STACK_SIZE, split_serial_receive_thread, NULL, NULL, NULL, - K_PRIO_PREEMPT(CONFIG_SERIAL_THREAD_PRIORITY), 0, 0); + K_PRIO_PREEMPT(CONFIG_ZMK_SPLIT_SERIAL_THREAD_PRIORITY), 0, 0); static void peripheral_event_work_callback(struct k_work *work) { @@ -186,5 +183,3 @@ static void split_serial_receive_thread(void *unused, void *unused1, void *unuse }; } -#endif /* DT_HAS_CHOSEN(zmk_matrix_transform) */ - diff --git a/app/src/split/serial/service.c b/app/src/split/serial/service.c index d3b7e3253f6..a0559967e23 100644 --- a/app/src/split/serial/service.c +++ b/app/src/split/serial/service.c @@ -18,7 +18,9 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include -#if DT_HAS_CHOSEN(zmk_split_serial) +#if !DT_HAS_CHOSEN(zmk_split_serial) + #error "No zmk-split-serial node is chosen" +#endif #define UART_NODE1 DT_CHOSEN(zmk_split_serial) const struct device *serial_dev = DEVICE_DT_GET(UART_NODE1); @@ -26,12 +28,12 @@ static int uart_ready = 0; static uint8_t position_state[SPLIT_DATA_LEN]; -K_THREAD_STACK_DEFINE(service_q_stack, CONFIG_ZMK_SPLIT_SERIAL_PERIPHERAL_STACK_SIZE); +K_THREAD_STACK_DEFINE(service_q_stack, CONFIG_ZMK_SPLIT_SERIAL_THREAD_STACK_SIZE); struct k_work_q service_work_q; K_MSGQ_DEFINE(position_state_msgq, sizeof(char[SPLIT_DATA_LEN]), - CONFIG_ZMK_SPLIT_SERIAL_PERIPHERAL_POSITION_QUEUE_SIZE, 4); + CONFIG_ZMK_SPLIT_SERIAL_THREAD_QUEUE_SIZE, 4); void send_data_via_uart(const struct device *dev, char *data, size_t len) { @@ -95,12 +97,10 @@ int service_init(const struct device *_arg) { uart_ready = 1; LOG_INF("UART device:%s ready", serial_dev->name); k_work_q_start(&service_work_q, service_q_stack, K_THREAD_STACK_SIZEOF(service_q_stack), - CONFIG_ZMK_SPLIT_SERIAL_PERIPHERAL_PRIORITY); + CONFIG_ZMK_SPLIT_SERIAL_THREAD_PRIORITY); return 0; } SYS_INIT(service_init, APPLICATION, CONFIG_ZMK_USB_INIT_PRIORITY); -#endif /* DT_HAS_CHOSEN(zmk_matrix_transform) */ - From ace8cc307e6a3bc0931046595e4375af909f5f9f Mon Sep 17 00:00:00 2001 From: Megamind <68985133+megamind4089@users.noreply.github.com> Date: Sun, 6 Feb 2022 16:18:35 +0800 Subject: [PATCH 3/6] Clang formatter changes --- app/include/zmk/split/common.h | 1 - app/src/activity.c | 4 +- app/src/split/serial/central.c | 72 ++++++++++++++++------------------ app/src/split/serial/service.c | 32 +++++++-------- 4 files changed, 50 insertions(+), 59 deletions(-) diff --git a/app/include/zmk/split/common.h b/app/include/zmk/split/common.h index 821c0d86b5b..222c52aad8d 100644 --- a/app/include/zmk/split/common.h +++ b/app/include/zmk/split/common.h @@ -21,4 +21,3 @@ typedef struct _split_data_t { int zmk_split_position_pressed(uint8_t position); int zmk_split_position_released(uint8_t position); - diff --git a/app/src/activity.c b/app/src/activity.c index f31e608dbc5..458fceead99 100644 --- a/app/src/activity.c +++ b/app/src/activity.c @@ -62,8 +62,8 @@ void activity_work_handler(struct k_work *work) { } else #endif /* IS_ENABLED(CONFIG_ZMK_SLEEP) */ if (inactive_time > MAX_IDLE_MS) { - set_state(ZMK_ACTIVITY_IDLE); - } + set_state(ZMK_ACTIVITY_IDLE); + } } K_WORK_DEFINE(activity_work, activity_work_handler); diff --git a/app/src/split/serial/central.c b/app/src/split/serial/central.c index 54e8dd2b9a6..a36eeec268b 100644 --- a/app/src/split/serial/central.c +++ b/app/src/split/serial/central.c @@ -1,8 +1,8 @@ /* -* Copyright (c) 2022 The ZMK Contributors -* -* SPDX-License-Identifier: MIT -*/ + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ #include #include @@ -20,7 +20,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #if !DT_HAS_CHOSEN(zmk_split_serial) - #error "No zmk-split-serial node is chosen" +#error "No zmk-split-serial node is chosen" #endif #define UART_NODE1 DT_CHOSEN(zmk_split_serial) @@ -29,17 +29,16 @@ static int uart_ready = 0; static void split_serial_receive_thread(void *unused, void *unused1, void *unused2); -K_MEM_SLAB_DEFINE(split_memory_slab, sizeof(split_data_t), \ +K_MEM_SLAB_DEFINE(split_memory_slab, sizeof(split_data_t), CONFIG_ZMK_SPLIT_SERIAL_THREAD_QUEUE_SIZE, 4); -K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed), \ +K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed), CONFIG_ZMK_SPLIT_SERIAL_THREAD_QUEUE_SIZE, 4); K_THREAD_DEFINE(split_central, CONFIG_ZMK_SPLIT_SERIAL_THREAD_STACK_SIZE, split_serial_receive_thread, NULL, NULL, NULL, K_PRIO_PREEMPT(CONFIG_ZMK_SPLIT_SERIAL_THREAD_PRIORITY), 0, 0); - static 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) { @@ -64,7 +63,6 @@ static uint8_t split_central_notify_func(const void *data, uint16_t length) { return 0; } - for (int i = 0; i < SPLIT_DATA_LEN; i++) { changed_positions[i] = split_data->data[i] ^ position_state[i]; position_state[i] = split_data->data[i]; @@ -102,51 +100,48 @@ static char *alloc_position_state_buffer() { return block_ptr; } - static void free_position_state_buffer(char *block_ptr) { k_mem_slab_free(&split_memory_slab, (void **)&block_ptr); } - static void uart_callback(const struct device *dev, struct uart_event *evt, void *user_data) { char *buf = NULL; switch (evt->type) { - case UART_RX_STOPPED: - LOG_DBG("UART device:%s rx stopped", serial_dev->name); - break; - - case UART_RX_BUF_REQUEST: - LOG_DBG("UART device:%s rx extra buf req", serial_dev->name); - buf = alloc_position_state_buffer(); - if (NULL != buf) { - int ret = uart_rx_buf_rsp(serial_dev, buf, sizeof(split_data_t)); - if (0 != ret) { - LOG_WRN("UART device:%s rx extra buf req add failed: %d", serial_dev->name, ret); - free_position_state_buffer(buf); - } + case UART_RX_STOPPED: + LOG_DBG("UART device:%s rx stopped", serial_dev->name); + break; + + case UART_RX_BUF_REQUEST: + LOG_DBG("UART device:%s rx extra buf req", serial_dev->name); + buf = alloc_position_state_buffer(); + if (NULL != buf) { + int ret = uart_rx_buf_rsp(serial_dev, buf, sizeof(split_data_t)); + if (0 != ret) { + LOG_WRN("UART device:%s rx extra buf req add failed: %d", serial_dev->name, ret); + free_position_state_buffer(buf); } - break; + } + break; - case UART_RX_RDY: - LOG_DBG("UART device:%s rx buf ready", serial_dev->name); - break; + case UART_RX_RDY: + LOG_DBG("UART device:%s rx buf ready", serial_dev->name); + break; - case UART_RX_BUF_RELEASED: - LOG_DBG("UART device:%s rx buf released", serial_dev->name); - split_central_notify_func(evt->data.rx_buf.buf, sizeof(split_data_t)); - free_position_state_buffer(evt->data.rx_buf.buf); - break; + case UART_RX_BUF_RELEASED: + LOG_DBG("UART device:%s rx buf released", serial_dev->name); + split_central_notify_func(evt->data.rx_buf.buf, sizeof(split_data_t)); + free_position_state_buffer(evt->data.rx_buf.buf); + break; - default: - LOG_DBG("UART device:%s unhandled event: %u", serial_dev->name, evt->type); - break; + default: + LOG_DBG("UART device:%s unhandled event: %u", serial_dev->name, evt->type); + break; }; return; } - static void split_serial_receive_thread(void *unused, void *unused1, void *unused2) { if (!device_is_ready(serial_dev)) { LOG_WRN("UART device:%s not ready", serial_dev->name); @@ -162,7 +157,7 @@ static void split_serial_receive_thread(void *unused, void *unused1, void *unuse uart_ready = 1; LOG_DBG("UART device:%s ready", serial_dev->name); - while(true) { + while (true) { char *buf = alloc_position_state_buffer(); if (NULL == buf) { k_msleep(100); @@ -182,4 +177,3 @@ static void split_serial_receive_thread(void *unused, void *unused1, void *unuse } }; } - diff --git a/app/src/split/serial/service.c b/app/src/split/serial/service.c index a0559967e23..d5e29a68c64 100644 --- a/app/src/split/serial/service.c +++ b/app/src/split/serial/service.c @@ -1,8 +1,8 @@ /* -* Copyright (c) 2022 The ZMK Contributors -* -* SPDX-License-Identifier: MIT -*/ + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ #include #include @@ -19,7 +19,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #if !DT_HAS_CHOSEN(zmk_split_serial) - #error "No zmk-split-serial node is chosen" +#error "No zmk-split-serial node is chosen" #endif #define UART_NODE1 DT_CHOSEN(zmk_split_serial) @@ -35,13 +35,12 @@ struct k_work_q service_work_q; K_MSGQ_DEFINE(position_state_msgq, sizeof(char[SPLIT_DATA_LEN]), CONFIG_ZMK_SPLIT_SERIAL_THREAD_QUEUE_SIZE, 4); - void send_data_via_uart(const struct device *dev, char *data, size_t len) { if (!uart_ready) { return; } - for (int i=0; i < len; i++) { + for (int i = 0; i < len; i++) { uart_poll_out(serial_dev, data[i]); } } @@ -61,15 +60,15 @@ 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[SPLIT_DATA_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; + case -EAGAIN: { + LOG_WRN("Position state message queue full, popping first message and queueing again"); + uint8_t discarded_state[SPLIT_DATA_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; } } @@ -103,4 +102,3 @@ int service_init(const struct device *_arg) { } SYS_INIT(service_init, APPLICATION, CONFIG_ZMK_USB_INIT_PRIORITY); - From 528fc96d60b597d0197fc8714702edba6f3f03d1 Mon Sep 17 00:00:00 2001 From: Megamind <68985133+megamind4089@users.noreply.github.com> Date: Mon, 7 Feb 2022 00:12:20 +0800 Subject: [PATCH 4/6] Update TX to async and fix RX thread support Refactor the code to common code --- app/CMakeLists.txt | 2 + app/Kconfig | 2 +- app/include/zmk/split/serial/common.h | 20 ++++ app/src/split/serial/central.c | 111 ++----------------- app/src/split/serial/common.c | 153 ++++++++++++++++++++++++++ app/src/split/serial/service.c | 46 +++----- 6 files changed, 197 insertions(+), 137 deletions(-) create mode 100644 app/include/zmk/split/serial/common.h create mode 100644 app/src/split/serial/common.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 41a26d5cc4c..1cd2176431c 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -78,9 +78,11 @@ endif() if (CONFIG_ZMK_SPLIT_SERIAL AND (NOT CONFIG_ZMK_SPLIT_SERIAL_ROLE_CENTRAL)) target_sources(app PRIVATE src/split_listener.c) target_sources(app PRIVATE src/split/serial/service.c) + target_sources(app PRIVATE src/split/serial/common.c) endif() if (CONFIG_ZMK_SPLIT_SERIAL AND CONFIG_ZMK_SPLIT_SERIAL_ROLE_CENTRAL) target_sources(app PRIVATE src/split/serial/central.c) + target_sources(app PRIVATE src/split/serial/common.c) endif() target_sources_ifdef(CONFIG_USB app PRIVATE src/usb.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/hog.c) diff --git a/app/Kconfig b/app/Kconfig index d406864ec9b..676daa1e3bb 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -243,7 +243,7 @@ endchoice config ZMK_SPLIT_SERIAL_THREAD_STACK_SIZE int "Serial split thread stack size" - default 128 + default 1024 config ZMK_SPLIT_SERIAL_THREAD_PRIORITY int "Serial split thread priority" diff --git a/app/include/zmk/split/serial/common.h b/app/include/zmk/split/serial/common.h new file mode 100644 index 00000000000..53054232506 --- /dev/null +++ b/app/include/zmk/split/serial/common.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include + +typedef int (*rx_complete_t)(const uint8_t *data, size_t length); + +void split_serial_async_init(rx_complete_t complete_fn); + +void split_serial_async_send(uint8_t *data, size_t length); + +uint8_t *alloc_position_state_buffer(k_timeout_t timeout); + +void free_position_state_buffer(const uint8_t *data); diff --git a/app/src/split/serial/central.c b/app/src/split/serial/central.c index a36eeec268b..ade025c127a 100644 --- a/app/src/split/serial/central.c +++ b/app/src/split/serial/central.c @@ -5,6 +5,7 @@ */ #include +#include #include #include @@ -19,26 +20,9 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include -#if !DT_HAS_CHOSEN(zmk_split_serial) -#error "No zmk-split-serial node is chosen" -#endif - -#define UART_NODE1 DT_CHOSEN(zmk_split_serial) -const struct device *serial_dev = DEVICE_DT_GET(UART_NODE1); -static int uart_ready = 0; - -static void split_serial_receive_thread(void *unused, void *unused1, void *unused2); - -K_MEM_SLAB_DEFINE(split_memory_slab, sizeof(split_data_t), - CONFIG_ZMK_SPLIT_SERIAL_THREAD_QUEUE_SIZE, 4); - K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed), CONFIG_ZMK_SPLIT_SERIAL_THREAD_QUEUE_SIZE, 4); -K_THREAD_DEFINE(split_central, CONFIG_ZMK_SPLIT_SERIAL_THREAD_STACK_SIZE, - split_serial_receive_thread, NULL, NULL, NULL, - K_PRIO_PREEMPT(CONFIG_ZMK_SPLIT_SERIAL_THREAD_PRIORITY), 0, 0); - static 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) { @@ -49,10 +33,10 @@ static void peripheral_event_work_callback(struct k_work *work) { K_WORK_DEFINE(peripheral_event_work, peripheral_event_work_callback); -static uint8_t split_central_notify_func(const void *data, uint16_t length) { +static int split_central_notify_func(const uint8_t *data, size_t length) { static uint8_t position_state[SPLIT_DATA_LEN]; uint8_t changed_positions[SPLIT_DATA_LEN]; - const split_data_t *split_data = data; + const split_data_t *split_data = (const split_data_t *)data; uint16_t crc; LOG_DBG("[NOTIFICATION] data %p type:%u CRC:%u", data, split_data->type, split_data->crc); @@ -90,90 +74,9 @@ static uint8_t split_central_notify_func(const void *data, uint16_t length) { return 0; } -static char *alloc_position_state_buffer() { - char *block_ptr = NULL; - if (k_mem_slab_alloc(&split_memory_slab, (void **)&block_ptr, K_NO_WAIT) == 0) { - memset(block_ptr, 0, SPLIT_DATA_LEN); - } else { - LOG_WRN("Memory allocation time-out"); - } - return block_ptr; -} - -static void free_position_state_buffer(char *block_ptr) { - k_mem_slab_free(&split_memory_slab, (void **)&block_ptr); -} - -static void uart_callback(const struct device *dev, struct uart_event *evt, void *user_data) { - char *buf = NULL; - - switch (evt->type) { - - case UART_RX_STOPPED: - LOG_DBG("UART device:%s rx stopped", serial_dev->name); - break; - - case UART_RX_BUF_REQUEST: - LOG_DBG("UART device:%s rx extra buf req", serial_dev->name); - buf = alloc_position_state_buffer(); - if (NULL != buf) { - int ret = uart_rx_buf_rsp(serial_dev, buf, sizeof(split_data_t)); - if (0 != ret) { - LOG_WRN("UART device:%s rx extra buf req add failed: %d", serial_dev->name, ret); - free_position_state_buffer(buf); - } - } - break; - - case UART_RX_RDY: - LOG_DBG("UART device:%s rx buf ready", serial_dev->name); - break; - - case UART_RX_BUF_RELEASED: - LOG_DBG("UART device:%s rx buf released", serial_dev->name); - split_central_notify_func(evt->data.rx_buf.buf, sizeof(split_data_t)); - free_position_state_buffer(evt->data.rx_buf.buf); - break; - - default: - LOG_DBG("UART device:%s unhandled event: %u", serial_dev->name, evt->type); - break; - }; - return; +static int split_serial_central_init(const struct device *dev) { + split_serial_async_init(split_central_notify_func); + return 0; } -static void split_serial_receive_thread(void *unused, void *unused1, void *unused2) { - if (!device_is_ready(serial_dev)) { - LOG_WRN("UART device:%s not ready", serial_dev->name); - return; - } - - int ret = uart_callback_set(serial_dev, uart_callback, NULL); - if (ret == -ENOTSUP || ret == -ENOSYS) { - LOG_WRN("UART device:%s ASYNC not supported", serial_dev->name); - return; - } - - uart_ready = 1; - LOG_DBG("UART device:%s ready", serial_dev->name); - - while (true) { - char *buf = alloc_position_state_buffer(); - if (NULL == buf) { - k_msleep(100); - continue; - } - - int ret = uart_rx_enable(serial_dev, buf, sizeof(split_data_t), SYS_FOREVER_MS); - if (ret == -ENOTSUP) { - LOG_WRN("UART device:%s not supporting DMA", serial_dev->name); - free_position_state_buffer(buf); - return; - } - if (ret != 0 && ret != -EBUSY) { - LOG_WRN("UART device:%s RX error:%d", serial_dev->name, ret); - free_position_state_buffer(buf); - continue; - } - }; -} +SYS_INIT(split_serial_central_init, APPLICATION, CONFIG_ZMK_USB_INIT_PRIORITY); diff --git a/app/src/split/serial/common.c b/app/src/split/serial/common.c new file mode 100644 index 00000000000..be1c9fda506 --- /dev/null +++ b/app/src/split/serial/common.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include + +#include +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include + +#if !DT_HAS_CHOSEN(zmk_split_serial) +#error "No zmk-split-serial node is chosen" +#endif + +#define UART_NODE1 DT_CHOSEN(zmk_split_serial) +const struct device *serial_dev = DEVICE_DT_GET(UART_NODE1); +static int uart_ready = 0; + +K_MEM_SLAB_DEFINE(split_memory_slab, sizeof(split_data_t), + CONFIG_ZMK_SPLIT_SERIAL_THREAD_QUEUE_SIZE, 4); + +static K_SEM_DEFINE(split_serial_rx_sem, 1, 1); + +static K_SEM_DEFINE(split_serial_tx_sem, 1, 1); + +rx_complete_t split_serial_rx_complete_fn = NULL; + +uint8_t *alloc_position_state_buffer(k_timeout_t timeout) { + uint8_t *block_ptr = NULL; + if (k_mem_slab_alloc(&split_memory_slab, (void **)&block_ptr, timeout) == 0) { + memset(block_ptr, 0, SPLIT_DATA_LEN); + } else { + LOG_WRN("Memory allocation time-out"); + } + return block_ptr; +} + +void free_position_state_buffer(const uint8_t *data) { + k_mem_slab_free(&split_memory_slab, (void **)&data); +} + +static void enable_rx(const struct device *dev) { + int ret; + uint8_t *buf = NULL; + while (!(buf = alloc_position_state_buffer(K_MSEC(100)))) { + }; + + while (0 != (ret = uart_rx_enable(serial_dev, buf, sizeof(split_data_t), SYS_FOREVER_MS))) { + LOG_WRN("UART device:%s RX error:%d", serial_dev->name, ret); + k_sleep(K_MSEC(100)); + } + return; +} + +static void uart_callback(const struct device *dev, struct uart_event *evt, void *user_data) { + uint8_t *buf = NULL; + + switch (evt->type) { + + case UART_RX_STOPPED: + LOG_DBG("UART device:%s rx stopped", serial_dev->name); + break; + + case UART_RX_BUF_REQUEST: + LOG_DBG("UART device:%s rx extra buf req", serial_dev->name); + buf = alloc_position_state_buffer(K_NO_WAIT); + if (NULL != buf) { + int ret = uart_rx_buf_rsp(serial_dev, buf, sizeof(split_data_t)); + if (0 != ret) { + LOG_WRN("UART device:%s rx extra buf req add failed: %d", serial_dev->name, ret); + free_position_state_buffer(buf); + } + } + break; + + case UART_RX_RDY: + LOG_DBG("UART device:%s rx buf ready", serial_dev->name); + break; + + case UART_RX_BUF_RELEASED: + LOG_DBG("UART device:%s rx buf released", serial_dev->name); + if (split_serial_rx_complete_fn) { + split_serial_rx_complete_fn(evt->data.rx_buf.buf, sizeof(split_data_t)); + } + free_position_state_buffer(evt->data.rx_buf.buf); + break; + + case UART_RX_DISABLED: + LOG_WRN("UART device:%s rx disabled", serial_dev->name); + enable_rx(serial_dev); + break; + + case UART_TX_DONE: + LOG_DBG("UART device:%s tx done", serial_dev->name); + free_position_state_buffer(evt->data.tx.buf); + k_sem_give(&split_serial_tx_sem); + break; + + case UART_TX_ABORTED: + LOG_WRN("UART device:%s tx aborted", serial_dev->name); + k_sem_give(&split_serial_tx_sem); + break; + + default: + LOG_DBG("UART device:%s unhandled event: %u", serial_dev->name, evt->type); + break; + }; + return; +} + +void split_serial_async_send(uint8_t *data, size_t len) { + if (!uart_ready) { + return; + } + + k_sem_take(&split_serial_tx_sem, K_FOREVER); + int err = uart_tx(serial_dev, data, len, 0); + if (0 != err) { + LOG_WRN("Failed to send data via UART: (%d)", err); + } +} + +void split_serial_async_init(rx_complete_t rx_comp_fn) { + if (!device_is_ready(serial_dev)) { + LOG_ERR("UART device:%s not ready", serial_dev->name); + return; + } + + int ret = uart_callback_set(serial_dev, uart_callback, NULL); + if (ret == -ENOTSUP || ret == -ENOSYS) { + LOG_WRN("UART device:%s ASYNC not supported", serial_dev->name); + return; + } + + split_serial_rx_complete_fn = rx_comp_fn; + + uart_ready = 1; + LOG_ERR("UART device:%s ready", serial_dev->name); + + enable_rx(serial_dev); +} diff --git a/app/src/split/serial/service.c b/app/src/split/serial/service.c index d5e29a68c64..feb1dbd89df 100644 --- a/app/src/split/serial/service.c +++ b/app/src/split/serial/service.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -18,14 +19,6 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include -#if !DT_HAS_CHOSEN(zmk_split_serial) -#error "No zmk-split-serial node is chosen" -#endif - -#define UART_NODE1 DT_CHOSEN(zmk_split_serial) -const struct device *serial_dev = DEVICE_DT_GET(UART_NODE1); -static int uart_ready = 0; - static uint8_t position_state[SPLIT_DATA_LEN]; K_THREAD_STACK_DEFINE(service_q_stack, CONFIG_ZMK_SPLIT_SERIAL_THREAD_STACK_SIZE); @@ -35,28 +28,24 @@ struct k_work_q service_work_q; K_MSGQ_DEFINE(position_state_msgq, sizeof(char[SPLIT_DATA_LEN]), CONFIG_ZMK_SPLIT_SERIAL_THREAD_QUEUE_SIZE, 4); -void send_data_via_uart(const struct device *dev, char *data, size_t len) { - if (!uart_ready) { - return; - } +static void send_position_state_callback(struct k_work *work) { + split_data_t *split_data = NULL; - for (int i = 0; i < len; i++) { - uart_poll_out(serial_dev, data[i]); - } -} + while (!(split_data = (split_data_t *)alloc_position_state_buffer(K_MSEC(100)))) { + }; -void send_position_state_callback(struct k_work *work) { - split_data_t split_data = {.type = SPLIT_TYPE_KEYPOSITION}; + memset(split_data, sizeof(split_data_t), 0); + split_data->type = SPLIT_TYPE_KEYPOSITION; - while (k_msgq_get(&position_state_msgq, &split_data.data, K_NO_WAIT) == 0) { - split_data.crc = crc16_ansi(split_data.data, sizeof(split_data.data)); - send_data_via_uart(serial_dev, (void *)&split_data, sizeof(split_data)); + while (k_msgq_get(&position_state_msgq, split_data->data, K_NO_WAIT) == 0) { + split_data->crc = crc16_ansi(split_data->data, sizeof(split_data->data)); + split_serial_async_send((uint8_t *)split_data, sizeof(*split_data)); } }; K_WORK_DEFINE(service_position_notify_work, send_position_state_callback); -int send_position_state() { +static int send_position_state() { int err = k_msgq_put(&position_state_msgq, position_state, K_MSEC(100)); if (err) { switch (err) { @@ -86,19 +75,12 @@ int zmk_split_position_released(uint8_t position) { return send_position_state(); } -int service_init(const struct device *_arg) { - - if (!device_is_ready(serial_dev)) { - LOG_WRN("UART device:%s not ready", serial_dev->name); - return 1; - } - - uart_ready = 1; - LOG_INF("UART device:%s ready", serial_dev->name); +static int split_serial_service_init(const struct device *dev) { + split_serial_async_init(NULL); k_work_q_start(&service_work_q, service_q_stack, K_THREAD_STACK_SIZEOF(service_q_stack), CONFIG_ZMK_SPLIT_SERIAL_THREAD_PRIORITY); return 0; } -SYS_INIT(service_init, APPLICATION, CONFIG_ZMK_USB_INIT_PRIORITY); +SYS_INIT(split_serial_service_init, APPLICATION, CONFIG_ZMK_USB_INIT_PRIORITY); From 8e6b5f5bd8b50e215630e770da92cde4233922a9 Mon Sep 17 00:00:00 2001 From: Megamind <68985133+megamind4089@users.noreply.github.com> Date: Mon, 7 Feb 2022 14:29:35 +0800 Subject: [PATCH 5/6] nitpicks --- app/include/zmk/split/serial/common.h | 6 ++++-- app/src/split/serial/common.c | 18 +++++++++--------- app/src/split/serial/service.c | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/include/zmk/split/serial/common.h b/app/include/zmk/split/serial/common.h index 53054232506..03b08847d55 100644 --- a/app/include/zmk/split/serial/common.h +++ b/app/include/zmk/split/serial/common.h @@ -9,12 +9,14 @@ #include #include +/* Caller does/should not need to free `data` + * Data will be freed immediately after calling this callback */ typedef int (*rx_complete_t)(const uint8_t *data, size_t length); void split_serial_async_init(rx_complete_t complete_fn); void split_serial_async_send(uint8_t *data, size_t length); -uint8_t *alloc_position_state_buffer(k_timeout_t timeout); +uint8_t *alloc_split_serial_buffer(k_timeout_t timeout); -void free_position_state_buffer(const uint8_t *data); +void free_split_serial_buffer(const uint8_t *data); diff --git a/app/src/split/serial/common.c b/app/src/split/serial/common.c index be1c9fda506..61388edd449 100644 --- a/app/src/split/serial/common.c +++ b/app/src/split/serial/common.c @@ -37,7 +37,7 @@ static K_SEM_DEFINE(split_serial_tx_sem, 1, 1); rx_complete_t split_serial_rx_complete_fn = NULL; -uint8_t *alloc_position_state_buffer(k_timeout_t timeout) { +uint8_t *alloc_split_serial_buffer(k_timeout_t timeout) { uint8_t *block_ptr = NULL; if (k_mem_slab_alloc(&split_memory_slab, (void **)&block_ptr, timeout) == 0) { memset(block_ptr, 0, SPLIT_DATA_LEN); @@ -47,14 +47,14 @@ uint8_t *alloc_position_state_buffer(k_timeout_t timeout) { return block_ptr; } -void free_position_state_buffer(const uint8_t *data) { +void free_split_serial_buffer(const uint8_t *data) { k_mem_slab_free(&split_memory_slab, (void **)&data); } static void enable_rx(const struct device *dev) { int ret; uint8_t *buf = NULL; - while (!(buf = alloc_position_state_buffer(K_MSEC(100)))) { + while (!(buf = alloc_split_serial_buffer(K_MSEC(100)))) { }; while (0 != (ret = uart_rx_enable(serial_dev, buf, sizeof(split_data_t), SYS_FOREVER_MS))) { @@ -75,12 +75,12 @@ static void uart_callback(const struct device *dev, struct uart_event *evt, void case UART_RX_BUF_REQUEST: LOG_DBG("UART device:%s rx extra buf req", serial_dev->name); - buf = alloc_position_state_buffer(K_NO_WAIT); + buf = alloc_split_serial_buffer(K_NO_WAIT); if (NULL != buf) { int ret = uart_rx_buf_rsp(serial_dev, buf, sizeof(split_data_t)); if (0 != ret) { LOG_WRN("UART device:%s rx extra buf req add failed: %d", serial_dev->name, ret); - free_position_state_buffer(buf); + free_split_serial_buffer(buf); } } break; @@ -94,7 +94,7 @@ static void uart_callback(const struct device *dev, struct uart_event *evt, void if (split_serial_rx_complete_fn) { split_serial_rx_complete_fn(evt->data.rx_buf.buf, sizeof(split_data_t)); } - free_position_state_buffer(evt->data.rx_buf.buf); + free_split_serial_buffer(evt->data.rx_buf.buf); break; case UART_RX_DISABLED: @@ -104,7 +104,7 @@ static void uart_callback(const struct device *dev, struct uart_event *evt, void case UART_TX_DONE: LOG_DBG("UART device:%s tx done", serial_dev->name); - free_position_state_buffer(evt->data.tx.buf); + free_split_serial_buffer(evt->data.tx.buf); k_sem_give(&split_serial_tx_sem); break; @@ -140,14 +140,14 @@ void split_serial_async_init(rx_complete_t rx_comp_fn) { int ret = uart_callback_set(serial_dev, uart_callback, NULL); if (ret == -ENOTSUP || ret == -ENOSYS) { - LOG_WRN("UART device:%s ASYNC not supported", serial_dev->name); + LOG_ERR("UART device:%s ASYNC not supported", serial_dev->name); return; } split_serial_rx_complete_fn = rx_comp_fn; uart_ready = 1; - LOG_ERR("UART device:%s ready", serial_dev->name); + LOG_INF("UART device:%s ready", serial_dev->name); enable_rx(serial_dev); } diff --git a/app/src/split/serial/service.c b/app/src/split/serial/service.c index feb1dbd89df..6010b02afb2 100644 --- a/app/src/split/serial/service.c +++ b/app/src/split/serial/service.c @@ -31,7 +31,7 @@ K_MSGQ_DEFINE(position_state_msgq, sizeof(char[SPLIT_DATA_LEN]), static void send_position_state_callback(struct k_work *work) { split_data_t *split_data = NULL; - while (!(split_data = (split_data_t *)alloc_position_state_buffer(K_MSEC(100)))) { + while (!(split_data = (split_data_t *)alloc_split_serial_buffer(K_MSEC(100)))) { }; memset(split_data, sizeof(split_data_t), 0); From 5a8744f183aeeb66373163538cc070c0bed52ddd Mon Sep 17 00:00:00 2001 From: Megamind <68985133+megamind4089@users.noreply.github.com> Date: Thu, 10 Feb 2022 22:45:49 +0800 Subject: [PATCH 6/6] add support for lily58 --- app/boards/shields/lily58/Kconfig.defconfig | 26 ++++++++++++++++--- .../shields/lily58/boards/stemcell.overlay | 16 ++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 app/boards/shields/lily58/boards/stemcell.overlay diff --git a/app/boards/shields/lily58/Kconfig.defconfig b/app/boards/shields/lily58/Kconfig.defconfig index c09fcd7d4dd..839e31d8fc0 100644 --- a/app/boards/shields/lily58/Kconfig.defconfig +++ b/app/boards/shields/lily58/Kconfig.defconfig @@ -1,18 +1,38 @@ +if SHIELD_LILY58_LEFT || SHIELD_LILY58_RIGHT + +config ZMK_SPLIT + default y + if SHIELD_LILY58_LEFT config ZMK_KEYBOARD_NAME default "Lily58" +if ZMK_BLE config ZMK_SPLIT_BLE_ROLE_CENTRAL default y +endif +if ZMK_USB +choice ZMK_SPLIT_SERIAL_ROLE + default ZMK_SPLIT_SERIAL_ROLE_CENTRAL +endchoice endif -if SHIELD_LILY58_LEFT || SHIELD_LILY58_RIGHT +#SHIELD_LILY58_LEFT +endif -config ZMK_SPLIT - default y +if SHIELD_LILY58_RIGHT + +if ZMK_USB +choice ZMK_SPLIT_SERIAL_ROLE + default ZMK_SPLIT_SERIAL_ROLE_PERIPHERAL +endchoice +endif + +#SHIELD_LILY58_LEFT +endif if ZMK_DISPLAY diff --git a/app/boards/shields/lily58/boards/stemcell.overlay b/app/boards/shields/lily58/boards/stemcell.overlay new file mode 100644 index 00000000000..8bc0ea71b3a --- /dev/null +++ b/app/boards/shields/lily58/boards/stemcell.overlay @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + chosen { + zmk,split-serial = &usart2; + }; +}; + +&usart2 { + status = "okay"; +}; +