mirror of
https://github.com/Keychron/qmk_firmware.git
synced 2024-11-24 17:37:40 +06:00
172e6a7030
* Extensible split data sync capability through transactions. - Split common transport has been split up between the transport layer and data layer. - Split "transactions" model used, with convergence between I2C and serial data definitions. - Slave matrix "generation count" is used to determine if the full slave matrix needs to be retrieved. - Encoders get the same "generation count" treatment. - All other blocks of data are synchronised when a change is detected. - All transmissions have a globally-configurable deadline before a transmission is forced (`FORCED_SYNC_THROTTLE_MS`, default 100ms). - Added atomicity for all core-synced data, preventing partial updates - Added retries to AVR i2c_master's i2c_start, to minimise the number of failed transactions when interrupts are disabled on the slave due to atomicity checks. - Some keyboards have had slight modifications made in order to ensure that they still build due to firmware size restrictions. * Fixup LED_MATRIX compile. * Parameterise ERROR_DISCONNECT_COUNT.
671 lines
28 KiB
C
671 lines
28 KiB
C
/* Copyright 2021 QMK
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
|
|
#include "debug.h"
|
|
#include "matrix.h"
|
|
#include "quantum.h"
|
|
#include "transactions.h"
|
|
#include "transport.h"
|
|
#include "transaction_id_define.h"
|
|
|
|
#define SYNC_TIMER_OFFSET 2
|
|
|
|
#ifndef FORCED_SYNC_THROTTLE_MS
|
|
# define FORCED_SYNC_THROTTLE_MS 100
|
|
#endif // FORCED_SYNC_THROTTLE_MS
|
|
|
|
#define sizeof_member(type, member) sizeof(((type *)NULL)->member)
|
|
|
|
#define trans_initiator2target_initializer_cb(member, cb) \
|
|
{ &dummy, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), 0, 0, cb }
|
|
#define trans_initiator2target_initializer(member) trans_initiator2target_initializer_cb(member, NULL)
|
|
|
|
#define trans_target2initiator_initializer_cb(member, cb) \
|
|
{ &dummy, 0, 0, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), cb }
|
|
#define trans_target2initiator_initializer(member) trans_target2initiator_initializer_cb(member, NULL)
|
|
|
|
#define transport_write(id, data, length) transport_execute_transaction(id, data, length, NULL, 0)
|
|
#define transport_read(id, data, length) transport_execute_transaction(id, NULL, 0, data, length)
|
|
|
|
static uint8_t crc8(const void *data, size_t len) {
|
|
const uint8_t *p = (const uint8_t *)data;
|
|
uint8_t crc = 0xff;
|
|
size_t i, j;
|
|
for (i = 0; i < len; i++) {
|
|
crc ^= p[i];
|
|
for (j = 0; j < 8; j++) {
|
|
if ((crc & 0x80) != 0)
|
|
crc = (uint8_t)((crc << 1) ^ 0x31);
|
|
else
|
|
crc <<= 1;
|
|
}
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
|
|
// 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)
|
|
|
|
////////////////////////////////////////////////////
|
|
// Helpers
|
|
|
|
bool transaction_handler_master(bool okay, matrix_row_t master_matrix[], matrix_row_t slave_matrix[], const char *prefix, bool (*handler)(matrix_row_t master_matrix[], matrix_row_t slave_matrix[])) {
|
|
if (okay) {
|
|
bool this_okay = true;
|
|
for (int iter = 1; iter <= 10; ++iter) {
|
|
if (!this_okay) {
|
|
for (int i = 0; i < iter * iter; ++i) {
|
|
wait_us(10);
|
|
}
|
|
}
|
|
ATOMIC_BLOCK_FORCEON { this_okay = handler(master_matrix, slave_matrix); };
|
|
if (this_okay) break;
|
|
}
|
|
okay &= this_okay;
|
|
if (!okay) {
|
|
dprintf("Failed to execute %s\n", prefix);
|
|
}
|
|
}
|
|
return okay;
|
|
}
|
|
|
|
#define TRANSACTION_HANDLER_MASTER(prefix) \
|
|
do { \
|
|
okay &= transaction_handler_master(okay, master_matrix, slave_matrix, #prefix, &prefix##_master); \
|
|
} while (0)
|
|
|
|
#define TRANSACTION_HANDLER_SLAVE(prefix) \
|
|
do { \
|
|
ATOMIC_BLOCK_FORCEON { prefix##_slave(master_matrix, slave_matrix); }; \
|
|
} while (0)
|
|
|
|
inline static bool read_if_checksum_mismatch(int8_t trans_id_checksum, int8_t trans_id_retrieve, uint32_t *last_update, void *destination, const void *equiv_shmem, size_t length) {
|
|
uint8_t curr_checksum;
|
|
bool okay = transport_read(trans_id_checksum, &curr_checksum, sizeof(curr_checksum));
|
|
if (okay && (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || curr_checksum != crc8(equiv_shmem, length))) {
|
|
okay &= transport_read(trans_id_retrieve, destination, length);
|
|
okay &= curr_checksum == crc8(equiv_shmem, length);
|
|
if (okay) {
|
|
*last_update = timer_read32();
|
|
}
|
|
} else {
|
|
memcpy(destination, equiv_shmem, length);
|
|
}
|
|
return okay;
|
|
}
|
|
|
|
inline static bool send_if_condition(int8_t trans_id, uint32_t *last_update, bool condition, void *source, size_t length) {
|
|
bool okay = true;
|
|
if (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || condition) {
|
|
okay &= transport_write(trans_id, source, length);
|
|
if (okay) {
|
|
*last_update = timer_read32();
|
|
}
|
|
}
|
|
return okay;
|
|
}
|
|
|
|
inline static bool send_if_data_mismatch(int8_t trans_id, uint32_t *last_update, void *source, const void *equiv_shmem, size_t length) {
|
|
// Just run a memcmp to compare the source and equivalent shmem location
|
|
return send_if_condition(trans_id, last_update, (memcmp(source, equiv_shmem, length) != 0), source, length);
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
// Slave matrix
|
|
|
|
static bool slave_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
static matrix_row_t last_matrix[(MATRIX_ROWS) / 2] = {0}; // last successfully-read matrix, so we can replicate if there are checksum errors
|
|
matrix_row_t temp_matrix[(MATRIX_ROWS) / 2]; // holding area while we test whether or not checksum is correct
|
|
|
|
bool okay = read_if_checksum_mismatch(GET_SLAVE_MATRIX_CHECKSUM, GET_SLAVE_MATRIX_DATA, &last_update, temp_matrix, split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix));
|
|
if (okay) {
|
|
// Checksum matches the received data, save as the last matrix state
|
|
memcpy(last_matrix, temp_matrix, sizeof(temp_matrix));
|
|
}
|
|
// Copy out the last-known-good matrix state to the slave matrix
|
|
memcpy(slave_matrix, last_matrix, sizeof(last_matrix));
|
|
return okay;
|
|
}
|
|
|
|
static void slave_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
memcpy(split_shmem->smatrix.matrix, slave_matrix, sizeof(split_shmem->smatrix.matrix));
|
|
split_shmem->smatrix.checksum = crc8(split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix));
|
|
}
|
|
|
|
// clang-format off
|
|
#define TRANSACTIONS_SLAVE_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(slave_matrix_handlers)
|
|
#define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(slave_matrix_handlers)
|
|
#define TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS \
|
|
[GET_SLAVE_MATRIX_CHECKSUM] = trans_target2initiator_initializer(smatrix.checksum), \
|
|
[GET_SLAVE_MATRIX_DATA] = trans_target2initiator_initializer(smatrix.matrix),
|
|
// clang-format on
|
|
|
|
////////////////////////////////////////////////////
|
|
// Master matrix
|
|
|
|
#ifdef SPLIT_TRANSPORT_MIRROR
|
|
|
|
static bool master_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
return send_if_data_mismatch(PUT_MASTER_MATRIX, &last_update, master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));
|
|
}
|
|
|
|
static void master_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
// Always copy to the master matrix
|
|
memcpy(master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));
|
|
}
|
|
|
|
# define TRANSACTIONS_MASTER_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(master_matrix_handlers)
|
|
# define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(master_matrix_handlers)
|
|
# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS [PUT_MASTER_MATRIX] = trans_initiator2target_initializer(mmatrix.matrix),
|
|
|
|
#else // SPLIT_TRANSPORT_MIRROR
|
|
|
|
# define TRANSACTIONS_MASTER_MATRIX_MASTER()
|
|
# define TRANSACTIONS_MASTER_MATRIX_SLAVE()
|
|
# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS
|
|
|
|
#endif // SPLIT_TRANSPORT_MIRROR
|
|
|
|
////////////////////////////////////////////////////
|
|
// Encoders
|
|
|
|
#ifdef ENCODER_ENABLE
|
|
|
|
static bool encoder_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
uint8_t temp_state[NUMBER_OF_ENCODERS];
|
|
|
|
bool okay = read_if_checksum_mismatch(GET_ENCODERS_CHECKSUM, GET_ENCODERS_DATA, &last_update, temp_state, split_shmem->encoders.state, sizeof(temp_state));
|
|
if (okay) encoder_update_raw(temp_state);
|
|
return okay;
|
|
}
|
|
|
|
static void encoder_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
uint8_t encoder_state[NUMBER_OF_ENCODERS];
|
|
encoder_state_raw(encoder_state);
|
|
// Always prepare the encoder state for read.
|
|
memcpy(split_shmem->encoders.state, encoder_state, sizeof(encoder_state));
|
|
// Now update the checksum given that the encoders has been written to
|
|
split_shmem->encoders.checksum = crc8(encoder_state, sizeof(encoder_state));
|
|
}
|
|
|
|
// clang-format off
|
|
# define TRANSACTIONS_ENCODERS_MASTER() TRANSACTION_HANDLER_MASTER(encoder_handlers)
|
|
# define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE(encoder_handlers)
|
|
# define TRANSACTIONS_ENCODERS_REGISTRATIONS \
|
|
[GET_ENCODERS_CHECKSUM] = trans_target2initiator_initializer(encoders.checksum), \
|
|
[GET_ENCODERS_DATA] = trans_target2initiator_initializer(encoders.state),
|
|
// clang-format on
|
|
|
|
#else // ENCODER_ENABLE
|
|
|
|
# define TRANSACTIONS_ENCODERS_MASTER()
|
|
# define TRANSACTIONS_ENCODERS_SLAVE()
|
|
# define TRANSACTIONS_ENCODERS_REGISTRATIONS
|
|
|
|
#endif // ENCODER_ENABLE
|
|
|
|
////////////////////////////////////////////////////
|
|
// Sync timer
|
|
|
|
#ifndef DISABLE_SYNC_TIMER
|
|
|
|
static bool sync_timer_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
|
|
bool okay = true;
|
|
if (timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS) {
|
|
uint32_t sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
|
|
okay &= transport_write(PUT_SYNC_TIMER, &sync_timer, sizeof(sync_timer));
|
|
if (okay) {
|
|
last_update = timer_read32();
|
|
}
|
|
}
|
|
return okay;
|
|
}
|
|
|
|
static void sync_timer_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_sync_timer = 0;
|
|
if (last_sync_timer != split_shmem->sync_timer) {
|
|
last_sync_timer = split_shmem->sync_timer;
|
|
sync_timer_update(last_sync_timer);
|
|
}
|
|
}
|
|
|
|
# define TRANSACTIONS_SYNC_TIMER_MASTER() TRANSACTION_HANDLER_MASTER(sync_timer_handlers)
|
|
# define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE(sync_timer_handlers)
|
|
# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS [PUT_SYNC_TIMER] = trans_initiator2target_initializer(sync_timer),
|
|
|
|
#else // DISABLE_SYNC_TIMER
|
|
|
|
# define TRANSACTIONS_SYNC_TIMER_MASTER()
|
|
# define TRANSACTIONS_SYNC_TIMER_SLAVE()
|
|
# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS
|
|
|
|
#endif // DISABLE_SYNC_TIMER
|
|
|
|
////////////////////////////////////////////////////
|
|
// Layer state
|
|
|
|
#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
|
|
|
|
static bool layer_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_layer_state_update = 0;
|
|
static uint32_t last_default_layer_state_update = 0;
|
|
|
|
bool okay = send_if_condition(PUT_LAYER_STATE, &last_layer_state_update, (layer_state != split_shmem->layers.layer_state), &layer_state, sizeof(layer_state));
|
|
if (okay) {
|
|
okay &= send_if_condition(PUT_DEFAULT_LAYER_STATE, &last_default_layer_state_update, (default_layer_state != split_shmem->layers.default_layer_state), &default_layer_state, sizeof(default_layer_state));
|
|
}
|
|
return okay;
|
|
}
|
|
|
|
static void layer_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
layer_state = split_shmem->layers.layer_state;
|
|
default_layer_state = split_shmem->layers.default_layer_state;
|
|
}
|
|
|
|
// clang-format off
|
|
# define TRANSACTIONS_LAYER_STATE_MASTER() TRANSACTION_HANDLER_MASTER(layer_state_handlers)
|
|
# define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(layer_state_handlers)
|
|
# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS \
|
|
[PUT_LAYER_STATE] = trans_initiator2target_initializer(layers.layer_state), \
|
|
[PUT_DEFAULT_LAYER_STATE] = trans_initiator2target_initializer(layers.default_layer_state),
|
|
// clang-format on
|
|
|
|
#else // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
|
|
|
|
# define TRANSACTIONS_LAYER_STATE_MASTER()
|
|
# define TRANSACTIONS_LAYER_STATE_SLAVE()
|
|
# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS
|
|
|
|
#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
|
|
|
|
////////////////////////////////////////////////////
|
|
// LED state
|
|
|
|
#ifdef SPLIT_LED_STATE_ENABLE
|
|
|
|
static bool led_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
uint8_t led_state = host_keyboard_leds();
|
|
return send_if_data_mismatch(PUT_LED_STATE, &last_update, &led_state, &split_shmem->led_state, sizeof(led_state));
|
|
}
|
|
|
|
static void led_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
void set_split_host_keyboard_leds(uint8_t led_state);
|
|
set_split_host_keyboard_leds(split_shmem->led_state);
|
|
}
|
|
|
|
# define TRANSACTIONS_LED_STATE_MASTER() TRANSACTION_HANDLER_MASTER(led_state_handlers)
|
|
# define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(led_state_handlers)
|
|
# define TRANSACTIONS_LED_STATE_REGISTRATIONS [PUT_LED_STATE] = trans_initiator2target_initializer(led_state),
|
|
|
|
#else // SPLIT_LED_STATE_ENABLE
|
|
|
|
# define TRANSACTIONS_LED_STATE_MASTER()
|
|
# define TRANSACTIONS_LED_STATE_SLAVE()
|
|
# define TRANSACTIONS_LED_STATE_REGISTRATIONS
|
|
|
|
#endif // SPLIT_LED_STATE_ENABLE
|
|
|
|
////////////////////////////////////////////////////
|
|
// Mods
|
|
|
|
#ifdef SPLIT_MODS_ENABLE
|
|
|
|
static bool mods_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
bool mods_need_sync = timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS;
|
|
split_mods_sync_t new_mods;
|
|
new_mods.real_mods = get_mods();
|
|
if (!mods_need_sync && new_mods.real_mods != split_shmem->mods.real_mods) {
|
|
mods_need_sync = true;
|
|
}
|
|
|
|
new_mods.weak_mods = get_weak_mods();
|
|
if (!mods_need_sync && new_mods.weak_mods != split_shmem->mods.weak_mods) {
|
|
mods_need_sync = true;
|
|
}
|
|
|
|
# ifndef NO_ACTION_ONESHOT
|
|
new_mods.oneshot_mods = get_oneshot_mods();
|
|
if (!mods_need_sync && new_mods.oneshot_mods != split_shmem->mods.oneshot_mods) {
|
|
mods_need_sync = true;
|
|
}
|
|
# endif // NO_ACTION_ONESHOT
|
|
|
|
bool okay = true;
|
|
if (mods_need_sync) {
|
|
okay &= transport_write(PUT_MODS, &new_mods, sizeof(new_mods));
|
|
if (okay) {
|
|
last_update = timer_read32();
|
|
}
|
|
}
|
|
|
|
return okay;
|
|
}
|
|
|
|
static void mods_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
set_mods(split_shmem->mods.real_mods);
|
|
set_weak_mods(split_shmem->mods.weak_mods);
|
|
# ifndef NO_ACTION_ONESHOT
|
|
set_oneshot_mods(split_shmem->mods.oneshot_mods);
|
|
# endif
|
|
}
|
|
|
|
# define TRANSACTIONS_MODS_MASTER() TRANSACTION_HANDLER_MASTER(mods_handlers)
|
|
# define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods_handlers)
|
|
# define TRANSACTIONS_MODS_REGISTRATIONS [PUT_MODS] = trans_initiator2target_initializer(mods),
|
|
|
|
#else // SPLIT_MODS_ENABLE
|
|
|
|
# define TRANSACTIONS_MODS_MASTER()
|
|
# define TRANSACTIONS_MODS_SLAVE()
|
|
# define TRANSACTIONS_MODS_REGISTRATIONS
|
|
|
|
#endif // SPLIT_MODS_ENABLE
|
|
|
|
////////////////////////////////////////////////////
|
|
// Backlight
|
|
|
|
#ifdef BACKLIGHT_ENABLE
|
|
|
|
static bool backlight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0;
|
|
return send_if_condition(PUT_BACKLIGHT, &last_update, (level != split_shmem->backlight_level), &level, sizeof(level));
|
|
}
|
|
|
|
static void backlight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { backlight_set(split_shmem->backlight_level); }
|
|
|
|
# define TRANSACTIONS_BACKLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(backlight_handlers)
|
|
# define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight_handlers)
|
|
# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS [PUT_BACKLIGHT] = trans_initiator2target_initializer(backlight_level),
|
|
|
|
#else // BACKLIGHT_ENABLE
|
|
|
|
# define TRANSACTIONS_BACKLIGHT_MASTER()
|
|
# define TRANSACTIONS_BACKLIGHT_SLAVE()
|
|
# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS
|
|
|
|
#endif // BACKLIGHT_ENABLE
|
|
|
|
////////////////////////////////////////////////////
|
|
// RGBLIGHT
|
|
|
|
#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
|
|
|
|
static bool rgblight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
rgblight_syncinfo_t rgblight_sync;
|
|
rgblight_get_syncinfo(&rgblight_sync);
|
|
if (send_if_condition(PUT_RGBLIGHT, &last_update, (rgblight_sync.status.change_flags != 0), &rgblight_sync, sizeof(rgblight_sync))) {
|
|
rgblight_clear_change_flags();
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void rgblight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
// Update the RGB with the new data
|
|
if (split_shmem->rgblight_sync.status.change_flags != 0) {
|
|
rgblight_update_sync(&split_shmem->rgblight_sync, false);
|
|
split_shmem->rgblight_sync.status.change_flags = 0;
|
|
}
|
|
}
|
|
|
|
# define TRANSACTIONS_RGBLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(rgblight_handlers)
|
|
# define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight_handlers)
|
|
# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS [PUT_RGBLIGHT] = trans_initiator2target_initializer(rgblight_sync),
|
|
|
|
#else // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
|
|
|
|
# define TRANSACTIONS_RGBLIGHT_MASTER()
|
|
# define TRANSACTIONS_RGBLIGHT_SLAVE()
|
|
# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS
|
|
|
|
#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
|
|
|
|
////////////////////////////////////////////////////
|
|
// LED Matrix
|
|
|
|
#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
|
|
|
|
static bool led_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
led_matrix_sync_t led_matrix_sync;
|
|
memcpy(&led_matrix_sync.led_matrix, &led_matrix_eeconfig, sizeof(led_eeconfig_t));
|
|
led_matrix_sync.led_suspend_state = led_matrix_get_suspend_state();
|
|
return send_if_data_mismatch(PUT_LED_MATRIX, &last_update, &led_matrix_sync, &split_shmem->led_matrix_sync, sizeof(led_matrix_sync));
|
|
}
|
|
|
|
static void led_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
memcpy(&led_matrix_eeconfig, &split_shmem->led_matrix_sync.led_matrix, sizeof(led_eeconfig_t));
|
|
led_matrix_set_suspend_state(split_shmem->led_matrix_sync.led_suspend_state);
|
|
}
|
|
|
|
# define TRANSACTIONS_LED_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(led_matrix_handlers)
|
|
# define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix_handlers)
|
|
# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS [PUT_LED_MATRIX] = trans_initiator2target_initializer(led_matrix_sync),
|
|
|
|
#else // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
|
|
|
|
# define TRANSACTIONS_LED_MATRIX_MASTER()
|
|
# define TRANSACTIONS_LED_MATRIX_SLAVE()
|
|
# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS
|
|
|
|
#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
|
|
|
|
////////////////////////////////////////////////////
|
|
// RGB Matrix
|
|
|
|
#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
|
|
|
|
static bool rgb_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
rgb_matrix_sync_t rgb_matrix_sync;
|
|
memcpy(&rgb_matrix_sync.rgb_matrix, &rgb_matrix_config, sizeof(rgb_config_t));
|
|
rgb_matrix_sync.rgb_suspend_state = rgb_matrix_get_suspend_state();
|
|
return send_if_data_mismatch(PUT_RGB_MATRIX, &last_update, &rgb_matrix_sync, &split_shmem->rgb_matrix_sync, sizeof(rgb_matrix_sync));
|
|
}
|
|
|
|
static void rgb_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
memcpy(&rgb_matrix_config, &split_shmem->rgb_matrix_sync.rgb_matrix, sizeof(rgb_config_t));
|
|
rgb_matrix_set_suspend_state(split_shmem->rgb_matrix_sync.rgb_suspend_state);
|
|
}
|
|
|
|
# define TRANSACTIONS_RGB_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(rgb_matrix_handlers)
|
|
# define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix_handlers)
|
|
# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS [PUT_RGB_MATRIX] = trans_initiator2target_initializer(rgb_matrix_sync),
|
|
|
|
#else // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
|
|
|
|
# define TRANSACTIONS_RGB_MATRIX_MASTER()
|
|
# define TRANSACTIONS_RGB_MATRIX_SLAVE()
|
|
# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS
|
|
|
|
#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
|
|
|
|
////////////////////////////////////////////////////
|
|
// WPM
|
|
|
|
#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
|
|
|
|
static bool wpm_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
uint8_t current_wpm = get_current_wpm();
|
|
return send_if_condition(PUT_WPM, &last_update, (current_wpm != split_shmem->current_wpm), ¤t_wpm, sizeof(current_wpm));
|
|
}
|
|
|
|
static void wpm_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { set_current_wpm(split_shmem->current_wpm); }
|
|
|
|
# define TRANSACTIONS_WPM_MASTER() TRANSACTION_HANDLER_MASTER(wpm_handlers)
|
|
# define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE(wpm_handlers)
|
|
# define TRANSACTIONS_WPM_REGISTRATIONS [PUT_WPM] = trans_initiator2target_initializer(current_wpm),
|
|
|
|
#else // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
|
|
|
|
# define TRANSACTIONS_WPM_MASTER()
|
|
# define TRANSACTIONS_WPM_SLAVE()
|
|
# define TRANSACTIONS_WPM_REGISTRATIONS
|
|
|
|
#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
|
|
|
|
////////////////////////////////////////////////////
|
|
|
|
uint8_t dummy;
|
|
split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = {
|
|
// Set defaults
|
|
[0 ...(NUM_TOTAL_TRANSACTIONS - 1)] = {NULL, 0, 0, 0, 0, 0},
|
|
|
|
#ifdef USE_I2C
|
|
[I2C_EXECUTE_CALLBACK] = trans_initiator2target_initializer(transaction_id),
|
|
#endif // USE_I2C
|
|
|
|
// clang-format off
|
|
TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS
|
|
TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS
|
|
TRANSACTIONS_ENCODERS_REGISTRATIONS
|
|
TRANSACTIONS_SYNC_TIMER_REGISTRATIONS
|
|
TRANSACTIONS_LAYER_STATE_REGISTRATIONS
|
|
TRANSACTIONS_LED_STATE_REGISTRATIONS
|
|
TRANSACTIONS_MODS_REGISTRATIONS
|
|
TRANSACTIONS_BACKLIGHT_REGISTRATIONS
|
|
TRANSACTIONS_RGBLIGHT_REGISTRATIONS
|
|
TRANSACTIONS_LED_MATRIX_REGISTRATIONS
|
|
TRANSACTIONS_RGB_MATRIX_REGISTRATIONS
|
|
TRANSACTIONS_WPM_REGISTRATIONS
|
|
// clang-format on
|
|
|
|
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
|
|
[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.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)
|
|
};
|
|
|
|
bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
bool okay = true;
|
|
TRANSACTIONS_SLAVE_MATRIX_MASTER();
|
|
TRANSACTIONS_MASTER_MATRIX_MASTER();
|
|
TRANSACTIONS_ENCODERS_MASTER();
|
|
TRANSACTIONS_SYNC_TIMER_MASTER();
|
|
TRANSACTIONS_LAYER_STATE_MASTER();
|
|
TRANSACTIONS_LED_STATE_MASTER();
|
|
TRANSACTIONS_MODS_MASTER();
|
|
TRANSACTIONS_BACKLIGHT_MASTER();
|
|
TRANSACTIONS_RGBLIGHT_MASTER();
|
|
TRANSACTIONS_LED_MATRIX_MASTER();
|
|
TRANSACTIONS_RGB_MATRIX_MASTER();
|
|
TRANSACTIONS_WPM_MASTER();
|
|
return okay;
|
|
}
|
|
|
|
void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
TRANSACTIONS_SLAVE_MATRIX_SLAVE();
|
|
TRANSACTIONS_MASTER_MATRIX_SLAVE();
|
|
TRANSACTIONS_ENCODERS_SLAVE();
|
|
TRANSACTIONS_SYNC_TIMER_SLAVE();
|
|
TRANSACTIONS_LAYER_STATE_SLAVE();
|
|
TRANSACTIONS_LED_STATE_SLAVE();
|
|
TRANSACTIONS_MODS_SLAVE();
|
|
TRANSACTIONS_BACKLIGHT_SLAVE();
|
|
TRANSACTIONS_RGBLIGHT_SLAVE();
|
|
TRANSACTIONS_LED_MATRIX_SLAVE();
|
|
TRANSACTIONS_RGB_MATRIX_SLAVE();
|
|
TRANSACTIONS_WPM_SLAVE();
|
|
}
|
|
|
|
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
|
|
|
|
void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback) {
|
|
// Prevent invoking RPC on QMK core sync data
|
|
if (transaction_id <= GET_RPC_RESP_DATA) return;
|
|
|
|
// Set the callback
|
|
split_transaction_table[transaction_id].slave_callback = callback;
|
|
split_transaction_table[transaction_id].initiator2target_offset = offsetof(split_shared_memory_t, rpc_m2s_buffer);
|
|
split_transaction_table[transaction_id].target2initiator_offset = offsetof(split_shared_memory_t, rpc_s2m_buffer);
|
|
}
|
|
|
|
bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
|
|
// Prevent invoking RPC on QMK core sync data
|
|
if (transaction_id <= GET_RPC_RESP_DATA) return false;
|
|
// Prevent sizing issues
|
|
if (initiator2target_buffer_size > RPC_M2S_BUFFER_SIZE) return false;
|
|
if (target2initiator_buffer_size > RPC_S2M_BUFFER_SIZE) return false;
|
|
|
|
// Prepare the metadata block
|
|
rpc_sync_info_t info = {.transaction_id = transaction_id, .m2s_length = initiator2target_buffer_size, .s2m_length = target2initiator_buffer_size};
|
|
|
|
// Make sure the local side knows that we're not sending the full block of data
|
|
split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = initiator2target_buffer_size;
|
|
split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = target2initiator_buffer_size;
|
|
|
|
// Run through the sequence:
|
|
// * set the transaction ID and lengths
|
|
// * send the request data
|
|
// * execute RPC callback
|
|
// * retrieve the response data
|
|
if (!transport_write(PUT_RPC_INFO, &info, sizeof(info))) {
|
|
return false;
|
|
}
|
|
if (!transport_write(PUT_RPC_REQ_DATA, initiator2target_buffer, initiator2target_buffer_size)) {
|
|
return false;
|
|
}
|
|
if (!transport_write(EXECUTE_RPC, &transaction_id, sizeof(transaction_id))) {
|
|
return false;
|
|
}
|
|
if (!transport_read(GET_RPC_RESP_DATA, target2initiator_buffer, target2initiator_buffer_size)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
|
|
// The RPC info block contains the intended transaction ID, as well as the sizes for both inbound and outbound data.
|
|
// Ignore the args -- the `split_shmem` already has the info, we just need to act upon it.
|
|
// We must keep the `split_transaction_table` non-const, so that it is able to be modified at runtime.
|
|
|
|
split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = split_shmem->rpc_info.m2s_length;
|
|
split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = split_shmem->rpc_info.s2m_length;
|
|
}
|
|
|
|
void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
|
|
// We can assume that the buffer lengths are correctly set, now, given that sequentially the rpc_info callback was already executed.
|
|
// Go through the rpc_info and execute _that_ transaction's callback, with the scratch buffers as inputs.
|
|
int8_t transaction_id = split_shmem->rpc_info.transaction_id;
|
|
if (transaction_id < NUM_TOTAL_TRANSACTIONS) {
|
|
split_transaction_desc_t *trans = &split_transaction_table[transaction_id];
|
|
if (trans->slave_callback) {
|
|
trans->slave_callback(split_shmem->rpc_info.m2s_length, split_shmem->rpc_m2s_buffer, split_shmem->rpc_info.s2m_length, split_shmem->rpc_s2m_buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
|