Allow custom data sync for community modules (#25955)

* Allow custom data sync for community modules

* Stub out community_config.h codegen

* Fix SPLIT_TRANSACTION_RPC logic
This commit is contained in:
Joel Challis
2026-02-19 11:03:26 +00:00
committed by GitHub
parent a0166fef43
commit 5591a68b65
10 changed files with 154 additions and 13 deletions
+16 -1
View File
@@ -187,6 +187,11 @@ include $(COMMUNITY_RULES_MK)
ifneq ($(COMMUNITY_MODULES),)
$(INTERMEDIATE_OUTPUT)/src/community_config.h: $(KEYMAP_JSON) $(DD_CONFIG_FILES)
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
$(eval CMD=$(QMK_BIN) generate-community-config-h -kb $(KEYBOARD) --quiet --output $(INTERMEDIATE_OUTPUT)/src/community_config.h $(KEYMAP_JSON))
@$(BUILD_CMD)
$(INTERMEDIATE_OUTPUT)/src/community_modules.h: $(KEYMAP_JSON) $(DD_CONFIG_FILES)
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
$(eval CMD=$(QMK_BIN) generate-community-modules-h -kb $(KEYBOARD) --quiet --output $(INTERMEDIATE_OUTPUT)/src/community_modules.h $(KEYMAP_JSON))
@@ -217,9 +222,15 @@ $(INTERMEDIATE_OUTPUT)/src/rgb_matrix_community_modules.inc: $(KEYMAP_JSON) $(DD
$(eval CMD=$(QMK_BIN) generate-rgb-matrix-community-modules-inc -kb $(KEYBOARD) --quiet --output $(INTERMEDIATE_OUTPUT)/src/rgb_matrix_community_modules.inc $(KEYMAP_JSON))
@$(BUILD_CMD)
$(INTERMEDIATE_OUTPUT)/src/split_transaction_id_community_modules.inc: $(KEYMAP_JSON) $(DD_CONFIG_FILES)
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
$(eval CMD=$(QMK_BIN) generate-split-transaction-id-community-modules-inc -kb $(KEYBOARD) --quiet --output $(INTERMEDIATE_OUTPUT)/src/split_transaction_id_community_modules.inc $(KEYMAP_JSON))
@$(BUILD_CMD)
COMMUNITY_CONFIG_H = $(INTERMEDIATE_OUTPUT)/src/community_config.h
SRC += $(INTERMEDIATE_OUTPUT)/src/community_modules.c
generated-files: $(INTERMEDIATE_OUTPUT)/src/community_modules.h $(INTERMEDIATE_OUTPUT)/src/community_modules.c $(INTERMEDIATE_OUTPUT)/src/community_modules_introspection.c $(INTERMEDIATE_OUTPUT)/src/community_modules_introspection.h $(INTERMEDIATE_OUTPUT)/src/led_matrix_community_modules.inc $(INTERMEDIATE_OUTPUT)/src/rgb_matrix_community_modules.inc
generated-files: $(INTERMEDIATE_OUTPUT)/src/community_config.h $(INTERMEDIATE_OUTPUT)/src/community_modules.h $(INTERMEDIATE_OUTPUT)/src/community_modules.c $(INTERMEDIATE_OUTPUT)/src/community_modules_introspection.c $(INTERMEDIATE_OUTPUT)/src/community_modules_introspection.h $(INTERMEDIATE_OUTPUT)/src/led_matrix_community_modules.inc $(INTERMEDIATE_OUTPUT)/src/rgb_matrix_community_modules.inc $(INTERMEDIATE_OUTPUT)/src/split_transaction_id_community_modules.inc
endif
@@ -320,6 +331,10 @@ define config_h_community_module_appender
endef
$(foreach module,$(COMMUNITY_MODULE_PATHS),$(eval $(call config_h_community_module_appender,$(module))))
ifneq ($(COMMUNITY_CONFIG_H),)
CONFIG_H += $(COMMUNITY_CONFIG_H)
endif
ifneq ("$(wildcard $(KEYBOARD_PATH_5)/config.h)","")
CONFIG_H += $(KEYBOARD_PATH_5)/config.h
endif
+3
View File
@@ -0,0 +1,3 @@
{
// This version exists to signify addition of split data sync support.
}
+8
View File
@@ -95,6 +95,10 @@ The use of `features` matches the definition normally provided within `keyboard.
The `keycodes` array allows a module to provide new keycodes (as well as corresponding aliases) to a keymap.
### `config.h`
This file will be automatically added to the build as if it were present in the keyboard or keymap.
### `rules.mk` / `post_rules.mk`
These two files follows standard QMK build system logic, allowing for `Makefile`-style customisation as if it were present in the keyboard or keymap.
@@ -131,6 +135,10 @@ This file defines LED matrix effects in the same form as used with `led_matrix_k
This file defines RGB matrix effects in the same form as used with `rgb_matrix_kb.inc` and `rgb_matrix_user.inc` (see [Custom RGB Matrix Effects](rgb_matrix#custom-rgb-matrix-effects)). Effect mode names are prepended with `RGB_MATRIX_COMMUNITY_MODULE_`.
### Custom split keyboard data sync
Defines follow the convention, `SPLIT_TRANSACTION_IDS_MODULE_<MODULE>` (see [Custom LED Matrix Effects](split_keyboard#custom-data-sync)).
### Compatible APIs
Community Modules may provide specializations for the following APIs:
@@ -194,6 +194,38 @@ def generate_community_modules_rules_mk(cli):
cli.log.info('Wrote rules.mk to %s.', cli.args.output)
@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate community_config.h for.')
@cli.argument('filename', nargs='?', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file')
@cli.subcommand('Creates a community_config.h from a keymap.json file.')
def generate_community_config_h(cli):
"""Creates a community_config.h from a keymap.json file
"""
if cli.args.output and cli.args.output.name == '-':
cli.args.output = None
lines = [
GPL2_HEADER_C_LIKE,
GENERATED_HEADER_C_LIKE,
'#pragma once',
'',
]
modules = get_modules(cli.args.keyboard, cli.args.filename)
if len(modules) > 0:
lines.append('// Split transactions')
for module in modules:
lines.extend([
f'#ifdef SPLIT_TRANSACTION_IDS_MODULE_{Path(module).name.upper()}',
'# define SPLIT_TRANSACTION_RPC',
'#endif',
])
lines.append('')
dump_lines(cli.args.output, lines, cli.args.quiet, remove_repeated_newlines=True)
@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate community_modules.h for.')
@@ -339,3 +371,26 @@ def generate_rgb_matrix_community_modules_inc(cli):
"""Creates an rgb_matrix_community_modules.inc from a keymap.json file
"""
_generate_include_per_module(cli, 'rgb_matrix_module.inc')
@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate split_transaction_id_community_modules.inc for.')
@cli.argument('filename', nargs='?', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file')
@cli.subcommand('Creates an split_transaction_id_community_modules.inc from a keymap.json file.')
def generate_split_transaction_id_community_modules_inc(cli):
"""Creates an split_transaction_id_community_modules.inc from a keymap.json file
"""
if cli.args.output and cli.args.output.name == '-':
cli.args.output = None
lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE]
for module in get_modules(cli.args.keyboard, cli.args.filename):
lines.extend([
f'#ifdef SPLIT_TRANSACTION_IDS_MODULE_{Path(module).name.upper()}',
f' SPLIT_TRANSACTION_IDS_MODULE_{Path(module).name.upper()},',
'#endif',
])
dump_lines(cli.args.output, lines, cli.args.quiet, remove_repeated_newlines=True)
+5
View File
@@ -0,0 +1,5 @@
// Copyright 2026 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define SPLIT_TRANSACTION_IDS_MODULE_SPLIT_DATA_SYNC EXAMPLE_MODULE_SYNC_A
@@ -0,0 +1,5 @@
{
"module_name": "Example split data sync",
"maintainer": "QMK Maintainers",
"license": "GPL-2.0-or-later"
}
@@ -0,0 +1,40 @@
// Copyright 2026 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#include "debug.h"
#include "timer.h"
#include "transactions.h"
typedef struct _master_to_slave_t {
int m2s_data;
} master_to_slave_t;
typedef struct _slave_to_master_t {
int s2m_data;
} slave_to_master_t;
static void module_sync_slave_handler(uint8_t in_buflen, const void *in_data, uint8_t out_buflen, void *out_data) {
const master_to_slave_t *m2s = (const master_to_slave_t *)in_data;
slave_to_master_t *s2m = (slave_to_master_t *)out_data;
s2m->s2m_data = m2s->m2s_data + 5; // whatever comes in, add 5 so it can be sent back
}
void keyboard_post_init_split_data_sync(void) {
transaction_register_rpc(EXAMPLE_MODULE_SYNC_A, module_sync_slave_handler);
}
void housekeeping_task_split_data_sync(void) {
if (is_keyboard_master()) {
// Interact with slave every 500ms
static uint32_t last_sync = 0;
if (timer_elapsed32(last_sync) > 500) {
master_to_slave_t m2s = {6};
slave_to_master_t s2m = {0};
if (transaction_rpc_exec(EXAMPLE_MODULE_SYNC_A, sizeof(m2s), &m2s, sizeof(s2m), &s2m)) {
last_sync = timer_read32();
dprintf("Slave value: %d\n", s2m.s2m_data); // this will now be 11, as the slave adds 5
} else {
dprint("Slave sync failed!\n");
}
}
}
}
+11 -2
View File
@@ -18,6 +18,10 @@
#include "compiler_support.h"
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
# define SPLIT_TRANSACTION_RPC
#endif
enum serial_transaction_id {
#ifdef USE_I2C
I2C_EXECUTE_CALLBACK,
@@ -99,12 +103,12 @@ enum serial_transaction_id {
PUT_ACTIVITY,
#endif // SPLIT_ACTIVITY_ENABLE
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
#if defined(SPLIT_TRANSACTION_RPC)
PUT_RPC_INFO,
PUT_RPC_REQ_DATA,
EXECUTE_RPC,
GET_RPC_RESP_DATA,
#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
#endif // defined(SPLIT_TRANSACTION_RPC)
// keyboard-specific
#ifdef SPLIT_TRANSACTION_IDS_KB
@@ -116,6 +120,11 @@ enum serial_transaction_id {
SPLIT_TRANSACTION_IDS_USER,
#endif // SPLIT_TRANSACTION_IDS_USER
// community module specific
#ifdef COMMUNITY_MODULES_ENABLE
# include "split_transaction_id_community_modules.inc"
#endif // COMMUNITY_MODULES_ENABLE
#if defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)
PUT_DETECTED_OS,
#endif // defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)
+6 -6
View File
@@ -85,11 +85,11 @@
#define transport_read(id, data, length) transport_execute_transaction(id, NULL, 0, data, length)
#define transport_exec(id) transport_execute_transaction(id, NULL, 0, NULL, 0)
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
#if defined(SPLIT_TRANSACTION_RPC)
// Forward-declare the RPC callback handlers
void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
#endif // defined(SPLIT_TRANSACTION_RPC)
////////////////////////////////////////////////////
// Helpers
@@ -943,12 +943,12 @@ split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = {
TRANSACTIONS_DETECTED_OS_REGISTRATIONS
// clang-format on
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
#if defined(SPLIT_TRANSACTION_RPC)
[PUT_RPC_INFO] = trans_initiator2target_initializer_cb(rpc_info, slave_rpc_info_callback),
[PUT_RPC_REQ_DATA] = trans_initiator2target_initializer(rpc_m2s_buffer),
[EXECUTE_RPC] = trans_initiator2target_initializer_cb(rpc_info.payload.transaction_id, slave_rpc_exec_callback),
[GET_RPC_RESP_DATA] = trans_target2initiator_initializer(rpc_s2m_buffer),
#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
#endif // defined(SPLIT_TRANSACTION_RPC)
};
bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
@@ -996,7 +996,7 @@ void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[
TRANSACTIONS_DETECTED_OS_SLAVE();
}
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
#if defined(SPLIT_TRANSACTION_RPC)
void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback) {
// Prevent invoking RPC on QMK core sync data
@@ -1073,4 +1073,4 @@ void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *i
}
}
#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
#endif // defined(SPLIT_TRANSACTION_RPC)
+5 -4
View File
@@ -22,6 +22,7 @@
#include "progmem.h"
#include "action_layer.h"
#include "matrix.h"
#include "transaction_id_define.h"
#ifndef RPC_M2S_BUFFER_SIZE
# define RPC_M2S_BUFFER_SIZE 32
@@ -132,7 +133,7 @@ typedef struct _split_slave_activity_sync_t {
} split_slave_activity_sync_t;
#endif // defined(SPLIT_ACTIVITY_ENABLE)
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
#if defined(SPLIT_TRANSACTION_RPC)
typedef struct _rpc_sync_info_t {
uint8_t checksum;
struct {
@@ -141,7 +142,7 @@ typedef struct _rpc_sync_info_t {
uint8_t s2m_length;
} payload;
} rpc_sync_info_t;
#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
#endif // defined(SPLIT_TRANSACTION_RPC)
#if defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)
# include "os_detection.h"
@@ -222,11 +223,11 @@ typedef struct _split_shared_memory_t {
split_slave_activity_sync_t activity_sync;
#endif // defined(SPLIT_ACTIVITY_ENABLE)
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
#if defined(SPLIT_TRANSACTION_RPC)
rpc_sync_info_t rpc_info;
uint8_t rpc_m2s_buffer[RPC_M2S_BUFFER_SIZE];
uint8_t rpc_s2m_buffer[RPC_S2M_BUFFER_SIZE];
#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
#endif // defined(SPLIT_TRANSACTION_RPC)
#if defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)
os_variant_t detected_os;