Skip to content

Commit

Permalink
fixup! feat(split): Add full-duplex wired split support
Browse files Browse the repository at this point in the history
  • Loading branch information
petejohanson committed Feb 5, 2025
1 parent a1ef130 commit e7b4422
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 25 deletions.
3 changes: 2 additions & 1 deletion app/src/split/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ DT_CHOSEN_ZMK_SPLIT_UART := zmk,split-uart

config ZMK_SPLIT_WIRED
bool "Wired Split"
default $(dt_chosen_enabled,$(DT_CHOSEN_ZMK_SPLIT_UART)) if !ZMK_SPLIT_BLE
default y if !ZMK_SPLIT_BLE
depends on $(dt_chosen_enabled,$(DT_CHOSEN_ZMK_SPLIT_UART)) || DT_HAS_ZMK_WIRED_SPLIT_ENABLED
select SERIAL
select RING_BUFFER
select CRC
Expand Down
9 changes: 7 additions & 2 deletions app/src/split/wired/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ choice ZMK_SPLIT_WIRED_UART_MODE_DEFAULT

config ZMK_SPLIT_WIRED_UART_MODE_DEFAULT_ASYNC
bool "Async (DMA) Mode"
depends on SERIAL_SUPPORT_ASYNC
# For now, don't use async/DMA on nRF52 due to RX bug (fixed
# in newer Zephyr version?)
depends on SERIAL_SUPPORT_ASYNC && !SOC_FAMILY_NRF
select UART_ASYNC_API

config ZMK_SPLIT_WIRED_UART_MODE_DEFAULT_INTERRUPT
Expand Down Expand Up @@ -39,6 +41,9 @@ config ZMK_SPLIT_WIRED_EVENT_BUFFER_ITEMS
int "Number of peripheral events to buffer for TX/RX"

config ZMK_SPLIT_WIRED_HALF_DUPLEX_RX_TIMEOUT
int "RX timeout (in ticks) when polling peripheral(s)"
int "RX timeout (in ms) when polling peripheral(s) and waiting for any response"

config ZMK_SPLIT_WIRED_HALF_DUPLEX_RX_COMPLETE_TIMEOUT
int "RX complete timeout (in ticks) when polling peripheral(s) after receiving some response data"

endif
5 changes: 4 additions & 1 deletion app/src/split/wired/Kconfig.defaults
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ config ZMK_SPLIT_WIRED_ASYNC_RX_TIMEOUT
endif

config ZMK_SPLIT_WIRED_HALF_DUPLEX_RX_TIMEOUT
default 50
default 15

config ZMK_SPLIT_WIRED_HALF_DUPLEX_RX_COMPLETE_TIMEOUT
default 20

endif
19 changes: 10 additions & 9 deletions app/src/split/wired/central.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ static struct zmk_split_wired_async_state async_state = {
.process_tx_work = &publish_events,
.rx_bufs = {async_rx_buf[0], async_rx_buf[1]},
.rx_bufs_len = RX_BUFFER_SIZE / 2,
.rx_size_process_trigger = sizeof(struct event_envelope),
.rx_buf = &rx_buf,
.tx_buf = &tx_buf,
#if HAS_DIR_GPIO
Expand Down Expand Up @@ -152,17 +153,15 @@ static K_WORK_DELAYABLE_DEFINE(rx_done_work, rx_done_cb);
void rx_done_cb(struct k_work *work) {
k_sem_give(&tx_sem);

if (ring_buf_size_get(&tx_buf) > 0) {
begin_tx();
}

// Poll for the next event data!
split_central_wired_send_command(0,
(struct zmk_split_transport_central_command){
.type = ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_POLL_EVENTS,
});

k_work_reschedule(&rx_done_work, K_TICKS(CONFIG_ZMK_SPLIT_WIRED_HALF_DUPLEX_RX_TIMEOUT));
begin_tx();

k_work_reschedule(&rx_done_work, K_MSEC(CONFIG_ZMK_SPLIT_WIRED_HALF_DUPLEX_RX_TIMEOUT));
}

#endif
Expand All @@ -173,10 +172,10 @@ static void serial_cb(const struct device *dev, void *user_data) {

while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
if (uart_irq_rx_ready(dev)) {
zmk_split_wired_fifo_read(dev, &rx_buf, &publish_events);
zmk_split_wired_fifo_read(dev, &rx_buf, &publish_events, NULL);
#if IS_HALF_DUPLEX_MODE
k_work_reschedule(&rx_done_work,
K_TICKS(CONFIG_ZMK_SPLIT_WIRED_HALF_DUPLEX_RX_TIMEOUT));
K_TICKS(CONFIG_ZMK_SPLIT_WIRED_HALF_DUPLEX_RX_COMPLETE_TIMEOUT));
#endif
}

Expand Down Expand Up @@ -251,10 +250,11 @@ static int zmk_split_wired_central_init(void) {
#if IS_HALF_DUPLEX_MODE

#if HAS_DIR_GPIO
LOG_DBG("CONFIGURING AS OUTPUT");
gpio_pin_configure_dt(&dir_gpio, GPIO_OUTPUT_INACTIVE);
#endif

k_work_schedule(&rx_done_work, K_TICKS(CONFIG_ZMK_SPLIT_WIRED_HALF_DUPLEX_RX_TIMEOUT));
k_work_schedule(&rx_done_work, K_MSEC(CONFIG_ZMK_SPLIT_WIRED_HALF_DUPLEX_RX_TIMEOUT));

#endif

Expand Down Expand Up @@ -284,7 +284,8 @@ ZMK_SPLIT_TRANSPORT_CENTRAL_REGISTER(wired_central, &central_api);
static void publish_events_work(struct k_work *work) {

#if IS_HALF_DUPLEX_MODE
k_work_reschedule(&rx_done_work, K_TICKS(CONFIG_ZMK_SPLIT_WIRED_HALF_DUPLEX_RX_TIMEOUT));
k_work_reschedule(&rx_done_work,
K_MSEC(CONFIG_ZMK_SPLIT_WIRED_HALF_DUPLEX_RX_COMPLETE_TIMEOUT));
#endif // IS_HALF_DUPLEX_MODE

while (ring_buf_size_get(&rx_buf) >= sizeof(struct event_envelope)) {
Expand Down
31 changes: 27 additions & 4 deletions app/src/split/wired/peripheral.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,18 @@ static void publish_commands_work(struct k_work *work);

K_WORK_DEFINE(publish_commands, publish_commands_work);

static void process_tx_cb(void);
K_MSGQ_DEFINE(cmd_msg_queue, sizeof(struct zmk_split_transport_central_command), 3, 4);

#if IS_ENABLED(CONFIG_ZMK_SPLIT_WIRED_UART_MODE_DEFAULT_ASYNC)

uint8_t async_rx_buf[RX_BUFFER_SIZE / 2][2];

static struct zmk_split_wired_async_state async_state = {
.process_tx_work = &publish_commands,
.rx_bufs = {async_rx_buf[0], async_rx_buf[1]},
.rx_bufs_len = RX_BUFFER_SIZE / 2,
.rx_size_process_trigger = sizeof(struct command_envelope),
.process_tx_callback = process_tx_cb,
.rx_buf = &chosen_rx_buf,
.tx_buf = &chosen_tx_buf,
#if HAS_DIR_GPIO
Expand All @@ -106,7 +110,7 @@ static inline void set_dir(uint8_t tx) {}
static void serial_cb(const struct device *dev, void *user_data) {
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
if (uart_irq_rx_ready(dev)) {
zmk_split_wired_fifo_read(dev, &chosen_rx_buf, &publish_commands);
zmk_split_wired_fifo_read(dev, &chosen_rx_buf, NULL, process_tx_cb);
}

if (uart_irq_tx_complete(dev)) {
Expand Down Expand Up @@ -237,8 +241,14 @@ static const struct zmk_split_transport_peripheral_api peripheral_api = {

ZMK_SPLIT_TRANSPORT_PERIPHERAL_REGISTER(wired_peripheral, &peripheral_api);

static void publish_commands_work(struct k_work *work) {
static void process_tx_cb(void) {
if (ring_buf_size_get(&chosen_rx_buf) >= sizeof(struct command_envelope) * 2) {
LOG_WRN("GOT MULTIPLE TO PROCESS!");
}
while (ring_buf_size_get(&chosen_rx_buf) >= sizeof(struct command_envelope)) {
if (ring_buf_size_get(&chosen_rx_buf) != sizeof(struct command_envelope)) {
LOG_DBG("Buffer is odd sized %d", ring_buf_size_get(&chosen_rx_buf));
}
struct command_envelope env;
int item_err = zmk_split_wired_get_item(&chosen_rx_buf, (uint8_t *)&env,
sizeof(struct command_envelope));
Expand All @@ -247,7 +257,13 @@ static void publish_commands_work(struct k_work *work) {
if (env.cmd.type == ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_POLL_EVENTS) {
begin_tx();
} else {
zmk_split_transport_peripheral_command_handler(&wired_peripheral, env.cmd);
int ret = k_msgq_put(&cmd_msg_queue, &env.cmd, K_NO_WAIT);
if (ret < 0) {
LOG_WRN("Failed to queue command for processing (%d)", ret);
return;
}

k_work_submit(&publish_commands);
}
break;
case -EAGAIN:
Expand All @@ -258,3 +274,10 @@ static void publish_commands_work(struct k_work *work) {
}
}
}
static void publish_commands_work(struct k_work *work) {
struct zmk_split_transport_central_command cmd;

while (k_msgq_get(&cmd_msg_queue, &cmd, K_NO_WAIT) >= 0) {
zmk_split_transport_peripheral_command_handler(&wired_peripheral, cmd);
}
}
41 changes: 35 additions & 6 deletions app/src/split/wired/wired.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ void zmk_split_wired_poll_out(struct ring_buf *tx_buf, const struct device *uart
}

int zmk_split_wired_poll_in(struct ring_buf *rx_buf, const struct device *uart,
struct k_work *process_data_work, size_t envelope_size) {
struct k_work *process_data_work,
zmk_split_wired_process_tx_callback_t process_data_cb,
size_t envelope_size) {
uint8_t *buf;
uint32_t read = 0;
uint32_t claim_len = ring_buf_put_claim(rx_buf, &buf, ring_buf_space_get(rx_buf));
Expand All @@ -51,7 +53,11 @@ int zmk_split_wired_poll_in(struct ring_buf *rx_buf, const struct device *uart,
ring_buf_put_finish(rx_buf, read);

if (ring_buf_size_get(rx_buf) >= envelope_size) {
k_work_submit(process_data_work);
if (process_data_work) {
k_work_submit(process_data_work);
} else if (process_data_cb) {
process_data_cb();
}
}

// TODO: Also indicate if no bytes read at all?
Expand All @@ -63,7 +69,8 @@ int zmk_split_wired_poll_in(struct ring_buf *rx_buf, const struct device *uart,
#if IS_ENABLED(CONFIG_ZMK_SPLIT_WIRED_UART_MODE_DEFAULT_INTERRUPT)

void zmk_split_wired_fifo_read(const struct device *dev, struct ring_buf *buf,
struct k_work *process_work) {
struct k_work *process_work,
zmk_split_wired_process_tx_callback_t process_cb) {
// TODO: Add error checking on platforms that support it
uint32_t last_read = 0, len = 0;
do {
Expand All @@ -81,7 +88,11 @@ void zmk_split_wired_fifo_read(const struct device *dev, struct ring_buf *buf,
}
} while (last_read && last_read == len);

k_work_submit(process_work);
if (process_work) {
k_work_submit(process_work);
} else if (process_cb) {
process_cb();
}
}

void zmk_split_wired_fifo_fill(const struct device *dev, struct ring_buf *tx_buf) {
Expand Down Expand Up @@ -129,7 +140,13 @@ void zmk_split_wired_async_tx(struct zmk_split_wired_async_state *state) {
gpio_pin_set_dt(state->dir_gpio, 1);
}

uart_tx(state->uart, buf, claim_len, SYS_FOREVER_US);
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
LOG_DBG("Sending %d", claim_len);
#endif
int err = uart_tx(state->uart, buf, claim_len, SYS_FOREVER_US);
if (err < 0) {
LOG_DBG("NO TX %d", err);
}
}

static void restart_rx_work_cb(struct k_work *work) {
Expand All @@ -140,6 +157,8 @@ static void restart_rx_work_cb(struct k_work *work) {
atomic_set_bit(&state->state, ASYNC_STATE_BIT_RXBUF0_USED);
atomic_clear_bit(&state->state, ASYNC_STATE_BIT_RXBUF1_USED);

LOG_WRN("RESTART!");

int ret = uart_rx_enable(state->uart, state->rx_bufs[0], state->rx_bufs_len,
CONFIG_ZMK_SPLIT_WIRED_ASYNC_RX_TIMEOUT);
if (ret < 0) {
Expand All @@ -157,6 +176,7 @@ static void async_uart_cb(const struct device *dev, struct uart_event *ev, void
LOG_WRN("TX Aborted");
break;
case UART_TX_DONE:
LOG_DBG("TX Done %d", ev->data.tx.len);
ring_buf_get_finish(state->tx_buf, ev->data.tx.len);
if (ring_buf_size_get(state->tx_buf) > 0) {
zmk_split_wired_async_tx(state);
Expand All @@ -174,7 +194,16 @@ static void async_uart_cb(const struct device *dev, struct uart_event *ev, void
break;
}

k_work_submit(state->process_tx_work);
LOG_DBG("RX %d and now buffer is %d", received, ring_buf_size_get(state->rx_buf));
if (state->process_tx_callback) {
state->process_tx_callback();
} else if (state->process_tx_work) {
k_work_submit(state->process_tx_work);
}

// if (ring_buf_size_get(state->rx_buf) >= state->rx_size_process_trigger) {
// LOG_DBG("RX %d and now buffer is %d", received, ring_buf_size_get(state->rx_buf));
// }
break;
}
case UART_RX_BUF_RELEASED:
Expand Down
12 changes: 10 additions & 2 deletions app/src/split/wired/wired.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,24 @@ struct command_envelope {
uint32_t crc;
} __packed;

typedef void (*zmk_split_wired_process_tx_callback_t)(void);

#if IS_ENABLED(CONFIG_ZMK_SPLIT_WIRED_UART_MODE_DEFAULT_POLLING)

void zmk_split_wired_poll_out(struct ring_buf *tx_buf, const struct device *uart);

int zmk_split_wired_poll_in(struct ring_buf *rx_buf, const struct device *uart,
struct k_work *process_data_work, size_t envelope_size);
struct k_work *process_data_work,
zmk_split_wired_process_tx_callback_t process_data_cb,
size_t envelope_size);

#endif

#if IS_ENABLED(CONFIG_ZMK_SPLIT_WIRED_UART_MODE_DEFAULT_INTERRUPT)

void zmk_split_wired_fifo_read(const struct device *dev, struct ring_buf *buf,
struct k_work *process_work);
struct k_work *process_work,
zmk_split_wired_process_tx_callback_t process_cb);
void zmk_split_wired_fifo_fill(const struct device *dev, struct ring_buf *tx_buf);

#endif
Expand All @@ -51,10 +56,13 @@ struct zmk_split_wired_async_state {

uint8_t *rx_bufs[2];
size_t rx_bufs_len;
size_t rx_size_process_trigger;

struct ring_buf *tx_buf;
struct ring_buf *rx_buf;

zmk_split_wired_process_tx_callback_t process_tx_callback;

const struct device *uart;

struct k_work_delayable restart_rx_work;
Expand Down

0 comments on commit e7b4422

Please sign in to comment.