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.c723
1 files changed, 723 insertions, 0 deletions
diff --git a/quantum/split_common/transactions.c b/quantum/split_common/transactions.c
new file mode 100644
index 000000000..fd676f072
--- /dev/null
+++ b/quantum/split_common/transactions.c
@@ -0,0 +1,723 @@
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 "split_util.h"
27#include "transaction_id_define.h"
28
29#define SYNC_TIMER_OFFSET 2
30
31#ifndef FORCED_SYNC_THROTTLE_MS
32# define FORCED_SYNC_THROTTLE_MS 100
33#endif // FORCED_SYNC_THROTTLE_MS
34
35#define sizeof_member(type, member) sizeof(((type *)NULL)->member)
36
37#define trans_initiator2target_initializer_cb(member, cb) \
38 { &dummy, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), 0, 0, cb }
39#define trans_initiator2target_initializer(member) trans_initiator2target_initializer_cb(member, NULL)
40
41#define trans_target2initiator_initializer_cb(member, cb) \
42 { &dummy, 0, 0, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), cb }
43#define trans_target2initiator_initializer(member) trans_target2initiator_initializer_cb(member, NULL)
44
45#define transport_write(id, data, length) transport_execute_transaction(id, data, length, NULL, 0)
46#define transport_read(id, data, length) transport_execute_transaction(id, NULL, 0, data, length)
47
48#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
49// Forward-declare the RPC callback handlers
50void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
51void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
52#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
53
54////////////////////////////////////////////////////
55// Helpers
56
57static bool transaction_handler_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[], const char *prefix, bool (*handler)(matrix_row_t master_matrix[], matrix_row_t slave_matrix[])) {
58 int num_retries = is_transport_connected() ? 10 : 1;
59 for (int iter = 1; iter <= num_retries; ++iter) {
60 if (iter > 1) {
61 for (int i = 0; i < iter * iter; ++i) {
62 wait_us(10);
63 }
64 }
65 bool this_okay = true;
66 ATOMIC_BLOCK_FORCEON { this_okay = handler(master_matrix, slave_matrix); };
67 if (this_okay) return true;
68 }
69 dprintf("Failed to execute %s\n", prefix);
70 return false;
71}
72
73#define TRANSACTION_HANDLER_MASTER(prefix) \
74 do { \
75 if (!transaction_handler_master(master_matrix, slave_matrix, #prefix, &prefix##_handlers_master)) return false; \
76 } while (0)
77
78#define TRANSACTION_HANDLER_SLAVE(prefix) \
79 do { \
80 ATOMIC_BLOCK_FORCEON { prefix##_handlers_slave(master_matrix, slave_matrix); }; \
81 } while (0)
82
83inline 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) {
84 uint8_t curr_checksum;
85 bool okay = transport_read(trans_id_checksum, &curr_checksum, sizeof(curr_checksum));
86 if (okay && (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || curr_checksum != crc8(equiv_shmem, length))) {
87 okay &= transport_read(trans_id_retrieve, destination, length);
88 okay &= curr_checksum == crc8(equiv_shmem, length);
89 if (okay) {
90 *last_update = timer_read32();
91 }
92 } else {
93 memcpy(destination, equiv_shmem, length);
94 }
95 return okay;
96}
97
98inline static bool send_if_condition(int8_t trans_id, uint32_t *last_update, bool condition, void *source, size_t length) {
99 bool okay = true;
100 if (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || condition) {
101 okay &= transport_write(trans_id, source, length);
102 if (okay) {
103 *last_update = timer_read32();
104 }
105 }
106 return okay;
107}
108
109inline static bool send_if_data_mismatch(int8_t trans_id, uint32_t *last_update, void *source, const void *equiv_shmem, size_t length) {
110 // Just run a memcmp to compare the source and equivalent shmem location
111 return send_if_condition(trans_id, last_update, (memcmp(source, equiv_shmem, length) != 0), source, length);
112}
113
114////////////////////////////////////////////////////
115// Slave matrix
116
117static bool slave_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
118 static uint32_t last_update = 0;
119 static matrix_row_t last_matrix[(MATRIX_ROWS) / 2] = {0}; // last successfully-read matrix, so we can replicate if there are checksum errors
120 matrix_row_t temp_matrix[(MATRIX_ROWS) / 2]; // holding area while we test whether or not checksum is correct
121
122 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));
123 if (okay) {
124 // Checksum matches the received data, save as the last matrix state
125 memcpy(last_matrix, temp_matrix, sizeof(temp_matrix));
126 }
127 // Copy out the last-known-good matrix state to the slave matrix
128 memcpy(slave_matrix, last_matrix, sizeof(last_matrix));
129 return okay;
130}
131
132static void slave_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
133 memcpy(split_shmem->smatrix.matrix, slave_matrix, sizeof(split_shmem->smatrix.matrix));
134 split_shmem->smatrix.checksum = crc8(split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix));
135}
136
137// clang-format off
138#define TRANSACTIONS_SLAVE_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(slave_matrix)
139#define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(slave_matrix)
140#define TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS \
141 [GET_SLAVE_MATRIX_CHECKSUM] = trans_target2initiator_initializer(smatrix.checksum), \
142 [GET_SLAVE_MATRIX_DATA] = trans_target2initiator_initializer(smatrix.matrix),
143// clang-format on
144
145////////////////////////////////////////////////////
146// Master matrix
147
148#ifdef SPLIT_TRANSPORT_MIRROR
149
150static bool master_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
151 static uint32_t last_update = 0;
152 return send_if_data_mismatch(PUT_MASTER_MATRIX, &last_update, master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));
153}
154
155static void master_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
156 // Always copy to the master matrix
157 memcpy(master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));
158}
159
160# define TRANSACTIONS_MASTER_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(master_matrix)
161# define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(master_matrix)
162# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS [PUT_MASTER_MATRIX] = trans_initiator2target_initializer(mmatrix.matrix),
163
164#else // SPLIT_TRANSPORT_MIRROR
165
166# define TRANSACTIONS_MASTER_MATRIX_MASTER()
167# define TRANSACTIONS_MASTER_MATRIX_SLAVE()
168# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS
169
170#endif // SPLIT_TRANSPORT_MIRROR
171
172////////////////////////////////////////////////////
173// Encoders
174
175#ifdef ENCODER_ENABLE
176
177static bool encoder_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
178 static uint32_t last_update = 0;
179 uint8_t temp_state[NUMBER_OF_ENCODERS];
180
181 bool okay = read_if_checksum_mismatch(GET_ENCODERS_CHECKSUM, GET_ENCODERS_DATA, &last_update, temp_state, split_shmem->encoders.state, sizeof(temp_state));
182 if (okay) encoder_update_raw(temp_state);
183 return okay;
184}
185
186static void encoder_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
187 uint8_t encoder_state[NUMBER_OF_ENCODERS];
188 encoder_state_raw(encoder_state);
189 // Always prepare the encoder state for read.
190 memcpy(split_shmem->encoders.state, encoder_state, sizeof(encoder_state));
191 // Now update the checksum given that the encoders has been written to
192 split_shmem->encoders.checksum = crc8(encoder_state, sizeof(encoder_state));
193}
194
195// clang-format off
196# define TRANSACTIONS_ENCODERS_MASTER() TRANSACTION_HANDLER_MASTER(encoder)
197# define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE(encoder)
198# define TRANSACTIONS_ENCODERS_REGISTRATIONS \
199 [GET_ENCODERS_CHECKSUM] = trans_target2initiator_initializer(encoders.checksum), \
200 [GET_ENCODERS_DATA] = trans_target2initiator_initializer(encoders.state),
201// clang-format on
202
203#else // ENCODER_ENABLE
204
205# define TRANSACTIONS_ENCODERS_MASTER()
206# define TRANSACTIONS_ENCODERS_SLAVE()
207# define TRANSACTIONS_ENCODERS_REGISTRATIONS
208
209#endif // ENCODER_ENABLE
210
211////////////////////////////////////////////////////
212// Sync timer
213
214#ifndef DISABLE_SYNC_TIMER
215
216static bool sync_timer_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
217 static uint32_t last_update = 0;
218
219 bool okay = true;
220 if (timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS) {
221 uint32_t sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
222 okay &= transport_write(PUT_SYNC_TIMER, &sync_timer, sizeof(sync_timer));
223 if (okay) {
224 last_update = timer_read32();
225 }
226 }
227 return okay;
228}
229
230static void sync_timer_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
231 static uint32_t last_sync_timer = 0;
232 if (last_sync_timer != split_shmem->sync_timer) {
233 last_sync_timer = split_shmem->sync_timer;
234 sync_timer_update(last_sync_timer);
235 }
236}
237
238# define TRANSACTIONS_SYNC_TIMER_MASTER() TRANSACTION_HANDLER_MASTER(sync_timer)
239# define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE(sync_timer)
240# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS [PUT_SYNC_TIMER] = trans_initiator2target_initializer(sync_timer),
241
242#else // DISABLE_SYNC_TIMER
243
244# define TRANSACTIONS_SYNC_TIMER_MASTER()
245# define TRANSACTIONS_SYNC_TIMER_SLAVE()
246# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS
247
248#endif // DISABLE_SYNC_TIMER
249
250////////////////////////////////////////////////////
251// Layer state
252
253#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
254
255static bool layer_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
256 static uint32_t last_layer_state_update = 0;
257 static uint32_t last_default_layer_state_update = 0;
258
259 bool okay = send_if_condition(PUT_LAYER_STATE, &last_layer_state_update, (layer_state != split_shmem->layers.layer_state), &layer_state, sizeof(layer_state));
260 if (okay) {
261 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));
262 }
263 return okay;
264}
265
266static void layer_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
267 layer_state = split_shmem->layers.layer_state;
268 default_layer_state = split_shmem->layers.default_layer_state;
269}
270
271// clang-format off
272# define TRANSACTIONS_LAYER_STATE_MASTER() TRANSACTION_HANDLER_MASTER(layer_state)
273# define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(layer_state)
274# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS \
275 [PUT_LAYER_STATE] = trans_initiator2target_initializer(layers.layer_state), \
276 [PUT_DEFAULT_LAYER_STATE] = trans_initiator2target_initializer(layers.default_layer_state),
277// clang-format on
278
279#else // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
280
281# define TRANSACTIONS_LAYER_STATE_MASTER()
282# define TRANSACTIONS_LAYER_STATE_SLAVE()
283# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS
284
285#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
286
287////////////////////////////////////////////////////
288// LED state
289
290#ifdef SPLIT_LED_STATE_ENABLE
291
292static bool led_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
293 static uint32_t last_update = 0;
294 uint8_t led_state = host_keyboard_leds();
295 return send_if_data_mismatch(PUT_LED_STATE, &last_update, &led_state, &split_shmem->led_state, sizeof(led_state));
296}
297
298static void led_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
299 void set_split_host_keyboard_leds(uint8_t led_state);
300 set_split_host_keyboard_leds(split_shmem->led_state);
301}
302
303# define TRANSACTIONS_LED_STATE_MASTER() TRANSACTION_HANDLER_MASTER(led_state)
304# define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(led_state)
305# define TRANSACTIONS_LED_STATE_REGISTRATIONS [PUT_LED_STATE] = trans_initiator2target_initializer(led_state),
306
307#else // SPLIT_LED_STATE_ENABLE
308
309# define TRANSACTIONS_LED_STATE_MASTER()
310# define TRANSACTIONS_LED_STATE_SLAVE()
311# define TRANSACTIONS_LED_STATE_REGISTRATIONS
312
313#endif // SPLIT_LED_STATE_ENABLE
314
315////////////////////////////////////////////////////
316// Mods
317
318#ifdef SPLIT_MODS_ENABLE
319
320static bool mods_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
321 static uint32_t last_update = 0;
322 bool mods_need_sync = timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS;
323 split_mods_sync_t new_mods;
324 new_mods.real_mods = get_mods();
325 if (!mods_need_sync && new_mods.real_mods != split_shmem->mods.real_mods) {
326 mods_need_sync = true;
327 }
328
329 new_mods.weak_mods = get_weak_mods();
330 if (!mods_need_sync && new_mods.weak_mods != split_shmem->mods.weak_mods) {
331 mods_need_sync = true;
332 }
333
334# ifndef NO_ACTION_ONESHOT
335 new_mods.oneshot_mods = get_oneshot_mods();
336 if (!mods_need_sync && new_mods.oneshot_mods != split_shmem->mods.oneshot_mods) {
337 mods_need_sync = true;
338 }
339# endif // NO_ACTION_ONESHOT
340
341 bool okay = true;
342 if (mods_need_sync) {
343 okay &= transport_write(PUT_MODS, &new_mods, sizeof(new_mods));
344 if (okay) {
345 last_update = timer_read32();
346 }
347 }
348
349 return okay;
350}
351
352static void mods_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
353 set_mods(split_shmem->mods.real_mods);
354 set_weak_mods(split_shmem->mods.weak_mods);
355# ifndef NO_ACTION_ONESHOT
356 set_oneshot_mods(split_shmem->mods.oneshot_mods);
357# endif
358}
359
360# define TRANSACTIONS_MODS_MASTER() TRANSACTION_HANDLER_MASTER(mods)
361# define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods)
362# define TRANSACTIONS_MODS_REGISTRATIONS [PUT_MODS] = trans_initiator2target_initializer(mods),
363
364#else // SPLIT_MODS_ENABLE
365
366# define TRANSACTIONS_MODS_MASTER()
367# define TRANSACTIONS_MODS_SLAVE()
368# define TRANSACTIONS_MODS_REGISTRATIONS
369
370#endif // SPLIT_MODS_ENABLE
371
372////////////////////////////////////////////////////
373// Backlight
374
375#ifdef BACKLIGHT_ENABLE
376
377static bool backlight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
378 static uint32_t last_update = 0;
379 uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0;
380 return send_if_condition(PUT_BACKLIGHT, &last_update, (level != split_shmem->backlight_level), &level, sizeof(level));
381}
382
383static void backlight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { backlight_set(split_shmem->backlight_level); }
384
385# define TRANSACTIONS_BACKLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(backlight)
386# define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight)
387# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS [PUT_BACKLIGHT] = trans_initiator2target_initializer(backlight_level),
388
389#else // BACKLIGHT_ENABLE
390
391# define TRANSACTIONS_BACKLIGHT_MASTER()
392# define TRANSACTIONS_BACKLIGHT_SLAVE()
393# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS
394
395#endif // BACKLIGHT_ENABLE
396
397////////////////////////////////////////////////////
398// RGBLIGHT
399
400#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
401
402static bool rgblight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
403 static uint32_t last_update = 0;
404 rgblight_syncinfo_t rgblight_sync;
405 rgblight_get_syncinfo(&rgblight_sync);
406 if (send_if_condition(PUT_RGBLIGHT, &last_update, (rgblight_sync.status.change_flags != 0), &rgblight_sync, sizeof(rgblight_sync))) {
407 rgblight_clear_change_flags();
408 } else {
409 return false;
410 }
411 return true;
412}
413
414static void rgblight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
415 // Update the RGB with the new data
416 if (split_shmem->rgblight_sync.status.change_flags != 0) {
417 rgblight_update_sync(&split_shmem->rgblight_sync, false);
418 split_shmem->rgblight_sync.status.change_flags = 0;
419 }
420}
421
422# define TRANSACTIONS_RGBLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(rgblight)
423# define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight)
424# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS [PUT_RGBLIGHT] = trans_initiator2target_initializer(rgblight_sync),
425
426#else // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
427
428# define TRANSACTIONS_RGBLIGHT_MASTER()
429# define TRANSACTIONS_RGBLIGHT_SLAVE()
430# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS
431
432#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
433
434////////////////////////////////////////////////////
435// LED Matrix
436
437#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
438
439static bool led_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
440 static uint32_t last_update = 0;
441 led_matrix_sync_t led_matrix_sync;
442 memcpy(&led_matrix_sync.led_matrix, &led_matrix_eeconfig, sizeof(led_eeconfig_t));
443 led_matrix_sync.led_suspend_state = led_matrix_get_suspend_state();
444 return send_if_data_mismatch(PUT_LED_MATRIX, &last_update, &led_matrix_sync, &split_shmem->led_matrix_sync, sizeof(led_matrix_sync));
445}
446
447static void led_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
448 memcpy(&led_matrix_eeconfig, &split_shmem->led_matrix_sync.led_matrix, sizeof(led_eeconfig_t));
449 led_matrix_set_suspend_state(split_shmem->led_matrix_sync.led_suspend_state);
450}
451
452# define TRANSACTIONS_LED_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(led_matrix)
453# define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix)
454# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS [PUT_LED_MATRIX] = trans_initiator2target_initializer(led_matrix_sync),
455
456#else // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
457
458# define TRANSACTIONS_LED_MATRIX_MASTER()
459# define TRANSACTIONS_LED_MATRIX_SLAVE()
460# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS
461
462#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
463
464////////////////////////////////////////////////////
465// RGB Matrix
466
467#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
468
469static bool rgb_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
470 static uint32_t last_update = 0;
471 rgb_matrix_sync_t rgb_matrix_sync;
472 memcpy(&rgb_matrix_sync.rgb_matrix, &rgb_matrix_config, sizeof(rgb_config_t));
473 rgb_matrix_sync.rgb_suspend_state = rgb_matrix_get_suspend_state();
474 return send_if_data_mismatch(PUT_RGB_MATRIX, &last_update, &rgb_matrix_sync, &split_shmem->rgb_matrix_sync, sizeof(rgb_matrix_sync));
475}
476
477static void rgb_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
478 memcpy(&rgb_matrix_config, &split_shmem->rgb_matrix_sync.rgb_matrix, sizeof(rgb_config_t));
479 rgb_matrix_set_suspend_state(split_shmem->rgb_matrix_sync.rgb_suspend_state);
480}
481
482# define TRANSACTIONS_RGB_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(rgb_matrix)
483# define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix)
484# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS [PUT_RGB_MATRIX] = trans_initiator2target_initializer(rgb_matrix_sync),
485
486#else // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
487
488# define TRANSACTIONS_RGB_MATRIX_MASTER()
489# define TRANSACTIONS_RGB_MATRIX_SLAVE()
490# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS
491
492#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
493
494////////////////////////////////////////////////////
495// WPM
496
497#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
498
499static bool wpm_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
500 static uint32_t last_update = 0;
501 uint8_t current_wpm = get_current_wpm();
502 return send_if_condition(PUT_WPM, &last_update, (current_wpm != split_shmem->current_wpm), &current_wpm, sizeof(current_wpm));
503}
504
505static void wpm_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { set_current_wpm(split_shmem->current_wpm); }
506
507# define TRANSACTIONS_WPM_MASTER() TRANSACTION_HANDLER_MASTER(wpm)
508# define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE(wpm)
509# define TRANSACTIONS_WPM_REGISTRATIONS [PUT_WPM] = trans_initiator2target_initializer(current_wpm),
510
511#else // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
512
513# define TRANSACTIONS_WPM_MASTER()
514# define TRANSACTIONS_WPM_SLAVE()
515# define TRANSACTIONS_WPM_REGISTRATIONS
516
517#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
518
519////////////////////////////////////////////////////
520// OLED
521
522#if defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
523
524static bool oled_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
525 static uint32_t last_update = 0;
526 bool current_oled_state = is_oled_on();
527 return send_if_condition(PUT_OLED, &last_update, (current_oled_state != split_shmem->current_oled_state), &current_oled_state, sizeof(current_oled_state));
528}
529
530static void oled_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
531 if (split_shmem->current_oled_state) {
532 oled_on();
533 } else {
534 oled_off();
535 }
536}
537
538# define TRANSACTIONS_OLED_MASTER() TRANSACTION_HANDLER_MASTER(oled)
539# define TRANSACTIONS_OLED_SLAVE() TRANSACTION_HANDLER_SLAVE(oled)
540# define TRANSACTIONS_OLED_REGISTRATIONS [PUT_OLED] = trans_initiator2target_initializer(current_oled_state),
541
542#else // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
543
544# define TRANSACTIONS_OLED_MASTER()
545# define TRANSACTIONS_OLED_SLAVE()
546# define TRANSACTIONS_OLED_REGISTRATIONS
547
548#endif // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
549
550////////////////////////////////////////////////////
551// ST7565
552
553#if defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
554
555static bool st7565_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
556 static uint32_t last_update = 0;
557 bool current_st7565_state = st7565_is_on();
558 return send_if_condition(PUT_ST7565, &last_update, (current_st7565_state != split_shmem->current_st7565_state), &current_st7565_state, sizeof(current_st7565_state));
559}
560
561static void st7565_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
562 if (split_shmem->current_st7565_state) {
563 st7565_on();
564 } else {
565 st7565_off();
566 }
567}
568
569# define TRANSACTIONS_ST7565_MASTER() TRANSACTION_HANDLER_MASTER(st7565)
570# define TRANSACTIONS_ST7565_SLAVE() TRANSACTION_HANDLER_SLAVE(st7565)
571# define TRANSACTIONS_ST7565_REGISTRATIONS [PUT_ST7565] = trans_initiator2target_initializer(current_st7565_state),
572
573#else // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
574
575# define TRANSACTIONS_ST7565_MASTER()
576# define TRANSACTIONS_ST7565_SLAVE()
577# define TRANSACTIONS_ST7565_REGISTRATIONS
578
579#endif // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
580
581////////////////////////////////////////////////////
582
583uint8_t dummy;
584split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = {
585 // Set defaults
586 [0 ...(NUM_TOTAL_TRANSACTIONS - 1)] = {NULL, 0, 0, 0, 0, 0},
587
588#ifdef USE_I2C
589 [I2C_EXECUTE_CALLBACK] = trans_initiator2target_initializer(transaction_id),
590#endif // USE_I2C
591
592 // clang-format off
593 TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS
594 TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS
595 TRANSACTIONS_ENCODERS_REGISTRATIONS
596 TRANSACTIONS_SYNC_TIMER_REGISTRATIONS
597 TRANSACTIONS_LAYER_STATE_REGISTRATIONS
598 TRANSACTIONS_LED_STATE_REGISTRATIONS
599 TRANSACTIONS_MODS_REGISTRATIONS
600 TRANSACTIONS_BACKLIGHT_REGISTRATIONS
601 TRANSACTIONS_RGBLIGHT_REGISTRATIONS
602 TRANSACTIONS_LED_MATRIX_REGISTRATIONS
603 TRANSACTIONS_RGB_MATRIX_REGISTRATIONS
604 TRANSACTIONS_WPM_REGISTRATIONS
605 TRANSACTIONS_OLED_REGISTRATIONS
606 TRANSACTIONS_ST7565_REGISTRATIONS
607// clang-format on
608
609#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
610 [PUT_RPC_INFO] = trans_initiator2target_initializer_cb(rpc_info, slave_rpc_info_callback),
611 [PUT_RPC_REQ_DATA] = trans_initiator2target_initializer(rpc_m2s_buffer),
612 [EXECUTE_RPC] = trans_initiator2target_initializer_cb(rpc_info.transaction_id, slave_rpc_exec_callback),
613 [GET_RPC_RESP_DATA] = trans_target2initiator_initializer(rpc_s2m_buffer),
614#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
615};
616
617bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
618 TRANSACTIONS_SLAVE_MATRIX_MASTER();
619 TRANSACTIONS_MASTER_MATRIX_MASTER();
620 TRANSACTIONS_ENCODERS_MASTER();
621 TRANSACTIONS_SYNC_TIMER_MASTER();
622 TRANSACTIONS_LAYER_STATE_MASTER();
623 TRANSACTIONS_LED_STATE_MASTER();
624 TRANSACTIONS_MODS_MASTER();
625 TRANSACTIONS_BACKLIGHT_MASTER();
626 TRANSACTIONS_RGBLIGHT_MASTER();
627 TRANSACTIONS_LED_MATRIX_MASTER();
628 TRANSACTIONS_RGB_MATRIX_MASTER();
629 TRANSACTIONS_WPM_MASTER();
630 TRANSACTIONS_OLED_MASTER();
631 TRANSACTIONS_ST7565_MASTER();
632 return true;
633}
634
635void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
636 TRANSACTIONS_SLAVE_MATRIX_SLAVE();
637 TRANSACTIONS_MASTER_MATRIX_SLAVE();
638 TRANSACTIONS_ENCODERS_SLAVE();
639 TRANSACTIONS_SYNC_TIMER_SLAVE();
640 TRANSACTIONS_LAYER_STATE_SLAVE();
641 TRANSACTIONS_LED_STATE_SLAVE();
642 TRANSACTIONS_MODS_SLAVE();
643 TRANSACTIONS_BACKLIGHT_SLAVE();
644 TRANSACTIONS_RGBLIGHT_SLAVE();
645 TRANSACTIONS_LED_MATRIX_SLAVE();
646 TRANSACTIONS_RGB_MATRIX_SLAVE();
647 TRANSACTIONS_WPM_SLAVE();
648 TRANSACTIONS_OLED_SLAVE();
649 TRANSACTIONS_ST7565_SLAVE();
650}
651
652#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
653
654void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback) {
655 // Prevent invoking RPC on QMK core sync data
656 if (transaction_id <= GET_RPC_RESP_DATA) return;
657
658 // Set the callback
659 split_transaction_table[transaction_id].slave_callback = callback;
660 split_transaction_table[transaction_id].initiator2target_offset = offsetof(split_shared_memory_t, rpc_m2s_buffer);
661 split_transaction_table[transaction_id].target2initiator_offset = offsetof(split_shared_memory_t, rpc_s2m_buffer);
662}
663
664bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
665 // Prevent transaction attempts while transport is disconnected
666 if (!is_transport_connected()) {
667 return false;
668 }
669 // Prevent invoking RPC on QMK core sync data
670 if (transaction_id <= GET_RPC_RESP_DATA) return false;
671 // Prevent sizing issues
672 if (initiator2target_buffer_size > RPC_M2S_BUFFER_SIZE) return false;
673 if (target2initiator_buffer_size > RPC_S2M_BUFFER_SIZE) return false;
674
675 // Prepare the metadata block
676 rpc_sync_info_t info = {.transaction_id = transaction_id, .m2s_length = initiator2target_buffer_size, .s2m_length = target2initiator_buffer_size};
677
678 // Make sure the local side knows that we're not sending the full block of data
679 split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = initiator2target_buffer_size;
680 split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = target2initiator_buffer_size;
681
682 // Run through the sequence:
683 // * set the transaction ID and lengths
684 // * send the request data
685 // * execute RPC callback
686 // * retrieve the response data
687 if (!transport_write(PUT_RPC_INFO, &info, sizeof(info))) {
688 return false;
689 }
690 if (!transport_write(PUT_RPC_REQ_DATA, initiator2target_buffer, initiator2target_buffer_size)) {
691 return false;
692 }
693 if (!transport_write(EXECUTE_RPC, &transaction_id, sizeof(transaction_id))) {
694 return false;
695 }
696 if (!transport_read(GET_RPC_RESP_DATA, target2initiator_buffer, target2initiator_buffer_size)) {
697 return false;
698 }
699 return true;
700}
701
702void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
703 // The RPC info block contains the intended transaction ID, as well as the sizes for both inbound and outbound data.
704 // Ignore the args -- the `split_shmem` already has the info, we just need to act upon it.
705 // We must keep the `split_transaction_table` non-const, so that it is able to be modified at runtime.
706
707 split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = split_shmem->rpc_info.m2s_length;
708 split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = split_shmem->rpc_info.s2m_length;
709}
710
711void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
712 // We can assume that the buffer lengths are correctly set, now, given that sequentially the rpc_info callback was already executed.
713 // Go through the rpc_info and execute _that_ transaction's callback, with the scratch buffers as inputs.
714 int8_t transaction_id = split_shmem->rpc_info.transaction_id;
715 if (transaction_id < NUM_TOTAL_TRANSACTIONS) {
716 split_transaction_desc_t *trans = &split_transaction_table[transaction_id];
717 if (trans->slave_callback) {
718 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);
719 }
720 }
721}
722
723#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)