diff options
Diffstat (limited to 'quantum/split_common')
| -rw-r--r-- | quantum/split_common/matrix.c | 309 | ||||
| -rw-r--r-- | quantum/split_common/post_config.h | 9 | ||||
| -rw-r--r-- | quantum/split_common/split_util.c | 4 | ||||
| -rw-r--r-- | quantum/split_common/transaction_id_define.h | 94 | ||||
| -rw-r--r-- | quantum/split_common/transactions.c | 655 | ||||
| -rw-r--r-- | quantum/split_common/transactions.h | 54 | ||||
| -rw-r--r-- | quantum/split_common/transport.c | 484 | ||||
| -rw-r--r-- | quantum/split_common/transport.h | 165 | 
8 files changed, 1047 insertions, 727 deletions
| diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c deleted file mode 100644 index 039e7d9773..0000000000 --- a/quantum/split_common/matrix.c +++ /dev/null @@ -1,309 +0,0 @@ -/* -Copyright 2012 Jun Wako <[email protected]> - -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 <stdint.h> -#include <stdbool.h> -#include "util.h" -#include "matrix.h" -#include "debounce.h" -#include "quantum.h" -#include "split_util.h" -#include "config.h" -#include "transport.h" - -#define ERROR_DISCONNECT_COUNT 5 - -#define ROWS_PER_HAND (MATRIX_ROWS / 2) - -#ifdef DIRECT_PINS -static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS; -#elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW) -static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; -static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; -#endif - -/* matrix state(1:on, 0:off) */ -extern matrix_row_t raw_matrix[MATRIX_ROWS];  // raw values -extern matrix_row_t matrix[MATRIX_ROWS];      // debounced values - -// row offsets for each hand -uint8_t thisHand, thatHand; - -// user-defined overridable functions -__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); } -__attribute__((weak)) void matrix_slave_scan_user(void) {} - -static inline void setPinOutput_writeLow(pin_t pin) { -    ATOMIC_BLOCK_FORCEON { -        setPinOutput(pin); -        writePinLow(pin); -    } -} - -static inline void setPinInputHigh_atomic(pin_t pin) { -    ATOMIC_BLOCK_FORCEON { setPinInputHigh(pin); } -} - -// matrix code - -#ifdef DIRECT_PINS - -static void init_pins(void) { -    for (int row = 0; row < MATRIX_ROWS; row++) { -        for (int col = 0; col < MATRIX_COLS; col++) { -            pin_t pin = direct_pins[row][col]; -            if (pin != NO_PIN) { -                setPinInputHigh(pin); -            } -        } -    } -} - -static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { -    // Start with a clear matrix row -    matrix_row_t current_row_value = 0; - -    for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) { -        pin_t pin = direct_pins[current_row][col_index]; -        if (pin != NO_PIN) { -            current_row_value |= readPin(pin) ? 0 : (MATRIX_ROW_SHIFTER << col_index); -        } -    } - -    // If the row has changed, store the row and return the changed flag. -    if (current_matrix[current_row] != current_row_value) { -        current_matrix[current_row] = current_row_value; -        return true; -    } -    return false; -} - -#elif defined(DIODE_DIRECTION) -#    if (DIODE_DIRECTION == COL2ROW) - -static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); } - -static void unselect_row(uint8_t row) { setPinInputHigh_atomic(row_pins[row]); } - -static void unselect_rows(void) { -    for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { -        setPinInputHigh_atomic(row_pins[x]); -    } -} - -static void init_pins(void) { -    unselect_rows(); -    for (uint8_t x = 0; x < MATRIX_COLS; x++) { -        setPinInputHigh_atomic(col_pins[x]); -    } -} - -static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { -    // Start with a clear matrix row -    matrix_row_t current_row_value = 0; - -    // Select row -    select_row(current_row); -    matrix_output_select_delay(); - -    // For each col... -    for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) { -        // Select the col pin to read (active low) -        uint8_t pin_state = readPin(col_pins[col_index]); - -        // Populate the matrix row with the state of the col pin -        current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index); -    } - -    // Unselect row -    unselect_row(current_row); -    matrix_output_unselect_delay();  // wait for all Col signals to go HIGH - -    // If the row has changed, store the row and return the changed flag. -    if (current_matrix[current_row] != current_row_value) { -        current_matrix[current_row] = current_row_value; -        return true; -    } -    return false; -} - -#    elif (DIODE_DIRECTION == ROW2COL) - -static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); } - -static void unselect_col(uint8_t col) { setPinInputHigh_atomic(col_pins[col]); } - -static void unselect_cols(void) { -    for (uint8_t x = 0; x < MATRIX_COLS; x++) { -        setPinInputHigh_atomic(col_pins[x]); -    } -} - -static void init_pins(void) { -    unselect_cols(); -    for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { -        setPinInputHigh_atomic(row_pins[x]); -    } -} - -static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { -    bool matrix_changed = false; - -    // Select col -    select_col(current_col); -    matrix_output_select_delay(); - -    // For each row... -    for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) { -        // Store last value of row prior to reading -        matrix_row_t last_row_value    = current_matrix[row_index]; -        matrix_row_t current_row_value = last_row_value; - -        // Check row pin state -        if (readPin(row_pins[row_index]) == 0) { -            // Pin LO, set col bit -            current_row_value |= (MATRIX_ROW_SHIFTER << current_col); -        } else { -            // Pin HI, clear col bit -            current_row_value &= ~(MATRIX_ROW_SHIFTER << current_col); -        } - -        // Determine if the matrix changed state -        if ((last_row_value != current_row_value)) { -            matrix_changed |= true; -            current_matrix[row_index] = current_row_value; -        } -    } - -    // Unselect col -    unselect_col(current_col); -    matrix_output_unselect_delay();  // wait for all Row signals to go HIGH - -    return matrix_changed; -} - -#    else -#        error DIODE_DIRECTION must be one of COL2ROW or ROW2COL! -#    endif -#else -#    error DIODE_DIRECTION is not defined! -#endif - -void matrix_init(void) { -    split_pre_init(); - -    // Set pinout for right half if pinout for that half is defined -    if (!isLeftHand) { -#ifdef DIRECT_PINS_RIGHT -        const pin_t direct_pins_right[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS_RIGHT; -        for (uint8_t i = 0; i < MATRIX_ROWS; i++) { -            for (uint8_t j = 0; j < MATRIX_COLS; j++) { -                direct_pins[i][j] = direct_pins_right[i][j]; -            } -        } -#endif -#ifdef MATRIX_ROW_PINS_RIGHT -        const pin_t row_pins_right[MATRIX_ROWS] = MATRIX_ROW_PINS_RIGHT; -        for (uint8_t i = 0; i < MATRIX_ROWS; i++) { -            row_pins[i] = row_pins_right[i]; -        } -#endif -#ifdef MATRIX_COL_PINS_RIGHT -        const pin_t col_pins_right[MATRIX_COLS] = MATRIX_COL_PINS_RIGHT; -        for (uint8_t i = 0; i < MATRIX_COLS; i++) { -            col_pins[i] = col_pins_right[i]; -        } -#endif -    } - -    thisHand = isLeftHand ? 0 : (ROWS_PER_HAND); -    thatHand = ROWS_PER_HAND - thisHand; - -    // initialize key pins -    init_pins(); - -    // initialize matrix state: all keys off -    for (uint8_t i = 0; i < MATRIX_ROWS; i++) { -        raw_matrix[i] = 0; -        matrix[i]     = 0; -    } - -    debounce_init(ROWS_PER_HAND); - -    matrix_init_quantum(); - -    split_post_init(); -} - -bool matrix_post_scan(void) { -    bool changed = false; -    if (is_keyboard_master()) { -        static uint8_t error_count; - -        matrix_row_t slave_matrix[ROWS_PER_HAND] = {0}; -        if (!transport_master(matrix + thisHand, slave_matrix)) { -            error_count++; - -            if (error_count > ERROR_DISCONNECT_COUNT) { -                // reset other half if disconnected -                for (int i = 0; i < ROWS_PER_HAND; ++i) { -                    matrix[thatHand + i] = 0; -                    slave_matrix[i]      = 0; -                } - -                changed = true; -            } -        } else { -            error_count = 0; - -            for (int i = 0; i < ROWS_PER_HAND; ++i) { -                if (matrix[thatHand + i] != slave_matrix[i]) { -                    matrix[thatHand + i] = slave_matrix[i]; -                    changed              = true; -                } -            } -        } - -        matrix_scan_quantum(); -    } else { -        transport_slave(matrix + thatHand, matrix + thisHand); - -        matrix_slave_scan_kb(); -    } - -    return changed; -} - -uint8_t matrix_scan(void) { -    bool local_changed = false; - -#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) -    // Set row, read cols -    for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) { -        local_changed |= read_cols_on_row(raw_matrix, current_row); -    } -#elif (DIODE_DIRECTION == ROW2COL) -    // Set col, read rows -    for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { -        local_changed |= read_rows_on_col(raw_matrix, current_col); -    } -#endif - -    debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, local_changed); - -    bool remote_changed = matrix_post_scan(); -    return (uint8_t)(local_changed || remote_changed); -} diff --git a/quantum/split_common/post_config.h b/quantum/split_common/post_config.h index 4ae1d52732..a4c0a1956b 100644 --- a/quantum/split_common/post_config.h +++ b/quantum/split_common/post_config.h @@ -7,13 +7,4 @@  #    ifndef F_SCL  #        define F_SCL 100000UL  // SCL frequency  #    endif - -#else  // use serial -// When using serial, the user must define RGBLIGHT_SPLIT explicitly -//  in config.h as needed. -//      see quantum/rgblight_post_config.h -#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) -// When using serial and RGBLIGHT_SPLIT need separate transaction -#        define SERIAL_USE_MULTI_TRANSACTION -#    endif  #endif diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c index 9e75e19ce0..989829d2dc 100644 --- a/quantum/split_common/split_util.c +++ b/quantum/split_common/split_util.c @@ -77,7 +77,11 @@ __attribute__((weak)) bool is_keyboard_left(void) {  #if defined(SPLIT_HAND_PIN)      // Test pin SPLIT_HAND_PIN for High/Low, if low it's right hand      setPinInput(SPLIT_HAND_PIN); +#    ifdef SPLIT_HAND_PIN_LOW_IS_LEFT +    return !readPin(SPLIT_HAND_PIN); +#    else      return readPin(SPLIT_HAND_PIN); +#    endif  #elif defined(SPLIT_HAND_MATRIX_GRID)  #    ifdef SPLIT_HAND_MATRIX_GRID_LOW_IS_RIGHT      return peek_matrix_intersection(SPLIT_HAND_MATRIX_GRID); diff --git a/quantum/split_common/transaction_id_define.h b/quantum/split_common/transaction_id_define.h new file mode 100644 index 0000000000..464c73478a --- /dev/null +++ b/quantum/split_common/transaction_id_define.h @@ -0,0 +1,94 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +enum serial_transaction_id { +#ifdef USE_I2C +    I2C_EXECUTE_CALLBACK, +#endif  // USE_I2C + +    GET_SLAVE_MATRIX_CHECKSUM, +    GET_SLAVE_MATRIX_DATA, + +#ifdef SPLIT_TRANSPORT_MIRROR +    PUT_MASTER_MATRIX, +#endif  // SPLIT_TRANSPORT_MIRROR + +#ifdef ENCODER_ENABLE +    GET_ENCODERS_CHECKSUM, +    GET_ENCODERS_DATA, +#endif  // ENCODER_ENABLE + +#ifndef DISABLE_SYNC_TIMER +    PUT_SYNC_TIMER, +#endif  // DISABLE_SYNC_TIMER + +#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) +    PUT_LAYER_STATE, +    PUT_DEFAULT_LAYER_STATE, +#endif  // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +#ifdef SPLIT_LED_STATE_ENABLE +    PUT_LED_STATE, +#endif  // SPLIT_LED_STATE_ENABLE + +#ifdef SPLIT_MODS_ENABLE +    PUT_MODS, +#endif  // SPLIT_MODS_ENABLE + +#ifdef BACKLIGHT_ENABLE +    PUT_BACKLIGHT, +#endif  // BACKLIGHT_ENABLE + +#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) +    PUT_RGBLIGHT, +#endif  // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) +    PUT_LED_MATRIX, +#endif  // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) +    PUT_RGB_MATRIX, +#endif  // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) +    PUT_WPM, +#endif  // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) +    PUT_RPC_INFO, +    PUT_RPC_REQ_DATA, +    EXECUTE_RPC, +    GET_RPC_RESP_DATA, +#endif  // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + +// keyboard-specific +#ifdef SPLIT_TRANSACTION_IDS_KB +    SPLIT_TRANSACTION_IDS_KB, +#endif  // SPLIT_TRANSACTION_IDS_KB + +// user/keymap-specific +#ifdef SPLIT_TRANSACTION_IDS_USER +    SPLIT_TRANSACTION_IDS_USER, +#endif  // SPLIT_TRANSACTION_IDS_USER + +    NUM_TOTAL_TRANSACTIONS +}; + +// Ensure we only use 5 bits for transaction +_Static_assert(NUM_TOTAL_TRANSACTIONS <= (1 << 5), "Max number of usable transactions exceeded"); diff --git a/quantum/split_common/transactions.c b/quantum/split_common/transactions.c new file mode 100644 index 0000000000..abad626e00 --- /dev/null +++ b/quantum/split_common/transactions.c @@ -0,0 +1,655 @@ +/* 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 "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 + +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) diff --git a/quantum/split_common/transactions.h b/quantum/split_common/transactions.h new file mode 100644 index 0000000000..4306ba1d87 --- /dev/null +++ b/quantum/split_common/transactions.h @@ -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) diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c index 9ed0f7591b..a711ef85f0 100644 --- a/quantum/split_common/transport.c +++ b/quantum/split_common/transport.c @@ -1,452 +1,118 @@ -#include <string.h> -#include <stddef.h> - -#include "config.h" -#include "matrix.h" -#include "quantum.h" - -#define ROWS_PER_HAND (MATRIX_ROWS / 2) -#define SYNC_TIMER_OFFSET 2 - -#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) +/* 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 "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; +#include <string.h> +#include <debug.h> -static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg; +#include "transactions.h" +#include "transport.h" +#include "transaction_id_define.h" +#include "atomic_util.h" -#    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) +#ifdef USE_I2C -#    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; -        } -    } -#    endif +// 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"); -#    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 +split_shared_memory_t *const split_shmem = (split_shared_memory_t *)i2c_slave_reg; -#    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 +void transport_master_init(void) { i2c_init(); } +void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); } -#    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; -        } +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 -#    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; +    // 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); +} + +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]; - -#    ifdef ENCODER_ENABLE -    uint8_t encoder_state[NUMBER_OF_ENCODERS]; -#    endif - -} 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; +static split_shared_memory_t shared_memory; +split_shared_memory_t *const split_shmem = &shared_memory; -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)); } +void transport_master_init(void) { soft_serial_initiator_init(); } +void transport_slave_init(void) { soft_serial_target_init(); } -#    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(); -        } -    } -} - -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; +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);      } -} -#    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) { -        return false; -    } -#    else -    transport_rgblight_master(); -    if (soft_serial_transaction(GET_SLAVE_MATRIX) != TRANSACTION_END) { +    if (soft_serial_transaction(id) != 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 - -    // 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 - -#    ifdef ENCODER_ENABLE -    encoder_state_raw((uint8_t *)serial_s2m_buffer.encoder_state); -#    endif +#endif  // USE_I2C -#    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 -} +bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { return transactions_master(master_matrix, slave_matrix); } -#endif +void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { transactions_slave(master_matrix, slave_matrix); }
\ No newline at end of file diff --git a/quantum/split_common/transport.h b/quantum/split_common/transport.h index a9f66301bf..2e07f6b25c 100644 --- a/quantum/split_common/transport.h +++ b/quantum/split_common/transport.h @@ -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;
\ No newline at end of file | 
