transactions.c
44775 bytes
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 <stdint.h>
18#include <string.h>
19#include <stddef.h>
20
21#include "crc.h"
22#include "debug.h"
23#include "matrix.h"
24#include "host.h"
25#include "action_util.h"
26#include "sync_timer.h"
27#include "wait.h"
28#include "transactions.h"
29#include "transport.h"
30#include "transaction_id_define.h"
31#include "split_util.h"
32#include "synchronization_util.h"
33
34#ifdef BACKLIGHT_ENABLE
35# include "backlight.h"
36#endif
37#ifdef RGBLIGHT_ENABLE
38# include "rgblight.h"
39#endif
40#ifdef LED_MATRIX_ENABLE
41# include "led_matrix.h"
42#endif
43#ifdef RGB_MATRIX_ENABLE
44# include "rgb_matrix.h"
45#endif
46#ifdef OLED_ENABLE
47# include "oled_driver.h"
48#endif
49#ifdef ST7565_ENABLE
50# include "st7565.h"
51#endif
52#ifdef ENCODER_ENABLE
53# include "encoder.h"
54#endif
55#ifdef HAPTIC_ENABLE
56# include "haptic.h"
57#endif
58#ifdef POINTING_DEVICE_ENABLE
59# include "pointing_device.h"
60#endif
61#ifdef OS_DETECTION_ENABLE
62# include "os_detection.h"
63#endif
64#ifdef WPM_ENABLE
65# include "wpm.h"
66#endif
67
68#define SYNC_TIMER_OFFSET 2
69
70#ifndef FORCED_SYNC_THROTTLE_MS
71# define FORCED_SYNC_THROTTLE_MS 100
72#endif // FORCED_SYNC_THROTTLE_MS
73
74#define sizeof_member(type, member) sizeof(((type *)NULL)->member)
75
76#define trans_initiator2target_initializer_cb(member, cb) \
77 { sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), 0, 0, cb }
78#define trans_initiator2target_initializer(member) trans_initiator2target_initializer_cb(member, NULL)
79
80#define trans_target2initiator_initializer_cb(member, cb) \
81 { 0, 0, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), cb }
82#define trans_target2initiator_initializer(member) trans_target2initiator_initializer_cb(member, NULL)
83
84#define trans_initiator2target_cb(cb) \
85 { 0, 0, 0, 0, cb }
86
87#define transport_write(id, data, length) transport_execute_transaction(id, data, length, NULL, 0)
88#define transport_read(id, data, length) transport_execute_transaction(id, NULL, 0, data, length)
89#define transport_exec(id) transport_execute_transaction(id, NULL, 0, NULL, 0)
90
91#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
92// Forward-declare the RPC callback handlers
93void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
94void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
95#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
96
97////////////////////////////////////////////////////
98// Helpers
99
100static 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[])) {
101 int num_retries = is_transport_connected() ? 10 : 1;
102 for (int iter = 1; iter <= num_retries; ++iter) {
103 if (iter > 1) {
104 for (int i = 0; i < iter * iter; ++i) {
105 wait_us(10);
106 }
107 }
108 bool this_okay = true;
109 this_okay = handler(master_matrix, slave_matrix);
110 if (this_okay) return true;
111 }
112 dprintf("Failed to execute %s\n", prefix);
113 return false;
114}
115
116#define TRANSACTION_HANDLER_MASTER(prefix) \
117 do { \
118 if (!transaction_handler_master(master_matrix, slave_matrix, #prefix, &prefix##_handlers_master)) return false; \
119 } while (0)
120
121/**
122 * @brief Constructs a transaction handler that doesn't acquire a lock to the
123 * split shared memory. Therefore the locking and unlocking has to be done
124 * manually inside the handler. Use this macro only if the handler is
125 * non-deterministic in runtime and thus needs a manual lock unlock
126 * implementation to hold the lock for the shortest possible time.
127 */
128#define TRANSACTION_HANDLER_SLAVE(prefix) \
129 do { \
130 prefix##_handlers_slave(master_matrix, slave_matrix); \
131 } while (0)
132
133/**
134 * @brief Constructs a transaction handler that automatically acquires a lock to
135 * safely access the split shared memory and releases the lock again after
136 * processing the handler. Use this macro if the handler is fast and
137 * deterministic in runtime and thus holds the lock only for a very short time.
138 * If not fallback to manually locking and unlocking inside the handler.
139 */
140#define TRANSACTION_HANDLER_SLAVE_AUTOLOCK(prefix) \
141 do { \
142 split_shared_memory_lock(); \
143 prefix##_handlers_slave(master_matrix, slave_matrix); \
144 split_shared_memory_unlock(); \
145 } while (0)
146
147inline 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) {
148 uint8_t curr_checksum;
149 bool okay = transport_read(trans_id_checksum, &curr_checksum, sizeof(curr_checksum));
150 if (okay && (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || curr_checksum != crc8(equiv_shmem, length))) {
151 okay &= transport_read(trans_id_retrieve, destination, length);
152 okay &= curr_checksum == crc8(equiv_shmem, length);
153 if (okay) {
154 *last_update = timer_read32();
155 }
156 } else {
157 memcpy(destination, equiv_shmem, length);
158 }
159 return okay;
160}
161
162inline static bool send_if_condition(int8_t trans_id, uint32_t *last_update, bool condition, void *source, size_t length) {
163 bool okay = true;
164 if (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || condition) {
165 okay &= transport_write(trans_id, source, length);
166 if (okay) {
167 *last_update = timer_read32();
168 }
169 }
170 return okay;
171}
172
173inline static bool send_if_data_mismatch(int8_t trans_id, uint32_t *last_update, void *source, const void *equiv_shmem, size_t length) {
174 // Just run a memcmp to compare the source and equivalent shmem location
175 return send_if_condition(trans_id, last_update, (memcmp(source, equiv_shmem, length) != 0), source, length);
176}
177
178////////////////////////////////////////////////////
179// Slave matrix
180
181static bool slave_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
182 static uint32_t last_update = 0;
183 static matrix_row_t last_matrix[(MATRIX_ROWS) / 2] = {0}; // last successfully-read matrix, so we can replicate if there are checksum errors
184 matrix_row_t temp_matrix[(MATRIX_ROWS) / 2]; // holding area while we test whether or not checksum is correct
185
186 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));
187 if (okay) {
188 // Checksum matches the received data, save as the last matrix state
189 memcpy(last_matrix, temp_matrix, sizeof(temp_matrix));
190 }
191 // Copy out the last-known-good matrix state to the slave matrix
192 memcpy(slave_matrix, last_matrix, sizeof(last_matrix));
193 return okay;
194}
195
196static void slave_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
197 memcpy(split_shmem->smatrix.matrix, slave_matrix, sizeof(split_shmem->smatrix.matrix));
198 split_shmem->smatrix.checksum = crc8(split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix));
199}
200
201// clang-format off
202#define TRANSACTIONS_SLAVE_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(slave_matrix)
203#define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(slave_matrix)
204#define TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS \
205 [GET_SLAVE_MATRIX_CHECKSUM] = trans_target2initiator_initializer(smatrix.checksum), \
206 [GET_SLAVE_MATRIX_DATA] = trans_target2initiator_initializer(smatrix.matrix),
207// clang-format on
208
209////////////////////////////////////////////////////
210// Master matrix
211
212#ifdef SPLIT_TRANSPORT_MIRROR
213
214static bool master_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
215 static uint32_t last_update = 0;
216 return send_if_data_mismatch(PUT_MASTER_MATRIX, &last_update, master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));
217}
218
219static void master_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
220 // Always copy to the master matrix
221 memcpy(master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));
222}
223
224# define TRANSACTIONS_MASTER_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(master_matrix)
225# define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(master_matrix)
226# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS [PUT_MASTER_MATRIX] = trans_initiator2target_initializer(mmatrix.matrix),
227
228#else // SPLIT_TRANSPORT_MIRROR
229
230# define TRANSACTIONS_MASTER_MATRIX_MASTER()
231# define TRANSACTIONS_MASTER_MATRIX_SLAVE()
232# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS
233
234#endif // SPLIT_TRANSPORT_MIRROR
235
236////////////////////////////////////////////////////
237// Encoders
238
239#ifdef ENCODER_ENABLE
240
241static bool encoder_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
242 static uint32_t last_update = 0;
243 static uint8_t last_checksum = 0;
244 encoder_events_t temp_events;
245
246 bool okay = read_if_checksum_mismatch(GET_ENCODERS_CHECKSUM, GET_ENCODERS_DATA, &last_update, &temp_events, &split_shmem->encoders.events, sizeof(temp_events));
247 if (okay) {
248 if (last_checksum != split_shmem->encoders.checksum) {
249 bool actioned = false;
250 uint8_t index;
251 bool clockwise;
252 while (okay && encoder_dequeue_event_advanced(&split_shmem->encoders.events, &index, &clockwise)) {
253 okay &= encoder_queue_event(index, clockwise);
254 actioned = true;
255 }
256
257 if (actioned) {
258 okay &= transport_exec(CMD_ENCODER_DRAIN);
259 }
260 last_checksum = split_shmem->encoders.checksum;
261 }
262 }
263 return okay;
264}
265
266static void encoder_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
267 // Always prepare the encoder state for read.
268 encoder_retrieve_events(&split_shmem->encoders.events);
269 // Now update the checksum given that the encoders has been written to
270 split_shmem->encoders.checksum = crc8(&split_shmem->encoders.events, sizeof(split_shmem->encoders.events));
271}
272
273static void encoder_handlers_slave_drain(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
274 encoder_signal_queue_drain();
275}
276
277// clang-format off
278# define TRANSACTIONS_ENCODERS_MASTER() TRANSACTION_HANDLER_MASTER(encoder)
279# define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(encoder)
280# define TRANSACTIONS_ENCODERS_REGISTRATIONS \
281 [GET_ENCODERS_CHECKSUM] = trans_target2initiator_initializer(encoders.checksum), \
282 [GET_ENCODERS_DATA] = trans_target2initiator_initializer(encoders.events), \
283 [CMD_ENCODER_DRAIN] = trans_initiator2target_cb(encoder_handlers_slave_drain),
284// clang-format on
285
286#else // ENCODER_ENABLE
287
288# define TRANSACTIONS_ENCODERS_MASTER()
289# define TRANSACTIONS_ENCODERS_SLAVE()
290# define TRANSACTIONS_ENCODERS_REGISTRATIONS
291
292#endif // ENCODER_ENABLE
293
294////////////////////////////////////////////////////
295// Sync timer
296
297#ifndef DISABLE_SYNC_TIMER
298
299static bool sync_timer_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
300 static uint32_t last_update = 0;
301
302 bool okay = true;
303 if (timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS) {
304 uint32_t sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
305 okay &= transport_write(PUT_SYNC_TIMER, &sync_timer, sizeof(sync_timer));
306 if (okay) {
307 last_update = timer_read32();
308 }
309 }
310 return okay;
311}
312
313static void sync_timer_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
314 static uint32_t last_sync_timer = 0;
315 if (last_sync_timer != split_shmem->sync_timer) {
316 last_sync_timer = split_shmem->sync_timer;
317 sync_timer_update(last_sync_timer);
318 }
319}
320
321# define TRANSACTIONS_SYNC_TIMER_MASTER() TRANSACTION_HANDLER_MASTER(sync_timer)
322# define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(sync_timer)
323# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS [PUT_SYNC_TIMER] = trans_initiator2target_initializer(sync_timer),
324
325#else // DISABLE_SYNC_TIMER
326
327# define TRANSACTIONS_SYNC_TIMER_MASTER()
328# define TRANSACTIONS_SYNC_TIMER_SLAVE()
329# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS
330
331#endif // DISABLE_SYNC_TIMER
332
333////////////////////////////////////////////////////
334// Layer state
335
336#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
337
338static bool layer_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
339 static uint32_t last_layer_state_update = 0;
340 static uint32_t last_default_layer_state_update = 0;
341
342 bool okay = send_if_condition(PUT_LAYER_STATE, &last_layer_state_update, (layer_state != split_shmem->layers.layer_state), &layer_state, sizeof(layer_state));
343 if (okay) {
344 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));
345 }
346 return okay;
347}
348
349static void layer_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
350 layer_state = split_shmem->layers.layer_state;
351 default_layer_state = split_shmem->layers.default_layer_state;
352}
353
354// clang-format off
355# define TRANSACTIONS_LAYER_STATE_MASTER() TRANSACTION_HANDLER_MASTER(layer_state)
356# define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(layer_state)
357# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS \
358 [PUT_LAYER_STATE] = trans_initiator2target_initializer(layers.layer_state), \
359 [PUT_DEFAULT_LAYER_STATE] = trans_initiator2target_initializer(layers.default_layer_state),
360// clang-format on
361
362#else // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
363
364# define TRANSACTIONS_LAYER_STATE_MASTER()
365# define TRANSACTIONS_LAYER_STATE_SLAVE()
366# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS
367
368#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
369
370////////////////////////////////////////////////////
371// LED state
372
373#ifdef SPLIT_LED_STATE_ENABLE
374
375static bool led_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
376 static uint32_t last_update = 0;
377 uint8_t led_state = host_keyboard_leds();
378 return send_if_data_mismatch(PUT_LED_STATE, &last_update, &led_state, &split_shmem->led_state, sizeof(led_state));
379}
380
381static void led_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
382 void set_split_host_keyboard_leds(uint8_t led_state);
383 set_split_host_keyboard_leds(split_shmem->led_state);
384}
385
386# define TRANSACTIONS_LED_STATE_MASTER() TRANSACTION_HANDLER_MASTER(led_state)
387# define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(led_state)
388# define TRANSACTIONS_LED_STATE_REGISTRATIONS [PUT_LED_STATE] = trans_initiator2target_initializer(led_state),
389
390#else // SPLIT_LED_STATE_ENABLE
391
392# define TRANSACTIONS_LED_STATE_MASTER()
393# define TRANSACTIONS_LED_STATE_SLAVE()
394# define TRANSACTIONS_LED_STATE_REGISTRATIONS
395
396#endif // SPLIT_LED_STATE_ENABLE
397
398////////////////////////////////////////////////////
399// Mods
400
401#ifdef SPLIT_MODS_ENABLE
402
403static bool mods_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
404 static uint32_t last_update = 0;
405 bool mods_need_sync = timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS;
406 split_mods_sync_t new_mods;
407 new_mods.real_mods = get_mods();
408 if (!mods_need_sync && new_mods.real_mods != split_shmem->mods.real_mods) {
409 mods_need_sync = true;
410 }
411
412 new_mods.weak_mods = get_weak_mods();
413 if (!mods_need_sync && new_mods.weak_mods != split_shmem->mods.weak_mods) {
414 mods_need_sync = true;
415 }
416
417# ifndef NO_ACTION_ONESHOT
418 new_mods.oneshot_mods = get_oneshot_mods();
419 if (!mods_need_sync && new_mods.oneshot_mods != split_shmem->mods.oneshot_mods) {
420 mods_need_sync = true;
421 }
422 new_mods.oneshot_locked_mods = get_oneshot_locked_mods();
423 if (!mods_need_sync && new_mods.oneshot_locked_mods != split_shmem->mods.oneshot_locked_mods) {
424 mods_need_sync = true;
425 }
426# endif // NO_ACTION_ONESHOT
427
428 bool okay = true;
429 if (mods_need_sync) {
430 okay &= transport_write(PUT_MODS, &new_mods, sizeof(new_mods));
431 if (okay) {
432 last_update = timer_read32();
433 }
434 }
435
436 return okay;
437}
438
439static void mods_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
440 split_shared_memory_lock();
441 split_mods_sync_t mods;
442 memcpy(&mods, &split_shmem->mods, sizeof(split_mods_sync_t));
443 split_shared_memory_unlock();
444
445 set_mods(mods.real_mods);
446 set_weak_mods(mods.weak_mods);
447# ifndef NO_ACTION_ONESHOT
448 set_oneshot_mods(mods.oneshot_mods);
449 set_oneshot_locked_mods(mods.oneshot_locked_mods);
450# endif
451}
452
453# define TRANSACTIONS_MODS_MASTER() TRANSACTION_HANDLER_MASTER(mods)
454# define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods)
455# define TRANSACTIONS_MODS_REGISTRATIONS [PUT_MODS] = trans_initiator2target_initializer(mods),
456
457#else // SPLIT_MODS_ENABLE
458
459# define TRANSACTIONS_MODS_MASTER()
460# define TRANSACTIONS_MODS_SLAVE()
461# define TRANSACTIONS_MODS_REGISTRATIONS
462
463#endif // SPLIT_MODS_ENABLE
464
465////////////////////////////////////////////////////
466// Backlight
467
468#ifdef BACKLIGHT_ENABLE
469
470static bool backlight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
471 static uint32_t last_update = 0;
472 uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0;
473 return send_if_condition(PUT_BACKLIGHT, &last_update, (level != split_shmem->backlight_level), &level, sizeof(level));
474}
475
476static void backlight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
477 split_shared_memory_lock();
478 uint8_t backlight_level = split_shmem->backlight_level;
479 split_shared_memory_unlock();
480
481 backlight_level_noeeprom(backlight_level);
482}
483
484# define TRANSACTIONS_BACKLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(backlight)
485# define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight)
486# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS [PUT_BACKLIGHT] = trans_initiator2target_initializer(backlight_level),
487
488#else // BACKLIGHT_ENABLE
489
490# define TRANSACTIONS_BACKLIGHT_MASTER()
491# define TRANSACTIONS_BACKLIGHT_SLAVE()
492# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS
493
494#endif // BACKLIGHT_ENABLE
495
496////////////////////////////////////////////////////
497// RGBLIGHT
498
499#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
500
501static bool rgblight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
502 static uint32_t last_update = 0;
503 rgblight_syncinfo_t rgblight_sync;
504 rgblight_get_syncinfo(&rgblight_sync);
505 if (send_if_condition(PUT_RGBLIGHT, &last_update, (rgblight_sync.status.change_flags != 0), &rgblight_sync, sizeof(rgblight_sync))) {
506 rgblight_clear_change_flags();
507 } else {
508 return false;
509 }
510 return true;
511}
512
513static void rgblight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
514 split_shared_memory_lock();
515 // Update the RGB with the new data
516 rgblight_syncinfo_t rgblight_sync;
517 memcpy(&rgblight_sync, &split_shmem->rgblight_sync, sizeof(rgblight_syncinfo_t));
518 split_shmem->rgblight_sync.status.change_flags = 0;
519 split_shared_memory_unlock();
520
521 if (rgblight_sync.status.change_flags != 0) {
522 rgblight_update_sync(&rgblight_sync, false);
523 }
524}
525
526# define TRANSACTIONS_RGBLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(rgblight)
527# define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight)
528# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS [PUT_RGBLIGHT] = trans_initiator2target_initializer(rgblight_sync),
529
530#else // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
531
532# define TRANSACTIONS_RGBLIGHT_MASTER()
533# define TRANSACTIONS_RGBLIGHT_SLAVE()
534# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS
535
536#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
537
538////////////////////////////////////////////////////
539// LED Matrix
540
541#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
542
543static bool led_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
544 static uint32_t last_update = 0;
545 led_matrix_sync_t led_matrix_sync;
546 memcpy(&led_matrix_sync.led_matrix, &led_matrix_eeconfig, sizeof(led_eeconfig_t));
547 led_matrix_sync.led_suspend_state = led_matrix_get_suspend_state();
548 return send_if_data_mismatch(PUT_LED_MATRIX, &last_update, &led_matrix_sync, &split_shmem->led_matrix_sync, sizeof(led_matrix_sync));
549}
550
551static void led_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
552 split_shared_memory_lock();
553 memcpy(&led_matrix_eeconfig, &split_shmem->led_matrix_sync.led_matrix, sizeof(led_eeconfig_t));
554 bool led_suspend_state = split_shmem->led_matrix_sync.led_suspend_state;
555 split_shared_memory_unlock();
556
557 led_matrix_set_suspend_state(led_suspend_state);
558}
559
560# define TRANSACTIONS_LED_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(led_matrix)
561# define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix)
562# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS [PUT_LED_MATRIX] = trans_initiator2target_initializer(led_matrix_sync),
563
564#else // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
565
566# define TRANSACTIONS_LED_MATRIX_MASTER()
567# define TRANSACTIONS_LED_MATRIX_SLAVE()
568# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS
569
570#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
571
572////////////////////////////////////////////////////
573// RGB Matrix
574
575#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
576
577static bool rgb_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
578 static uint32_t last_update = 0;
579 rgb_matrix_sync_t rgb_matrix_sync;
580 memcpy(&rgb_matrix_sync.rgb_matrix, &rgb_matrix_config, sizeof(rgb_config_t));
581 rgb_matrix_sync.rgb_suspend_state = rgb_matrix_get_suspend_state();
582 return send_if_data_mismatch(PUT_RGB_MATRIX, &last_update, &rgb_matrix_sync, &split_shmem->rgb_matrix_sync, sizeof(rgb_matrix_sync));
583}
584
585static void rgb_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
586 split_shared_memory_lock();
587 memcpy(&rgb_matrix_config, &split_shmem->rgb_matrix_sync.rgb_matrix, sizeof(rgb_config_t));
588 bool rgb_suspend_state = split_shmem->rgb_matrix_sync.rgb_suspend_state;
589 split_shared_memory_unlock();
590
591 rgb_matrix_set_suspend_state(rgb_suspend_state);
592}
593
594# define TRANSACTIONS_RGB_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(rgb_matrix)
595# define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix)
596# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS [PUT_RGB_MATRIX] = trans_initiator2target_initializer(rgb_matrix_sync),
597
598#else // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
599
600# define TRANSACTIONS_RGB_MATRIX_MASTER()
601# define TRANSACTIONS_RGB_MATRIX_SLAVE()
602# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS
603
604#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
605
606////////////////////////////////////////////////////
607// WPM
608
609#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
610
611static bool wpm_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
612 static uint32_t last_update = 0;
613 uint8_t current_wpm = get_current_wpm();
614 return send_if_condition(PUT_WPM, &last_update, (current_wpm != split_shmem->current_wpm), ¤t_wpm, sizeof(current_wpm));
615}
616
617static void wpm_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
618 set_current_wpm(split_shmem->current_wpm);
619}
620
621# define TRANSACTIONS_WPM_MASTER() TRANSACTION_HANDLER_MASTER(wpm)
622# define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(wpm)
623# define TRANSACTIONS_WPM_REGISTRATIONS [PUT_WPM] = trans_initiator2target_initializer(current_wpm),
624
625#else // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
626
627# define TRANSACTIONS_WPM_MASTER()
628# define TRANSACTIONS_WPM_SLAVE()
629# define TRANSACTIONS_WPM_REGISTRATIONS
630
631#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
632
633////////////////////////////////////////////////////
634// OLED
635
636#if defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
637
638static bool oled_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
639 static uint32_t last_update = 0;
640 bool current_oled_state = is_oled_on();
641 return send_if_condition(PUT_OLED, &last_update, (current_oled_state != split_shmem->current_oled_state), ¤t_oled_state, sizeof(current_oled_state));
642}
643
644static void oled_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
645 split_shared_memory_lock();
646 uint8_t current_oled_state = split_shmem->current_oled_state;
647 split_shared_memory_unlock();
648
649 if (current_oled_state) {
650 oled_on();
651 } else {
652 oled_off();
653 }
654}
655
656# define TRANSACTIONS_OLED_MASTER() TRANSACTION_HANDLER_MASTER(oled)
657# define TRANSACTIONS_OLED_SLAVE() TRANSACTION_HANDLER_SLAVE(oled)
658# define TRANSACTIONS_OLED_REGISTRATIONS [PUT_OLED] = trans_initiator2target_initializer(current_oled_state),
659
660#else // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
661
662# define TRANSACTIONS_OLED_MASTER()
663# define TRANSACTIONS_OLED_SLAVE()
664# define TRANSACTIONS_OLED_REGISTRATIONS
665
666#endif // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
667
668////////////////////////////////////////////////////
669// ST7565
670
671#if defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
672
673static bool st7565_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
674 static uint32_t last_update = 0;
675 bool current_st7565_state = st7565_is_on();
676 return send_if_condition(PUT_ST7565, &last_update, (current_st7565_state != split_shmem->current_st7565_state), ¤t_st7565_state, sizeof(current_st7565_state));
677}
678
679static void st7565_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
680 split_shared_memory_lock();
681 uint8_t current_st7565_state = split_shmem->current_st7565_state;
682 split_shared_memory_unlock();
683
684 if (current_st7565_state) {
685 st7565_on();
686 } else {
687 st7565_off();
688 }
689}
690
691# define TRANSACTIONS_ST7565_MASTER() TRANSACTION_HANDLER_MASTER(st7565)
692# define TRANSACTIONS_ST7565_SLAVE() TRANSACTION_HANDLER_SLAVE(st7565)
693# define TRANSACTIONS_ST7565_REGISTRATIONS [PUT_ST7565] = trans_initiator2target_initializer(current_st7565_state),
694
695#else // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
696
697# define TRANSACTIONS_ST7565_MASTER()
698# define TRANSACTIONS_ST7565_SLAVE()
699# define TRANSACTIONS_ST7565_REGISTRATIONS
700
701#endif // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
702
703////////////////////////////////////////////////////
704// POINTING
705
706#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
707
708static bool pointing_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
709# if defined(POINTING_DEVICE_LEFT)
710 if (is_keyboard_left()) {
711 return true;
712 }
713# elif defined(POINTING_DEVICE_RIGHT)
714 if (!is_keyboard_left()) {
715 return true;
716 }
717# endif
718 static uint32_t last_update = 0;
719 static uint32_t last_cpi_update = 0;
720 static uint16_t last_cpi = 0;
721 report_mouse_t temp_state;
722 uint16_t temp_cpi;
723 bool okay = read_if_checksum_mismatch(GET_POINTING_CHECKSUM, GET_POINTING_DATA, &last_update, &temp_state, &split_shmem->pointing.report, sizeof(temp_state));
724 if (okay) pointing_device_set_shared_report(temp_state);
725 temp_cpi = pointing_device_get_shared_cpi();
726 if (temp_cpi) {
727 split_shmem->pointing.cpi = temp_cpi;
728 okay = send_if_condition(PUT_POINTING_CPI, &last_cpi_update, last_cpi != temp_cpi, &split_shmem->pointing.cpi, sizeof(split_shmem->pointing.cpi));
729 if (okay) {
730 last_cpi = temp_cpi;
731 }
732 }
733 return okay;
734}
735
736extern const pointing_device_driver_t *pointing_device_driver;
737
738static void pointing_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
739# if defined(POINTING_DEVICE_LEFT)
740 if (!is_keyboard_left()) {
741 return;
742 }
743# elif defined(POINTING_DEVICE_RIGHT)
744 if (is_keyboard_left()) {
745 return;
746 }
747# endif
748# if (POINTING_DEVICE_TASK_THROTTLE_MS > 0)
749 static uint32_t last_exec = 0;
750 if (timer_elapsed32(last_exec) < POINTING_DEVICE_TASK_THROTTLE_MS) {
751 return;
752 }
753 last_exec = timer_read32();
754# endif
755
756 uint16_t temp_cpi = !pointing_device_driver->get_cpi ? 0 : pointing_device_driver->get_cpi(); // check for NULL
757
758 split_shared_memory_lock();
759 split_slave_pointing_sync_t pointing;
760 memcpy(&pointing, &split_shmem->pointing, sizeof(split_slave_pointing_sync_t));
761 split_shared_memory_unlock();
762
763 if (pointing.cpi && pointing.cpi != temp_cpi && pointing_device_driver->set_cpi) {
764 pointing_device_driver->set_cpi(pointing.cpi);
765 }
766
767 pointing.report = pointing_device_driver->get_report((report_mouse_t){0});
768 // Now update the checksum given that the pointing has been written to
769 pointing.checksum = crc8(&pointing.report, sizeof(report_mouse_t));
770
771 split_shared_memory_lock();
772 memcpy(&split_shmem->pointing, &pointing, sizeof(split_slave_pointing_sync_t));
773 split_shared_memory_unlock();
774}
775
776# define TRANSACTIONS_POINTING_MASTER() TRANSACTION_HANDLER_MASTER(pointing)
777# define TRANSACTIONS_POINTING_SLAVE() TRANSACTION_HANDLER_SLAVE(pointing)
778# define TRANSACTIONS_POINTING_REGISTRATIONS [GET_POINTING_CHECKSUM] = trans_target2initiator_initializer(pointing.checksum), [GET_POINTING_DATA] = trans_target2initiator_initializer(pointing.report), [PUT_POINTING_CPI] = trans_initiator2target_initializer(pointing.cpi),
779
780#else // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
781
782# define TRANSACTIONS_POINTING_MASTER()
783# define TRANSACTIONS_POINTING_SLAVE()
784# define TRANSACTIONS_POINTING_REGISTRATIONS
785
786#endif // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
787
788////////////////////////////////////////////////////
789// WATCHDOG
790
791#if defined(SPLIT_WATCHDOG_ENABLE)
792
793static bool watchdog_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
794 bool okay = true;
795 if (!split_watchdog_check()) {
796 okay = transport_write(PUT_WATCHDOG, &okay, sizeof(okay));
797 split_watchdog_update(okay);
798 }
799 return okay;
800}
801
802static void watchdog_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
803 split_watchdog_update(split_shmem->watchdog_pinged);
804}
805
806# define TRANSACTIONS_WATCHDOG_MASTER() TRANSACTION_HANDLER_MASTER(watchdog)
807# define TRANSACTIONS_WATCHDOG_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(watchdog)
808# define TRANSACTIONS_WATCHDOG_REGISTRATIONS [PUT_WATCHDOG] = trans_initiator2target_initializer(watchdog_pinged),
809
810#else // defined(SPLIT_WATCHDOG_ENABLE)
811
812# define TRANSACTIONS_WATCHDOG_MASTER()
813# define TRANSACTIONS_WATCHDOG_SLAVE()
814# define TRANSACTIONS_WATCHDOG_REGISTRATIONS
815
816#endif // defined(SPLIT_WATCHDOG_ENABLE)
817
818#if defined(HAPTIC_ENABLE) && defined(SPLIT_HAPTIC_ENABLE)
819
820uint8_t split_haptic_play = 0xFF;
821extern haptic_config_t haptic_config;
822
823static bool haptic_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
824 static uint32_t last_update = 0;
825 split_slave_haptic_sync_t haptic_sync;
826
827 memcpy(&haptic_sync.haptic_config, &haptic_config, sizeof(haptic_config_t));
828 haptic_sync.haptic_play = split_haptic_play;
829
830 bool okay = send_if_data_mismatch(PUT_HAPTIC, &last_update, &haptic_sync, &split_shmem->haptic_sync, sizeof(haptic_sync));
831
832 split_haptic_play = 0xFF;
833
834 return okay;
835}
836
837static void haptic_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
838 memcpy(&haptic_config, &split_shmem->haptic_sync.haptic_config, sizeof(haptic_config_t));
839
840 if (split_shmem->haptic_sync.haptic_play != 0xFF) {
841 haptic_set_mode(split_shmem->haptic_sync.haptic_play);
842 haptic_play();
843 }
844}
845
846// clang-format off
847# define TRANSACTIONS_HAPTIC_MASTER() TRANSACTION_HANDLER_MASTER(haptic)
848# define TRANSACTIONS_HAPTIC_SLAVE() TRANSACTION_HANDLER_SLAVE(haptic)
849# define TRANSACTIONS_HAPTIC_REGISTRATIONS [PUT_HAPTIC] = trans_initiator2target_initializer(haptic_sync),
850// clang-format on
851
852#else // defined(HAPTIC_ENABLE) && defined(SPLIT_HAPTIC_ENABLE)
853
854# define TRANSACTIONS_HAPTIC_MASTER()
855# define TRANSACTIONS_HAPTIC_SLAVE()
856# define TRANSACTIONS_HAPTIC_REGISTRATIONS
857
858#endif // defined(HAPTIC_ENABLE) && defined(SPLIT_HAPTIC_ENABLE)
859
860#if defined(SPLIT_ACTIVITY_ENABLE)
861
862static bool activity_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
863 static uint32_t last_update = 0;
864 split_slave_activity_sync_t activity_sync;
865 activity_sync.matrix_timestamp = last_matrix_activity_time();
866 activity_sync.encoder_timestamp = last_encoder_activity_time();
867 activity_sync.pointing_device_timestamp = last_pointing_device_activity_time();
868 return send_if_data_mismatch(PUT_ACTIVITY, &last_update, &activity_sync, &split_shmem->activity_sync, sizeof(activity_sync));
869}
870
871static void activity_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
872 set_activity_timestamps(split_shmem->activity_sync.matrix_timestamp, split_shmem->activity_sync.encoder_timestamp, split_shmem->activity_sync.pointing_device_timestamp);
873}
874
875// clang-format off
876# define TRANSACTIONS_ACTIVITY_MASTER() TRANSACTION_HANDLER_MASTER(activity)
877# define TRANSACTIONS_ACTIVITY_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(activity)
878# define TRANSACTIONS_ACTIVITY_REGISTRATIONS [PUT_ACTIVITY] = trans_initiator2target_initializer(activity_sync),
879// clang-format on
880
881#else // defined(SPLIT_ACTIVITY_ENABLE)
882
883# define TRANSACTIONS_ACTIVITY_MASTER()
884# define TRANSACTIONS_ACTIVITY_SLAVE()
885# define TRANSACTIONS_ACTIVITY_REGISTRATIONS
886
887#endif // defined(SPLIT_ACTIVITY_ENABLE)
888
889////////////////////////////////////////////////////
890// Detected OS
891
892#if defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)
893
894static bool detected_os_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
895 static uint32_t last_detected_os_update = 0;
896 os_variant_t detected_os = detected_host_os();
897 bool okay = send_if_condition(PUT_DETECTED_OS, &last_detected_os_update, (detected_os != split_shmem->detected_os), &detected_os, sizeof(os_variant_t));
898 return okay;
899}
900
901static void detected_os_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
902 slave_update_detected_host_os(split_shmem->detected_os);
903}
904
905# define TRANSACTIONS_DETECTED_OS_MASTER() TRANSACTION_HANDLER_MASTER(detected_os)
906# define TRANSACTIONS_DETECTED_OS_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(detected_os)
907# define TRANSACTIONS_DETECTED_OS_REGISTRATIONS [PUT_DETECTED_OS] = trans_initiator2target_initializer(detected_os),
908
909#else // defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)
910
911# define TRANSACTIONS_DETECTED_OS_MASTER()
912# define TRANSACTIONS_DETECTED_OS_SLAVE()
913# define TRANSACTIONS_DETECTED_OS_REGISTRATIONS
914
915#endif // defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)
916
917////////////////////////////////////////////////////
918
919split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = {
920 // Set defaults
921 [0 ...(NUM_TOTAL_TRANSACTIONS - 1)] = {0, 0, 0, 0, 0},
922
923#ifdef USE_I2C
924 [I2C_EXECUTE_CALLBACK] = trans_initiator2target_initializer(transaction_id),
925#endif // USE_I2C
926
927 // clang-format off
928 TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS
929 TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS
930 TRANSACTIONS_ENCODERS_REGISTRATIONS
931 TRANSACTIONS_SYNC_TIMER_REGISTRATIONS
932 TRANSACTIONS_LAYER_STATE_REGISTRATIONS
933 TRANSACTIONS_LED_STATE_REGISTRATIONS
934 TRANSACTIONS_MODS_REGISTRATIONS
935 TRANSACTIONS_BACKLIGHT_REGISTRATIONS
936 TRANSACTIONS_RGBLIGHT_REGISTRATIONS
937 TRANSACTIONS_LED_MATRIX_REGISTRATIONS
938 TRANSACTIONS_RGB_MATRIX_REGISTRATIONS
939 TRANSACTIONS_WPM_REGISTRATIONS
940 TRANSACTIONS_OLED_REGISTRATIONS
941 TRANSACTIONS_ST7565_REGISTRATIONS
942 TRANSACTIONS_POINTING_REGISTRATIONS
943 TRANSACTIONS_WATCHDOG_REGISTRATIONS
944 TRANSACTIONS_HAPTIC_REGISTRATIONS
945 TRANSACTIONS_ACTIVITY_REGISTRATIONS
946 TRANSACTIONS_DETECTED_OS_REGISTRATIONS
947// clang-format on
948
949#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
950 [PUT_RPC_INFO] = trans_initiator2target_initializer_cb(rpc_info, slave_rpc_info_callback),
951 [PUT_RPC_REQ_DATA] = trans_initiator2target_initializer(rpc_m2s_buffer),
952 [EXECUTE_RPC] = trans_initiator2target_initializer_cb(rpc_info.payload.transaction_id, slave_rpc_exec_callback),
953 [GET_RPC_RESP_DATA] = trans_target2initiator_initializer(rpc_s2m_buffer),
954#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
955};
956
957bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
958 TRANSACTIONS_SLAVE_MATRIX_MASTER();
959 TRANSACTIONS_MASTER_MATRIX_MASTER();
960 TRANSACTIONS_ENCODERS_MASTER();
961 TRANSACTIONS_SYNC_TIMER_MASTER();
962 TRANSACTIONS_LAYER_STATE_MASTER();
963 TRANSACTIONS_LED_STATE_MASTER();
964 TRANSACTIONS_MODS_MASTER();
965 TRANSACTIONS_BACKLIGHT_MASTER();
966 TRANSACTIONS_RGBLIGHT_MASTER();
967 TRANSACTIONS_LED_MATRIX_MASTER();
968 TRANSACTIONS_RGB_MATRIX_MASTER();
969 TRANSACTIONS_WPM_MASTER();
970 TRANSACTIONS_OLED_MASTER();
971 TRANSACTIONS_ST7565_MASTER();
972 TRANSACTIONS_POINTING_MASTER();
973 TRANSACTIONS_WATCHDOG_MASTER();
974 TRANSACTIONS_HAPTIC_MASTER();
975 TRANSACTIONS_ACTIVITY_MASTER();
976 TRANSACTIONS_DETECTED_OS_MASTER();
977 return true;
978}
979
980void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
981 TRANSACTIONS_SLAVE_MATRIX_SLAVE();
982 TRANSACTIONS_MASTER_MATRIX_SLAVE();
983 TRANSACTIONS_ENCODERS_SLAVE();
984 TRANSACTIONS_SYNC_TIMER_SLAVE();
985 TRANSACTIONS_LAYER_STATE_SLAVE();
986 TRANSACTIONS_LED_STATE_SLAVE();
987 TRANSACTIONS_MODS_SLAVE();
988 TRANSACTIONS_BACKLIGHT_SLAVE();
989 TRANSACTIONS_RGBLIGHT_SLAVE();
990 TRANSACTIONS_LED_MATRIX_SLAVE();
991 TRANSACTIONS_RGB_MATRIX_SLAVE();
992 TRANSACTIONS_WPM_SLAVE();
993 TRANSACTIONS_OLED_SLAVE();
994 TRANSACTIONS_ST7565_SLAVE();
995 TRANSACTIONS_POINTING_SLAVE();
996 TRANSACTIONS_WATCHDOG_SLAVE();
997 TRANSACTIONS_HAPTIC_SLAVE();
998 TRANSACTIONS_ACTIVITY_SLAVE();
999 TRANSACTIONS_DETECTED_OS_SLAVE();
1000}
1001
1002#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
1003
1004void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback) {
1005 // Prevent invoking RPC on QMK core sync data
1006 if (transaction_id <= GET_RPC_RESP_DATA) return;
1007
1008 // Set the callback
1009 split_transaction_table[transaction_id].slave_callback = callback;
1010 split_transaction_table[transaction_id].initiator2target_offset = offsetof(split_shared_memory_t, rpc_m2s_buffer);
1011 split_transaction_table[transaction_id].target2initiator_offset = offsetof(split_shared_memory_t, rpc_s2m_buffer);
1012}
1013
1014bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
1015 // Prevent transaction attempts while transport is disconnected
1016 if (!is_transport_connected()) {
1017 return false;
1018 }
1019 // Prevent invoking RPC on QMK core sync data
1020 if (transaction_id <= GET_RPC_RESP_DATA) return false;
1021 // Prevent sizing issues
1022 if (initiator2target_buffer_size > RPC_M2S_BUFFER_SIZE) return false;
1023 if (target2initiator_buffer_size > RPC_S2M_BUFFER_SIZE) return false;
1024
1025 // Prepare the metadata block
1026 rpc_sync_info_t info = {.payload = {.transaction_id = transaction_id, .m2s_length = initiator2target_buffer_size, .s2m_length = target2initiator_buffer_size}};
1027 info.checksum = crc8(&info.payload, sizeof(info.payload));
1028
1029 // Make sure the local side knows that we're not sending the full block of data
1030 split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = initiator2target_buffer_size;
1031 split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = target2initiator_buffer_size;
1032
1033 // Run through the sequence:
1034 // * set the transaction ID and lengths
1035 // * send the request data
1036 // * execute RPC callback
1037 // * retrieve the response data
1038 if (!transport_write(PUT_RPC_INFO, &info, sizeof(info))) {
1039 return false;
1040 }
1041 if (!transport_write(PUT_RPC_REQ_DATA, initiator2target_buffer, initiator2target_buffer_size)) {
1042 return false;
1043 }
1044 if (!transport_write(EXECUTE_RPC, &transaction_id, sizeof(transaction_id))) {
1045 return false;
1046 }
1047 if (!transport_read(GET_RPC_RESP_DATA, target2initiator_buffer, target2initiator_buffer_size)) {
1048 return false;
1049 }
1050 return true;
1051}
1052
1053void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
1054 // The RPC info block contains the intended transaction ID, as well as the sizes for both inbound and outbound data.
1055 // Ignore the args -- the `split_shmem` already has the info, we just need to act upon it.
1056 // We must keep the `split_transaction_table` non-const, so that it is able to be modified at runtime.
1057
1058 split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = split_shmem->rpc_info.payload.m2s_length;
1059 split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = split_shmem->rpc_info.payload.s2m_length;
1060}
1061
1062void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
1063 // We can assume that the buffer lengths are correctly set, now, given that sequentially the rpc_info callback was already executed.
1064 // Go through the rpc_info and execute _that_ transaction's callback, with the scratch buffers as inputs.
1065 // As a safety precaution we check that the received payload matches its checksum first.
1066 if (crc8(&split_shmem->rpc_info.payload, sizeof(split_shmem->rpc_info.payload)) != split_shmem->rpc_info.checksum) {
1067 return;
1068 }
1069
1070 int8_t transaction_id = split_shmem->rpc_info.payload.transaction_id;
1071 if (transaction_id < NUM_TOTAL_TRANSACTIONS) {
1072 split_transaction_desc_t *trans = &split_transaction_table[transaction_id];
1073 if (trans->slave_callback) {
1074 trans->slave_callback(split_shmem->rpc_info.payload.m2s_length, split_shmem->rpc_m2s_buffer, split_shmem->rpc_info.payload.s2m_length, split_shmem->rpc_s2m_buffer);
1075 }
1076 }
1077}
1078
1079#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)