forked from zmkfirmware/zmk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
slicemk: serial split: initial central implementation
- Loading branch information
1 parent
00bd2d5
commit cd9c580
Showing
4 changed files
with
187 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |