0ae20e7457
* Make solo half of split keyboards (more) usable. Using only one half of a split keyboard (that's using the split_common framework to communicate) is not a great experience, since several read timeouts per scan cycle cause an unusably slow scan rate. This change blocks all split communication attempts for 500 ms (configurable) after an error occurs, causing the scan rate to become at least _more_ usable, but might need some tweaking to work fully on most keyboards. One read timeout still needs to occur after the 500 ms has passed, and if that timeout isn't low enough, some scan cycles may still be too slow. * Fix lint complaint. * Require 25 consecutive comm errors to see comms as disconnected. The number of max errors can be overridden by defining `SPLIT_MAX_CONNECTION_ERRORS`. * Add comments to new defines, and ability to disable disconnection check. Also increase `SPLIT_MAX_CONNECTION_ERRORS` to 40, since it's divisible by most relevant numbers for the description. * Make lint happy ...again * Only update `connection_check_timer` when needed. * Add new defines to split keyboard documentation. * Move connection timeout logic to transport.c, add `is_transport_connected`. * Use split_common disconnection logic in matrix.c. Instead of doing more or less the same thing twice. * Move disconnection logic to `transport_master`. Is a cleaner implementation, and causes the scan rate while disconnected to increase instead of decrease. * Lint fixes. * Lower default `SERIAL_USART_TIMEOUT` to 20 ms. The read timeout must be low enough to not cause exessively long scan cycles when using a solo split half. 10 ms was determined from testing to work fine even with the slowest defined baudrate of 19200 (5 ms was too low for that case), so 20 ms should be fine for most cases. * Remove `SERIAL_USART_TIMEOUT` from ergodox_infinity/config.h Was somewhat mistakenly included in an earlier PR. * Fix building with `USE_I2C`. * Reduce built firmware size. Not really sure why this works, the idea was taken from tzarc's work on split disconnection. * Tweak and improve opt-out for split disconnection logic. There are now two ways to opt out from this feature: * Set `SPLIT_MAX_CONNECTION_ERRORS` to 0. This will completely disable the connection status checks (also affects the slave matrix reset logic in matrix.c, though). * Set `SPLIT_CONNECTION_CHECK_TIMEOUT` to 0. This will only disable the communication throttling while disconnected. Will make the firmware smaller. * Make split disconnection logic work with custom transports. Includes a fallback implementation for keyboards using a custom split_util.c but not a custom matrix.c (currently no such keyboard seems to be merged, though). * Remove unnecessary include of timer.h Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Joel Challis <git@zvecr.com>
724 lines
31 KiB
C
724 lines
31 KiB
C
/* Copyright 2021 QMK
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
|
|
#include "crc.h"
|
|
#include "debug.h"
|
|
#include "matrix.h"
|
|
#include "quantum.h"
|
|
#include "transactions.h"
|
|
#include "transport.h"
|
|
#include "split_util.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)
|
|
|
|
#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
|
|
|
|
static bool transaction_handler_master(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[])) {
|
|
int num_retries = is_transport_connected() ? 10 : 1;
|
|
for (int iter = 1; iter <= num_retries; ++iter) {
|
|
if (iter > 1) {
|
|
for (int i = 0; i < iter * iter; ++i) {
|
|
wait_us(10);
|
|
}
|
|
}
|
|
bool this_okay = true;
|
|
ATOMIC_BLOCK_FORCEON { this_okay = handler(master_matrix, slave_matrix); };
|
|
if (this_okay) return true;
|
|
}
|
|
dprintf("Failed to execute %s\n", prefix);
|
|
return false;
|
|
}
|
|
|
|
#define TRANSACTION_HANDLER_MASTER(prefix) \
|
|
do { \
|
|
if (!transaction_handler_master(master_matrix, slave_matrix, #prefix, &prefix##_handlers_master)) return false; \
|
|
} while (0)
|
|
|
|
#define TRANSACTION_HANDLER_SLAVE(prefix) \
|
|
do { \
|
|
ATOMIC_BLOCK_FORCEON { prefix##_handlers_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)
|
|
#define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(slave_matrix)
|
|
#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)
|
|
# define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(master_matrix)
|
|
# 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)
|
|
# define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE(encoder)
|
|
# 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)
|
|
# define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE(sync_timer)
|
|
# 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)
|
|
# define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(layer_state)
|
|
# 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)
|
|
# define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(led_state)
|
|
# 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)
|
|
# define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods)
|
|
# 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)
|
|
# define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight)
|
|
# 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)
|
|
# define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight)
|
|
# 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)
|
|
# define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix)
|
|
# 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)
|
|
# define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix)
|
|
# 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)
|
|
# define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE(wpm)
|
|
# 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)
|
|
|
|
////////////////////////////////////////////////////
|
|
// OLED
|
|
|
|
#if defined(OLED_DRIVER_ENABLE) && defined(SPLIT_OLED_ENABLE)
|
|
|
|
static bool oled_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
bool current_oled_state = is_oled_on();
|
|
return send_if_condition(PUT_OLED, &last_update, (current_oled_state != split_shmem->current_oled_state), ¤t_oled_state, sizeof(current_oled_state));
|
|
}
|
|
|
|
static void oled_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
if (split_shmem->current_oled_state) {
|
|
oled_on();
|
|
} else {
|
|
oled_off();
|
|
}
|
|
}
|
|
|
|
# define TRANSACTIONS_OLED_MASTER() TRANSACTION_HANDLER_MASTER(oled)
|
|
# define TRANSACTIONS_OLED_SLAVE() TRANSACTION_HANDLER_SLAVE(oled)
|
|
# define TRANSACTIONS_OLED_REGISTRATIONS [PUT_OLED] = trans_initiator2target_initializer(current_oled_state),
|
|
|
|
#else // defined(OLED_DRIVER_ENABLE) && defined(SPLIT_OLED_ENABLE)
|
|
|
|
# define TRANSACTIONS_OLED_MASTER()
|
|
# define TRANSACTIONS_OLED_SLAVE()
|
|
# define TRANSACTIONS_OLED_REGISTRATIONS
|
|
|
|
#endif // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
|
|
|
|
////////////////////////////////////////////////////
|
|
// ST7565
|
|
|
|
#if defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
|
|
|
|
static bool st7565_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
static uint32_t last_update = 0;
|
|
bool current_st7565_state = st7565_is_on();
|
|
return send_if_condition(PUT_ST7565, &last_update, (current_st7565_state != split_shmem->current_st7565_state), ¤t_st7565_state, sizeof(current_st7565_state));
|
|
}
|
|
|
|
static void st7565_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
|
|
if (split_shmem->current_st7565_state) {
|
|
st7565_on();
|
|
} else {
|
|
st7565_off();
|
|
}
|
|
}
|
|
|
|
# define TRANSACTIONS_ST7565_MASTER() TRANSACTION_HANDLER_MASTER(st7565)
|
|
# define TRANSACTIONS_ST7565_SLAVE() TRANSACTION_HANDLER_SLAVE(st7565)
|
|
# define TRANSACTIONS_ST7565_REGISTRATIONS [PUT_ST7565] = trans_initiator2target_initializer(current_st7565_state),
|
|
|
|
#else // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
|
|
|
|
# define TRANSACTIONS_ST7565_MASTER()
|
|
# define TRANSACTIONS_ST7565_SLAVE()
|
|
# define TRANSACTIONS_ST7565_REGISTRATIONS
|
|
|
|
#endif // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_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
|
|
TRANSACTIONS_OLED_REGISTRATIONS
|
|
TRANSACTIONS_ST7565_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[]) {
|
|
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();
|
|
TRANSACTIONS_OLED_MASTER();
|
|
TRANSACTIONS_ST7565_MASTER();
|
|
return true;
|
|
}
|
|
|
|
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();
|
|
TRANSACTIONS_OLED_SLAVE();
|
|
TRANSACTIONS_ST7565_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 transaction attempts while transport is disconnected
|
|
if (!is_transport_connected()) {
|
|
return false;
|
|
}
|
|
// 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)
|