Feature: Full-Duplex Wired Split (#2766)
refactor(split): Refactor split code for extension Extract central/peripheral code to allow for plugging in alternate transports, instead of tying all split logic to BT. feat(split): Add full-duplex wired split support * Depends on full-duplex hardware UART for communication. * Supports all existing central commands/peripheral events, including sensors/inputs from peripherals. * Only one wired split peripheral supported (for now) * Relies on chosen `zmk,split-uart` referencing the UART device. docs: Add wired split config docs. Migrate split to its own dedicated config file, and add details on wired split config. Co-authored-by: Nicolas Munnich <98408764+Nick-Munnich@users.noreply.github.com> fix: Properly override stack size on RP2040 Move the system work queue stack size override on RP2040 ouf of a `ZMK_BLE` conditional so it is properly applied generally for that SoC. --------- Co-authored-by: Nicolas Munnich <98408764+Nick-Munnich@users.noreply.github.com>
This commit is contained in:
parent
5ba7e260f4
commit
147c340c6e
44 changed files with 2201 additions and 373 deletions
|
|
@ -18,9 +18,11 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#include <zephyr/bluetooth/uuid.h>
|
||||
|
||||
#include <drivers/behavior.h>
|
||||
#include <zmk/stdlib.h>
|
||||
#include <zmk/behavior.h>
|
||||
#include <zmk/matrix.h>
|
||||
#include <zmk/physical_layouts.h>
|
||||
#include <zmk/split/transport/peripheral.h>
|
||||
#include <zmk/split/bluetooth/uuid.h>
|
||||
#include <zmk/split/bluetooth/service.h>
|
||||
|
||||
|
|
@ -60,48 +62,7 @@ static ssize_t split_svc_pos_state(struct bt_conn *conn, const struct bt_gatt_at
|
|||
|
||||
static ssize_t split_svc_run_behavior(struct bt_conn *conn, const struct bt_gatt_attr *attrs,
|
||||
const void *buf, uint16_t len, uint16_t offset,
|
||||
uint8_t flags) {
|
||||
struct zmk_split_run_behavior_payload *payload = attrs->user_data;
|
||||
uint16_t end_addr = offset + len;
|
||||
|
||||
LOG_DBG("offset %d len %d", offset, len);
|
||||
|
||||
if (end_addr > sizeof(struct zmk_split_run_behavior_payload)) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||
}
|
||||
|
||||
memcpy(payload + offset, buf, len);
|
||||
|
||||
// We run if:
|
||||
// 1: We've gotten all the position/state/param data.
|
||||
// 2: We have a null terminated string for the behavior device label.
|
||||
const size_t behavior_dev_offset =
|
||||
offsetof(struct zmk_split_run_behavior_payload, behavior_dev);
|
||||
if ((end_addr > sizeof(struct zmk_split_run_behavior_data)) &&
|
||||
payload->behavior_dev[end_addr - behavior_dev_offset - 1] == '\0') {
|
||||
struct zmk_behavior_binding binding = {
|
||||
.param1 = payload->data.param1,
|
||||
.param2 = payload->data.param2,
|
||||
.behavior_dev = payload->behavior_dev,
|
||||
};
|
||||
LOG_DBG("%s with params %d %d: pressed? %d", binding.behavior_dev, binding.param1,
|
||||
binding.param2, payload->data.state);
|
||||
struct zmk_behavior_binding_event event = {.position = payload->data.position,
|
||||
.timestamp = k_uptime_get()};
|
||||
int err;
|
||||
if (payload->data.state > 0) {
|
||||
err = behavior_keymap_binding_pressed(&binding, event);
|
||||
} else {
|
||||
err = behavior_keymap_binding_released(&binding, event);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Failed to invoke behavior %s: %d", binding.behavior_dev, err);
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
uint8_t flags);
|
||||
|
||||
static ssize_t split_svc_num_of_positions(struct bt_conn *conn, const struct bt_gatt_attr *attrs,
|
||||
void *buf, uint16_t len, uint16_t offset) {
|
||||
|
|
@ -285,12 +246,12 @@ int send_position_state() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int zmk_split_bt_position_pressed(uint8_t position) {
|
||||
static int zmk_split_bt_position_pressed(uint8_t position) {
|
||||
WRITE_BIT(position_state[position / 8], position % 8, true);
|
||||
return send_position_state();
|
||||
}
|
||||
|
||||
int zmk_split_bt_position_released(uint8_t position) {
|
||||
static int zmk_split_bt_position_released(uint8_t position) {
|
||||
WRITE_BIT(position_state[position / 8], position % 8, false);
|
||||
return send_position_state();
|
||||
}
|
||||
|
|
@ -332,9 +293,9 @@ int send_sensor_state(struct sensor_event ev) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int zmk_split_bt_sensor_triggered(uint8_t sensor_index,
|
||||
const struct zmk_sensor_channel_data channel_data[],
|
||||
size_t channel_data_size) {
|
||||
static int zmk_split_bt_sensor_triggered(uint8_t sensor_index,
|
||||
const struct zmk_sensor_channel_data channel_data[],
|
||||
size_t channel_data_size) {
|
||||
if (channel_data_size > ZMK_SENSOR_EVENT_MAX_CHANNELS) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -349,7 +310,8 @@ int zmk_split_bt_sensor_triggered(uint8_t sensor_index,
|
|||
|
||||
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
|
||||
int zmk_split_bt_report_input(uint8_t reg, uint8_t type, uint16_t code, int32_t value, bool sync) {
|
||||
static int zmk_split_bt_report_input(uint8_t reg, uint8_t type, uint16_t code, int32_t value,
|
||||
bool sync) {
|
||||
|
||||
for (size_t i = 0; i < split_svc.attr_count; i++) {
|
||||
if (bt_uuid_cmp(split_svc.attrs[i].uuid,
|
||||
|
|
@ -380,3 +342,98 @@ static int service_init(void) {
|
|||
}
|
||||
|
||||
SYS_INIT(service_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY);
|
||||
|
||||
static int zmk_peripheral_ble_report_event(const struct zmk_split_transport_peripheral_event *ev) {
|
||||
switch (ev->type) {
|
||||
case ZMK_SPLIT_TRANSPORT_PERIPHERAL_EVENT_TYPE_KEY_POSITION_EVENT:
|
||||
if (ev->data.key_position_event.pressed) {
|
||||
zmk_split_bt_position_pressed(ev->data.key_position_event.position);
|
||||
} else {
|
||||
zmk_split_bt_position_released(ev->data.key_position_event.position);
|
||||
}
|
||||
break;
|
||||
#if ZMK_KEYMAP_HAS_SENSORS
|
||||
case ZMK_SPLIT_TRANSPORT_PERIPHERAL_EVENT_TYPE_SENSOR_EVENT:
|
||||
zmk_split_bt_sensor_triggered(ev->data.sensor_event.sensor_index,
|
||||
&ev->data.sensor_event.channel_data, 1);
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
case ZMK_SPLIT_TRANSPORT_PERIPHERAL_EVENT_TYPE_INPUT_EVENT:
|
||||
return zmk_split_bt_report_input(ev->data.input_event.reg, ev->data.input_event.type,
|
||||
ev->data.input_event.code, ev->data.input_event.value,
|
||||
ev->data.input_event.sync);
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING)
|
||||
case ZMK_SPLIT_TRANSPORT_PERIPHERAL_EVENT_TYPE_BATTERY_EVENT:
|
||||
// The BLE transport uses standard BAS service for propagation, so just return success here.
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
LOG_WRN("Unhandled event type %d", ev->type);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct zmk_split_transport_peripheral_api peripheral_api = {
|
||||
.report_event = zmk_peripheral_ble_report_event,
|
||||
};
|
||||
|
||||
ZMK_SPLIT_TRANSPORT_PERIPHERAL_REGISTER(bt_peripheral, &peripheral_api);
|
||||
|
||||
static ssize_t split_svc_run_behavior(struct bt_conn *conn, const struct bt_gatt_attr *attrs,
|
||||
const void *buf, uint16_t len, uint16_t offset,
|
||||
uint8_t flags) {
|
||||
struct zmk_split_run_behavior_payload *payload = attrs->user_data;
|
||||
uint16_t end_addr = offset + len;
|
||||
|
||||
LOG_DBG("offset %d len %d", offset, len);
|
||||
|
||||
if (end_addr > sizeof(struct zmk_split_run_behavior_payload)) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||
}
|
||||
|
||||
memcpy(payload + offset, buf, len);
|
||||
|
||||
// We run if:
|
||||
// 1: We've gotten all the position/state/param data.
|
||||
// 2: We have a null terminated string for the behavior device label.
|
||||
const size_t behavior_dev_offset =
|
||||
offsetof(struct zmk_split_run_behavior_payload, behavior_dev);
|
||||
if ((end_addr > sizeof(struct zmk_split_run_behavior_data)) &&
|
||||
payload->behavior_dev[end_addr - behavior_dev_offset - 1] == '\0') {
|
||||
|
||||
struct zmk_split_transport_central_command cmd = {
|
||||
.type = ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_INVOKE_BEHAVIOR,
|
||||
.data = {.invoke_behavior = {
|
||||
.param1 = payload->data.param1,
|
||||
.param2 = payload->data.param2,
|
||||
.position = payload->data.position,
|
||||
.state = payload->data.state,
|
||||
}}};
|
||||
|
||||
const size_t payload_dev_size = sizeof(cmd.data.invoke_behavior.behavior_dev);
|
||||
if (strlcpy(cmd.data.invoke_behavior.behavior_dev, payload->behavior_dev,
|
||||
payload_dev_size) >= payload_dev_size) {
|
||||
LOG_ERR("Truncated behavior label %s to %s before invoking peripheral behavior",
|
||||
payload->behavior_dev, cmd.data.invoke_behavior.behavior_dev);
|
||||
}
|
||||
|
||||
LOG_DBG("%s with params %d %d: pressed? %d", cmd.data.invoke_behavior.behavior_dev,
|
||||
cmd.data.invoke_behavior.param1, cmd.data.invoke_behavior.param2,
|
||||
cmd.data.invoke_behavior.state);
|
||||
|
||||
int err = zmk_split_transport_peripheral_command_handler(&bt_peripheral, cmd);
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Failed to invoke behavior %s: %d", payload->behavior_dev, err);
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue