aboutsummaryrefslogtreecommitdiff
path: root/quantum/split_common/transactions.c
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/split_common/transactions.c')
-rw-r--r--quantum/split_common/transactions.c655
1 files changed, 655 insertions, 0 deletions
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)