From cd9c5802b19fd0b39db0a4f0ab17c4c8772e3f16 Mon Sep 17 00:00:00 2001 From: Xudong Zheng <7pkvm5aw@slicealias.com> Date: Thu, 7 Sep 2023 18:03:50 -0400 Subject: [PATCH] slicemk: serial split: initial central implementation --- app/src/split/CMakeLists.txt | 6 +- app/src/split/Kconfig | 3 + app/src/split/serial/CMakeLists.txt | 6 + app/src/split/serial/central.c | 173 ++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 app/src/split/serial/CMakeLists.txt create mode 100644 app/src/split/serial/central.c diff --git a/app/src/split/CMakeLists.txt b/app/src/split/CMakeLists.txt index 27abb82ad064..d4b673571b93 100644 --- a/app/src/split/CMakeLists.txt +++ b/app/src/split/CMakeLists.txt @@ -3,4 +3,8 @@ 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 dbe5f092644d..3fef6d558d6b 100644 --- a/app/src/split/Kconfig +++ b/app/src/split/Kconfig @@ -18,6 +18,9 @@ config ZMK_SPLIT_BLE select BT_USER_PHY_UPDATE select BT_AUTO_PHY_UPDATE +config ZMK_SPLIT_SERIAL + bool "Serial" + endchoice #ZMK_SPLIT diff --git a/app/src/split/serial/CMakeLists.txt b/app/src/split/serial/CMakeLists.txt new file mode 100644 index 000000000000..4f86c36e2981 --- /dev/null +++ b/app/src/split/serial/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2023 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if (CONFIG_ZMK_SPLIT_ROLE_CENTRAL) + target_sources(app PRIVATE central.c) +endif() diff --git a/app/src/split/serial/central.c b/app/src/split/serial/central.c new file mode 100644 index 000000000000..e964009d0757 --- /dev/null +++ b/app/src/split/serial/central.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +LOG_MODULE_REGISTER(slicemk); + +// TODO TODO TODO figure out nrf52 equivalent +// #include +#define FLASH_UNIQUE_ID_SIZE_BYTES 8 +static uint8_t flash_id[FLASH_UNIQUE_ID_SIZE_BYTES]; + +struct uart_data { + struct ring_buf *rx_rb, *tx_rb; + bool dtr; +}; + +// TODO TODO TODO rename rp2040 variables to reflect new zmk chosen +static const struct device *uart_rp2040_dev = DEVICE_DT_GET(DT_CHOSEN(zmk_split_serial)); + +RING_BUF_DECLARE(uart_rp2040_rx_rb, 512); +RING_BUF_DECLARE(uart_rp2040_tx_rb, 512); + +static struct uart_data uart_rp2040_data = { + .rx_rb = &uart_rp2040_rx_rb, + .tx_rb = &uart_rp2040_tx_rb, +}; + +#define POSITION_STATE_DATA_LEN 16 +static uint8_t position_state[POSITION_STATE_DATA_LEN]; +static uint8_t changed_positions[POSITION_STATE_DATA_LEN]; + +K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed), 10, 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); + ZMK_EVENT_RAISE(new_zmk_position_state_changed(ev)); + } +} + +K_WORK_DEFINE(peripheral_event_work, peripheral_event_work_callback); + +static void uart_handle(uint32_t cmd, uint8_t *data, uint8_t len) { + // TODO TODO TODO temp + if (cmd != 0x74656d70) { + return; + } + + // Skip if recipient address does not match and is not an array of zeros + // (destined for all recipients). + if (memcmp(flash_id, data, FLASH_UNIQUE_ID_SIZE_BYTES)) { + uint8_t zeros[FLASH_UNIQUE_ID_SIZE_BYTES] = {}; + if (memcmp(zeros, data, FLASH_UNIQUE_ID_SIZE_BYTES)) { + return; + } + } + + // Strip recipient address from payload. + data = &data[FLASH_UNIQUE_ID_SIZE_BYTES]; + len -= FLASH_UNIQUE_ID_SIZE_BYTES; + + 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()}; + + k_msgq_put(&peripheral_event_msgq, &ev, K_NO_WAIT); + k_work_submit(&peripheral_event_work); + } + } + } +} + +static void uart_callback_rx(const struct device *dev, struct uart_data *ud) { + uint8_t c; + while (uart_fifo_read(dev, &c, 1) == 1) { + ring_buf_put(ud->rx_rb, &c, 1); + } + + // Continue processing data as long as the buffer exceeds the header length + // (13 bytes). + uint8_t data[512]; + while (ring_buf_peek(ud->rx_rb, data, 13) >= 13) { + // Discard single byte if prefix does not match. + uint8_t prefix[] = "uart"; + if (memcmp(data, prefix, 4)) { + uint8_t discard; + ring_buf_get(ud->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(ud->rx_rb) < total) { + return; + } + + // Check message checksum and handle message. + uint32_t cmd, crc; + ring_buf_get(ud->rx_rb, data, total); + memcpy(&cmd, &data[4], sizeof(cmd)); + memcpy(&crc, &data[8], sizeof(crc)); + if (crc == crc32_ieee(&data[13], len)) { + uart_handle(cmd, &data[13], len); + } else { + LOG_ERR("received UART message with invalid CRC32 checksum"); + } + } +} + +void uart_callback(const struct device *dev, void *data) { + if (uart_irq_update(dev)) { + struct uart_data *ud = data; + if (uart_irq_rx_ready(dev)) { + uart_callback_rx(dev, ud); + } + } +} + +static int slicemk_uart_init_dev(const struct device *dev, struct uart_data *ud) { + if (!device_is_ready(dev)) { + LOG_ERR("failed to get UART device %s", dev->name); + return 1; + } + + // TODO TODO TODO 3.4 starts returning value + uart_irq_callback_user_data_set(dev, uart_callback, ud); + uart_irq_rx_enable(dev); + return 0; +} + +int slicemk_uart_init() { + // flash_get_unique_id(flash_id); + bt_addr_le_t addr = {.type = BT_ADDR_LE_RANDOM}; + sys_put_le32(NRF_FICR->DEVICEADDR[0], &addr.a.val[0]); + sys_put_le16(NRF_FICR->DEVICEADDR[1], &addr.a.val[4]); + BT_ADDR_SET_STATIC(&addr.a); + + LOG_HEXDUMP_INF(&addr, sizeof(addr), "TODO TODO TODO"); + memcpy(flash_id, &addr, sizeof(addr)); + LOG_HEXDUMP_INF(flash_id, sizeof(flash_id), "TODO TODO TODO 2"); + + int err = slicemk_uart_init_dev(uart_rp2040_dev, &uart_rp2040_data); + return err; +} + +SYS_INIT(slicemk_uart_init, APPLICATION, 0);