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