Skip to content

Commit

Permalink
slicemk: serial split: initial central implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
xudongzheng committed Sep 26, 2023
1 parent 00bd2d5 commit cd9c580
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 1 deletion.
6 changes: 5 additions & 1 deletion app/src/split/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@

if (CONFIG_ZMK_SPLIT_BLE)
add_subdirectory(bluetooth)
endif()
endif()

if (CONFIG_ZMK_SPLIT_SERIAL)
add_subdirectory(serial)
endif()
3 changes: 3 additions & 0 deletions app/src/split/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions app/src/split/serial/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()
173 changes: 173 additions & 0 deletions app/src/split/serial/central.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/crc.h>
#include <zephyr/sys/ring_buffer.h>

#include <zephyr/sys/byteorder.h>
#include <zephyr/bluetooth/bluetooth.h>

#include <zmk/events/position_state_changed.h>

LOG_MODULE_REGISTER(slicemk);

// TODO TODO TODO figure out nrf52 equivalent
// #include <hardware/flash.h>
#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);

0 comments on commit cd9c580

Please sign in to comment.