aboutsummaryrefslogtreecommitdiff
path: root/quantum/split_common
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/split_common')
-rw-r--r--quantum/split_common/matrix.c309
-rw-r--r--quantum/split_common/post_config.h9
-rw-r--r--quantum/split_common/split_util.c6
-rw-r--r--quantum/split_common/transaction_id_define.h94
-rw-r--r--quantum/split_common/transactions.c655
-rw-r--r--quantum/split_common/transactions.h54
-rw-r--r--quantum/split_common/transport.c484
-rw-r--r--quantum/split_common/transport.h165
8 files changed, 1048 insertions, 728 deletions
diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c
deleted file mode 100644
index 039e7d977..000000000
--- a/quantum/split_common/matrix.c
+++ /dev/null
@@ -1,309 +0,0 @@
1/*
2Copyright 2012 Jun Wako <wakojun@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17#include <stdint.h>
18#include <stdbool.h>
19#include "util.h"
20#include "matrix.h"
21#include "debounce.h"
22#include "quantum.h"
23#include "split_util.h"
24#include "config.h"
25#include "transport.h"
26
27#define ERROR_DISCONNECT_COUNT 5
28
29#define ROWS_PER_HAND (MATRIX_ROWS / 2)
30
31#ifdef DIRECT_PINS
32static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS;
33#elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW)
34static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
35static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
36#endif
37
38/* matrix state(1:on, 0:off) */
39extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
40extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values
41
42// row offsets for each hand
43uint8_t thisHand, thatHand;
44
45// user-defined overridable functions
46__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); }
47__attribute__((weak)) void matrix_slave_scan_user(void) {}
48
49static inline void setPinOutput_writeLow(pin_t pin) {
50 ATOMIC_BLOCK_FORCEON {
51 setPinOutput(pin);
52 writePinLow(pin);
53 }
54}
55
56static inline void setPinInputHigh_atomic(pin_t pin) {
57 ATOMIC_BLOCK_FORCEON { setPinInputHigh(pin); }
58}
59
60// matrix code
61
62#ifdef DIRECT_PINS
63
64static void init_pins(void) {
65 for (int row = 0; row < MATRIX_ROWS; row++) {
66 for (int col = 0; col < MATRIX_COLS; col++) {
67 pin_t pin = direct_pins[row][col];
68 if (pin != NO_PIN) {
69 setPinInputHigh(pin);
70 }
71 }
72 }
73}
74
75static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
76 // Start with a clear matrix row
77 matrix_row_t current_row_value = 0;
78
79 for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
80 pin_t pin = direct_pins[current_row][col_index];
81 if (pin != NO_PIN) {
82 current_row_value |= readPin(pin) ? 0 : (MATRIX_ROW_SHIFTER << col_index);
83 }
84 }
85
86 // If the row has changed, store the row and return the changed flag.
87 if (current_matrix[current_row] != current_row_value) {
88 current_matrix[current_row] = current_row_value;
89 return true;
90 }
91 return false;
92}
93
94#elif defined(DIODE_DIRECTION)
95# if (DIODE_DIRECTION == COL2ROW)
96
97static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); }
98
99static void unselect_row(uint8_t row) { setPinInputHigh_atomic(row_pins[row]); }
100
101static void unselect_rows(void) {
102 for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
103 setPinInputHigh_atomic(row_pins[x]);
104 }
105}
106
107static void init_pins(void) {
108 unselect_rows();
109 for (uint8_t x = 0; x < MATRIX_COLS; x++) {
110 setPinInputHigh_atomic(col_pins[x]);
111 }
112}
113
114static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
115 // Start with a clear matrix row
116 matrix_row_t current_row_value = 0;
117
118 // Select row
119 select_row(current_row);
120 matrix_output_select_delay();
121
122 // For each col...
123 for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
124 // Select the col pin to read (active low)
125 uint8_t pin_state = readPin(col_pins[col_index]);
126
127 // Populate the matrix row with the state of the col pin
128 current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index);
129 }
130
131 // Unselect row
132 unselect_row(current_row);
133 matrix_output_unselect_delay(); // wait for all Col signals to go HIGH
134
135 // If the row has changed, store the row and return the changed flag.
136 if (current_matrix[current_row] != current_row_value) {
137 current_matrix[current_row] = current_row_value;
138 return true;
139 }
140 return false;
141}
142
143# elif (DIODE_DIRECTION == ROW2COL)
144
145static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); }
146
147static void unselect_col(uint8_t col) { setPinInputHigh_atomic(col_pins[col]); }
148
149static void unselect_cols(void) {
150 for (uint8_t x = 0; x < MATRIX_COLS; x++) {
151 setPinInputHigh_atomic(col_pins[x]);
152 }
153}
154
155static void init_pins(void) {
156 unselect_cols();
157 for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
158 setPinInputHigh_atomic(row_pins[x]);
159 }
160}
161
162static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
163 bool matrix_changed = false;
164
165 // Select col
166 select_col(current_col);
167 matrix_output_select_delay();
168
169 // For each row...
170 for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) {
171 // Store last value of row prior to reading
172 matrix_row_t last_row_value = current_matrix[row_index];
173 matrix_row_t current_row_value = last_row_value;
174
175 // Check row pin state
176 if (readPin(row_pins[row_index]) == 0) {
177 // Pin LO, set col bit
178 current_row_value |= (MATRIX_ROW_SHIFTER << current_col);
179 } else {
180 // Pin HI, clear col bit
181 current_row_value &= ~(MATRIX_ROW_SHIFTER << current_col);
182 }
183
184 // Determine if the matrix changed state
185 if ((last_row_value != current_row_value)) {
186 matrix_changed |= true;
187 current_matrix[row_index] = current_row_value;
188 }
189 }
190
191 // Unselect col
192 unselect_col(current_col);
193 matrix_output_unselect_delay(); // wait for all Row signals to go HIGH
194
195 return matrix_changed;
196}
197
198# else
199# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL!
200# endif
201#else
202# error DIODE_DIRECTION is not defined!
203#endif
204
205void matrix_init(void) {
206 split_pre_init();
207
208 // Set pinout for right half if pinout for that half is defined
209 if (!isLeftHand) {
210#ifdef DIRECT_PINS_RIGHT
211 const pin_t direct_pins_right[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS_RIGHT;
212 for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
213 for (uint8_t j = 0; j < MATRIX_COLS; j++) {
214 direct_pins[i][j] = direct_pins_right[i][j];
215 }
216 }
217#endif
218#ifdef MATRIX_ROW_PINS_RIGHT
219 const pin_t row_pins_right[MATRIX_ROWS] = MATRIX_ROW_PINS_RIGHT;
220 for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
221 row_pins[i] = row_pins_right[i];
222 }
223#endif
224#ifdef MATRIX_COL_PINS_RIGHT
225 const pin_t col_pins_right[MATRIX_COLS] = MATRIX_COL_PINS_RIGHT;
226 for (uint8_t i = 0; i < MATRIX_COLS; i++) {
227 col_pins[i] = col_pins_right[i];
228 }
229#endif
230 }
231
232 thisHand = isLeftHand ? 0 : (ROWS_PER_HAND);
233 thatHand = ROWS_PER_HAND - thisHand;
234
235 // initialize key pins
236 init_pins();
237
238 // initialize matrix state: all keys off
239 for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
240 raw_matrix[i] = 0;
241 matrix[i] = 0;
242 }
243
244 debounce_init(ROWS_PER_HAND);
245
246 matrix_init_quantum();
247
248 split_post_init();
249}
250
251bool matrix_post_scan(void) {
252 bool changed = false;
253 if (is_keyboard_master()) {
254 static uint8_t error_count;
255
256 matrix_row_t slave_matrix[ROWS_PER_HAND] = {0};
257 if (!transport_master(matrix + thisHand, slave_matrix)) {
258 error_count++;
259
260 if (error_count > ERROR_DISCONNECT_COUNT) {
261 // reset other half if disconnected
262 for (int i = 0; i < ROWS_PER_HAND; ++i) {
263 matrix[thatHand + i] = 0;
264 slave_matrix[i] = 0;
265 }
266
267 changed = true;
268 }
269 } else {
270 error_count = 0;
271
272 for (int i = 0; i < ROWS_PER_HAND; ++i) {
273 if (matrix[thatHand + i] != slave_matrix[i]) {
274 matrix[thatHand + i] = slave_matrix[i];
275 changed = true;
276 }
277 }
278 }
279
280 matrix_scan_quantum();
281 } else {
282 transport_slave(matrix + thatHand, matrix + thisHand);
283
284 matrix_slave_scan_kb();
285 }
286
287 return changed;
288}
289
290uint8_t matrix_scan(void) {
291 bool local_changed = false;
292
293#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW)
294 // Set row, read cols
295 for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
296 local_changed |= read_cols_on_row(raw_matrix, current_row);
297 }
298#elif (DIODE_DIRECTION == ROW2COL)
299 // Set col, read rows
300 for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
301 local_changed |= read_rows_on_col(raw_matrix, current_col);
302 }
303#endif
304
305 debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, local_changed);
306
307 bool remote_changed = matrix_post_scan();
308 return (uint8_t)(local_changed || remote_changed);
309}
diff --git a/quantum/split_common/post_config.h b/quantum/split_common/post_config.h
index 4ae1d5273..a4c0a1956 100644
--- a/quantum/split_common/post_config.h
+++ b/quantum/split_common/post_config.h
@@ -7,13 +7,4 @@
7# ifndef F_SCL 7# ifndef F_SCL
8# define F_SCL 100000UL // SCL frequency 8# define F_SCL 100000UL // SCL frequency
9# endif 9# endif
10
11#else // use serial
12// When using serial, the user must define RGBLIGHT_SPLIT explicitly
13// in config.h as needed.
14// see quantum/rgblight_post_config.h
15# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
16// When using serial and RGBLIGHT_SPLIT need separate transaction
17# define SERIAL_USE_MULTI_TRANSACTION
18# endif
19#endif 10#endif
diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c
index 9e75e19ce..8d414f6fe 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) {
77#if defined(SPLIT_HAND_PIN) 77#if defined(SPLIT_HAND_PIN)
78 // Test pin SPLIT_HAND_PIN for High/Low, if low it's right hand 78 // Test pin SPLIT_HAND_PIN for High/Low, if low it's right hand
79 setPinInput(SPLIT_HAND_PIN); 79 setPinInput(SPLIT_HAND_PIN);
80# ifdef SPLIT_HAND_PIN_LOW_IS_LEFT
81 return !readPin(SPLIT_HAND_PIN);
82# else
80 return readPin(SPLIT_HAND_PIN); 83 return readPin(SPLIT_HAND_PIN);
84# endif
81#elif defined(SPLIT_HAND_MATRIX_GRID) 85#elif defined(SPLIT_HAND_MATRIX_GRID)
82# ifdef SPLIT_HAND_MATRIX_GRID_LOW_IS_RIGHT 86# ifdef SPLIT_HAND_MATRIX_GRID_LOW_IS_RIGHT
83 return peek_matrix_intersection(SPLIT_HAND_MATRIX_GRID); 87 return peek_matrix_intersection(SPLIT_HAND_MATRIX_GRID);
@@ -102,7 +106,7 @@ __attribute__((weak)) bool is_keyboard_master(void) {
102 106
103 // Avoid NO_USB_STARTUP_CHECK - Disable USB as the previous checks seem to enable it somehow 107 // Avoid NO_USB_STARTUP_CHECK - Disable USB as the previous checks seem to enable it somehow
104 if (usbstate == SLAVE) { 108 if (usbstate == SLAVE) {
105 usb_disable(); 109 usb_disconnect();
106 } 110 }
107 } 111 }
108 112
diff --git a/quantum/split_common/transaction_id_define.h b/quantum/split_common/transaction_id_define.h
new file mode 100644
index 000000000..464c73478
--- /dev/null
+++ b/quantum/split_common/transaction_id_define.h
@@ -0,0 +1,94 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19enum serial_transaction_id {
20#ifdef USE_I2C
21 I2C_EXECUTE_CALLBACK,
22#endif // USE_I2C
23
24 GET_SLAVE_MATRIX_CHECKSUM,
25 GET_SLAVE_MATRIX_DATA,
26
27#ifdef SPLIT_TRANSPORT_MIRROR
28 PUT_MASTER_MATRIX,
29#endif // SPLIT_TRANSPORT_MIRROR
30
31#ifdef ENCODER_ENABLE
32 GET_ENCODERS_CHECKSUM,
33 GET_ENCODERS_DATA,
34#endif // ENCODER_ENABLE
35
36#ifndef DISABLE_SYNC_TIMER
37 PUT_SYNC_TIMER,
38#endif // DISABLE_SYNC_TIMER
39
40#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
41 PUT_LAYER_STATE,
42 PUT_DEFAULT_LAYER_STATE,
43#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
44
45#ifdef SPLIT_LED_STATE_ENABLE
46 PUT_LED_STATE,
47#endif // SPLIT_LED_STATE_ENABLE
48
49#ifdef SPLIT_MODS_ENABLE
50 PUT_MODS,
51#endif // SPLIT_MODS_ENABLE
52
53#ifdef BACKLIGHT_ENABLE
54 PUT_BACKLIGHT,
55#endif // BACKLIGHT_ENABLE
56
57#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
58 PUT_RGBLIGHT,
59#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
60
61#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
62 PUT_LED_MATRIX,
63#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
64
65#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
66 PUT_RGB_MATRIX,
67#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
68
69#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
70 PUT_WPM,
71#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
72
73#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
74 PUT_RPC_INFO,
75 PUT_RPC_REQ_DATA,
76 EXECUTE_RPC,
77 GET_RPC_RESP_DATA,
78#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
79
80// keyboard-specific
81#ifdef SPLIT_TRANSACTION_IDS_KB
82 SPLIT_TRANSACTION_IDS_KB,
83#endif // SPLIT_TRANSACTION_IDS_KB
84
85// user/keymap-specific
86#ifdef SPLIT_TRANSACTION_IDS_USER
87 SPLIT_TRANSACTION_IDS_USER,
88#endif // SPLIT_TRANSACTION_IDS_USER
89
90 NUM_TOTAL_TRANSACTIONS
91};
92
93// Ensure we only use 5 bits for transaction
94_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 000000000..abad626e0
--- /dev/null
+++ b/quantum/split_common/transactions.c
@@ -0,0 +1,655 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <string.h>
18#include <stddef.h>
19
20#include "crc.h"
21#include "debug.h"
22#include "matrix.h"
23#include "quantum.h"
24#include "transactions.h"
25#include "transport.h"
26#include "transaction_id_define.h"
27
28#define SYNC_TIMER_OFFSET 2
29
30#ifndef FORCED_SYNC_THROTTLE_MS
31# define FORCED_SYNC_THROTTLE_MS 100
32#endif // FORCED_SYNC_THROTTLE_MS
33
34#define sizeof_member(type, member) sizeof(((type *)NULL)->member)
35
36#define trans_initiator2target_initializer_cb(member, cb) \
37 { &dummy, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), 0, 0, cb }
38#define trans_initiator2target_initializer(member) trans_initiator2target_initializer_cb(member, NULL)
39
40#define trans_target2initiator_initializer_cb(member, cb) \
41 { &dummy, 0, 0, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), cb }
42#define trans_target2initiator_initializer(member) trans_target2initiator_initializer_cb(member, NULL)
43
44#define transport_write(id, data, length) transport_execute_transaction(id, data, length, NULL, 0)
45#define transport_read(id, data, length) transport_execute_transaction(id, NULL, 0, data, length)
46
47#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
48// Forward-declare the RPC callback handlers
49void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
50void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
51#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
52
53////////////////////////////////////////////////////
54// Helpers
55
56bool 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[])) {
57 if (okay) {
58 bool this_okay = true;
59 for (int iter = 1; iter <= 10; ++iter) {
60 if (!this_okay) {
61 for (int i = 0; i < iter * iter; ++i) {
62 wait_us(10);
63 }
64 }
65 ATOMIC_BLOCK_FORCEON { this_okay = handler(master_matrix, slave_matrix); };
66 if (this_okay) break;
67 }
68 okay &= this_okay;
69 if (!okay) {
70 dprintf("Failed to execute %s\n", prefix);
71 }
72 }
73 return okay;
74}
75
76#define TRANSACTION_HANDLER_MASTER(prefix) \
77 do { \
78 okay &= transaction_handler_master(okay, master_matrix, slave_matrix, #prefix, &prefix##_master); \
79 } while (0)
80
81#define TRANSACTION_HANDLER_SLAVE(prefix) \
82 do { \
83 ATOMIC_BLOCK_FORCEON { prefix##_slave(master_matrix, slave_matrix); }; \
84 } while (0)
85
86inline 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) {
87 uint8_t curr_checksum;
88 bool okay = transport_read(trans_id_checksum, &curr_checksum, sizeof(curr_checksum));
89 if (okay && (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || curr_checksum != crc8(equiv_shmem, length))) {
90 okay &= transport_read(trans_id_retrieve, destination, length);
91 okay &= curr_checksum == crc8(equiv_shmem, length);
92 if (okay) {
93 *last_update = timer_read32();
94 }
95 } else {
96 memcpy(destination, equiv_shmem, length);
97 }
98 return okay;
99}
100
101inline static bool send_if_condition(int8_t trans_id, uint32_t *last_update, bool condition, void *source, size_t length) {
102 bool okay = true;
103 if (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || condition) {
104 okay &= transport_write(trans_id, source, length);
105 if (okay) {
106 *last_update = timer_read32();
107 }
108 }
109 return okay;
110}
111
112inline static bool send_if_data_mismatch(int8_t trans_id, uint32_t *last_update, void *source, const void *equiv_shmem, size_t length) {
113 // Just run a memcmp to compare the source and equivalent shmem location
114 return send_if_condition(trans_id, last_update, (memcmp(source, equiv_shmem, length) != 0), source, length);
115}
116
117////////////////////////////////////////////////////
118// Slave matrix
119
120static bool slave_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
121 static uint32_t last_update = 0;
122 static matrix_row_t last_matrix[(MATRIX_ROWS) / 2] = {0}; // last successfully-read matrix, so we can replicate if there are checksum errors
123 matrix_row_t temp_matrix[(MATRIX_ROWS) / 2]; // holding area while we test whether or not checksum is correct
124
125 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));
126 if (okay) {
127 // Checksum matches the received data, save as the last matrix state
128 memcpy(last_matrix, temp_matrix, sizeof(temp_matrix));
129 }
130 // Copy out the last-known-good matrix state to the slave matrix
131 memcpy(slave_matrix, last_matrix, sizeof(last_matrix));
132 return okay;
133}
134
135static void slave_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
136 memcpy(split_shmem->smatrix.matrix, slave_matrix, sizeof(split_shmem->smatrix.matrix));
137 split_shmem->smatrix.checksum = crc8(split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix));
138}
139
140// clang-format off
141#define TRANSACTIONS_SLAVE_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(slave_matrix_handlers)
142#define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(slave_matrix_handlers)
143#define TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS \
144 [GET_SLAVE_MATRIX_CHECKSUM] = trans_target2initiator_initializer(smatrix.checksum), \
145 [GET_SLAVE_MATRIX_DATA] = trans_target2initiator_initializer(smatrix.matrix),
146// clang-format on
147
148////////////////////////////////////////////////////
149// Master matrix
150
151#ifdef SPLIT_TRANSPORT_MIRROR
152
153static bool master_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
154 static uint32_t last_update = 0;
155 return send_if_data_mismatch(PUT_MASTER_MATRIX, &last_update, master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));
156}
157
158static void master_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
159 // Always copy to the master matrix
160 memcpy(master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));
161}
162
163# define TRANSACTIONS_MASTER_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(master_matrix_handlers)
164# define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(master_matrix_handlers)
165# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS [PUT_MASTER_MATRIX] = trans_initiator2target_initializer(mmatrix.matrix),
166
167#else // SPLIT_TRANSPORT_MIRROR
168
169# define TRANSACTIONS_MASTER_MATRIX_MASTER()
170# define TRANSACTIONS_MASTER_MATRIX_SLAVE()
171# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS
172
173#endif // SPLIT_TRANSPORT_MIRROR
174
175////////////////////////////////////////////////////
176// Encoders
177
178#ifdef ENCODER_ENABLE
179
180static bool encoder_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
181 static uint32_t last_update = 0;
182 uint8_t temp_state[NUMBER_OF_ENCODERS];
183
184 bool okay = read_if_checksum_mismatch(GET_ENCODERS_CHECKSUM, GET_ENCODERS_DATA, &last_update, temp_state, split_shmem->encoders.state, sizeof(temp_state));
185 if (okay) encoder_update_raw(temp_state);
186 return okay;
187}
188
189static void encoder_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
190 uint8_t encoder_state[NUMBER_OF_ENCODERS];
191 encoder_state_raw(encoder_state);
192 // Always prepare the encoder state for read.
193 memcpy(split_shmem->encoders.state, encoder_state, sizeof(encoder_state));
194 // Now update the checksum given that the encoders has been written to
195 split_shmem->encoders.checksum = crc8(encoder_state, sizeof(encoder_state));
196}
197
198// clang-format off
199# define TRANSACTIONS_ENCODERS_MASTER() TRANSACTION_HANDLER_MASTER(encoder_handlers)
200# define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE(encoder_handlers)
201# define TRANSACTIONS_ENCODERS_REGISTRATIONS \
202 [GET_ENCODERS_CHECKSUM] = trans_target2initiator_initializer(encoders.checksum), \
203 [GET_ENCODERS_DATA] = trans_target2initiator_initializer(encoders.state),
204// clang-format on
205
206#else // ENCODER_ENABLE
207
208# define TRANSACTIONS_ENCODERS_MASTER()
209# define TRANSACTIONS_ENCODERS_SLAVE()
210# define TRANSACTIONS_ENCODERS_REGISTRATIONS
211
212#endif // ENCODER_ENABLE
213
214////////////////////////////////////////////////////
215// Sync timer
216
217#ifndef DISABLE_SYNC_TIMER
218
219static bool sync_timer_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
220 static uint32_t last_update = 0;
221
222 bool okay = true;
223 if (timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS) {
224 uint32_t sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
225 okay &= transport_write(PUT_SYNC_TIMER, &sync_timer, sizeof(sync_timer));
226 if (okay) {
227 last_update = timer_read32();
228 }
229 }
230 return okay;
231}
232
233static void sync_timer_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
234 static uint32_t last_sync_timer = 0;
235 if (last_sync_timer != split_shmem->sync_timer) {
236 last_sync_timer = split_shmem->sync_timer;
237 sync_timer_update(last_sync_timer);
238 }
239}
240
241# define TRANSACTIONS_SYNC_TIMER_MASTER() TRANSACTION_HANDLER_MASTER(sync_timer_handlers)
242# define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE(sync_timer_handlers)
243# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS [PUT_SYNC_TIMER] = trans_initiator2target_initializer(sync_timer),
244
245#else // DISABLE_SYNC_TIMER
246
247# define TRANSACTIONS_SYNC_TIMER_MASTER()
248# define TRANSACTIONS_SYNC_TIMER_SLAVE()
249# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS
250
251#endif // DISABLE_SYNC_TIMER
252
253////////////////////////////////////////////////////
254// Layer state
255
256#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
257
258static bool layer_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
259 static uint32_t last_layer_state_update = 0;
260 static uint32_t last_default_layer_state_update = 0;
261
262 bool okay = send_if_condition(PUT_LAYER_STATE, &last_layer_state_update, (layer_state != split_shmem->layers.layer_state), &layer_state, sizeof(layer_state));
263 if (okay) {
264 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));
265 }
266 return okay;
267}
268
269static void layer_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
270 layer_state = split_shmem->layers.layer_state;
271 default_layer_state = split_shmem->layers.default_layer_state;
272}
273
274// clang-format off
275# define TRANSACTIONS_LAYER_STATE_MASTER() TRANSACTION_HANDLER_MASTER(layer_state_handlers)
276# define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(layer_state_handlers)
277# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS \
278 [PUT_LAYER_STATE] = trans_initiator2target_initializer(layers.layer_state), \
279 [PUT_DEFAULT_LAYER_STATE] = trans_initiator2target_initializer(layers.default_layer_state),
280// clang-format on
281
282#else // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
283
284# define TRANSACTIONS_LAYER_STATE_MASTER()
285# define TRANSACTIONS_LAYER_STATE_SLAVE()
286# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS
287
288#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
289
290////////////////////////////////////////////////////
291// LED state
292
293#ifdef SPLIT_LED_STATE_ENABLE
294
295static bool led_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
296 static uint32_t last_update = 0;
297 uint8_t led_state = host_keyboard_leds();
298 return send_if_data_mismatch(PUT_LED_STATE, &last_update, &led_state, &split_shmem->led_state, sizeof(led_state));
299}
300
301static void led_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
302 void set_split_host_keyboard_leds(uint8_t led_state);
303 set_split_host_keyboard_leds(split_shmem->led_state);
304}
305
306# define TRANSACTIONS_LED_STATE_MASTER() TRANSACTION_HANDLER_MASTER(led_state_handlers)
307# define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(led_state_handlers)
308# define TRANSACTIONS_LED_STATE_REGISTRATIONS [PUT_LED_STATE] = trans_initiator2target_initializer(led_state),
309
310#else // SPLIT_LED_STATE_ENABLE
311
312# define TRANSACTIONS_LED_STATE_MASTER()
313# define TRANSACTIONS_LED_STATE_SLAVE()
314# define TRANSACTIONS_LED_STATE_REGISTRATIONS
315
316#endif // SPLIT_LED_STATE_ENABLE
317
318////////////////////////////////////////////////////
319// Mods
320
321#ifdef SPLIT_MODS_ENABLE
322
323static bool mods_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
324 static uint32_t last_update = 0;
325 bool mods_need_sync = timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS;
326 split_mods_sync_t new_mods;
327 new_mods.real_mods = get_mods();
328 if (!mods_need_sync && new_mods.real_mods != split_shmem->mods.real_mods) {
329 mods_need_sync = true;
330 }
331
332 new_mods.weak_mods = get_weak_mods();
333 if (!mods_need_sync && new_mods.weak_mods != split_shmem->mods.weak_mods) {
334 mods_need_sync = true;
335 }
336
337# ifndef NO_ACTION_ONESHOT
338 new_mods.oneshot_mods = get_oneshot_mods();
339 if (!mods_need_sync && new_mods.oneshot_mods != split_shmem->mods.oneshot_mods) {
340 mods_need_sync = true;
341 }
342# endif // NO_ACTION_ONESHOT
343
344 bool okay = true;
345 if (mods_need_sync) {
346 okay &= transport_write(PUT_MODS, &new_mods, sizeof(new_mods));
347 if (okay) {
348 last_update = timer_read32();
349 }
350 }
351
352 return okay;
353}
354
355static void mods_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
356 set_mods(split_shmem->mods.real_mods);
357 set_weak_mods(split_shmem->mods.weak_mods);
358# ifndef NO_ACTION_ONESHOT
359 set_oneshot_mods(split_shmem->mods.oneshot_mods);
360# endif
361}
362
363# define TRANSACTIONS_MODS_MASTER() TRANSACTION_HANDLER_MASTER(mods_handlers)
364# define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods_handlers)
365# define TRANSACTIONS_MODS_REGISTRATIONS [PUT_MODS] = trans_initiator2target_initializer(mods),
366
367#else // SPLIT_MODS_ENABLE
368
369# define TRANSACTIONS_MODS_MASTER()
370# define TRANSACTIONS_MODS_SLAVE()
371# define TRANSACTIONS_MODS_REGISTRATIONS
372
373#endif // SPLIT_MODS_ENABLE
374
375////////////////////////////////////////////////////
376// Backlight
377
378#ifdef BACKLIGHT_ENABLE
379
380static bool backlight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
381 static uint32_t last_update = 0;
382 uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0;
383 return send_if_condition(PUT_BACKLIGHT, &last_update, (level != split_shmem->backlight_level), &level, sizeof(level));
384}
385
386static void backlight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { backlight_set(split_shmem->backlight_level); }
387
388# define TRANSACTIONS_BACKLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(backlight_handlers)
389# define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight_handlers)
390# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS [PUT_BACKLIGHT] = trans_initiator2target_initializer(backlight_level),
391
392#else // BACKLIGHT_ENABLE
393
394# define TRANSACTIONS_BACKLIGHT_MASTER()
395# define TRANSACTIONS_BACKLIGHT_SLAVE()
396# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS
397
398#endif // BACKLIGHT_ENABLE
399
400////////////////////////////////////////////////////
401// RGBLIGHT
402
403#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
404
405static bool rgblight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
406 static uint32_t last_update = 0;
407 rgblight_syncinfo_t rgblight_sync;
408 rgblight_get_syncinfo(&rgblight_sync);
409 if (send_if_condition(PUT_RGBLIGHT, &last_update, (rgblight_sync.status.change_flags != 0), &rgblight_sync, sizeof(rgblight_sync))) {
410 rgblight_clear_change_flags();
411 } else {
412 return false;
413 }
414 return true;
415}
416
417static void rgblight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
418 // Update the RGB with the new data
419 if (split_shmem->rgblight_sync.status.change_flags != 0) {
420 rgblight_update_sync(&split_shmem->rgblight_sync, false);
421 split_shmem->rgblight_sync.status.change_flags = 0;
422 }
423}
424
425# define TRANSACTIONS_RGBLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(rgblight_handlers)
426# define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight_handlers)
427# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS [PUT_RGBLIGHT] = trans_initiator2target_initializer(rgblight_sync),
428
429#else // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
430
431# define TRANSACTIONS_RGBLIGHT_MASTER()
432# define TRANSACTIONS_RGBLIGHT_SLAVE()
433# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS
434
435#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
436
437////////////////////////////////////////////////////
438// LED Matrix
439
440#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
441
442static bool led_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
443 static uint32_t last_update = 0;
444 led_matrix_sync_t led_matrix_sync;
445 memcpy(&led_matrix_sync.led_matrix, &led_matrix_eeconfig, sizeof(led_eeconfig_t));
446 led_matrix_sync.led_suspend_state = led_matrix_get_suspend_state();
447 return send_if_data_mismatch(PUT_LED_MATRIX, &last_update, &led_matrix_sync, &split_shmem->led_matrix_sync, sizeof(led_matrix_sync));
448}
449
450static void led_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
451 memcpy(&led_matrix_eeconfig, &split_shmem->led_matrix_sync.led_matrix, sizeof(led_eeconfig_t));
452 led_matrix_set_suspend_state(split_shmem->led_matrix_sync.led_suspend_state);
453}
454
455# define TRANSACTIONS_LED_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(led_matrix_handlers)
456# define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix_handlers)
457# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS [PUT_LED_MATRIX] = trans_initiator2target_initializer(led_matrix_sync),
458
459#else // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
460
461# define TRANSACTIONS_LED_MATRIX_MASTER()
462# define TRANSACTIONS_LED_MATRIX_SLAVE()
463# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS
464
465#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
466
467////////////////////////////////////////////////////
468// RGB Matrix
469
470#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
471
472static bool rgb_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
473 static uint32_t last_update = 0;
474 rgb_matrix_sync_t rgb_matrix_sync;
475 memcpy(&rgb_matrix_sync.rgb_matrix, &rgb_matrix_config, sizeof(rgb_config_t));
476 rgb_matrix_sync.rgb_suspend_state = rgb_matrix_get_suspend_state();
477 return send_if_data_mismatch(PUT_RGB_MATRIX, &last_update, &rgb_matrix_sync, &split_shmem->rgb_matrix_sync, sizeof(rgb_matrix_sync));
478}
479
480static void rgb_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
481 memcpy(&rgb_matrix_config, &split_shmem->rgb_matrix_sync.rgb_matrix, sizeof(rgb_config_t));
482 rgb_matrix_set_suspend_state(split_shmem->rgb_matrix_sync.rgb_suspend_state);
483}
484
485# define TRANSACTIONS_RGB_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(rgb_matrix_handlers)
486# define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix_handlers)
487# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS [PUT_RGB_MATRIX] = trans_initiator2target_initializer(rgb_matrix_sync),
488
489#else // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
490
491# define TRANSACTIONS_RGB_MATRIX_MASTER()
492# define TRANSACTIONS_RGB_MATRIX_SLAVE()
493# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS
494
495#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
496
497////////////////////////////////////////////////////
498// WPM
499
500#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
501
502static bool wpm_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
503 static uint32_t last_update = 0;
504 uint8_t current_wpm = get_current_wpm();
505 return send_if_condition(PUT_WPM, &last_update, (current_wpm != split_shmem->current_wpm), &current_wpm, sizeof(current_wpm));
506}
507
508static void wpm_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { set_current_wpm(split_shmem->current_wpm); }
509
510# define TRANSACTIONS_WPM_MASTER() TRANSACTION_HANDLER_MASTER(wpm_handlers)
511# define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE(wpm_handlers)
512# define TRANSACTIONS_WPM_REGISTRATIONS [PUT_WPM] = trans_initiator2target_initializer(current_wpm),
513
514#else // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
515
516# define TRANSACTIONS_WPM_MASTER()
517# define TRANSACTIONS_WPM_SLAVE()
518# define TRANSACTIONS_WPM_REGISTRATIONS
519
520#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
521
522////////////////////////////////////////////////////
523
524uint8_t dummy;
525split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = {
526 // Set defaults
527 [0 ...(NUM_TOTAL_TRANSACTIONS - 1)] = {NULL, 0, 0, 0, 0, 0},
528
529#ifdef USE_I2C
530 [I2C_EXECUTE_CALLBACK] = trans_initiator2target_initializer(transaction_id),
531#endif // USE_I2C
532
533 // clang-format off
534 TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS
535 TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS
536 TRANSACTIONS_ENCODERS_REGISTRATIONS
537 TRANSACTIONS_SYNC_TIMER_REGISTRATIONS
538 TRANSACTIONS_LAYER_STATE_REGISTRATIONS
539 TRANSACTIONS_LED_STATE_REGISTRATIONS
540 TRANSACTIONS_MODS_REGISTRATIONS
541 TRANSACTIONS_BACKLIGHT_REGISTRATIONS
542 TRANSACTIONS_RGBLIGHT_REGISTRATIONS
543 TRANSACTIONS_LED_MATRIX_REGISTRATIONS
544 TRANSACTIONS_RGB_MATRIX_REGISTRATIONS
545 TRANSACTIONS_WPM_REGISTRATIONS
546// clang-format on
547
548#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
549 [PUT_RPC_INFO] = trans_initiator2target_initializer_cb(rpc_info, slave_rpc_info_callback),
550 [PUT_RPC_REQ_DATA] = trans_initiator2target_initializer(rpc_m2s_buffer),
551 [EXECUTE_RPC] = trans_initiator2target_initializer_cb(rpc_info.transaction_id, slave_rpc_exec_callback),
552 [GET_RPC_RESP_DATA] = trans_target2initiator_initializer(rpc_s2m_buffer),
553#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
554};
555
556bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
557 bool okay = true;
558 TRANSACTIONS_SLAVE_MATRIX_MASTER();
559 TRANSACTIONS_MASTER_MATRIX_MASTER();
560 TRANSACTIONS_ENCODERS_MASTER();
561 TRANSACTIONS_SYNC_TIMER_MASTER();
562 TRANSACTIONS_LAYER_STATE_MASTER();
563 TRANSACTIONS_LED_STATE_MASTER();
564 TRANSACTIONS_MODS_MASTER();
565 TRANSACTIONS_BACKLIGHT_MASTER();
566 TRANSACTIONS_RGBLIGHT_MASTER();
567 TRANSACTIONS_LED_MATRIX_MASTER();
568 TRANSACTIONS_RGB_MATRIX_MASTER();
569 TRANSACTIONS_WPM_MASTER();
570 return okay;
571}
572
573void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
574 TRANSACTIONS_SLAVE_MATRIX_SLAVE();
575 TRANSACTIONS_MASTER_MATRIX_SLAVE();
576 TRANSACTIONS_ENCODERS_SLAVE();
577 TRANSACTIONS_SYNC_TIMER_SLAVE();
578 TRANSACTIONS_LAYER_STATE_SLAVE();
579 TRANSACTIONS_LED_STATE_SLAVE();
580 TRANSACTIONS_MODS_SLAVE();
581 TRANSACTIONS_BACKLIGHT_SLAVE();
582 TRANSACTIONS_RGBLIGHT_SLAVE();
583 TRANSACTIONS_LED_MATRIX_SLAVE();
584 TRANSACTIONS_RGB_MATRIX_SLAVE();
585 TRANSACTIONS_WPM_SLAVE();
586}
587
588#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
589
590void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback) {
591 // Prevent invoking RPC on QMK core sync data
592 if (transaction_id <= GET_RPC_RESP_DATA) return;
593
594 // Set the callback
595 split_transaction_table[transaction_id].slave_callback = callback;
596 split_transaction_table[transaction_id].initiator2target_offset = offsetof(split_shared_memory_t, rpc_m2s_buffer);
597 split_transaction_table[transaction_id].target2initiator_offset = offsetof(split_shared_memory_t, rpc_s2m_buffer);
598}
599
600bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
601 // Prevent invoking RPC on QMK core sync data
602 if (transaction_id <= GET_RPC_RESP_DATA) return false;
603 // Prevent sizing issues
604 if (initiator2target_buffer_size > RPC_M2S_BUFFER_SIZE) return false;
605 if (target2initiator_buffer_size > RPC_S2M_BUFFER_SIZE) return false;
606
607 // Prepare the metadata block
608 rpc_sync_info_t info = {.transaction_id = transaction_id, .m2s_length = initiator2target_buffer_size, .s2m_length = target2initiator_buffer_size};
609
610 // Make sure the local side knows that we're not sending the full block of data
611 split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = initiator2target_buffer_size;
612 split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = target2initiator_buffer_size;
613
614 // Run through the sequence:
615 // * set the transaction ID and lengths
616 // * send the request data
617 // * execute RPC callback
618 // * retrieve the response data
619 if (!transport_write(PUT_RPC_INFO, &info, sizeof(info))) {
620 return false;
621 }
622 if (!transport_write(PUT_RPC_REQ_DATA, initiator2target_buffer, initiator2target_buffer_size)) {
623 return false;
624 }
625 if (!transport_write(EXECUTE_RPC, &transaction_id, sizeof(transaction_id))) {
626 return false;
627 }
628 if (!transport_read(GET_RPC_RESP_DATA, target2initiator_buffer, target2initiator_buffer_size)) {
629 return false;
630 }
631 return true;
632}
633
634void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
635 // The RPC info block contains the intended transaction ID, as well as the sizes for both inbound and outbound data.
636 // Ignore the args -- the `split_shmem` already has the info, we just need to act upon it.
637 // We must keep the `split_transaction_table` non-const, so that it is able to be modified at runtime.
638
639 split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = split_shmem->rpc_info.m2s_length;
640 split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = split_shmem->rpc_info.s2m_length;
641}
642
643void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
644 // We can assume that the buffer lengths are correctly set, now, given that sequentially the rpc_info callback was already executed.
645 // Go through the rpc_info and execute _that_ transaction's callback, with the scratch buffers as inputs.
646 int8_t transaction_id = split_shmem->rpc_info.transaction_id;
647 if (transaction_id < NUM_TOTAL_TRANSACTIONS) {
648 split_transaction_desc_t *trans = &split_transaction_table[transaction_id];
649 if (trans->slave_callback) {
650 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);
651 }
652 }
653}
654
655#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 000000000..4306ba1d8
--- /dev/null
+++ b/quantum/split_common/transactions.h
@@ -0,0 +1,54 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "stdint.h"
20#include "stdbool.h"
21
22#include "matrix.h"
23#include "transaction_id_define.h"
24#include "transport.h"
25
26typedef void (*slave_callback_t)(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
27
28// Split transaction Descriptor
29typedef struct _split_transaction_desc_t {
30 uint8_t * status;
31 uint8_t initiator2target_buffer_size;
32 uint16_t initiator2target_offset;
33 uint8_t target2initiator_buffer_size;
34 uint16_t target2initiator_offset;
35 slave_callback_t slave_callback;
36} split_transaction_desc_t;
37
38// Forward declaration for the split transactions
39extern split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS];
40
41#define split_shmem_offset_ptr(offset) ((void *)(((uint8_t *)split_shmem) + (offset)))
42#define split_trans_initiator2target_buffer(trans) (split_shmem_offset_ptr((trans)->initiator2target_offset))
43#define split_trans_target2initiator_buffer(trans) (split_shmem_offset_ptr((trans)->target2initiator_offset))
44
45// returns false if valid data not received from slave
46bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
47void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
48
49void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback);
50
51bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
52
53#define transaction_rpc_send(transaction_id, initiator2target_buffer_size, initiator2target_buffer) transaction_rpc_exec(transaction_id, initiator2target_buffer_size, initiator2target_buffer, 0, NULL)
54#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 9ed0f7591..a711ef85f 100644
--- a/quantum/split_common/transport.c
+++ b/quantum/split_common/transport.c
@@ -1,452 +1,118 @@
1#include <string.h> 1/* Copyright 2021 QMK
2#include <stddef.h> 2 *
3 3 * This program is free software: you can redistribute it and/or modify
4#include "config.h" 4 * it under the terms of the GNU General Public License as published by
5#include "matrix.h" 5 * the Free Software Foundation, either version 2 of the License, or
6#include "quantum.h" 6 * (at your option) any later version.
7 7 *
8#define ROWS_PER_HAND (MATRIX_ROWS / 2) 8 * This program is distributed in the hope that it will be useful,
9#define SYNC_TIMER_OFFSET 2 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11#ifdef RGBLIGHT_ENABLE 11 * GNU General Public License for more details.
12# include "rgblight.h" 12 *
13#endif 13 * You should have received a copy of the GNU General Public License
14 14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15#ifdef BACKLIGHT_ENABLE 15 */
16# include "backlight.h"
17#endif
18
19#ifdef ENCODER_ENABLE
20# include "encoder.h"
21static pin_t encoders_pad[] = ENCODERS_PAD_A;
22# define NUMBER_OF_ENCODERS (sizeof(encoders_pad) / sizeof(pin_t))
23#endif
24
25#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
26# include "led_matrix.h"
27#endif
28#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
29# include "rgb_matrix.h"
30#endif
31
32#if defined(USE_I2C)
33 16
34# include "i2c_master.h" 17#include <string.h>
35# include "i2c_slave.h" 18#include <debug.h>
36
37typedef struct _I2C_slave_buffer_t {
38# ifndef DISABLE_SYNC_TIMER
39 uint32_t sync_timer;
40# endif
41# ifdef SPLIT_TRANSPORT_MIRROR
42 matrix_row_t mmatrix[ROWS_PER_HAND];
43# endif
44 matrix_row_t smatrix[ROWS_PER_HAND];
45# ifdef SPLIT_MODS_ENABLE
46 uint8_t real_mods;
47 uint8_t weak_mods;
48# ifndef NO_ACTION_ONESHOT
49 uint8_t oneshot_mods;
50# endif
51# endif
52# ifdef BACKLIGHT_ENABLE
53 uint8_t backlight_level;
54# endif
55# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
56 rgblight_syncinfo_t rgblight_sync;
57# endif
58# ifdef ENCODER_ENABLE
59 uint8_t encoder_state[NUMBER_OF_ENCODERS];
60# endif
61# ifdef WPM_ENABLE
62 uint8_t current_wpm;
63# endif
64# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
65 led_eeconfig_t led_matrix;
66 bool led_suspend_state;
67# endif
68# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
69 rgb_config_t rgb_matrix;
70 bool rgb_suspend_state;
71# endif
72} I2C_slave_buffer_t;
73 19
74static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg; 20#include "transactions.h"
21#include "transport.h"
22#include "transaction_id_define.h"
23#include "atomic_util.h"
75 24
76# define I2C_SYNC_TIME_START offsetof(I2C_slave_buffer_t, sync_timer) 25#ifdef USE_I2C
77# define I2C_KEYMAP_MASTER_START offsetof(I2C_slave_buffer_t, mmatrix)
78# define I2C_KEYMAP_SLAVE_START offsetof(I2C_slave_buffer_t, smatrix)
79# define I2C_REAL_MODS_START offsetof(I2C_slave_buffer_t, real_mods)
80# define I2C_WEAK_MODS_START offsetof(I2C_slave_buffer_t, weak_mods)
81# define I2C_ONESHOT_MODS_START offsetof(I2C_slave_buffer_t, oneshot_mods)
82# define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level)
83# define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync)
84# define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state)
85# define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm)
86# define I2C_LED_MATRIX_START offsetof(I2C_slave_buffer_t, led_matrix)
87# define I2C_LED_SUSPEND_START offsetof(I2C_slave_buffer_t, led_suspend_state)
88# define I2C_RGB_MATRIX_START offsetof(I2C_slave_buffer_t, rgb_matrix)
89# define I2C_RGB_SUSPEND_START offsetof(I2C_slave_buffer_t, rgb_suspend_state)
90 26
91# define TIMEOUT 100 27# ifndef SLAVE_I2C_TIMEOUT
28# define SLAVE_I2C_TIMEOUT 100
29# endif // SLAVE_I2C_TIMEOUT
92 30
93# ifndef SLAVE_I2C_ADDRESS 31# ifndef SLAVE_I2C_ADDRESS
94# define SLAVE_I2C_ADDRESS 0x32 32# define SLAVE_I2C_ADDRESS 0x32
95# endif 33# endif
96 34
97// Get rows from other half over i2c 35# include "i2c_master.h"
98bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { 36# include "i2c_slave.h"
99 i2c_readReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_SLAVE_START, (void *)slave_matrix, sizeof(i2c_buffer->smatrix), TIMEOUT);
100# ifdef SPLIT_TRANSPORT_MIRROR
101 i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_MASTER_START, (void *)master_matrix, sizeof(i2c_buffer->mmatrix), TIMEOUT);
102# endif
103 37
104 // write backlight info 38// Ensure the I2C buffer has enough space
105# ifdef BACKLIGHT_ENABLE 39_Static_assert(sizeof(split_shared_memory_t) <= I2C_SLAVE_REG_COUNT, "split_shared_memory_t too large for I2C_SLAVE_REG_COUNT");
106 uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0;
107 if (level != i2c_buffer->backlight_level) {
108 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_BACKLIGHT_START, (void *)&level, sizeof(level), TIMEOUT) >= 0) {
109 i2c_buffer->backlight_level = level;
110 }
111 }
112# endif
113 40
114# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) 41split_shared_memory_t *const split_shmem = (split_shared_memory_t *)i2c_slave_reg;
115 if (rgblight_get_change_flags()) {
116 rgblight_syncinfo_t rgblight_sync;
117 rgblight_get_syncinfo(&rgblight_sync);
118 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_START, (void *)&rgblight_sync, sizeof(rgblight_sync), TIMEOUT) >= 0) {
119 rgblight_clear_change_flags();
120 }
121 }
122# endif
123 42
124# ifdef ENCODER_ENABLE 43void transport_master_init(void) { i2c_init(); }
125 i2c_readReg(SLAVE_I2C_ADDRESS, I2C_ENCODER_START, (void *)i2c_buffer->encoder_state, sizeof(i2c_buffer->encoder_state), TIMEOUT); 44void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); }
126 encoder_update_raw(i2c_buffer->encoder_state);
127# endif
128 45
129# ifdef WPM_ENABLE 46i2c_status_t transport_trigger_callback(int8_t id) {
130 uint8_t current_wpm = get_current_wpm(); 47 // If there's no callback, indicate that we were successful
131 if (current_wpm != i2c_buffer->current_wpm) { 48 if (!split_transaction_table[id].slave_callback) {
132 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WPM_START, (void *)&current_wpm, sizeof(current_wpm), TIMEOUT) >= 0) { 49 return I2C_STATUS_SUCCESS;
133 i2c_buffer->current_wpm = current_wpm;
134 }
135 } 50 }
136# endif
137 51
138# ifdef SPLIT_MODS_ENABLE 52 // Kick off the "callback executor", now that data has been written to the slave
139 uint8_t real_mods = get_mods(); 53 split_shmem->transaction_id = id;
140 if (real_mods != i2c_buffer->real_mods) { 54 split_transaction_desc_t *trans = &split_transaction_table[I2C_EXECUTE_CALLBACK];
141 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_REAL_MODS_START, (void *)&real_mods, sizeof(real_mods), TIMEOUT) >= 0) { 55 return i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, SLAVE_I2C_TIMEOUT);
142 i2c_buffer->real_mods = real_mods; 56}
57
58bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) {
59 i2c_status_t status;
60 split_transaction_desc_t *trans = &split_transaction_table[id];
61 if (initiator2target_length > 0) {
62 size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length;
63 memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len);
64 if ((status = i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) {
65 return false;
143 } 66 }
144 } 67 }
145 68
146 uint8_t weak_mods = get_weak_mods(); 69 // If we need to execute a callback on the slave, do so
147 if (weak_mods != i2c_buffer->weak_mods) { 70 if ((status = transport_trigger_callback(id)) < 0) {
148 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WEAK_MODS_START, (void *)&weak_mods, sizeof(weak_mods), TIMEOUT) >= 0) { 71 return false;
149 i2c_buffer->weak_mods = weak_mods;
150 }
151 } 72 }
152 73
153# ifndef NO_ACTION_ONESHOT 74 if (target2initiator_length > 0) {
154 uint8_t oneshot_mods = get_oneshot_mods(); 75 size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length;
155 if (oneshot_mods != i2c_buffer->oneshot_mods) { 76 if ((status = i2c_readReg(SLAVE_I2C_ADDRESS, trans->target2initiator_offset, split_trans_target2initiator_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) {
156 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_ONESHOT_MODS_START, (void *)&oneshot_mods, sizeof(oneshot_mods), TIMEOUT) >= 0) { 77 return false;
157 i2c_buffer->oneshot_mods = oneshot_mods;
158 } 78 }
79 memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len);
159 } 80 }
160# endif
161# endif
162 81
163# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
164 i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_MATRIX_START, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix), TIMEOUT);
165 bool suspend_state = led_matrix_get_suspend_state();
166 i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->led_suspend_state), TIMEOUT);
167# endif
168# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
169 i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_MATRIX_START, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix), TIMEOUT);
170 bool suspend_state = rgb_matrix_get_suspend_state();
171 i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->rgb_suspend_state), TIMEOUT);
172# endif
173
174# ifndef DISABLE_SYNC_TIMER
175 i2c_buffer->sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
176 i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_SYNC_TIME_START, (void *)&i2c_buffer->sync_timer, sizeof(i2c_buffer->sync_timer), TIMEOUT);
177# endif
178 return true; 82 return true;
179} 83}
180 84
181void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { 85#else // USE_I2C
182# ifndef DISABLE_SYNC_TIMER
183 sync_timer_update(i2c_buffer->sync_timer);
184# endif
185 // Copy matrix to I2C buffer
186 memcpy((void *)i2c_buffer->smatrix, (void *)slave_matrix, sizeof(i2c_buffer->smatrix));
187# ifdef SPLIT_TRANSPORT_MIRROR
188 memcpy((void *)master_matrix, (void *)i2c_buffer->mmatrix, sizeof(i2c_buffer->mmatrix));
189# endif
190
191// Read Backlight Info
192# ifdef BACKLIGHT_ENABLE
193 backlight_set(i2c_buffer->backlight_level);
194# endif
195
196# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
197 // Update the RGB with the new data
198 if (i2c_buffer->rgblight_sync.status.change_flags != 0) {
199 rgblight_update_sync(&i2c_buffer->rgblight_sync, false);
200 i2c_buffer->rgblight_sync.status.change_flags = 0;
201 }
202# endif
203
204# ifdef ENCODER_ENABLE
205 encoder_state_raw(i2c_buffer->encoder_state);
206# endif
207
208# ifdef WPM_ENABLE
209 set_current_wpm(i2c_buffer->current_wpm);
210# endif
211
212# ifdef SPLIT_MODS_ENABLE
213 set_mods(i2c_buffer->real_mods);
214 set_weak_mods(i2c_buffer->weak_mods);
215# ifndef NO_ACTION_ONESHOT
216 set_oneshot_mods(i2c_buffer->oneshot_mods);
217# endif
218# endif
219
220# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
221 memcpy((void *)i2c_buffer->led_matrix, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix));
222 led_matrix_set_suspend_state(i2c_buffer->led_suspend_state);
223# endif
224# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
225 memcpy((void *)i2c_buffer->rgb_matrix, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix));
226 rgb_matrix_set_suspend_state(i2c_buffer->rgb_suspend_state);
227# endif
228}
229
230void transport_master_init(void) { i2c_init(); }
231
232void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); }
233
234#else // USE_SERIAL
235 86
236# include "serial.h" 87# include "serial.h"
237 88
238typedef struct _Serial_s2m_buffer_t { 89static split_shared_memory_t shared_memory;
239 // TODO: if MATRIX_COLS > 8 change to uint8_t packed_matrix[] for pack/unpack 90split_shared_memory_t *const split_shmem = &shared_memory;
240 matrix_row_t smatrix[ROWS_PER_HAND];
241
242# ifdef ENCODER_ENABLE
243 uint8_t encoder_state[NUMBER_OF_ENCODERS];
244# endif
245
246} Serial_s2m_buffer_t;
247
248typedef struct _Serial_m2s_buffer_t {
249# ifdef SPLIT_MODS_ENABLE
250 uint8_t real_mods;
251 uint8_t weak_mods;
252# ifndef NO_ACTION_ONESHOT
253 uint8_t oneshot_mods;
254# endif
255# endif
256# ifndef DISABLE_SYNC_TIMER
257 uint32_t sync_timer;
258# endif
259# ifdef SPLIT_TRANSPORT_MIRROR
260 matrix_row_t mmatrix[ROWS_PER_HAND];
261# endif
262# ifdef BACKLIGHT_ENABLE
263 uint8_t backlight_level;
264# endif
265# ifdef WPM_ENABLE
266 uint8_t current_wpm;
267# endif
268# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
269 led_eeconfig_t led_matrix;
270 bool led_suspend_state;
271# endif
272# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
273 rgb_config_t rgb_matrix;
274 bool rgb_suspend_state;
275# endif
276} Serial_m2s_buffer_t;
277
278# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
279// When MCUs on both sides drive their respective RGB LED chains,
280// it is necessary to synchronize, so it is necessary to communicate RGB
281// information. In that case, define RGBLIGHT_SPLIT with info on the number
282// of LEDs on each half.
283//
284// Otherwise, if the master side MCU drives both sides RGB LED chains,
285// there is no need to communicate.
286
287typedef struct _Serial_rgblight_t {
288 rgblight_syncinfo_t rgblight_sync;
289} Serial_rgblight_t;
290 91
291volatile Serial_rgblight_t serial_rgblight = {}; 92void transport_master_init(void) { soft_serial_initiator_init(); }
292uint8_t volatile status_rgblight = 0; 93void transport_slave_init(void) { soft_serial_target_init(); }
293# endif
294
295volatile Serial_s2m_buffer_t serial_s2m_buffer = {};
296volatile Serial_m2s_buffer_t serial_m2s_buffer = {};
297uint8_t volatile status0 = 0;
298
299enum serial_transaction_id {
300 GET_SLAVE_MATRIX = 0,
301# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
302 PUT_RGBLIGHT,
303# endif
304};
305
306SSTD_t transactions[] = {
307 [GET_SLAVE_MATRIX] =
308 {
309 (uint8_t *)&status0,
310 sizeof(serial_m2s_buffer),
311 (uint8_t *)&serial_m2s_buffer,
312 sizeof(serial_s2m_buffer),
313 (uint8_t *)&serial_s2m_buffer,
314 },
315# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
316 [PUT_RGBLIGHT] =
317 {
318 (uint8_t *)&status_rgblight, sizeof(serial_rgblight), (uint8_t *)&serial_rgblight, 0, NULL // no slave to master transfer
319 },
320# endif
321};
322
323void transport_master_init(void) { soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); }
324
325void transport_slave_init(void) { soft_serial_target_init(transactions, TID_LIMIT(transactions)); }
326 94
327# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) 95bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) {
328 96 split_transaction_desc_t *trans = &split_transaction_table[id];
329// rgblight synchronization information communication. 97 if (initiator2target_length > 0) {
330 98 size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length;
331void transport_rgblight_master(void) { 99 memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len);
332 if (rgblight_get_change_flags()) {
333 rgblight_get_syncinfo((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync);
334 if (soft_serial_transaction(PUT_RGBLIGHT) == TRANSACTION_END) {
335 rgblight_clear_change_flags();
336 }
337 }
338}
339
340void transport_rgblight_slave(void) {
341 if (status_rgblight == TRANSACTION_ACCEPTED) {
342 rgblight_update_sync((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync, false);
343 status_rgblight = TRANSACTION_END;
344 } 100 }
345}
346 101
347# else 102 if (soft_serial_transaction(id) != TRANSACTION_END) {
348# define transport_rgblight_master()
349# define transport_rgblight_slave()
350# endif
351
352bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
353# ifndef SERIAL_USE_MULTI_TRANSACTION
354 if (soft_serial_transaction() != TRANSACTION_END) {
355 return false;
356 }
357# else
358 transport_rgblight_master();
359 if (soft_serial_transaction(GET_SLAVE_MATRIX) != TRANSACTION_END) {
360 return false; 103 return false;
361 } 104 }
362# endif
363 105
364 // TODO: if MATRIX_COLS > 8 change to unpack() 106 if (target2initiator_length > 0) {
365 for (int i = 0; i < ROWS_PER_HAND; ++i) { 107 size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length;
366 slave_matrix[i] = serial_s2m_buffer.smatrix[i]; 108 memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len);
367# ifdef SPLIT_TRANSPORT_MIRROR
368 serial_m2s_buffer.mmatrix[i] = master_matrix[i];
369# endif
370 } 109 }
371 110
372# ifdef BACKLIGHT_ENABLE
373 // Write backlight level for slave to read
374 serial_m2s_buffer.backlight_level = is_backlight_enabled() ? get_backlight_level() : 0;
375# endif
376
377# ifdef ENCODER_ENABLE
378 encoder_update_raw((uint8_t *)serial_s2m_buffer.encoder_state);
379# endif
380
381# ifdef WPM_ENABLE
382 // Write wpm to slave
383 serial_m2s_buffer.current_wpm = get_current_wpm();
384# endif
385
386# ifdef SPLIT_MODS_ENABLE
387 serial_m2s_buffer.real_mods = get_mods();
388 serial_m2s_buffer.weak_mods = get_weak_mods();
389# ifndef NO_ACTION_ONESHOT
390 serial_m2s_buffer.oneshot_mods = get_oneshot_mods();
391# endif
392# endif
393
394# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
395 serial_m2s_buffer.led_matrix = led_matrix_eeconfig;
396 serial_m2s_buffer.led_suspend_state = led_matrix_get_suspend_state();
397# endif
398# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
399 serial_m2s_buffer.rgb_matrix = rgb_matrix_config;
400 serial_m2s_buffer.rgb_suspend_state = rgb_matrix_get_suspend_state();
401# endif
402
403# ifndef DISABLE_SYNC_TIMER
404 serial_m2s_buffer.sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
405# endif
406 return true; 111 return true;
407} 112}
408 113
409void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { 114#endif // USE_I2C
410 transport_rgblight_slave();
411# ifndef DISABLE_SYNC_TIMER
412 sync_timer_update(serial_m2s_buffer.sync_timer);
413# endif
414
415 // TODO: if MATRIX_COLS > 8 change to pack()
416 for (int i = 0; i < ROWS_PER_HAND; ++i) {
417 serial_s2m_buffer.smatrix[i] = slave_matrix[i];
418# ifdef SPLIT_TRANSPORT_MIRROR
419 master_matrix[i] = serial_m2s_buffer.mmatrix[i];
420# endif
421 }
422# ifdef BACKLIGHT_ENABLE
423 backlight_set(serial_m2s_buffer.backlight_level);
424# endif
425
426# ifdef ENCODER_ENABLE
427 encoder_state_raw((uint8_t *)serial_s2m_buffer.encoder_state);
428# endif
429 115
430# ifdef WPM_ENABLE 116bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { return transactions_master(master_matrix, slave_matrix); }
431 set_current_wpm(serial_m2s_buffer.current_wpm);
432# endif
433
434# ifdef SPLIT_MODS_ENABLE
435 set_mods(serial_m2s_buffer.real_mods);
436 set_weak_mods(serial_m2s_buffer.weak_mods);
437# ifndef NO_ACTION_ONESHOT
438 set_oneshot_mods(serial_m2s_buffer.oneshot_mods);
439# endif
440# endif
441
442# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
443 led_matrix_eeconfig = serial_m2s_buffer.led_matrix;
444 led_matrix_set_suspend_state(serial_m2s_buffer.led_suspend_state);
445# endif
446# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
447 rgb_matrix_config = serial_m2s_buffer.rgb_matrix;
448 rgb_matrix_set_suspend_state(serial_m2s_buffer.rgb_suspend_state);
449# endif
450}
451 117
452#endif 118void 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 a9f66301b..2e07f6b25 100644
--- a/quantum/split_common/transport.h
+++ b/quantum/split_common/transport.h
@@ -1,10 +1,175 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
1#pragma once 17#pragma once
2 18
19#include "stdint.h"
20#include "stdbool.h"
21
22#include "progmem.h"
23#include "action_layer.h"
3#include "matrix.h" 24#include "matrix.h"
4 25
26#ifndef RPC_M2S_BUFFER_SIZE
27# define RPC_M2S_BUFFER_SIZE 32
28#endif // RPC_M2S_BUFFER_SIZE
29
30#ifndef RPC_S2M_BUFFER_SIZE
31# define RPC_S2M_BUFFER_SIZE 32
32#endif // RPC_S2M_BUFFER_SIZE
33
5void transport_master_init(void); 34void transport_master_init(void);
6void transport_slave_init(void); 35void transport_slave_init(void);
7 36
8// returns false if valid data not received from slave 37// returns false if valid data not received from slave
9bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); 38bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
10void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); 39void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
40
41bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length);
42
43#ifdef ENCODER_ENABLE
44# include "encoder.h"
45# define NUMBER_OF_ENCODERS (sizeof((pin_t[])ENCODERS_PAD_A) / sizeof(pin_t))
46#endif // ENCODER_ENABLE
47
48#ifdef BACKLIGHT_ENABLE
49# include "backlight.h"
50#endif // BACKLIGHT_ENABLE
51
52#ifdef RGBLIGHT_ENABLE
53# include "rgblight.h"
54#endif // RGBLIGHT_ENABLE
55
56typedef struct _split_slave_matrix_sync_t {
57 uint8_t checksum;
58 matrix_row_t matrix[(MATRIX_ROWS) / 2];
59} split_slave_matrix_sync_t;
60
61#ifdef SPLIT_TRANSPORT_MIRROR
62typedef struct _split_master_matrix_sync_t {
63 matrix_row_t matrix[(MATRIX_ROWS) / 2];
64} split_master_matrix_sync_t;
65#endif // SPLIT_TRANSPORT_MIRROR
66
67#ifdef ENCODER_ENABLE
68typedef struct _split_slave_encoder_sync_t {
69 uint8_t checksum;
70 uint8_t state[NUMBER_OF_ENCODERS];
71} split_slave_encoder_sync_t;
72#endif // ENCODER_ENABLE
73
74#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
75typedef struct _split_layers_sync_t {
76 layer_state_t layer_state;
77 layer_state_t default_layer_state;
78} split_layers_sync_t;
79#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
80
81#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
82# include "led_matrix.h"
83
84typedef struct _led_matrix_sync_t {
85 led_eeconfig_t led_matrix;
86 bool led_suspend_state;
87} led_matrix_sync_t;
88#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
89
90#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
91# include "rgb_matrix.h"
92
93typedef struct _rgb_matrix_sync_t {
94 rgb_config_t rgb_matrix;
95 bool rgb_suspend_state;
96} rgb_matrix_sync_t;
97#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
98
99#ifdef SPLIT_MODS_ENABLE
100typedef struct _split_mods_sync_t {
101 uint8_t real_mods;
102 uint8_t weak_mods;
103# ifndef NO_ACTION_ONESHOT
104 uint8_t oneshot_mods;
105# endif // NO_ACTION_ONESHOT
106} split_mods_sync_t;
107#endif // SPLIT_MODS_ENABLE
108
109#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
110typedef struct _rpc_sync_info_t {
111 int8_t transaction_id;
112 uint8_t m2s_length;
113 uint8_t s2m_length;
114} rpc_sync_info_t;
115#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
116
117typedef struct _split_shared_memory_t {
118#ifdef USE_I2C
119 int8_t transaction_id;
120#endif // USE_I2C
121
122 split_slave_matrix_sync_t smatrix;
123
124#ifdef SPLIT_TRANSPORT_MIRROR
125 split_master_matrix_sync_t mmatrix;
126#endif // SPLIT_TRANSPORT_MIRROR
127
128#ifdef ENCODER_ENABLE
129 split_slave_encoder_sync_t encoders;
130#endif // ENCODER_ENABLE
131
132#ifndef DISABLE_SYNC_TIMER
133 uint32_t sync_timer;
134#endif // DISABLE_SYNC_TIMER
135
136#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
137 split_layers_sync_t layers;
138#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
139
140#ifdef SPLIT_LED_STATE_ENABLE
141 uint8_t led_state;
142#endif // SPLIT_LED_STATE_ENABLE
143
144#ifdef SPLIT_MODS_ENABLE
145 split_mods_sync_t mods;
146#endif // SPLIT_MODS_ENABLE
147
148#ifdef BACKLIGHT_ENABLE
149 uint8_t backlight_level;
150#endif // BACKLIGHT_ENABLE
151
152#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
153 rgblight_syncinfo_t rgblight_sync;
154#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
155
156#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
157 led_matrix_sync_t led_matrix_sync;
158#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
159
160#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
161 rgb_matrix_sync_t rgb_matrix_sync;
162#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
163
164#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
165 uint8_t current_wpm;
166#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
167
168#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
169 rpc_sync_info_t rpc_info;
170 uint8_t rpc_m2s_buffer[RPC_M2S_BUFFER_SIZE];
171 uint8_t rpc_s2m_buffer[RPC_S2M_BUFFER_SIZE];
172#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
173} split_shared_memory_t;
174
175extern split_shared_memory_t *const split_shmem; \ No newline at end of file