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 6, 2025
1 parent e7b4422 commit d3f52d7
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 79 deletions.
8 changes: 4 additions & 4 deletions app/include/zmk/split/transport/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ enum zmk_split_transport_peripheral_event_type {
};

struct zmk_split_transport_peripheral_event {
enum zmk_split_transport_peripheral_event_type type;

union {
struct {
uint8_t position;
Expand All @@ -39,8 +41,6 @@ struct zmk_split_transport_peripheral_event {
int32_t value;
} input_event;
} data;

enum zmk_split_transport_peripheral_event_type type;
} __packed;

enum zmk_split_transport_central_command_type {
Expand All @@ -51,6 +51,8 @@ enum zmk_split_transport_central_command_type {
} __packed;

struct zmk_split_transport_central_command {
enum zmk_split_transport_central_command_type type;

union {
struct {
char behavior_dev[16];
Expand All @@ -68,6 +70,4 @@ struct zmk_split_transport_central_command {
zmk_hid_indicators_t indicators;
} set_hid_indicators;
} data;

enum zmk_split_transport_central_command_type type;
} __packed;
64 changes: 49 additions & 15 deletions app/src/split/wired/central.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define IS_HALF_DUPLEX_MODE \
(DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) && DT_INST_PROP_OR(0, half_duplex, false))

#define RX_BUFFER_SIZE (sizeof(struct event_envelope) * CONFIG_ZMK_SPLIT_WIRED_EVENT_BUFFER_ITEMS)
#define TX_BUFFER_SIZE (sizeof(struct command_envelope) * CONFIG_ZMK_SPLIT_WIRED_CMD_BUFFER_ITEMS)
#define RX_BUFFER_SIZE \
((sizeof(struct event_envelope) + sizeof(struct msg_postfix)) * \
CONFIG_ZMK_SPLIT_WIRED_EVENT_BUFFER_ITEMS)
#define TX_BUFFER_SIZE \
((sizeof(struct command_envelope) + sizeof(struct msg_postfix)) * \
CONFIG_ZMK_SPLIT_WIRED_CMD_BUFFER_ITEMS)

#if IS_HALF_DUPLEX_MODE

Expand Down Expand Up @@ -85,13 +89,13 @@ 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_size_process_trigger = MSG_EXTRA_SIZE + 1,
.rx_buf = &rx_buf,
.tx_buf = &tx_buf,
#if HAS_DIR_GPIO
.dir_gpio = &dir_gpio,
#endif
};
;

#endif

Expand All @@ -115,28 +119,58 @@ static void begin_tx(void) {
#endif
}

static ssize_t get_payload_data_size(const struct zmk_split_transport_central_command *cmd) {
switch (cmd->type) {
case ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_POLL_EVENTS:
return 0;
case ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_TRIGGER_BEHAVIOR:
return sizeof(cmd->data.trigger_behavior);
case ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_PHYSICAL_LAYOUT:
return sizeof(cmd->data.set_physical_layout);
case ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_HID_INDICATORS:
return sizeof(cmd->data.set_hid_indicators);
default:
return -ENOTSUP;
}
}

static int split_central_wired_send_command(uint8_t source,
struct zmk_split_transport_central_command cmd) {
if (source != 0) {
return -EINVAL;
}

uint8_t *buffer;
size_t len = ring_buf_put_claim(&tx_buf, &buffer, sizeof(struct command_envelope));
ssize_t data_size = get_payload_data_size(&cmd);
if (data_size < 0) {
LOG_WRN("Failed to determine payload data size %d", data_size);
return data_size;
}

// Data + type + source
size_t payload_size =
data_size + sizeof(source) + sizeof(enum zmk_split_transport_central_command_type);

if (len < sizeof(struct command_envelope)) {
if (ring_buf_space_get(&tx_buf) < MSG_EXTRA_SIZE + payload_size) {
LOG_WRN("No room to send command to the peripheral %d", source);
return -ENOSPC;
}

struct command_envelope env = {
.magic_prefix = ZMK_SPLIT_WIRED_ENVELOPE_MAGIC_PREFIX, .source = source, .cmd = cmd};
struct command_envelope env = {.prefix =
{
.magic_prefix = ZMK_SPLIT_WIRED_ENVELOPE_MAGIC_PREFIX,
.payload_size = payload_size,
},
.payload = {
.source = source,
.cmd = cmd,
}};

env.crc = crc32_ieee((void *)&env, sizeof(env) - 4);
struct msg_postfix postfix = {.crc =
crc32_ieee((void *)&env, sizeof(env.prefix) + payload_size)};

memcpy(buffer, &env, sizeof(env));
ring_buf_put(&tx_buf, (uint8_t *)&env, sizeof(env.prefix) + payload_size);
ring_buf_put(&tx_buf, (uint8_t *)&postfix, sizeof(postfix));

ring_buf_put_finish(&tx_buf, len);
if (can_tx() >= 0) {
begin_tx();
}
Expand Down Expand Up @@ -288,14 +322,14 @@ static void publish_events_work(struct k_work *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)) {
while (ring_buf_size_get(&rx_buf) > MSG_EXTRA_SIZE) {
struct event_envelope env;
int item_err =
zmk_split_wired_get_item(&rx_buf, (uint8_t *)&env, sizeof(struct event_envelope));
switch (item_err) {
case 0:
zmk_split_transport_central_peripheral_event_handler(&wired_central, env.source,
env.event);
zmk_split_transport_central_peripheral_event_handler(&wired_central, env.payload.source,
env.payload.event);
break;
case -EAGAIN:
break;
Expand Down
98 changes: 61 additions & 37 deletions app/src/split/wired/peripheral.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,12 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define IS_HALF_DUPLEX_MODE \
(DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) && DT_INST_PROP_OR(0, half_duplex, false))

#define TX_BUFFER_SIZE (sizeof(struct event_envelope) * CONFIG_ZMK_SPLIT_WIRED_EVENT_BUFFER_ITEMS)
#define RX_BUFFER_SIZE (sizeof(struct command_envelope) * CONFIG_ZMK_SPLIT_WIRED_CMD_BUFFER_ITEMS)
#define TX_BUFFER_SIZE \
((sizeof(struct event_envelope) + sizeof(struct msg_postfix)) * \
CONFIG_ZMK_SPLIT_WIRED_EVENT_BUFFER_ITEMS)
#define RX_BUFFER_SIZE \
((sizeof(struct command_envelope) + sizeof(struct msg_postfix)) * \
CONFIG_ZMK_SPLIT_WIRED_CMD_BUFFER_ITEMS)

RING_BUF_DECLARE(chosen_rx_buf, RX_BUFFER_SIZE);
RING_BUF_DECLARE(chosen_tx_buf, TX_BUFFER_SIZE);
Expand Down Expand Up @@ -198,40 +202,66 @@ static void begin_tx(void) {
#endif
}

static ssize_t get_payload_data_size(const struct zmk_split_transport_peripheral_event *evt) {
switch (evt->type) {
case ZMK_SPLIT_TRANSPORT_PERIPHERAL_EVENT_TYPE_INPUT_EVENT:
return sizeof(evt->data.input_event);
case ZMK_SPLIT_TRANSPORT_PERIPHERAL_EVENT_TYPE_KEY_POSITION:
return sizeof(evt->data.key_position);
case ZMK_SPLIT_TRANSPORT_PERIPHERAL_EVENT_TYPE_SENSOR_EVENT:
return sizeof(evt->data.sensor_event);
default:
return -ENOTSUP;
}
}

static int
split_peripheral_wired_report_event(const struct zmk_split_transport_peripheral_event *event) {
size_t added = 0;
ssize_t data_size = get_payload_data_size(event);
if (data_size < 0) {
LOG_WRN("Failed to determine payload data size %d", data_size);
return data_size;
}

struct event_envelope env = {
.magic_prefix = ZMK_SPLIT_WIRED_ENVELOPE_MAGIC_PREFIX,
.source = peripheral_id,
.event = *event,
};
// Data + type + source
size_t payload_size =
data_size + sizeof(peripheral_id) + sizeof(enum zmk_split_transport_peripheral_event_type);

if (ring_buf_space_get(&chosen_tx_buf) < sizeof(env)) {
LOG_ERR("Insufficient room for sending the peripheral event");
if (ring_buf_space_get(&chosen_tx_buf) < MSG_EXTRA_SIZE + payload_size) {
LOG_WRN("No room to send peripheral to the central (have %d but only space for %d)",
MSG_EXTRA_SIZE + payload_size, ring_buf_space_get(&chosen_tx_buf));
return -ENOSPC;
}

env.crc = crc32_ieee((void *)&env, sizeof(env) - 4);

while (added < sizeof(env)) {
uint8_t *buf;
size_t claim = ring_buf_put_claim(&chosen_tx_buf, &buf, sizeof(env) - added);

if (claim == 0) {
break;
}

memcpy(buf, (uint8_t *)&env + added, claim);

added += claim;
ring_buf_put_finish(&chosen_tx_buf, claim);
struct event_envelope env = {.prefix =
{
.magic_prefix = ZMK_SPLIT_WIRED_ENVELOPE_MAGIC_PREFIX,
.payload_size = payload_size,
},
.payload = {
.source = peripheral_id,
.event = *event,
}};

struct msg_postfix postfix = {.crc =
crc32_ieee((void *)&env, sizeof(env.prefix) + payload_size)};

LOG_HEXDUMP_DBG(&env, sizeof(env.prefix) + payload_size, "Payload");

size_t put = ring_buf_put(&chosen_tx_buf, (uint8_t *)&env, sizeof(env.prefix) + payload_size);
if (put != sizeof(env.prefix) + payload_size) {
LOG_WRN("Failed to put the whole message (%d vs %d)", put,
sizeof(env.prefix) + payload_size);
}
put = ring_buf_put(&chosen_tx_buf, (uint8_t *)&postfix, sizeof(postfix));
if (put != sizeof(postfix)) {
LOG_WRN("Failed to put the whole message (%d vs %d)", put, sizeof(postfix));
}

#if !IS_HALF_DUPLEX_MODE
begin_tx();
begin_tx();
#endif
}

return 0;
}

Expand All @@ -242,22 +272,16 @@ static const struct zmk_split_transport_peripheral_api peripheral_api = {
ZMK_SPLIT_TRANSPORT_PERIPHERAL_REGISTER(wired_peripheral, &peripheral_api);

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));
}
while (ring_buf_size_get(&chosen_rx_buf) > MSG_EXTRA_SIZE) {
struct command_envelope env;
int item_err = zmk_split_wired_get_item(&chosen_rx_buf, (uint8_t *)&env,
sizeof(struct command_envelope));
switch (item_err) {
case 0:
if (env.cmd.type == ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_POLL_EVENTS) {
if (env.payload.cmd.type == ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_POLL_EVENTS) {
begin_tx();
} else {
int ret = k_msgq_put(&cmd_msg_queue, &env.cmd, K_NO_WAIT);
int ret = k_msgq_put(&cmd_msg_queue, &env.payload.cmd, K_NO_WAIT);
if (ret < 0) {
LOG_WRN("Failed to queue command for processing (%d)", ret);
return;
Expand All @@ -267,10 +291,10 @@ static void process_tx_cb(void) {
}
break;
case -EAGAIN:
break;
return;
default:
LOG_WRN("Issue fetching an item from the RX buffer: %d", item_err);
break;
return;
}
}
}
Expand Down
49 changes: 32 additions & 17 deletions app/src/split/wired/wired.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,15 +266,16 @@ int zmk_split_wired_async_init(struct zmk_split_wired_async_state *state) {
#endif

int zmk_split_wired_get_item(struct ring_buf *rx_buf, uint8_t *env, size_t env_size) {
while (ring_buf_size_get(rx_buf) >= env_size) {
size_t bytes_left = env_size;
while (ring_buf_size_get(rx_buf) > sizeof(struct msg_prefix) + sizeof(struct msg_postfix)) {
struct msg_prefix prefix;

uint8_t prefix[5] = {0};
__ASSERT_EVAL(
(void)ring_buf_peek(rx_buf, (uint8_t *)&prefix, sizeof(prefix)),
uint32_t peek_read = ring_buf_peek(rx_buf, (uint8_t *)&prefix, sizeof(prefix)),
peek_read == sizeof(prefix), "Somehow read less than we expect from the RX buffer");

uint32_t peek_read = ring_buf_peek(rx_buf, prefix, 4);
__ASSERT(peek_read == 4, "Somehow read less than we expect from the RX buffer");

if (strcmp(prefix, ZMK_SPLIT_WIRED_ENVELOPE_MAGIC_PREFIX) != 0) {
if (memcmp(&prefix.magic_prefix, &ZMK_SPLIT_WIRED_ENVELOPE_MAGIC_PREFIX,
sizeof(prefix.magic_prefix)) != 0) {
uint8_t discarded_byte;
ring_buf_get(rx_buf, &discarded_byte, 1);

Expand All @@ -283,20 +284,34 @@ int zmk_split_wired_get_item(struct ring_buf *rx_buf, uint8_t *env, size_t env_s
continue;
}

while (bytes_left > 0) {
size_t read = ring_buf_get(rx_buf, env + (env_size - bytes_left), bytes_left);
bytes_left -= read;
size_t payload_to_read = sizeof(prefix) + prefix.payload_size;

if (payload_to_read > env_size) {
LOG_WRN("Invalid message with payload %d bigger than expected max %d", payload_to_read,
env_size);
return -EINVAL;
}

if (ring_buf_size_get(rx_buf) < payload_to_read + sizeof(struct msg_postfix)) {
return -EAGAIN;
}

// Avoid unaligned access by copying into our 32-bit integer on the stack
uint32_t env_crc;
memcpy(&env_crc, env + (env_size - 4), 4);
// Now that prefix matches, read it out so we can read the rest of the payload.
__ASSERT_EVAL((void)ring_buf_get(rx_buf, env, payload_to_read),
uint32_t read = ring_buf_get(rx_buf, env, payload_to_read),
read == payload_to_read,
"Somehow read less than we expect from the RX buffer");

struct msg_postfix postfix;
__ASSERT_EVAL((void)ring_buf_get(rx_buf, (uint8_t *)&postfix, sizeof(postfix)),
uint32_t read = ring_buf_get(rx_buf, (uint8_t *)&postfix, sizeof(postfix)),
read == sizeof(postfix),
"Somehow read less of the postfix than we expect from the RX buffer");

// Exclude the trailing 4 bytes that contain the received CRC
uint32_t crc = crc32_ieee(env, env_size - 4);
if (crc != env_crc) {
uint32_t crc = crc32_ieee(env, payload_to_read);
if (crc != postfix.crc) {
LOG_WRN("Data corruption in received peripheral event, ignoring %d vs %d", crc,
env_crc);
postfix.crc);
return -EINVAL;
}

Expand Down
Loading

0 comments on commit d3f52d7

Please sign in to comment.