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:
Pete Johanson
2025-03-18 00:48:32 -06:00
committed by GitHub
parent 5ba7e260f4
commit 147c340c6e
44 changed files with 2201 additions and 373 deletions

View File

@@ -0,0 +1,9 @@
/*
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/linker/linker-defs.h>
ITERABLE_SECTION_ROM(zmk_split_transport_central, 4)

View File

@@ -0,0 +1,9 @@
/*
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/linker/linker-defs.h>
ITERABLE_SECTION_ROM(zmk_split_transport_peripheral, 4)

View File

@@ -10,8 +10,7 @@
#include <zmk/ble/profile.h>
#define ZMK_BLE_IS_CENTRAL \
(IS_ENABLED(CONFIG_ZMK_SPLIT) && IS_ENABLED(CONFIG_ZMK_BLE) && \
IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL))
(IS_ENABLED(CONFIG_ZMK_SPLIT_BLE) && IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL))
#if ZMK_BLE_IS_CENTRAL
#define ZMK_BLE_PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS)

View File

@@ -1,24 +0,0 @@
#pragma once
#include <zephyr/bluetooth/addr.h>
#include <zmk/behavior.h>
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
#include <zmk/hid_indicators_types.h>
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
int zmk_split_bt_invoke_behavior(uint8_t source, struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event, bool state);
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
int zmk_split_bt_update_hid_indicator(zmk_hid_indicators_t indicators);
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
int zmk_split_get_peripheral_battery_level(uint8_t source, uint8_t *level);
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)

View File

@@ -37,11 +37,3 @@ struct zmk_split_input_event_payload {
uint32_t value;
uint8_t sync;
} __packed;
int zmk_split_bt_position_pressed(uint8_t position);
int zmk_split_bt_position_released(uint8_t position);
int zmk_split_bt_sensor_triggered(uint8_t sensor_index,
const struct zmk_sensor_channel_data channel_data[],
size_t channel_data_size);
int zmk_split_bt_report_input(uint8_t reg, uint8_t type, uint16_t code, int32_t value, bool sync);

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zephyr/bluetooth/addr.h>
#include <zmk/behavior.h>
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE)
#include <zmk/ble.h>
#define BLE_PERIPHERAL_COUNT ZMK_SPLIT_BLE_PERIPHERAL_COUNT
#else
#define BLE_PERIPHERAL_COUNT 0
#endif
#if IS_ENABLED(CONFIG_ZMK_SPLIT_WIRED)
#define WIRED_PERIPHERAL_COUNT 1
#else
#define WIRED_PERIPHERAL_COUNT 0
#endif
#define ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT MAX(BLE_PERIPHERAL_COUNT, WIRED_PERIPHERAL_COUNT)
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
#include <zmk/hid_indicators_types.h>
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
int zmk_split_central_invoke_behavior(uint8_t source, struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event, bool state);
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
int zmk_split_central_update_hid_indicator(zmk_hid_indicators_t indicators);
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
int zmk_split_central_get_peripheral_battery_level(uint8_t source, uint8_t *level);
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)

View File

@@ -0,0 +1,11 @@
/*
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zmk/split/transport/types.h>
int zmk_split_peripheral_report_event(const struct zmk_split_transport_peripheral_event *event);

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zephyr/types.h>
#include <zmk/split/transport/types.h>
typedef int (*zmk_split_transport_central_send_command_t)(
uint8_t source, struct zmk_split_transport_central_command cmd);
typedef int (*zmk_split_transport_central_get_available_source_ids_t)(uint8_t *sources);
struct zmk_split_transport_central_api {
zmk_split_transport_central_send_command_t send_command;
zmk_split_transport_central_get_available_source_ids_t get_available_source_ids;
};
struct zmk_split_transport_central {
const struct zmk_split_transport_central_api *api;
};
int zmk_split_transport_central_peripheral_event_handler(
const struct zmk_split_transport_central *transport, uint8_t source,
struct zmk_split_transport_peripheral_event ev);
#define ZMK_SPLIT_TRANSPORT_CENTRAL_REGISTER(name, _api) \
STRUCT_SECTION_ITERABLE(zmk_split_transport_central, name) = { \
.api = _api, \
};

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zephyr/types.h>
#include <zmk/split/transport/types.h>
typedef int (*zmk_split_central_report_event_callback_t)(
const struct zmk_split_transport_peripheral_event *event);
struct zmk_split_transport_peripheral_api {
zmk_split_central_report_event_callback_t report_event;
};
struct zmk_split_transport_peripheral {
const struct zmk_split_transport_peripheral_api *api;
};
int zmk_split_transport_peripheral_command_handler(
const struct zmk_split_transport_peripheral *transport,
struct zmk_split_transport_central_command cmd);
#define ZMK_SPLIT_TRANSPORT_PERIPHERAL_REGISTER(name, _api) \
STRUCT_SECTION_ITERABLE(zmk_split_transport_peripheral, name) = { \
.api = _api, \
};

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zmk/hid_indicators_types.h>
#include <zmk/sensors.h>
#include <zephyr/sys/util.h>
enum zmk_split_transport_peripheral_event_type {
ZMK_SPLIT_TRANSPORT_PERIPHERAL_EVENT_TYPE_KEY_POSITION_EVENT,
ZMK_SPLIT_TRANSPORT_PERIPHERAL_EVENT_TYPE_SENSOR_EVENT,
ZMK_SPLIT_TRANSPORT_PERIPHERAL_EVENT_TYPE_INPUT_EVENT,
ZMK_SPLIT_TRANSPORT_PERIPHERAL_EVENT_TYPE_BATTERY_EVENT,
};
struct zmk_split_transport_peripheral_event {
enum zmk_split_transport_peripheral_event_type type;
union {
struct {
uint8_t position;
uint8_t pressed;
} key_position_event;
struct {
struct zmk_sensor_channel_data channel_data;
uint8_t sensor_index;
} sensor_event;
struct {
uint8_t reg;
uint8_t sync;
uint8_t type;
uint16_t code;
int32_t value;
} input_event;
struct {
uint8_t level;
} battery_event;
} data;
} __packed;
enum zmk_split_transport_central_command_type {
ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_POLL_EVENTS,
ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_INVOKE_BEHAVIOR,
ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_PHYSICAL_LAYOUT,
ZMK_SPLIT_TRANSPORT_CENTRAL_CMD_TYPE_SET_HID_INDICATORS,
} __packed;
struct zmk_split_transport_central_command {
enum zmk_split_transport_central_command_type type;
union {
struct {
char behavior_dev[16];
uint32_t param1, param2;
uint32_t position;
uint8_t event_source;
uint8_t state;
} invoke_behavior;
struct {
uint8_t layout_idx;
} set_physical_layout;
struct {
zmk_hid_indicators_t indicators;
} set_hid_indicators;
} data;
} __packed;