Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Full-Duplex Wired Split #2766

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions app/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,6 @@ config BT_CTLR_PHY_2M
config BT_TINYCRYPT_ECC
default y if BT_HCI && !BT_CTLR

config SYSTEM_WORKQUEUE_STACK_SIZE
default 4096 if SOC_RP2040
default 2048

config ZMK_BLE_THREAD_STACK_SIZE
int "BLE notify thread stack size"
default 768
Expand Down
4 changes: 4 additions & 0 deletions app/Kconfig.defaults
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Copyright (c) 2024 The ZMK Contributors
# SPDX-License-Identifier: MIT

config SYSTEM_WORKQUEUE_STACK_SIZE
default 2048 if SOC_RP2040
default 2048 if ZMK_BLE

# HID
if ZMK_HID_REPORT_TYPE_HKRO

Expand Down
2 changes: 1 addition & 1 deletion app/boards/arm/glove80/Kconfig.defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ if BOARD_GLOVE80_LH
config BOARD
default "glove80 lh"

config ZMK_SPLIT_BLE_ROLE_CENTRAL
config ZMK_SPLIT_ROLE_CENTRAL
default y

endif # BOARD_GLOVE80_LH
Expand Down
9 changes: 9 additions & 0 deletions app/boards/shields/zmk_uno/zmk_uno_split.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,20 @@ left_encoder: &encoder {
status = "disabled";
};

&arduino_serial {
status = "okay";
};

/ {
chosen {
zmk,physical-layout = &split_matrix_physical_layout;
};

wired_split {
compatible = "zmk,wired-split";
device = <&arduino_serial>;
};

split_matrix_transform: split_matrix_transform {
compatible = "zmk,matrix-transform";
rows = <4>;
Expand Down
21 changes: 21 additions & 0 deletions app/dts/bindings/zmk,wired-split.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright (c) 2025 The ZMK Contributors
# SPDX-License-Identifier: MIT

description: |
Complete specification of wired split connection

compatible: "zmk,wired-split"

properties:
device:
type: phandle
required: true
description: The UART device for wired split communication

half-duplex:
type: boolean
description: "Experimental: Enable half-duplex protocol mode"

dir-gpios:
type: phandle-array
description: "Experimental: Set the communication direction. Used for RS-422 style comms."
9 changes: 9 additions & 0 deletions app/include/linker/zmk-split-transport-central.ld
Original file line number Diff line number Diff line change
@@ -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)
9 changes: 9 additions & 0 deletions app/include/linker/zmk-split-transport-peripheral.ld
Original file line number Diff line number Diff line change
@@ -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)
3 changes: 1 addition & 2 deletions app/include/zmk/ble.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
24 changes: 0 additions & 24 deletions app/include/zmk/split/bluetooth/central.h

This file was deleted.

8 changes: 0 additions & 8 deletions app/include/zmk/split/bluetooth/service.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
48 changes: 48 additions & 0 deletions app/include/zmk/split/central.h
Original file line number Diff line number Diff line change
@@ -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)
11 changes: 11 additions & 0 deletions app/include/zmk/split/peripheral.h
Original file line number Diff line number Diff line change
@@ -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);
33 changes: 33 additions & 0 deletions app/include/zmk/split/transport/central.h
Original file line number Diff line number Diff line change
@@ -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, \
};
31 changes: 31 additions & 0 deletions app/include/zmk/split/transport/peripheral.h
Original file line number Diff line number Diff line change
@@ -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, \
};
76 changes: 76 additions & 0 deletions app/include/zmk/split/transport/types.h
Original file line number Diff line number Diff line change
@@ -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;
15 changes: 7 additions & 8 deletions app/src/behavior.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@

#endif

#include <zmk/ble.h>
#if ZMK_BLE_IS_CENTRAL
#include <zmk/split/bluetooth/central.h>
#if IS_ENABLED(CONFIG_ZMK_SPLIT) && IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
#include <zmk/split/central.h>
#endif

#include <drivers/behavior.h>
Expand Down Expand Up @@ -95,19 +94,19 @@ int zmk_behavior_invoke_binding(const struct zmk_behavior_binding *src_binding,
case BEHAVIOR_LOCALITY_CENTRAL:
return invoke_locally(&binding, event, pressed);
case BEHAVIOR_LOCALITY_EVENT_SOURCE:
#if ZMK_BLE_IS_CENTRAL // source is a member of event because CONFIG_ZMK_SPLIT is enabled
#if IS_ENABLED(CONFIG_ZMK_SPLIT) && IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
if (event.source == ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL) {
return invoke_locally(&binding, event, pressed);
} else {
return zmk_split_bt_invoke_behavior(event.source, &binding, event, pressed);
return zmk_split_central_invoke_behavior(event.source, &binding, event, pressed);
}
#else
return invoke_locally(&binding, event, pressed);
#endif
case BEHAVIOR_LOCALITY_GLOBAL:
#if ZMK_BLE_IS_CENTRAL
for (int i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT; i++) {
zmk_split_bt_invoke_behavior(i, &binding, event, pressed);
#if IS_ENABLED(CONFIG_ZMK_SPLIT) && IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
for (int i = 0; i < ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT; i++) {
zmk_split_central_invoke_behavior(i, &binding, event, pressed);
}
#endif
return invoke_locally(&binding, event, pressed);
Expand Down
Loading