Extensible split data sync (#11930)
* 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.
This commit is contained in:
		
							parent
							
								
									ef92c9ee2c
								
							
						
					
					
						commit
						172e6a7030
					
				
					 29 changed files with 1389 additions and 693 deletions
				
			
		| 
						 | 
				
			
			@ -23,9 +23,11 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		|||
#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)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										94
									
								
								quantum/split_common/transaction_id_define.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								quantum/split_common/transaction_id_define.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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");
 | 
			
		||||
							
								
								
									
										670
									
								
								quantum/split_common/transactions.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										670
									
								
								quantum/split_common/transactions.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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), ¤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)
 | 
			
		||||
							
								
								
									
										54
									
								
								quantum/split_common/transactions.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								quantum/split_common/transactions.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,54 @@
 | 
			
		|||
/* 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
 | 
			
		||||
 | 
			
		||||
#include "stdint.h"
 | 
			
		||||
#include "stdbool.h"
 | 
			
		||||
 | 
			
		||||
#include "matrix.h"
 | 
			
		||||
#include "transaction_id_define.h"
 | 
			
		||||
#include "transport.h"
 | 
			
		||||
 | 
			
		||||
typedef void (*slave_callback_t)(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
 | 
			
		||||
 | 
			
		||||
// Split transaction Descriptor
 | 
			
		||||
typedef struct _split_transaction_desc_t {
 | 
			
		||||
    uint8_t *        status;
 | 
			
		||||
    uint8_t          initiator2target_buffer_size;
 | 
			
		||||
    uint16_t         initiator2target_offset;
 | 
			
		||||
    uint8_t          target2initiator_buffer_size;
 | 
			
		||||
    uint16_t         target2initiator_offset;
 | 
			
		||||
    slave_callback_t slave_callback;
 | 
			
		||||
} split_transaction_desc_t;
 | 
			
		||||
 | 
			
		||||
// Forward declaration for the split transactions
 | 
			
		||||
extern split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS];
 | 
			
		||||
 | 
			
		||||
#define split_shmem_offset_ptr(offset) ((void *)(((uint8_t *)split_shmem) + (offset)))
 | 
			
		||||
#define split_trans_initiator2target_buffer(trans) (split_shmem_offset_ptr((trans)->initiator2target_offset))
 | 
			
		||||
#define split_trans_target2initiator_buffer(trans) (split_shmem_offset_ptr((trans)->target2initiator_offset))
 | 
			
		||||
 | 
			
		||||
// returns false if valid data not received from slave
 | 
			
		||||
bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
 | 
			
		||||
void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
 | 
			
		||||
 | 
			
		||||
void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback);
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
#define transaction_rpc_send(transaction_id, initiator2target_buffer_size, initiator2target_buffer) transaction_rpc_exec(transaction_id, initiator2target_buffer_size, initiator2target_buffer, 0, NULL)
 | 
			
		||||
#define transaction_rpc_recv(transaction_id, target2initiator_buffer_size, target2initiator_buffer) transaction_rpc_exec(transaction_id, 0, NULL, target2initiator_buffer_size, target2initiator_buffer)
 | 
			
		||||
| 
						 | 
				
			
			@ -1,452 +1,118 @@
 | 
			
		|||
/* 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 "config.h"
 | 
			
		||||
#include "matrix.h"
 | 
			
		||||
#include "quantum.h"
 | 
			
		||||
#include "transactions.h"
 | 
			
		||||
#include "transport.h"
 | 
			
		||||
#include "transaction_id_define.h"
 | 
			
		||||
#include "atomic_util.h"
 | 
			
		||||
 | 
			
		||||
#define ROWS_PER_HAND (MATRIX_ROWS / 2)
 | 
			
		||||
#define SYNC_TIMER_OFFSET 2
 | 
			
		||||
#ifdef USE_I2C
 | 
			
		||||
 | 
			
		||||
#ifdef RGBLIGHT_ENABLE
 | 
			
		||||
#    include "rgblight.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef BACKLIGHT_ENABLE
 | 
			
		||||
#    include "backlight.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef ENCODER_ENABLE
 | 
			
		||||
#    include "encoder.h"
 | 
			
		||||
static pin_t encoders_pad[] = ENCODERS_PAD_A;
 | 
			
		||||
#    define NUMBER_OF_ENCODERS (sizeof(encoders_pad) / sizeof(pin_t))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
 | 
			
		||||
#    include "led_matrix.h"
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
 | 
			
		||||
#    include "rgb_matrix.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(USE_I2C)
 | 
			
		||||
 | 
			
		||||
#    include "i2c_master.h"
 | 
			
		||||
#    include "i2c_slave.h"
 | 
			
		||||
 | 
			
		||||
typedef struct _I2C_slave_buffer_t {
 | 
			
		||||
#    ifndef DISABLE_SYNC_TIMER
 | 
			
		||||
    uint32_t sync_timer;
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifdef SPLIT_TRANSPORT_MIRROR
 | 
			
		||||
    matrix_row_t mmatrix[ROWS_PER_HAND];
 | 
			
		||||
#    endif
 | 
			
		||||
    matrix_row_t smatrix[ROWS_PER_HAND];
 | 
			
		||||
#    ifdef SPLIT_MODS_ENABLE
 | 
			
		||||
    uint8_t real_mods;
 | 
			
		||||
    uint8_t weak_mods;
 | 
			
		||||
#        ifndef NO_ACTION_ONESHOT
 | 
			
		||||
    uint8_t oneshot_mods;
 | 
			
		||||
#        endif
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifdef BACKLIGHT_ENABLE
 | 
			
		||||
    uint8_t backlight_level;
 | 
			
		||||
#    endif
 | 
			
		||||
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
 | 
			
		||||
    rgblight_syncinfo_t rgblight_sync;
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifdef ENCODER_ENABLE
 | 
			
		||||
    uint8_t encoder_state[NUMBER_OF_ENCODERS];
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifdef WPM_ENABLE
 | 
			
		||||
    uint8_t current_wpm;
 | 
			
		||||
#    endif
 | 
			
		||||
#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
 | 
			
		||||
    led_eeconfig_t led_matrix;
 | 
			
		||||
    bool           led_suspend_state;
 | 
			
		||||
#    endif
 | 
			
		||||
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
 | 
			
		||||
    rgb_config_t rgb_matrix;
 | 
			
		||||
    bool         rgb_suspend_state;
 | 
			
		||||
#    endif
 | 
			
		||||
} I2C_slave_buffer_t;
 | 
			
		||||
 | 
			
		||||
static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg;
 | 
			
		||||
 | 
			
		||||
#    define I2C_SYNC_TIME_START offsetof(I2C_slave_buffer_t, sync_timer)
 | 
			
		||||
#    define I2C_KEYMAP_MASTER_START offsetof(I2C_slave_buffer_t, mmatrix)
 | 
			
		||||
#    define I2C_KEYMAP_SLAVE_START offsetof(I2C_slave_buffer_t, smatrix)
 | 
			
		||||
#    define I2C_REAL_MODS_START offsetof(I2C_slave_buffer_t, real_mods)
 | 
			
		||||
#    define I2C_WEAK_MODS_START offsetof(I2C_slave_buffer_t, weak_mods)
 | 
			
		||||
#    define I2C_ONESHOT_MODS_START offsetof(I2C_slave_buffer_t, oneshot_mods)
 | 
			
		||||
#    define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level)
 | 
			
		||||
#    define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync)
 | 
			
		||||
#    define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state)
 | 
			
		||||
#    define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm)
 | 
			
		||||
#    define I2C_LED_MATRIX_START offsetof(I2C_slave_buffer_t, led_matrix)
 | 
			
		||||
#    define I2C_LED_SUSPEND_START offsetof(I2C_slave_buffer_t, led_suspend_state)
 | 
			
		||||
#    define I2C_RGB_MATRIX_START offsetof(I2C_slave_buffer_t, rgb_matrix)
 | 
			
		||||
#    define I2C_RGB_SUSPEND_START offsetof(I2C_slave_buffer_t, rgb_suspend_state)
 | 
			
		||||
 | 
			
		||||
#    define TIMEOUT 100
 | 
			
		||||
#    ifndef SLAVE_I2C_TIMEOUT
 | 
			
		||||
#        define SLAVE_I2C_TIMEOUT 100
 | 
			
		||||
#    endif  // SLAVE_I2C_TIMEOUT
 | 
			
		||||
 | 
			
		||||
#    ifndef SLAVE_I2C_ADDRESS
 | 
			
		||||
#        define SLAVE_I2C_ADDRESS 0x32
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
// Get rows from other half over i2c
 | 
			
		||||
bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
 | 
			
		||||
    i2c_readReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_SLAVE_START, (void *)slave_matrix, sizeof(i2c_buffer->smatrix), TIMEOUT);
 | 
			
		||||
#    ifdef SPLIT_TRANSPORT_MIRROR
 | 
			
		||||
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_MASTER_START, (void *)master_matrix, sizeof(i2c_buffer->mmatrix), TIMEOUT);
 | 
			
		||||
#    endif
 | 
			
		||||
#    include "i2c_master.h"
 | 
			
		||||
#    include "i2c_slave.h"
 | 
			
		||||
 | 
			
		||||
    // write backlight info
 | 
			
		||||
#    ifdef BACKLIGHT_ENABLE
 | 
			
		||||
    uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0;
 | 
			
		||||
    if (level != i2c_buffer->backlight_level) {
 | 
			
		||||
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_BACKLIGHT_START, (void *)&level, sizeof(level), TIMEOUT) >= 0) {
 | 
			
		||||
            i2c_buffer->backlight_level = level;
 | 
			
		||||
        }
 | 
			
		||||
// Ensure the I2C buffer has enough space
 | 
			
		||||
_Static_assert(sizeof(split_shared_memory_t) <= I2C_SLAVE_REG_COUNT, "split_shared_memory_t too large for I2C_SLAVE_REG_COUNT");
 | 
			
		||||
 | 
			
		||||
split_shared_memory_t *const split_shmem = (split_shared_memory_t *)i2c_slave_reg;
 | 
			
		||||
 | 
			
		||||
void transport_master_init(void) { i2c_init(); }
 | 
			
		||||
void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); }
 | 
			
		||||
 | 
			
		||||
i2c_status_t transport_trigger_callback(int8_t id) {
 | 
			
		||||
    // If there's no callback, indicate that we were successful
 | 
			
		||||
    if (!split_transaction_table[id].slave_callback) {
 | 
			
		||||
        return I2C_STATUS_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
 | 
			
		||||
    if (rgblight_get_change_flags()) {
 | 
			
		||||
        rgblight_syncinfo_t rgblight_sync;
 | 
			
		||||
        rgblight_get_syncinfo(&rgblight_sync);
 | 
			
		||||
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_START, (void *)&rgblight_sync, sizeof(rgblight_sync), TIMEOUT) >= 0) {
 | 
			
		||||
            rgblight_clear_change_flags();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#    endif
 | 
			
		||||
    // Kick off the "callback executor", now that data has been written to the slave
 | 
			
		||||
    split_shmem->transaction_id     = id;
 | 
			
		||||
    split_transaction_desc_t *trans = &split_transaction_table[I2C_EXECUTE_CALLBACK];
 | 
			
		||||
    return i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, SLAVE_I2C_TIMEOUT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#    ifdef ENCODER_ENABLE
 | 
			
		||||
    i2c_readReg(SLAVE_I2C_ADDRESS, I2C_ENCODER_START, (void *)i2c_buffer->encoder_state, sizeof(i2c_buffer->encoder_state), TIMEOUT);
 | 
			
		||||
    encoder_update_raw(i2c_buffer->encoder_state);
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifdef WPM_ENABLE
 | 
			
		||||
    uint8_t current_wpm = get_current_wpm();
 | 
			
		||||
    if (current_wpm != i2c_buffer->current_wpm) {
 | 
			
		||||
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WPM_START, (void *)¤t_wpm, sizeof(current_wpm), TIMEOUT) >= 0) {
 | 
			
		||||
            i2c_buffer->current_wpm = current_wpm;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifdef SPLIT_MODS_ENABLE
 | 
			
		||||
    uint8_t real_mods = get_mods();
 | 
			
		||||
    if (real_mods != i2c_buffer->real_mods) {
 | 
			
		||||
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_REAL_MODS_START, (void *)&real_mods, sizeof(real_mods), TIMEOUT) >= 0) {
 | 
			
		||||
            i2c_buffer->real_mods = real_mods;
 | 
			
		||||
bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) {
 | 
			
		||||
    i2c_status_t              status;
 | 
			
		||||
    split_transaction_desc_t *trans = &split_transaction_table[id];
 | 
			
		||||
    if (initiator2target_length > 0) {
 | 
			
		||||
        size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length;
 | 
			
		||||
        memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len);
 | 
			
		||||
        if ((status = i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t weak_mods = get_weak_mods();
 | 
			
		||||
    if (weak_mods != i2c_buffer->weak_mods) {
 | 
			
		||||
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WEAK_MODS_START, (void *)&weak_mods, sizeof(weak_mods), TIMEOUT) >= 0) {
 | 
			
		||||
            i2c_buffer->weak_mods = weak_mods;
 | 
			
		||||
        }
 | 
			
		||||
    // If we need to execute a callback on the slave, do so
 | 
			
		||||
    if ((status = transport_trigger_callback(id)) < 0) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#        ifndef NO_ACTION_ONESHOT
 | 
			
		||||
    uint8_t oneshot_mods = get_oneshot_mods();
 | 
			
		||||
    if (oneshot_mods != i2c_buffer->oneshot_mods) {
 | 
			
		||||
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_ONESHOT_MODS_START, (void *)&oneshot_mods, sizeof(oneshot_mods), TIMEOUT) >= 0) {
 | 
			
		||||
            i2c_buffer->oneshot_mods = oneshot_mods;
 | 
			
		||||
    if (target2initiator_length > 0) {
 | 
			
		||||
        size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length;
 | 
			
		||||
        if ((status = i2c_readReg(SLAVE_I2C_ADDRESS, trans->target2initiator_offset, split_trans_target2initiator_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len);
 | 
			
		||||
    }
 | 
			
		||||
#        endif
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
 | 
			
		||||
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_MATRIX_START, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix), TIMEOUT);
 | 
			
		||||
    bool suspend_state = led_matrix_get_suspend_state();
 | 
			
		||||
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->led_suspend_state), TIMEOUT);
 | 
			
		||||
#    endif
 | 
			
		||||
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
 | 
			
		||||
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_MATRIX_START, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix), TIMEOUT);
 | 
			
		||||
    bool suspend_state = rgb_matrix_get_suspend_state();
 | 
			
		||||
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->rgb_suspend_state), TIMEOUT);
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifndef DISABLE_SYNC_TIMER
 | 
			
		||||
    i2c_buffer->sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
 | 
			
		||||
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_SYNC_TIME_START, (void *)&i2c_buffer->sync_timer, sizeof(i2c_buffer->sync_timer), TIMEOUT);
 | 
			
		||||
#    endif
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
 | 
			
		||||
#    ifndef DISABLE_SYNC_TIMER
 | 
			
		||||
    sync_timer_update(i2c_buffer->sync_timer);
 | 
			
		||||
#    endif
 | 
			
		||||
    // Copy matrix to I2C buffer
 | 
			
		||||
    memcpy((void *)i2c_buffer->smatrix, (void *)slave_matrix, sizeof(i2c_buffer->smatrix));
 | 
			
		||||
#    ifdef SPLIT_TRANSPORT_MIRROR
 | 
			
		||||
    memcpy((void *)master_matrix, (void *)i2c_buffer->mmatrix, sizeof(i2c_buffer->mmatrix));
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
// Read Backlight Info
 | 
			
		||||
#    ifdef BACKLIGHT_ENABLE
 | 
			
		||||
    backlight_set(i2c_buffer->backlight_level);
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
 | 
			
		||||
    // Update the RGB with the new data
 | 
			
		||||
    if (i2c_buffer->rgblight_sync.status.change_flags != 0) {
 | 
			
		||||
        rgblight_update_sync(&i2c_buffer->rgblight_sync, false);
 | 
			
		||||
        i2c_buffer->rgblight_sync.status.change_flags = 0;
 | 
			
		||||
    }
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifdef ENCODER_ENABLE
 | 
			
		||||
    encoder_state_raw(i2c_buffer->encoder_state);
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifdef WPM_ENABLE
 | 
			
		||||
    set_current_wpm(i2c_buffer->current_wpm);
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifdef SPLIT_MODS_ENABLE
 | 
			
		||||
    set_mods(i2c_buffer->real_mods);
 | 
			
		||||
    set_weak_mods(i2c_buffer->weak_mods);
 | 
			
		||||
#        ifndef NO_ACTION_ONESHOT
 | 
			
		||||
    set_oneshot_mods(i2c_buffer->oneshot_mods);
 | 
			
		||||
#        endif
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
 | 
			
		||||
    memcpy((void *)i2c_buffer->led_matrix, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix));
 | 
			
		||||
    led_matrix_set_suspend_state(i2c_buffer->led_suspend_state);
 | 
			
		||||
#    endif
 | 
			
		||||
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
 | 
			
		||||
    memcpy((void *)i2c_buffer->rgb_matrix, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix));
 | 
			
		||||
    rgb_matrix_set_suspend_state(i2c_buffer->rgb_suspend_state);
 | 
			
		||||
#    endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void transport_master_init(void) { i2c_init(); }
 | 
			
		||||
 | 
			
		||||
void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); }
 | 
			
		||||
 | 
			
		||||
#else  // USE_SERIAL
 | 
			
		||||
#else  // USE_I2C
 | 
			
		||||
 | 
			
		||||
#    include "serial.h"
 | 
			
		||||
 | 
			
		||||
typedef struct _Serial_s2m_buffer_t {
 | 
			
		||||
    // TODO: if MATRIX_COLS > 8 change to uint8_t packed_matrix[] for pack/unpack
 | 
			
		||||
    matrix_row_t smatrix[ROWS_PER_HAND];
 | 
			
		||||
static split_shared_memory_t shared_memory;
 | 
			
		||||
split_shared_memory_t *const split_shmem = &shared_memory;
 | 
			
		||||
 | 
			
		||||
#    ifdef ENCODER_ENABLE
 | 
			
		||||
    uint8_t encoder_state[NUMBER_OF_ENCODERS];
 | 
			
		||||
#    endif
 | 
			
		||||
void transport_master_init(void) { soft_serial_initiator_init(); }
 | 
			
		||||
void transport_slave_init(void) { soft_serial_target_init(); }
 | 
			
		||||
 | 
			
		||||
} Serial_s2m_buffer_t;
 | 
			
		||||
 | 
			
		||||
typedef struct _Serial_m2s_buffer_t {
 | 
			
		||||
#    ifdef SPLIT_MODS_ENABLE
 | 
			
		||||
    uint8_t real_mods;
 | 
			
		||||
    uint8_t weak_mods;
 | 
			
		||||
#        ifndef NO_ACTION_ONESHOT
 | 
			
		||||
    uint8_t oneshot_mods;
 | 
			
		||||
#        endif
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifndef DISABLE_SYNC_TIMER
 | 
			
		||||
    uint32_t sync_timer;
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifdef SPLIT_TRANSPORT_MIRROR
 | 
			
		||||
    matrix_row_t mmatrix[ROWS_PER_HAND];
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifdef BACKLIGHT_ENABLE
 | 
			
		||||
    uint8_t backlight_level;
 | 
			
		||||
#    endif
 | 
			
		||||
#    ifdef WPM_ENABLE
 | 
			
		||||
    uint8_t current_wpm;
 | 
			
		||||
#    endif
 | 
			
		||||
#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
 | 
			
		||||
    led_eeconfig_t led_matrix;
 | 
			
		||||
    bool           led_suspend_state;
 | 
			
		||||
#    endif
 | 
			
		||||
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
 | 
			
		||||
    rgb_config_t rgb_matrix;
 | 
			
		||||
    bool         rgb_suspend_state;
 | 
			
		||||
#    endif
 | 
			
		||||
} Serial_m2s_buffer_t;
 | 
			
		||||
 | 
			
		||||
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
 | 
			
		||||
// When MCUs on both sides drive their respective RGB LED chains,
 | 
			
		||||
// it is necessary to synchronize, so it is necessary to communicate RGB
 | 
			
		||||
// information. In that case, define RGBLIGHT_SPLIT with info on the number
 | 
			
		||||
// of LEDs on each half.
 | 
			
		||||
//
 | 
			
		||||
// Otherwise, if the master side MCU drives both sides RGB LED chains,
 | 
			
		||||
// there is no need to communicate.
 | 
			
		||||
 | 
			
		||||
typedef struct _Serial_rgblight_t {
 | 
			
		||||
    rgblight_syncinfo_t rgblight_sync;
 | 
			
		||||
} Serial_rgblight_t;
 | 
			
		||||
 | 
			
		||||
volatile Serial_rgblight_t serial_rgblight = {};
 | 
			
		||||
uint8_t volatile status_rgblight           = 0;
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
volatile Serial_s2m_buffer_t serial_s2m_buffer = {};
 | 
			
		||||
volatile Serial_m2s_buffer_t serial_m2s_buffer = {};
 | 
			
		||||
uint8_t volatile status0                       = 0;
 | 
			
		||||
 | 
			
		||||
enum serial_transaction_id {
 | 
			
		||||
    GET_SLAVE_MATRIX = 0,
 | 
			
		||||
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
 | 
			
		||||
    PUT_RGBLIGHT,
 | 
			
		||||
#    endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
SSTD_t transactions[] = {
 | 
			
		||||
    [GET_SLAVE_MATRIX] =
 | 
			
		||||
        {
 | 
			
		||||
            (uint8_t *)&status0,
 | 
			
		||||
            sizeof(serial_m2s_buffer),
 | 
			
		||||
            (uint8_t *)&serial_m2s_buffer,
 | 
			
		||||
            sizeof(serial_s2m_buffer),
 | 
			
		||||
            (uint8_t *)&serial_s2m_buffer,
 | 
			
		||||
        },
 | 
			
		||||
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
 | 
			
		||||
    [PUT_RGBLIGHT] =
 | 
			
		||||
        {
 | 
			
		||||
            (uint8_t *)&status_rgblight, sizeof(serial_rgblight), (uint8_t *)&serial_rgblight, 0, NULL  // no slave to master transfer
 | 
			
		||||
        },
 | 
			
		||||
#    endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void transport_master_init(void) { soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); }
 | 
			
		||||
 | 
			
		||||
void transport_slave_init(void) { soft_serial_target_init(transactions, TID_LIMIT(transactions)); }
 | 
			
		||||
 | 
			
		||||
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
 | 
			
		||||
 | 
			
		||||
// rgblight synchronization information communication.
 | 
			
		||||
 | 
			
		||||
void transport_rgblight_master(void) {
 | 
			
		||||
    if (rgblight_get_change_flags()) {
 | 
			
		||||
        rgblight_get_syncinfo((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync);
 | 
			
		||||
        if (soft_serial_transaction(PUT_RGBLIGHT) == TRANSACTION_END) {
 | 
			
		||||
            rgblight_clear_change_flags();
 | 
			
		||||
        }
 | 
			
		||||
bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) {
 | 
			
		||||
    split_transaction_desc_t *trans = &split_transaction_table[id];
 | 
			
		||||
    if (initiator2target_length > 0) {
 | 
			
		||||
        size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length;
 | 
			
		||||
        memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void transport_rgblight_slave(void) {
 | 
			
		||||
    if (status_rgblight == TRANSACTION_ACCEPTED) {
 | 
			
		||||
        rgblight_update_sync((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync, false);
 | 
			
		||||
        status_rgblight = TRANSACTION_END;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#    else
 | 
			
		||||
#        define transport_rgblight_master()
 | 
			
		||||
#        define transport_rgblight_slave()
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
 | 
			
		||||
#    ifndef SERIAL_USE_MULTI_TRANSACTION
 | 
			
		||||
    if (soft_serial_transaction() != TRANSACTION_END) {
 | 
			
		||||
    if (soft_serial_transaction(id) != TRANSACTION_END) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
#    else
 | 
			
		||||
    transport_rgblight_master();
 | 
			
		||||
    if (soft_serial_transaction(GET_SLAVE_MATRIX) != TRANSACTION_END) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
    // TODO:  if MATRIX_COLS > 8 change to unpack()
 | 
			
		||||
    for (int i = 0; i < ROWS_PER_HAND; ++i) {
 | 
			
		||||
        slave_matrix[i] = serial_s2m_buffer.smatrix[i];
 | 
			
		||||
#    ifdef SPLIT_TRANSPORT_MIRROR
 | 
			
		||||
        serial_m2s_buffer.mmatrix[i] = master_matrix[i];
 | 
			
		||||
#    endif
 | 
			
		||||
    if (target2initiator_length > 0) {
 | 
			
		||||
        size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length;
 | 
			
		||||
        memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#    ifdef BACKLIGHT_ENABLE
 | 
			
		||||
    // Write backlight level for slave to read
 | 
			
		||||
    serial_m2s_buffer.backlight_level = is_backlight_enabled() ? get_backlight_level() : 0;
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifdef ENCODER_ENABLE
 | 
			
		||||
    encoder_update_raw((uint8_t *)serial_s2m_buffer.encoder_state);
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifdef WPM_ENABLE
 | 
			
		||||
    // Write wpm to slave
 | 
			
		||||
    serial_m2s_buffer.current_wpm = get_current_wpm();
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifdef SPLIT_MODS_ENABLE
 | 
			
		||||
    serial_m2s_buffer.real_mods = get_mods();
 | 
			
		||||
    serial_m2s_buffer.weak_mods = get_weak_mods();
 | 
			
		||||
#        ifndef NO_ACTION_ONESHOT
 | 
			
		||||
    serial_m2s_buffer.oneshot_mods = get_oneshot_mods();
 | 
			
		||||
#        endif
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
 | 
			
		||||
    serial_m2s_buffer.led_matrix        = led_matrix_eeconfig;
 | 
			
		||||
    serial_m2s_buffer.led_suspend_state = led_matrix_get_suspend_state();
 | 
			
		||||
#    endif
 | 
			
		||||
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
 | 
			
		||||
    serial_m2s_buffer.rgb_matrix        = rgb_matrix_config;
 | 
			
		||||
    serial_m2s_buffer.rgb_suspend_state = rgb_matrix_get_suspend_state();
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifndef DISABLE_SYNC_TIMER
 | 
			
		||||
    serial_m2s_buffer.sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
 | 
			
		||||
#    endif
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
 | 
			
		||||
    transport_rgblight_slave();
 | 
			
		||||
#    ifndef DISABLE_SYNC_TIMER
 | 
			
		||||
    sync_timer_update(serial_m2s_buffer.sync_timer);
 | 
			
		||||
#    endif
 | 
			
		||||
#endif  // USE_I2C
 | 
			
		||||
 | 
			
		||||
    // TODO: if MATRIX_COLS > 8 change to pack()
 | 
			
		||||
    for (int i = 0; i < ROWS_PER_HAND; ++i) {
 | 
			
		||||
        serial_s2m_buffer.smatrix[i] = slave_matrix[i];
 | 
			
		||||
#    ifdef SPLIT_TRANSPORT_MIRROR
 | 
			
		||||
        master_matrix[i] = serial_m2s_buffer.mmatrix[i];
 | 
			
		||||
#    endif
 | 
			
		||||
    }
 | 
			
		||||
#    ifdef BACKLIGHT_ENABLE
 | 
			
		||||
    backlight_set(serial_m2s_buffer.backlight_level);
 | 
			
		||||
#    endif
 | 
			
		||||
bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { return transactions_master(master_matrix, slave_matrix); }
 | 
			
		||||
 | 
			
		||||
#    ifdef ENCODER_ENABLE
 | 
			
		||||
    encoder_state_raw((uint8_t *)serial_s2m_buffer.encoder_state);
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifdef WPM_ENABLE
 | 
			
		||||
    set_current_wpm(serial_m2s_buffer.current_wpm);
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    ifdef SPLIT_MODS_ENABLE
 | 
			
		||||
    set_mods(serial_m2s_buffer.real_mods);
 | 
			
		||||
    set_weak_mods(serial_m2s_buffer.weak_mods);
 | 
			
		||||
#        ifndef NO_ACTION_ONESHOT
 | 
			
		||||
    set_oneshot_mods(serial_m2s_buffer.oneshot_mods);
 | 
			
		||||
#        endif
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
 | 
			
		||||
    led_matrix_eeconfig = serial_m2s_buffer.led_matrix;
 | 
			
		||||
    led_matrix_set_suspend_state(serial_m2s_buffer.led_suspend_state);
 | 
			
		||||
#    endif
 | 
			
		||||
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
 | 
			
		||||
    rgb_matrix_config = serial_m2s_buffer.rgb_matrix;
 | 
			
		||||
    rgb_matrix_set_suspend_state(serial_m2s_buffer.rgb_suspend_state);
 | 
			
		||||
#    endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { transactions_slave(master_matrix, slave_matrix); }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,10 +1,175 @@
 | 
			
		|||
/* 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
 | 
			
		||||
 | 
			
		||||
#include "stdint.h"
 | 
			
		||||
#include "stdbool.h"
 | 
			
		||||
 | 
			
		||||
#include "progmem.h"
 | 
			
		||||
#include "action_layer.h"
 | 
			
		||||
#include "matrix.h"
 | 
			
		||||
 | 
			
		||||
#ifndef RPC_M2S_BUFFER_SIZE
 | 
			
		||||
#    define RPC_M2S_BUFFER_SIZE 32
 | 
			
		||||
#endif  // RPC_M2S_BUFFER_SIZE
 | 
			
		||||
 | 
			
		||||
#ifndef RPC_S2M_BUFFER_SIZE
 | 
			
		||||
#    define RPC_S2M_BUFFER_SIZE 32
 | 
			
		||||
#endif  // RPC_S2M_BUFFER_SIZE
 | 
			
		||||
 | 
			
		||||
void transport_master_init(void);
 | 
			
		||||
void transport_slave_init(void);
 | 
			
		||||
 | 
			
		||||
// returns false if valid data not received from slave
 | 
			
		||||
bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
 | 
			
		||||
void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
 | 
			
		||||
 | 
			
		||||
bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length);
 | 
			
		||||
 | 
			
		||||
#ifdef ENCODER_ENABLE
 | 
			
		||||
#    include "encoder.h"
 | 
			
		||||
#    define NUMBER_OF_ENCODERS (sizeof((pin_t[])ENCODERS_PAD_A) / sizeof(pin_t))
 | 
			
		||||
#endif  // ENCODER_ENABLE
 | 
			
		||||
 | 
			
		||||
#ifdef BACKLIGHT_ENABLE
 | 
			
		||||
#    include "backlight.h"
 | 
			
		||||
#endif  // BACKLIGHT_ENABLE
 | 
			
		||||
 | 
			
		||||
#ifdef RGBLIGHT_ENABLE
 | 
			
		||||
#    include "rgblight.h"
 | 
			
		||||
#endif  // RGBLIGHT_ENABLE
 | 
			
		||||
 | 
			
		||||
typedef struct _split_slave_matrix_sync_t {
 | 
			
		||||
    uint8_t      checksum;
 | 
			
		||||
    matrix_row_t matrix[(MATRIX_ROWS) / 2];
 | 
			
		||||
} split_slave_matrix_sync_t;
 | 
			
		||||
 | 
			
		||||
#ifdef SPLIT_TRANSPORT_MIRROR
 | 
			
		||||
typedef struct _split_master_matrix_sync_t {
 | 
			
		||||
    matrix_row_t matrix[(MATRIX_ROWS) / 2];
 | 
			
		||||
} split_master_matrix_sync_t;
 | 
			
		||||
#endif  // SPLIT_TRANSPORT_MIRROR
 | 
			
		||||
 | 
			
		||||
#ifdef ENCODER_ENABLE
 | 
			
		||||
typedef struct _split_slave_encoder_sync_t {
 | 
			
		||||
    uint8_t checksum;
 | 
			
		||||
    uint8_t state[NUMBER_OF_ENCODERS];
 | 
			
		||||
} split_slave_encoder_sync_t;
 | 
			
		||||
#endif  // ENCODER_ENABLE
 | 
			
		||||
 | 
			
		||||
#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
 | 
			
		||||
typedef struct _split_layers_sync_t {
 | 
			
		||||
    layer_state_t layer_state;
 | 
			
		||||
    layer_state_t default_layer_state;
 | 
			
		||||
} split_layers_sync_t;
 | 
			
		||||
#endif  // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
 | 
			
		||||
 | 
			
		||||
#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
 | 
			
		||||
#    include "led_matrix.h"
 | 
			
		||||
 | 
			
		||||
typedef struct _led_matrix_sync_t {
 | 
			
		||||
    led_eeconfig_t led_matrix;
 | 
			
		||||
    bool           led_suspend_state;
 | 
			
		||||
} led_matrix_sync_t;
 | 
			
		||||
#endif  // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
 | 
			
		||||
 | 
			
		||||
#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
 | 
			
		||||
#    include "rgb_matrix.h"
 | 
			
		||||
 | 
			
		||||
typedef struct _rgb_matrix_sync_t {
 | 
			
		||||
    rgb_config_t rgb_matrix;
 | 
			
		||||
    bool         rgb_suspend_state;
 | 
			
		||||
} rgb_matrix_sync_t;
 | 
			
		||||
#endif  // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
 | 
			
		||||
 | 
			
		||||
#ifdef SPLIT_MODS_ENABLE
 | 
			
		||||
typedef struct _split_mods_sync_t {
 | 
			
		||||
    uint8_t real_mods;
 | 
			
		||||
    uint8_t weak_mods;
 | 
			
		||||
#    ifndef NO_ACTION_ONESHOT
 | 
			
		||||
    uint8_t oneshot_mods;
 | 
			
		||||
#    endif  // NO_ACTION_ONESHOT
 | 
			
		||||
} split_mods_sync_t;
 | 
			
		||||
#endif  // SPLIT_MODS_ENABLE
 | 
			
		||||
 | 
			
		||||
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
 | 
			
		||||
typedef struct _rpc_sync_info_t {
 | 
			
		||||
    int8_t  transaction_id;
 | 
			
		||||
    uint8_t m2s_length;
 | 
			
		||||
    uint8_t s2m_length;
 | 
			
		||||
} rpc_sync_info_t;
 | 
			
		||||
#endif  // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
 | 
			
		||||
 | 
			
		||||
typedef struct _split_shared_memory_t {
 | 
			
		||||
#ifdef USE_I2C
 | 
			
		||||
    int8_t transaction_id;
 | 
			
		||||
#endif  // USE_I2C
 | 
			
		||||
 | 
			
		||||
    split_slave_matrix_sync_t smatrix;
 | 
			
		||||
 | 
			
		||||
#ifdef SPLIT_TRANSPORT_MIRROR
 | 
			
		||||
    split_master_matrix_sync_t mmatrix;
 | 
			
		||||
#endif  // SPLIT_TRANSPORT_MIRROR
 | 
			
		||||
 | 
			
		||||
#ifdef ENCODER_ENABLE
 | 
			
		||||
    split_slave_encoder_sync_t encoders;
 | 
			
		||||
#endif  // ENCODER_ENABLE
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_SYNC_TIMER
 | 
			
		||||
    uint32_t sync_timer;
 | 
			
		||||
#endif  // DISABLE_SYNC_TIMER
 | 
			
		||||
 | 
			
		||||
#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
 | 
			
		||||
    split_layers_sync_t layers;
 | 
			
		||||
#endif  // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
 | 
			
		||||
 | 
			
		||||
#ifdef SPLIT_LED_STATE_ENABLE
 | 
			
		||||
    uint8_t led_state;
 | 
			
		||||
#endif  // SPLIT_LED_STATE_ENABLE
 | 
			
		||||
 | 
			
		||||
#ifdef SPLIT_MODS_ENABLE
 | 
			
		||||
    split_mods_sync_t mods;
 | 
			
		||||
#endif  // SPLIT_MODS_ENABLE
 | 
			
		||||
 | 
			
		||||
#ifdef BACKLIGHT_ENABLE
 | 
			
		||||
    uint8_t backlight_level;
 | 
			
		||||
#endif  // BACKLIGHT_ENABLE
 | 
			
		||||
 | 
			
		||||
#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
 | 
			
		||||
    rgblight_syncinfo_t rgblight_sync;
 | 
			
		||||
#endif  // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
 | 
			
		||||
 | 
			
		||||
#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
 | 
			
		||||
    led_matrix_sync_t led_matrix_sync;
 | 
			
		||||
#endif  // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
 | 
			
		||||
 | 
			
		||||
#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
 | 
			
		||||
    rgb_matrix_sync_t rgb_matrix_sync;
 | 
			
		||||
#endif  // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
 | 
			
		||||
 | 
			
		||||
#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
 | 
			
		||||
    uint8_t current_wpm;
 | 
			
		||||
#endif  // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
 | 
			
		||||
 | 
			
		||||
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
 | 
			
		||||
    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)
 | 
			
		||||
} split_shared_memory_t;
 | 
			
		||||
 | 
			
		||||
extern split_shared_memory_t *const split_shmem;
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue