summaryrefslogtreecommitdiffstats
path: root/quantum/split_common
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/split_common')
-rw-r--r--quantum/split_common/matrix.c83
-rw-r--r--quantum/split_common/post_config.h9
-rw-r--r--quantum/split_common/transaction_id_define.h94
-rw-r--r--quantum/split_common/transactions.c670
-rw-r--r--quantum/split_common/transactions.h54
-rw-r--r--quantum/split_common/transport.c484
-rw-r--r--quantum/split_common/transport.h165
7 files changed, 1096 insertions, 463 deletions
diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c
index 039e7d9773..56d91b07fe 100644
--- a/quantum/split_common/matrix.c
+++ b/quantum/split_common/matrix.c
@@ -16,23 +16,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdbool.h>
+#include <string.h>
#include "util.h"
#include "matrix.h"
#include "debounce.h"
#include "quantum.h"
#include "split_util.h"
#include "config.h"
-#include "transport.h"
+#include "transactions.h"
-#define ERROR_DISCONNECT_COUNT 5
+#ifndef ERROR_DISCONNECT_COUNT
+# define ERROR_DISCONNECT_COUNT 5
+#endif // ERROR_DISCONNECT_COUNT
#define ROWS_PER_HAND (MATRIX_ROWS / 2)
#ifdef DIRECT_PINS
static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS;
#elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW)
+# ifdef MATRIX_ROW_PINS
static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
+# endif // MATRIX_ROW_PINS
+# ifdef MATRIX_COL_PINS
static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
+# endif // MATRIX_COL_PINS
#endif
/* matrix state(1:on, 0:off) */
@@ -45,6 +52,9 @@ uint8_t thisHand, thatHand;
// user-defined overridable functions
__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); }
__attribute__((weak)) void matrix_slave_scan_user(void) {}
+__attribute__((weak)) void matrix_init_pins(void);
+__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);
+__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col);
static inline void setPinOutput_writeLow(pin_t pin) {
ATOMIC_BLOCK_FORCEON {
@@ -61,7 +71,7 @@ static inline void setPinInputHigh_atomic(pin_t pin) {
#ifdef DIRECT_PINS
-static void init_pins(void) {
+__attribute__((weak)) void matrix_init_pins(void) {
for (int row = 0; row < MATRIX_ROWS; row++) {
for (int col = 0; col < MATRIX_COLS; col++) {
pin_t pin = direct_pins[row][col];
@@ -72,7 +82,7 @@ static void init_pins(void) {
}
}
-static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
+__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
// Start with a clear matrix row
matrix_row_t current_row_value = 0;
@@ -83,16 +93,13 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
}
}
- // If the row has changed, store the row and return the changed flag.
- if (current_matrix[current_row] != current_row_value) {
- current_matrix[current_row] = current_row_value;
- return true;
- }
- return false;
+ // Update the matrix
+ current_matrix[current_row] = current_row_value;
}
#elif defined(DIODE_DIRECTION)
-# if (DIODE_DIRECTION == COL2ROW)
+# if defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)
+# if (DIODE_DIRECTION == COL2ROW)
static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); }
@@ -104,14 +111,14 @@ static void unselect_rows(void) {
}
}
-static void init_pins(void) {
+__attribute__((weak)) void matrix_init_pins(void) {
unselect_rows();
for (uint8_t x = 0; x < MATRIX_COLS; x++) {
setPinInputHigh_atomic(col_pins[x]);
}
}
-static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
+__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
// Start with a clear matrix row
matrix_row_t current_row_value = 0;
@@ -132,15 +139,11 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
unselect_row(current_row);
matrix_output_unselect_delay(); // wait for all Col signals to go HIGH
- // If the row has changed, store the row and return the changed flag.
- if (current_matrix[current_row] != current_row_value) {
- current_matrix[current_row] = current_row_value;
- return true;
- }
- return false;
+ // Update the matrix
+ current_matrix[current_row] = current_row_value;
}
-# elif (DIODE_DIRECTION == ROW2COL)
+# elif (DIODE_DIRECTION == ROW2COL)
static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); }
@@ -152,52 +155,39 @@ static void unselect_cols(void) {
}
}
-static void init_pins(void) {
+__attribute__((weak)) void matrix_init_pins(void) {
unselect_cols();
for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
setPinInputHigh_atomic(row_pins[x]);
}
}
-static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
- bool matrix_changed = false;
-
+__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
// Select col
select_col(current_col);
matrix_output_select_delay();
// For each row...
for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) {
- // Store last value of row prior to reading
- matrix_row_t last_row_value = current_matrix[row_index];
- matrix_row_t current_row_value = last_row_value;
-
// Check row pin state
if (readPin(row_pins[row_index]) == 0) {
// Pin LO, set col bit
- current_row_value |= (MATRIX_ROW_SHIFTER << current_col);
+ current_matrix[row_index] |= (MATRIX_ROW_SHIFTER << current_col);
} else {
// Pin HI, clear col bit
- current_row_value &= ~(MATRIX_ROW_SHIFTER << current_col);
- }
-
- // Determine if the matrix changed state
- if ((last_row_value != current_row_value)) {
- matrix_changed |= true;
- current_matrix[row_index] = current_row_value;
+ current_matrix[row_index] &= ~(MATRIX_ROW_SHIFTER << current_col);
}
}
// Unselect col
unselect_col(current_col);
matrix_output_unselect_delay(); // wait for all Row signals to go HIGH
-
- return matrix_changed;
}
-# else
-# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL!
-# endif
+# else
+# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL!
+# endif
+# endif // defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)
#else
# error DIODE_DIRECTION is not defined!
#endif
@@ -233,7 +223,7 @@ void matrix_init(void) {
thatHand = ROWS_PER_HAND - thisHand;
// initialize key pins
- init_pins();
+ matrix_init_pins();
// initialize matrix state: all keys off
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
@@ -288,20 +278,23 @@ bool matrix_post_scan(void) {
}
uint8_t matrix_scan(void) {
- bool local_changed = false;
+ matrix_row_t curr_matrix[MATRIX_ROWS] = {0};
#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW)
// Set row, read cols
for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
- local_changed |= read_cols_on_row(raw_matrix, current_row);
+ matrix_read_cols_on_row(curr_matrix, current_row);
}
#elif (DIODE_DIRECTION == ROW2COL)
// Set col, read rows
for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
- local_changed |= read_rows_on_col(raw_matrix, current_col);
+ matrix_read_rows_on_col(curr_matrix, current_col);
}
#endif
+ bool local_changed = memcmp(raw_matrix, curr_matrix, sizeof(curr_matrix)) != 0;
+ if (local_changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix));
+
debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, local_changed);
bool remote_changed = matrix_post_scan();
diff --git a/quantum/split_common/post_config.h b/quantum/split_common/post_config.h
index 4ae1d52732..a4c0a1956b 100644
--- a/quantum/split_common/post_config.h
+++ b/quantum/split_common/post_config.h
@@ -7,13 +7,4 @@
# ifndef F_SCL
# define F_SCL 100000UL // SCL frequency
# endif
-
-#else // use serial
-// When using serial, the user must define RGBLIGHT_SPLIT explicitly
-// in config.h as needed.
-// see quantum/rgblight_post_config.h
-# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
-// When using serial and RGBLIGHT_SPLIT need separate transaction
-# define SERIAL_USE_MULTI_TRANSACTION
-# endif
#endif
diff --git a/quantum/split_common/transaction_id_define.h b/quantum/split_common/transaction_id_define.h
new file mode 100644
index 0000000000..464c73478a
--- /dev/null
+++ b/quantum/split_common/transaction_id_define.h
@@ -0,0 +1,94 @@
+/* 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/>.
+ */
+
+#pragma once
+
+enum serial_transaction_id {
+#ifdef USE_I2C
+ I2C_EXECUTE_CALLBACK,
+#endif // USE_I2C
+
+ GET_SLAVE_MATRIX_CHECKSUM,
+ GET_SLAVE_MATRIX_DATA,
+
+#ifdef SPLIT_TRANSPORT_MIRROR
+ PUT_MASTER_MATRIX,
+#endif // SPLIT_TRANSPORT_MIRROR
+
+#ifdef ENCODER_ENABLE
+ GET_ENCODERS_CHECKSUM,
+ GET_ENCODERS_DATA,
+#endif // ENCODER_ENABLE
+
+#ifndef DISABLE_SYNC_TIMER
+ PUT_SYNC_TIMER,
+#endif // DISABLE_SYNC_TIMER
+
+#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
+ PUT_LAYER_STATE,
+ PUT_DEFAULT_LAYER_STATE,
+#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
+
+#ifdef SPLIT_LED_STATE_ENABLE
+ PUT_LED_STATE,
+#endif // SPLIT_LED_STATE_ENABLE
+
+#ifdef SPLIT_MODS_ENABLE
+ PUT_MODS,
+#endif // SPLIT_MODS_ENABLE
+
+#ifdef BACKLIGHT_ENABLE
+ PUT_BACKLIGHT,
+#endif // BACKLIGHT_ENABLE
+
+#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+ PUT_RGBLIGHT,
+#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+
+#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
+ PUT_LED_MATRIX,
+#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
+
+#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
+ PUT_RGB_MATRIX,
+#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+
+#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
+ PUT_WPM,
+#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
+
+#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+ PUT_RPC_INFO,
+ PUT_RPC_REQ_DATA,
+ EXECUTE_RPC,
+ GET_RPC_RESP_DATA,
+#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+
+// keyboard-specific
+#ifdef SPLIT_TRANSACTION_IDS_KB
+ SPLIT_TRANSACTION_IDS_KB,
+#endif // SPLIT_TRANSACTION_IDS_KB
+
+// user/keymap-specific
+#ifdef SPLIT_TRANSACTION_IDS_USER
+ SPLIT_TRANSACTION_IDS_USER,
+#endif // SPLIT_TRANSACTION_IDS_USER
+
+ NUM_TOTAL_TRANSACTIONS
+};
+
+// Ensure we only use 5 bits for transaction
+_Static_assert(NUM_TOTAL_TRANSACTIONS <= (1 << 5), "Max number of usable transactions exceeded");
diff --git a/quantum/split_common/transactions.c b/quantum/split_common/transactions.c
new file mode 100644
index 0000000000..99a8623b3c
--- /dev/null
+++ b/quantum/split_common/transactions.c
@@ -0,0 +1,670 @@
+/* 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), &current_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_