aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Brassel <nick@tzarc.org>2021-06-18 09:10:06 +1000
committerGitHub <noreply@github.com>2021-06-18 09:10:06 +1000
commit172e6a703041363decd6fc829542f33180c13beb (patch)
treea5d4afaa672ab44826865fd76b201e3899083192
parentef92c9ee2cf4745637635ec1895399e4f013914c (diff)
downloadqmk_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.
-rw-r--r--common_features.mk6
-rw-r--r--docs/config_options.md26
-rw-r--r--docs/feature_split_keyboard.md114
-rw-r--r--drivers/avr/i2c_master.c19
-rw-r--r--drivers/avr/i2c_slave.c29
-rw-r--r--drivers/avr/i2c_slave.h13
-rw-r--r--drivers/avr/serial.c82
-rw-r--r--drivers/avr/serial.h62
-rw-r--r--drivers/chibios/serial.c52
-rw-r--r--drivers/chibios/serial.h62
-rw-r--r--drivers/chibios/serial_usart.c47
-rw-r--r--drivers/serial.h46
-rw-r--r--keyboards/draculad/config.h2
-rw-r--r--keyboards/handwired/freoduo/rules.mk4
-rw-r--r--keyboards/helix/rev2/keymaps/default/rules.mk2
-rw-r--r--keyboards/keebio/iris/rev2/rules.mk2
-rw-r--r--keyboards/keebio/iris/rev3/rules.mk1
-rw-r--r--keyboards/keebio/iris/rev4/rules.mk2
-rw-r--r--keyboards/keebio/quefrency/rules.mk1
-rw-r--r--keyboards/keebio/viterbi/rev2/rules.mk2
-rw-r--r--keyboards/keebio/viterbi/rules.mk8
-rw-r--r--quantum/split_common/matrix.c6
-rw-r--r--quantum/split_common/post_config.h9
-rw-r--r--quantum/split_common/transaction_id_define.h94
-rw-r--r--quantum/split_common/transactions.c670
-rw-r--r--quantum/split_common/transactions.h54
-rw-r--r--quantum/split_common/transport.c484
-rw-r--r--quantum/split_common/transport.h165
-rw-r--r--tmk_core/common/host.c14
29 files changed, 1387 insertions, 691 deletions
diff --git a/common_features.mk b/common_features.mk
index 1a079a8df..15637d760 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -537,7 +537,11 @@ ifeq ($(strip $(SPLIT_KEYBOARD)), yes)
537 537
538 # Determine which (if any) transport files are required 538 # Determine which (if any) transport files are required
539 ifneq ($(strip $(SPLIT_TRANSPORT)), custom) 539 ifneq ($(strip $(SPLIT_TRANSPORT)), custom)
540 QUANTUM_LIB_SRC += $(QUANTUM_DIR)/split_common/transport.c 540 QUANTUM_SRC += $(QUANTUM_DIR)/split_common/transport.c \
541 $(QUANTUM_DIR)/split_common/transactions.c
542
543 OPT_DEFS += -DSPLIT_COMMON_TRANSACTIONS
544
541 # Functions added via QUANTUM_LIB_SRC are only included in the final binary if they're called. 545 # Functions added via QUANTUM_LIB_SRC are only included in the final binary if they're called.
542 # Unused functions are pruned away, which is why we can add multiple drivers here without bloat. 546 # Unused functions are pruned away, which is why we can add multiple drivers here without bloat.
543 ifeq ($(PLATFORM),AVR) 547 ifeq ($(PLATFORM),AVR)
diff --git a/docs/config_options.md b/docs/config_options.md
index 26fe8cea5..980195ac6 100644
--- a/docs/config_options.md
+++ b/docs/config_options.md
@@ -274,7 +274,7 @@ There are a few different ways to set handedness for split keyboards (listed in
274### Other Options 274### Other Options
275 275
276* `#define USE_I2C` 276* `#define USE_I2C`
277 * For using I2C instead of Serial (defaults to serial) 277 * For using I2C instead of Serial (default is serial; serial transport is supported on ARM -- I2C is AVR-only)
278 278
279* `#define SOFT_SERIAL_PIN D0` 279* `#define SOFT_SERIAL_PIN D0`
280 * When using serial, define this. `D0` or `D1`,`D2`,`D3`,`E6`. 280 * When using serial, define this. `D0` or `D1`,`D2`,`D3`,`E6`.
@@ -303,7 +303,7 @@ There are a few different ways to set handedness for split keyboards (listed in
303* `#define SPLIT_USB_DETECT` 303* `#define SPLIT_USB_DETECT`
304 * Detect (with timeout) USB connection when delegating master/slave 304 * Detect (with timeout) USB connection when delegating master/slave
305 * Default behavior for ARM 305 * Default behavior for ARM
306 * Required for AVR Teensy 306 * Required for AVR Teensy (without hardware mods)
307 307
308* `#define SPLIT_USB_TIMEOUT 2000` 308* `#define SPLIT_USB_TIMEOUT 2000`
309 * Maximum timeout when detecting master/slave when using `SPLIT_USB_DETECT` 309 * Maximum timeout when detecting master/slave when using `SPLIT_USB_DETECT`
@@ -311,6 +311,28 @@ There are a few different ways to set handedness for split keyboards (listed in
311* `#define SPLIT_USB_TIMEOUT_POLL 10` 311* `#define SPLIT_USB_TIMEOUT_POLL 10`
312 * Poll frequency when detecting master/slave when using `SPLIT_USB_DETECT` 312 * Poll frequency when detecting master/slave when using `SPLIT_USB_DETECT`
313 313
314* `#define FORCED_SYNC_THROTTLE_MS 100`
315 * Deadline for synchronizing data from master to slave when using the QMK-provided split transport.
316
317* `#define SPLIT_TRANSPORT_MIRROR`
318 * Mirrors the master-side matrix on the slave when using the QMK-provided split transport.
319
320* `#define SPLIT_LAYER_STATE_ENABLE`
321 * Ensures the current layer state is available on the slave when using the QMK-provided split transport.
322
323* `#define SPLIT_LED_STATE_ENABLE`
324 * Ensures the current host indicator state (caps/num/scroll) is available on the slave when using the QMK-provided split transport.
325
326* `#define SPLIT_MODS_ENABLE`
327 * Ensures the current modifier state (normal, weak, and oneshot) is available on the slave when using the QMK-provided split transport.
328
329* `#define SPLIT_WPM_ENABLE`
330 * Ensures the current WPM is available on the slave when using the QMK-provided split transport.
331
332* `#define SPLIT_TRANSACTION_IDS_KB .....`
333* `#define SPLIT_TRANSACTION_IDS_USER .....`
334 * Allows for custom data sync with the slave when using the QMK-provided split transport. See [custom data sync between sides](feature_split_keyboard.md#custom-data-sync) for more information.
335
314# The `rules.mk` File 336# The `rules.mk` File
315 337
316This is a [make](https://www.gnu.org/software/make/manual/make.html) file that is included by the top-level `Makefile`. It is used to set some information about the MCU that we will be compiling for as well as enabling and disabling certain features. 338This is a [make](https://www.gnu.org/software/make/manual/make.html) file that is included by the top-level `Makefile`. It is used to set some information about the MCU that we will be compiling for as well as enabling and disabling certain features.
diff --git a/docs/feature_split_keyboard.md b/docs/feature_split_keyboard.md
index 4ebf585f5..603c387c2 100644
--- a/docs/feature_split_keyboard.md
+++ b/docs/feature_split_keyboard.md
@@ -8,8 +8,7 @@ QMK Firmware has a generic implementation that is usable by any board, as well a
8 8
9For this, we will mostly be talking about the generic implementation used by the Let's Split and other keyboards. 9For this, we will mostly be talking about the generic implementation used by the Let's Split and other keyboards.
10 10
11!> ARM is not yet fully supported for Split Keyboards and has many limitations. Progress is being made, but we have not yet reached 100% feature parity. 11!> ARM split supports most QMK subsystems when using the 'serial' and 'serial_usart' drivers. I2C slave is currently unsupported.
12
13 12
14## Compatibility Overview 13## Compatibility Overview
15 14
@@ -169,7 +168,7 @@ Because not every split keyboard is identical, there are a number of additional
169#define USE_I2C 168#define USE_I2C
170``` 169```
171 170
172This enables I<sup>2</sup>C support for split keyboards. This isn't strictly for communication, but can be used for OLED or other I<sup>2</sup>C-based devices. 171This configures the use of I<sup>2</sup>C support for split keyboard transport (AVR only).
173 172
174```c 173```c
175#define SOFT_SERIAL_PIN D0 174#define SOFT_SERIAL_PIN D0
@@ -193,20 +192,115 @@ If you're having issues with serial communication, you can change this value, as
193* **`5`**: about 20kbps 192* **`5`**: about 20kbps
194 193
195```c 194```c
196#define SPLIT_MODS_ENABLE 195#define FORCED_SYNC_THROTTLE_MS 100
197``` 196```
198 197
199This enables transmitting modifier state (normal, weak and oneshot) to the non 198This sets the maximum number of milliseconds before forcing a synchronization of data from master to slave. Under normal circumstances this sync occurs whenever the data _changes_, for safety a data transfer occurs after this number of milliseconds if no change has been detected since the last sync.
200primary side of the split keyboard. This adds a few bytes of data to the split
201communication protocol and may impact the matrix scan speed when enabled.
202The purpose of this feature is to support cosmetic use of modifer state (e.g.
203displaying status on an OLED screen).
204 199
205```c 200```c
206#define SPLIT_TRANSPORT_MIRROR 201#define SPLIT_TRANSPORT_MIRROR
207``` 202```
208 203
209This mirrors the master side matrix to the slave side for features that react or require knowledge of master side key presses on the slave side. This adds a few bytes of data to the split communication protocol and may impact the matrix scan speed when enabled. The purpose of this feature is to support cosmetic use of key events (e.g. RGB reacting to Keypresses). 204This mirrors the master side matrix to the slave side for features that react or require knowledge of master side key presses on the slave side. The purpose of this feature is to support cosmetic use of key events (e.g. RGB reacting to keypresses). This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
205
206```c
207#define SPLIT_LAYER_STATE_ENABLE
208```
209
210This enables syncing of the layer state between both halves of the split keyboard. The main purpose of this feature is to enable support for use of things like OLED display of the currently active layer. This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
211
212```c
213#define SPLIT_LED_STATE_ENABLE
214```
215
216This enables syncing of the Host LED status (caps lock, num lock, etc) between both halves of the split keyboard. The main purpose of this feature is to enable support for use of things like OLED display of the Host LED status. This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
217
218```c
219#define SPLIT_MODS_ENABLE
220```
221
222This enables transmitting modifier state (normal, weak and oneshot) to the non primary side of the split keyboard. The purpose of this feature is to support cosmetic use of modifer state (e.g. displaying status on an OLED screen). This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
223
224```c
225#define SPLIT_WPM_ENABLE
226```
227
228This enables transmitting the current WPM to the slave side of the split keyboard. The purpose of this feature is to support cosmetic use of WPM (e.g. displaying the current value on an OLED screen). This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
229
230### Custom data sync between sides :id=custom-data-sync
231
232QMK's split transport allows for arbitrary data transactions at both the keyboard and user levels. This is modelled on a remote procedure call, with the master invoking a function on the slave side, with the ability to send data from master to slave, process it slave side, and send data back from slave to master.
233
234To leverage this, a keyboard or user/keymap can define a comma-separated list of _transaction IDs_:
235
236```c
237// for keyboard-level data sync:
238#define SPLIT_TRANSACTION_IDS_KB KEYBOARD_SYNC_A, KEYBOARD_SYNC_B
239// or, for user:
240#define SPLIT_TRANSACTION_IDS_USER USER_SYNC_A, USER_SYNC_B, USER_SYNC_C
241```
242
243These _transaction IDs_ then need a slave-side handler function to be registered with the split transport, for example:
244
245```c
246typedef struct _master_to_slave_t {
247 int m2s_data;
248} master_to_slave_t;
249
250typedef struct _slave_to_master_t {
251 int s2m_data;
252} slave_to_master_t;
253
254void user_sync_a_slave_handler(uint8_t in_buflen, const void* in_data, uint8_t out_buflen, void* out_data) {
255 const master_to_slave_t *m2s = (const master_to_slave_t*)in_data;
256 slave_to_master_t *s2m = (slave_to_master_t*)out_data;
257 s2m->s2m_data = m2s->m2s_data + 5; // whatever comes in, add 5 so it can be sent back
258}
259
260void keyboard_post_init_user(void) {
261 transaction_register_rpc(USER_SYNC_A, user_sync_a_slave_handler);
262}
263```
264
265The master side can then invoke the slave-side handler - for normal keyboard functionality to be minimally affected, any keyboard- or user-level code attempting to sync data should be throttled:
266
267```c
268void housekeeping_task_user(void) {
269 if (is_keyboard_master()) {
270 // Interact with slave every 500ms
271 static uint32_t last_sync = 0;
272 if (timer_elapsed32(last_sync) > 500) {
273 master_to_slave_t m2s = {6};
274 slave_to_master_t s2m = {0};
275 if(transaction_rpc_exec(USER_SYNC_A, sizeof(m2s), &m2s, sizeof(s2m), &s2m)) {
276 last_sync = timer_read32();
277 dprintf("Slave value: %d\n", s2m.s2m_data); // this will now be 11, as the slave adds 5
278 } else {
279 dprint("Slave sync failed!\n");
280 }
281 }
282 }
283}
284```
285
286!> It is recommended that any data sync between halves happens during the master side's _housekeeping task_. This ensures timely retries should failures occur.
287
288If only one-way data transfer is needed, helper methods are provided:
289
290```c
291bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
292bool transaction_rpc_send(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer);
293bool transaction_rpc_recv(int8_t transaction_id, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
294```
295
296By default, the inbound and outbound data is limited to a maximum of 32 bytes each. The sizes can be altered if required:
297
298```c
299// Master to slave:
300#define RPC_M2S_BUFFER_SIZE 48
301// Slave to master:
302#define RPC_S2M_BUFFER_SIZE 48
303```
210 304
211### Hardware Configuration Options 305### Hardware Configuration Options
212 306
diff --git a/drivers/avr/i2c_master.c b/drivers/avr/i2c_master.c
index b1e488529..2773e0077 100644
--- a/drivers/avr/i2c_master.c
+++ b/drivers/avr/i2c_master.c
@@ -28,8 +28,14 @@
28# define F_SCL 400000UL // SCL frequency 28# define F_SCL 400000UL // SCL frequency
29#endif 29#endif
30 30
31#ifndef I2C_START_RETRY_COUNT
32# define I2C_START_RETRY_COUNT 20
33#endif // I2C_START_RETRY_COUNT
34
31#define TWBR_val (((F_CPU / F_SCL) - 16) / 2) 35#define TWBR_val (((F_CPU / F_SCL) - 16) / 2)
32 36
37#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
38
33void i2c_init(void) { 39void i2c_init(void) {
34 TWSR = 0; /* no prescaler */ 40 TWSR = 0; /* no prescaler */
35 TWBR = (uint8_t)TWBR_val; 41 TWBR = (uint8_t)TWBR_val;
@@ -47,7 +53,7 @@ void i2c_init(void) {
47#endif 53#endif
48} 54}
49 55
50i2c_status_t i2c_start(uint8_t address, uint16_t timeout) { 56static i2c_status_t i2c_start_impl(uint8_t address, uint16_t timeout) {
51 // reset TWI control register 57 // reset TWI control register
52 TWCR = 0; 58 TWCR = 0;
53 // transmit START condition 59 // transmit START condition
@@ -86,6 +92,17 @@ i2c_status_t i2c_start(uint8_t address, uint16_t timeout) {
86 return I2C_STATUS_SUCCESS; 92 return I2C_STATUS_SUCCESS;
87} 93}
88 94
95i2c_status_t i2c_start(uint8_t address, uint16_t timeout) {
96 // Retry i2c_start_impl a bunch times in case the remote side has interrupts disabled.
97 uint16_t timeout_timer = timer_read();
98 uint16_t time_slice = MAX(1, (timeout == (I2C_TIMEOUT_INFINITE)) ? 5 : (timeout / (I2C_START_RETRY_COUNT))); // if it's infinite, wait 1ms between attempts, otherwise split up the entire timeout into the number of retries
99 i2c_status_t status;
100 do {
101 status = i2c_start_impl(address, time_slice);
102 } while ((status < 0) && ((timeout == I2C_TIMEOUT_INFINITE) || (timer_elapsed(timeout_timer) < timeout)));
103 return status;
104}
105
89i2c_status_t i2c_write(uint8_t data, uint16_t timeout) { 106i2c_status_t i2c_write(uint8_t data, uint16_t timeout) {
90 // load data into data register 107 // load data into data register
91 TWDR = data; 108 TWDR = data;
diff --git a/drivers/avr/i2c_slave.c b/drivers/avr/i2c_slave.c
index 62a378165..2907f164c 100644
--- a/drivers/avr/i2c_slave.c
+++ b/drivers/avr/i2c_slave.c
@@ -17,6 +17,7 @@
17 * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib 17 * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib
18 */ 18 */
19 19
20#include <stddef.h>
20#include <avr/io.h> 21#include <avr/io.h>
21#include <util/twi.h> 22#include <util/twi.h>
22#include <avr/interrupt.h> 23#include <avr/interrupt.h>
@@ -24,6 +25,12 @@
24 25
25#include "i2c_slave.h" 26#include "i2c_slave.h"
26 27
28#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
29# include "transactions.h"
30
31static volatile bool is_callback_executor = false;
32#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
33
27volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT]; 34volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
28 35
29static volatile uint8_t buffer_address; 36static volatile uint8_t buffer_address;
@@ -48,11 +55,14 @@ ISR(TWI_vect) {
48 case TW_SR_SLA_ACK: 55 case TW_SR_SLA_ACK:
49 // The device is now a slave receiver 56 // The device is now a slave receiver
50 slave_has_register_set = false; 57 slave_has_register_set = false;
58#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
59 is_callback_executor = false;
60#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
51 break; 61 break;
52 62
53 case TW_SR_DATA_ACK: 63 case TW_SR_DATA_ACK:
54 // This device is a slave receiver and has received data 64 // This device is a slave receiver and has received data
55 // First byte is the location then the bytes will be writen in buffer with auto-incriment 65 // First byte is the location then the bytes will be writen in buffer with auto-increment
56 if (!slave_has_register_set) { 66 if (!slave_has_register_set) {
57 buffer_address = TWDR; 67 buffer_address = TWDR;
58 68
@@ -60,10 +70,25 @@ ISR(TWI_vect) {
60 ack = 0; 70 ack = 0;
61 buffer_address = 0; 71 buffer_address = 0;
62 } 72 }
63 slave_has_register_set = true; // address has been receaved now fill in buffer 73 slave_has_register_set = true; // address has been received now fill in buffer
74
75#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
76 // Work out if we're attempting to execute a callback
77 is_callback_executor = buffer_address == split_transaction_table[I2C_EXECUTE_CALLBACK].initiator2target_offset;
78#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
64 } else { 79 } else {
65 i2c_slave_reg[buffer_address] = TWDR; 80 i2c_slave_reg[buffer_address] = TWDR;
66 buffer_address++; 81 buffer_address++;
82
83#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
84 // If we're intending to execute a transaction callback, do so, as we've just received the transaction ID
85 if (is_callback_executor) {
86 split_transaction_desc_t *trans = &split_transaction_table[split_shmem->transaction_id];
87 if (trans->slave_callback) {
88 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
89 }
90 }
91#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
67 } 92 }
68 break; 93 break;
69 94
diff --git a/drivers/avr/i2c_slave.h b/drivers/avr/i2c_slave.h
index 1cd0625ef..a8647c9da 100644
--- a/drivers/avr/i2c_slave.h
+++ b/drivers/avr/i2c_slave.h
@@ -22,7 +22,18 @@
22 22
23#pragma once 23#pragma once
24 24
25#define I2C_SLAVE_REG_COUNT 30 25#ifndef I2C_SLAVE_REG_COUNT
26
27# if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
28# include "transport.h"
29# define I2C_SLAVE_REG_COUNT sizeof(split_shared_memory_t)
30# else // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
31# define I2C_SLAVE_REG_COUNT 30
32# endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
33
34#endif // I2C_SLAVE_REG_COUNT
35
36_Static_assert(I2C_SLAVE_REG_COUNT < 256, "I2C target registers must be single byte");
26 37
27extern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT]; 38extern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
28 39
diff --git a/drivers/avr/serial.c b/drivers/avr/serial.c
index 3647bee0d..9a7345a53 100644
--- a/drivers/avr/serial.c
+++ b/drivers/avr/serial.c
@@ -224,15 +224,8 @@
224# define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2) 224# define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2)
225 225
226# define SLAVE_INT_WIDTH_US 1 226# define SLAVE_INT_WIDTH_US 1
227# ifndef SERIAL_USE_MULTI_TRANSACTION 227# define SLAVE_INT_ACK_WIDTH_UNIT 2
228# define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY 228# define SLAVE_INT_ACK_WIDTH 4
229# else
230# define SLAVE_INT_ACK_WIDTH_UNIT 2
231# define SLAVE_INT_ACK_WIDTH 4
232# endif
233
234static SSTD_t *Transaction_table = NULL;
235static uint8_t Transaction_table_size = 0;
236 229
237inline static void serial_delay(void) ALWAYS_INLINE; 230inline static void serial_delay(void) ALWAYS_INLINE;
238inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); } 231inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); }
@@ -259,16 +252,12 @@ inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); }
259inline static void serial_high(void) ALWAYS_INLINE; 252inline static void serial_high(void) ALWAYS_INLINE;
260inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); } 253inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); }
261 254
262void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) { 255void soft_serial_initiator_init(void) {
263 Transaction_table = sstd_table;
264 Transaction_table_size = (uint8_t)sstd_table_size;
265 serial_output(); 256 serial_output();
266 serial_high(); 257 serial_high();
267} 258}
268 259
269void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { 260void soft_serial_target_init(void) {
270 Transaction_table = sstd_table;
271 Transaction_table_size = (uint8_t)sstd_table_size;
272 serial_input_with_pullup(); 261 serial_input_with_pullup();
273 262
274 // Enable INT0-INT7 263 // Enable INT0-INT7
@@ -395,19 +384,14 @@ static inline uint8_t nibble_bits_count(uint8_t bits) {
395 384
396// interrupt handle to be used by the target device 385// interrupt handle to be used by the target device
397ISR(SERIAL_PIN_INTERRUPT) { 386ISR(SERIAL_PIN_INTERRUPT) {
398# ifndef SERIAL_USE_MULTI_TRANSACTION
399 serial_low();
400 serial_output();
401 SSTD_t *trans = Transaction_table;
402# else
403 // recive transaction table index 387 // recive transaction table index
404 uint8_t tid, bits; 388 uint8_t tid, bits;
405 uint8_t pecount = 0; 389 uint8_t pecount = 0;
406 sync_recv(); 390 sync_recv();
407 bits = serial_read_chunk(&pecount, 7); 391 bits = serial_read_chunk(&pecount, 8);
408 tid = bits >> 3; 392 tid = bits >> 3;
409 bits = (bits & 7) != nibble_bits_count(tid); 393 bits = (bits & 7) != (nibble_bits_count(tid) & 7);
410 if (bits || pecount > 0 || tid > Transaction_table_size) { 394 if (bits || pecount > 0 || tid > NUM_TOTAL_TRANSACTIONS) {
411 return; 395 return;
412 } 396 }
413 serial_delay_half1(); 397 serial_delay_half1();
@@ -415,18 +399,22 @@ ISR(SERIAL_PIN_INTERRUPT) {
415 serial_high(); // response step1 low->high 399 serial_high(); // response step1 low->high
416 serial_output(); 400 serial_output();
417 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH); 401 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH);
418 SSTD_t *trans = &Transaction_table[tid]; 402 split_transaction_desc_t *trans = &split_transaction_table[tid];
419 serial_low(); // response step2 ack high->low 403 serial_low(); // response step2 ack high->low
420# endif 404
405 // If the transaction has a callback, we can execute it now
406 if (trans->slave_callback) {
407 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
408 }
421 409
422 // target send phase 410 // target send phase
423 if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size); 411 if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size);
424 // target switch to input 412 // target switch to input
425 change_sender2reciver(); 413 change_sender2reciver();
426 414
427 // target recive phase 415 // target recive phase
428 if (trans->initiator2target_buffer_size > 0) { 416 if (trans->initiator2target_buffer_size > 0) {
429 if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size)) { 417 if (serial_recive_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) {
430 *trans->status = TRANSACTION_ACCEPTED; 418 *trans->status = TRANSACTION_ACCEPTED;
431 } else { 419 } else {
432 *trans->status = TRANSACTION_DATA_ERROR; 420 *trans->status = TRANSACTION_DATA_ERROR;
@@ -448,14 +436,12 @@ ISR(SERIAL_PIN_INTERRUPT) {
448// TRANSACTION_NO_RESPONSE 436// TRANSACTION_NO_RESPONSE
449// TRANSACTION_DATA_ERROR 437// TRANSACTION_DATA_ERROR
450// this code is very time dependent, so we need to disable interrupts 438// this code is very time dependent, so we need to disable interrupts
451# ifndef SERIAL_USE_MULTI_TRANSACTION
452int soft_serial_transaction(void) {
453 SSTD_t *trans = Transaction_table;
454# else
455int soft_serial_transaction(int sstd_index) { 439int soft_serial_transaction(int sstd_index) {
456 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; 440 if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
457 SSTD_t *trans = &Transaction_table[sstd_index]; 441 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
458# endif 442
443 if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
444
459 cli(); 445 cli();
460 446
461 // signal to the target that we want to start a transaction 447 // signal to the target that we want to start a transaction
@@ -463,27 +449,11 @@ int soft_serial_transaction(int sstd_index) {
463 serial_low(); 449 serial_low();
464 _delay_us(SLAVE_INT_WIDTH_US); 450 _delay_us(SLAVE_INT_WIDTH_US);
465 451
466# ifndef SERIAL_USE_MULTI_TRANSACTION
467 // wait for the target response
468 serial_input_with_pullup();
469 _delay_us(SLAVE_INT_RESPONSE_TIME);
470
471 // check if the target is present
472 if (serial_read_pin()) {
473 // target failed to pull the line low, assume not present
474 serial_output();
475 serial_high();
476 *trans->status = TRANSACTION_NO_RESPONSE;
477 sei();
478 return TRANSACTION_NO_RESPONSE;
479 }
480
481# else
482 // send transaction table index 452 // send transaction table index
483 int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index)); 453 int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index));
484 sync_send(); 454 sync_send();
485 _delay_sub_us(TID_SEND_ADJUST); 455 _delay_sub_us(TID_SEND_ADJUST);
486 serial_write_chunk(tid, 7); 456 serial_write_chunk(tid, 8);
487 serial_delay_half1(); 457 serial_delay_half1();
488 458
489 // wait for the target response (step1 low->high) 459 // wait for the target response (step1 low->high)
@@ -504,12 +474,11 @@ int soft_serial_transaction(int sstd_index) {
504 } 474 }
505 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT); 475 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);
506 } 476 }
507# endif
508 477
509 // initiator recive phase 478 // initiator recive phase
510 // if the target is present syncronize with it 479 // if the target is present syncronize with it
511 if (trans->target2initiator_buffer_size > 0) { 480 if (trans->target2initiator_buffer_size > 0) {
512 if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size)) { 481 if (!serial_recive_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) {
513 serial_output(); 482 serial_output();
514 serial_high(); 483 serial_high();
515 *trans->status = TRANSACTION_DATA_ERROR; 484 *trans->status = TRANSACTION_DATA_ERROR;
@@ -523,7 +492,7 @@ int soft_serial_transaction(int sstd_index) {
523 492
524 // initiator send phase 493 // initiator send phase
525 if (trans->initiator2target_buffer_size > 0) { 494 if (trans->initiator2target_buffer_size > 0) {
526 serial_send_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size); 495 serial_send_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size);
527 } 496 }
528 497
529 // always, release the line when not in use 498 // always, release the line when not in use
@@ -534,9 +503,8 @@ int soft_serial_transaction(int sstd_index) {
534 return TRANSACTION_END; 503 return TRANSACTION_END;
535} 504}
536 505
537# ifdef SERIAL_USE_MULTI_TRANSACTION
538int soft_serial_get_and_clean_status(int sstd_index) { 506int soft_serial_get_and_clean_status(int sstd_index) {
539 SSTD_t *trans = &Transaction_table[sstd_index]; 507 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
540 cli(); 508 cli();
541 int retval = *trans->status; 509 int retval = *trans->status;
542 *trans->status = 0; 510 *trans->status = 0;
@@ -544,8 +512,6 @@ int soft_serial_get_and_clean_status(int sstd_index) {
544 sei(); 512 sei();
545 return retval; 513 return retval;
546} 514}
547# endif
548
549#endif 515#endif
550 516
551// Helix serial.c history 517// Helix serial.c history
diff --git a/drivers/avr/serial.h b/drivers/avr/serial.h
deleted file mode 100644
index 53e66cf90..000000000
--- a/drivers/avr/serial.h
+++ /dev/null
@@ -1,62 +0,0 @@
1#pragma once
2
3#include <stdbool.h>
4
5// /////////////////////////////////////////////////////////////////
6// Need Soft Serial defines in config.h
7// /////////////////////////////////////////////////////////////////
8// ex.
9// #define SOFT_SERIAL_PIN ?? // ?? = D0,D1,D2,D3,E6
10// OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5
11// // 1: about 137kbps (default)
12// // 2: about 75kbps
13// // 3: about 39kbps
14// // 4: about 26kbps
15// // 5: about 20kbps
16//
17// //// USE simple API (using signle-type transaction function)
18// /* nothing */
19// //// USE flexible API (using multi-type transaction function)
20// #define SERIAL_USE_MULTI_TRANSACTION
21//
22// /////////////////////////////////////////////////////////////////
23
24// Soft Serial Transaction Descriptor
25typedef struct _SSTD_t {
26 uint8_t *status;
27 uint8_t initiator2target_buffer_size;
28 uint8_t *initiator2target_buffer;
29 uint8_t target2initiator_buffer_size;
30 uint8_t *target2initiator_buffer;
31} SSTD_t;
32#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t))
33
34// initiator is transaction start side
35void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size);
36// target is interrupt accept side
37void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size);
38
39// initiator resullt
40#define TRANSACTION_END 0
41#define TRANSACTION_NO_RESPONSE 0x1
42#define TRANSACTION_DATA_ERROR 0x2
43#define TRANSACTION_TYPE_ERROR 0x4
44#ifndef SERIAL_USE_MULTI_TRANSACTION
45int soft_serial_transaction(void);
46#else
47int soft_serial_transaction(int sstd_index);
48#endif
49
50// target status
51// *SSTD_t.status has
52// initiator:
53// TRANSACTION_END
54// or TRANSACTION_NO_RESPONSE
55// or TRANSACTION_DATA_ERROR
56// target:
57// TRANSACTION_DATA_ERROR
58// or TRANSACTION_ACCEPTED
59#define TRANSACTION_ACCEPTED 0x8
60#ifdef SERIAL_USE_MULTI_TRANSACTION
61int soft_serial_get_and_clean_status(int sstd_index);
62#endif
diff --git a/drivers/chibios/serial.c b/drivers/chibios/serial.c
index 54f7e1321..f54fbcee4 100644
--- a/drivers/chibios/serial.c
+++ b/drivers/chibios/serial.c
@@ -74,21 +74,12 @@ static THD_FUNCTION(Thread1, arg) {
74 } 74 }
75} 75}
76 76
77static SSTD_t *Transaction_table = NULL; 77void soft_serial_initiator_init(void) {
78static uint8_t Transaction_table_size = 0;
79
80void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) {
81 Transaction_table = sstd_table;
82 Transaction_table_size = (uint8_t)sstd_table_size;
83
84 serial_output(); 78 serial_output();
85 serial_high(); 79 serial_high();
86} 80}
87 81
88void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { 82void soft_serial_target_init(void) {
89 Transaction_table = sstd_table;
90 Transaction_table_size = (uint8_t)sstd_table_size;
91
92 serial_input(); 83 serial_input();
93 84
94 palEnablePadEvent(PAL_PORT(SOFT_SERIAL_PIN), PAL_PAD(SOFT_SERIAL_PIN), PAL_EVENT_MODE_FALLING_EDGE); 85 palEnablePadEvent(PAL_PORT(SOFT_SERIAL_PIN), PAL_PAD(SOFT_SERIAL_PIN), PAL_EVENT_MODE_FALLING_EDGE);
@@ -154,16 +145,14 @@ void interrupt_handler(void *arg) {
154 uint8_t checksum_computed = 0; 145 uint8_t checksum_computed = 0;
155 int sstd_index = 0; 146 int sstd_index = 0;
156 147
157#ifdef SERIAL_USE_MULTI_TRANSACTION
158 sstd_index = serial_read_byte(); 148 sstd_index = serial_read_byte();
159 sync_send(); 149 sync_send();
160#endif
161 150
162 SSTD_t *trans = &Transaction_table[sstd_index]; 151 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
163 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { 152 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) {
164 trans->initiator2target_buffer[i] = serial_read_byte(); 153 split_trans_initiator2target_buffer(trans)[i] = serial_read_byte();
165 sync_send(); 154 sync_send();
166 checksum_computed += trans->initiator2target_buffer[i]; 155 checksum_computed += split_trans_initiator2target_buffer(trans)[i];
167 } 156 }
168 checksum_computed ^= 7; 157 checksum_computed ^= 7;
169 uint8_t checksum_received = serial_read_byte(); 158 uint8_t checksum_received = serial_read_byte();
@@ -172,12 +161,17 @@ void interrupt_handler(void *arg) {
172 // wait for the sync to finish sending 161 // wait for the sync to finish sending
173 serial_delay(); 162 serial_delay();
174 163
164 // Allow any slave processing to occur
165 if (trans->slave_callback) {
166 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
167 }
168
175 uint8_t checksum = 0; 169 uint8_t checksum = 0;
176 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { 170 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) {
177 serial_write_byte(trans->target2initiator_buffer[i]); 171 serial_write_byte(split_trans_target2initiator_buffer(trans)[i]);
178 sync_send(); 172 sync_send();
179 serial_delay_half(); 173 serial_delay_half();
180 checksum += trans->target2initiator_buffer[i]; 174 checksum += split_trans_target2initiator_buffer(trans)[i];
181 } 175 }
182 serial_write_byte(checksum ^ 7); 176 serial_write_byte(checksum ^ 7);
183 sync_send(); 177 sync_send();
@@ -206,15 +200,10 @@ void interrupt_handler(void *arg) {
206// TRANSACTION_NO_RESPONSE 200// TRANSACTION_NO_RESPONSE
207// TRANSACTION_DATA_ERROR 201// TRANSACTION_DATA_ERROR
208// this code is very time dependent, so we need to disable interrupts 202// this code is very time dependent, so we need to disable interrupts
209#ifndef SERIAL_USE_MULTI_TRANSACTION
210int soft_serial_transaction(void) {
211 int sstd_index = 0;
212#else
213int soft_serial_transaction(int sstd_index) { 203int soft_serial_transaction(int sstd_index) {
214#endif 204 if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
215 205 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
216 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; 206 if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
217 SSTD_t *trans = &Transaction_table[sstd_index];
218 207
219 // TODO: remove extra delay between transactions 208 // TODO: remove extra delay between transactions
220 serial_delay(); 209 serial_delay();
@@ -244,14 +233,13 @@ int soft_serial_transaction(int sstd_index) {
244 233
245 uint8_t checksum = 0; 234 uint8_t checksum = 0;
246 // send data to the slave 235 // send data to the slave
247#ifdef SERIAL_USE_MULTI_TRANSACTION
248 serial_write_byte(sstd_index); // first chunk is transaction id 236 serial_write_byte(sstd_index); // first chunk is transaction id
249 sync_recv(); 237 sync_recv();
250#endif 238
251 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { 239 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) {
252 serial_write_byte(trans->initiator2target_buffer[i]); 240 serial_write_byte(split_trans_initiator2target_buffer(trans)[i]);
253 sync_recv(); 241 sync_recv();
254 checksum += trans->initiator2target_buffer[i]; 242 checksum += split_trans_initiator2target_buffer(trans)[i];
255 } 243 }
256 serial_write_byte(checksum ^ 7); 244 serial_write_byte(checksum ^ 7);
257 sync_recv(); 245 sync_recv();
@@ -262,9 +250,9 @@ int soft_serial_transaction(int sstd_index) {
262 // receive data from the slave 250 // receive data from the slave
263 uint8_t checksum_computed = 0; 251 uint8_t checksum_computed = 0;
264 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { 252 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) {
265 trans->target2initiator_buffer[i] = serial_read_byte(); 253 split_trans_target2initiator_buffer(trans)[i] = serial_read_byte();
266 sync_recv(); 254 sync_recv();
267 checksum_computed += trans->target2initiator_buffer[i]; 255 checksum_computed += split_trans_target2initiator_buffer(trans)[i];
268 } 256 }
269 checksum_computed ^= 7; 257 checksum_computed ^= 7;
270 uint8_t checksum_received = serial_read_byte(); 258 uint8_t checksum_received = serial_read_byte();
diff --git a/drivers/chibios/serial.h b/drivers/chibios/serial.h
deleted file mode 100644
index 0c1857d52..000000000
--- a/drivers/chibios/serial.h
+++ /dev/null
@@ -1,62 +0,0 @@
1#pragma once
2
3#include <stdbool.h>
4
5// /////////////////////////////////////////////////////////////////
6// Need Soft Serial defines in config.h
7// /////////////////////////////////////////////////////////////////
8// ex.
9// #define SOFT_SERIAL_PIN ?? // ?? = D0,D1,D2,D3,E6
10// OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5
11// // 1: about 137kbps (default)
12// // 2: about 75kbps
13// // 3: about 39kbps
14// // 4: about 26kbps
15// // 5: about 20kbps
16//
17// //// USE simple API (using signle-type transaction function)
18// /* nothing */
19// //// USE flexible API (using multi-type transaction function)
20// #define SERIAL_USE_MULTI_TRANSACTION
21//
22// /////////////////////////////////////////////////////////////////
23
24// Soft Serial Transaction Descriptor
25typedef struct _SSTD_t {
26 uint8_t *status;
27 uint8_t initiator2target_buffer_size;
28 uint8_t *initiator2target_buffer;
29 uint8_t target2initiator_buffer_size;
30 uint8_t *target2initiator_buffer;
31} SSTD_t;
32#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t))
33
34// initiator is transaction start side
35void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size);
36// target is interrupt accept side
37void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size);
38
39// initiator result
40#define TRANSACTION_END 0
41#define TRANSACTION_NO_RESPONSE 0x1
42#define TRANSACTION_DATA_ERROR 0x2
43#define TRANSACTION_TYPE_ERROR 0x4
44#ifndef SERIAL_USE_MULTI_TRANSACTION
45int soft_serial_transaction(void);
46#else
47int soft_serial_transaction(int sstd_index);
48#endif
49
50// target status
51// *SSTD_t.status has
52// initiator:
53// TRANSACTION_END
54// or TRANSACTION_NO_RESPONSE
55// or TRANSACTION_DATA_ERROR
56// target:
57// TRANSACTION_DATA_ERROR
58// or TRANSACTION_ACCEPTED
59#define TRANSACTION_ACCEPTED 0x8
60#ifdef SERIAL_USE_MULTI_TRANSACTION
61int soft_serial_get_and_clean_status(int sstd_index);
62#endif
diff --git a/drivers/chibios/serial_usart.c b/drivers/chibios/serial_usart.c
index cae29388c..9f180d2d7 100644
--- a/drivers/chibios/serial_usart.c
+++ b/drivers/chibios/serial_usart.c
@@ -113,37 +113,29 @@ void usart_slave_init(void) {
113 chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); 113 chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
114} 114}
115 115
116static SSTD_t* Transaction_table = NULL; 116void soft_serial_initiator_init(void) { usart_master_init(); }
117static uint8_t Transaction_table_size = 0;
118 117
119void soft_serial_initiator_init(SSTD_t* sstd_table, int sstd_table_size) { 118void soft_serial_target_init(void) { usart_slave_init(); }
120 Transaction_table = sstd_table;
121 Transaction_table_size = (uint8_t)sstd_table_size;
122
123 usart_master_init();
124}
125
126void soft_serial_target_init(SSTD_t* sstd_table, int sstd_table_size) {
127 Transaction_table = sstd_table;
128 Transaction_table_size = (uint8_t)sstd_table_size;
129
130 usart_slave_init();
131}
132 119
133void handle_soft_serial_slave(void) { 120void handle_soft_serial_slave(void) {
134 uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id 121 uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id
135 SSTD_t* trans = &Transaction_table[sstd_index]; 122 split_transaction_desc_t* trans = &split_transaction_table[sstd_index];
136 123
137 // Always write back the sstd_index as part of a basic handshake 124 // Always write back the sstd_index as part of a basic handshake
138 sstd_index ^= HANDSHAKE_MAGIC; 125 sstd_index ^= HANDSHAKE_MAGIC;
139 sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index)); 126 sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index));
140 127
141 if (trans->initiator2target_buffer_size) { 128 if (trans->initiator2target_buffer_size) {
142 sdRead(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size); 129 sdRead(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size);
130 }
131
132 // Allow any slave processing to occur
133 if (trans->slave_callback) {
134 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
143 } 135 }
144 136
145 if (trans->target2initiator_buffer_size) { 137 if (trans->target2initiator_buffer_size) {
146 sdWrite(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size); 138 sdWrite(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size);
147 } 139 }
148 140
149 if (trans->status) { 141 if (trans->status) {
@@ -160,17 +152,14 @@ void handle_soft_serial_slave(void) {
160// TRANSACTION_END 152// TRANSACTION_END
161// TRANSACTION_NO_RESPONSE 153// TRANSACTION_NO_RESPONSE
162// TRANSACTION_DATA_ERROR 154// TRANSACTION_DATA_ERROR
163#ifndef SERIAL_USE_MULTI_TRANSACTION
164int soft_serial_transaction(void) {
165 uint8_t sstd_index = 0;
166#else
167int soft_serial_transaction(int index) { 155int soft_serial_transaction(int index) {
168 uint8_t sstd_index = index; 156 uint8_t sstd_index = index;
169#endif
170 157
171 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; 158 if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
172 SSTD_t* trans = &Transaction_table[sstd_index]; 159 split_transaction_desc_t* trans = &split_transaction_table[sstd_index];
173 msg_t res = 0; 160 msg_t res = 0;
161
162 if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
174 163
175 sdClear(&SERIAL_USART_DRIVER); 164 sdClear(&SERIAL_USART_DRIVER);
176 165
@@ -189,7 +178,7 @@ int soft_serial_transaction(int index) {
189 } 178 }
190 179
191 if (trans->initiator2target_buffer_size) { 180 if (trans->initiator2target_buffer_size) {
192 res = sdWriteTimeout(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); 181 res = sdWriteTimeout(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT));
193 if (res < 0) { 182 if (res < 0) {
194 dprintf("serial::usart_transmit NO_RESPONSE\n"); 183 dprintf("serial::usart_transmit NO_RESPONSE\n");
195 return TRANSACTION_NO_RESPONSE; 184 return TRANSACTION_NO_RESPONSE;
@@ -197,7 +186,7 @@ int soft_serial_transaction(int index) {
197 } 186 }
198 187
199 if (trans->target2initiator_buffer_size) { 188 if (trans->target2initiator_buffer_size) {
200 res = sdReadTimeout(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); 189 res = sdReadTimeout(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT));
201 if (res < 0) { 190 if (res < 0) {
202 dprintf("serial::usart_receive NO_RESPONSE\n"); 191 dprintf("serial::usart_receive NO_RESPONSE\n");
203 return TRANSACTION_NO_RESPONSE; 192 return TRANSACTION_NO_RESPONSE;
diff --git a/drivers/serial.h b/drivers/serial.h
new file mode 100644
index 000000000..d9c2a69e9
--- /dev/null
+++ b/drivers/serial.h
@@ -0,0 +1,46 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20#include <stdbool.h>
21
22#include <transactions.h>
23
24// initiator is transaction start side
25void soft_serial_initiator_init(void);
26// target is interrupt accept side
27void soft_serial_target_init(void);
28
29// initiator result
30#define TRANSACTION_END 0
31#define TRANSACTION_NO_RESPONSE 0x1
32#define TRANSACTION_DATA_ERROR 0x2
33#define TRANSACTION_TYPE_ERROR 0x4
34int soft_serial_transaction(int sstd_index);
35
36// target status
37// *SSTD_t.status has
38// initiator:
39// TRANSACTION_END
40// or TRANSACTION_NO_RESPONSE
41// or TRANSACTION_DATA_ERROR
42// target:
43// TRANSACTION_DATA_ERROR
44// or TRANSACTION_ACCEPTED
45#define TRANSACTION_ACCEPTED 0x8
46int soft_serial_get_and_clean_status(int sstd_index);
diff --git a/keyboards/draculad/config.h b/keyboards/draculad/config.h
index 8a27fdea4..d8a9fbd37 100644
--- a/keyboards/draculad/config.h
+++ b/keyboards/draculad/config.h
@@ -65,3 +65,5 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
65#define UNUSED_PINS 65#define UNUSED_PINS
66 66
67#define EE_HANDS 67#define EE_HANDS
68
69#define LAYER_STATE_8BIT \ No newline at end of file
diff --git a/keyboards/handwired/freoduo/rules.mk b/keyboards/handwired/freoduo/rules.mk
index e3da3753e..d8923557d 100644
--- a/keyboards/handwired/freoduo/rules.mk
+++ b/keyboards/handwired/freoduo/rules.mk
@@ -11,7 +11,7 @@ BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration
11MOUSEKEY_ENABLE = yes # Mouse keys 11MOUSEKEY_ENABLE = yes # Mouse keys
12EXTRAKEY_ENABLE = yes # Audio control and System control 12EXTRAKEY_ENABLE = yes # Audio control and System control
13CONSOLE_ENABLE = no # Console for debug 13CONSOLE_ENABLE = no # Console for debug
14COMMAND_ENABLE = yes # Commands for debug and configuration 14COMMAND_ENABLE = no # Commands for debug and configuration
15# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE 15# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
16SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend 16SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
17# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work 17# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
@@ -21,4 +21,4 @@ RGBLIGHT_ENABLE = yes # Enable keyboard RGB underglow
21BLUETOOTH_ENABLE = no # Enable Bluetooth 21BLUETOOTH_ENABLE = no # Enable Bluetooth
22AUDIO_ENABLE = no # Audio output 22AUDIO_ENABLE = no # Audio output
23VELOCIKEY_ENABLE = yes 23VELOCIKEY_ENABLE = yes
24SPLIT_KEYBOARD = yes \ No newline at end of file 24SPLIT_KEYBOARD = yes
diff --git a/keyboards/helix/rev2/keymaps/default/rules.mk b/keyboards/helix/rev2/keymaps/default/rules.mk
index 206e836ec..c16f3e2b8 100644
--- a/keyboards/helix/rev2/keymaps/default/rules.mk
+++ b/keyboards/helix/rev2/keymaps/default/rules.mk
@@ -5,7 +5,7 @@
5# See TOP/keyboards/helix/rules.mk for a list of options that can be set. 5# See TOP/keyboards/helix/rules.mk for a list of options that can be set.
6# See TOP/docs/config_options.md for more information. 6# See TOP/docs/config_options.md for more information.
7# 7#
8LTO_ENABLE = no # if firmware size over limit, try this option 8LTO_ENABLE = yes # if firmware size over limit, try this option
9 9
10# Helix Spacific Build Options 10# Helix Spacific Build Options
11# you can uncomment and edit follows 7 Variables 11# you can uncomment and edit follows 7 Variables
diff --git a/keyboards/keebio/iris/rev2/rules.mk b/keyboards/keebio/iris/rev2/rules.mk
index 765fa7f07..197bad476 100644
--- a/keyboards/keebio/iris/rev2/rules.mk
+++ b/keyboards/keebio/iris/rev2/rules.mk
@@ -32,3 +32,5 @@ RGBLIGHT_ENABLE = yes # Enable WS2812 RGB underlight.
32SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend 32SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
33 33
34SPLIT_KEYBOARD = yes 34SPLIT_KEYBOARD = yes
35
36LTO_ENABLE = yes \ No newline at end of file
diff --git a/keyboards/keebio/iris/rev3/rules.mk b/keyboards/keebio/iris/rev3/rules.mk
index ea92bc98f..b64eab5a4 100644
--- a/keyboards/keebio/iris/rev3/rules.mk
+++ b/keyboards/keebio/iris/rev3/rules.mk
@@ -33,3 +33,4 @@ SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
33 33
34SPLIT_KEYBOARD = yes 34SPLIT_KEYBOARD = yes
35ENCODER_ENABLE = yes 35ENCODER_ENABLE = yes
36LTO_ENABLE = yes \ No newline at end of file
diff --git a/keyboards/keebio/iris/rev4/rules.mk b/keyboards/keebio/iris/rev4/rules.mk
index ea92bc98f..9bf5688da 100644
--- a/keyboards/keebio/iris/rev4/rules.mk
+++ b/keyboards/keebio/iris/rev4/rules.mk
@@ -33,3 +33,5 @@ SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
33 33
34SPLIT_KEYBOARD = yes 34SPLIT_KEYBOARD = yes
35ENCODER_ENABLE = yes 35ENCODER_ENABLE = yes
36
37LTO_ENABLE = yes \ No newline at end of file
diff --git a/keyboards/keebio/quefrency/rules.mk b/keyboards/keebio/quefrency/rules.mk
index 2c5ad0c36..c674e1016 100644
--- a/keyboards/keebio/quefrency/rules.mk
+++ b/keyboards/keebio/quefrency/rules.mk
@@ -19,3 +19,4 @@ SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
19SPLIT_KEYBOARD = yes 19SPLIT_KEYBOARD = yes
20 20
21DEFAULT_FOLDER = keebio/quefrency/rev1 21DEFAULT_FOLDER = keebio/quefrency/rev1
22LTO_ENABLE = yes
diff --git a/keyboards/keebio/viterbi/rev2/rules.mk b/keyboards/keebio/viterbi/rev2/rules.mk
index f95e7ae6a..829d6a56e 100644
--- a/keyboards/keebio/viterbi/rev2/rules.mk
+++ b/keyboards/keebio/viterbi/rev2/rules.mk
@@ -1,3 +1,5 @@
1BACKLIGHT_ENABLE = yes 1BACKLIGHT_ENABLE = yes
2 2
3LAYOUTS = ortho_5x14 3LAYOUTS = ortho_5x14
4
5LTO_ENABLE = yes \ No newline at end of file
diff --git a/keyboards/keebio/viterbi/rules.mk b/keyboards/keebio/viterbi/rules.mk
index 0b746d172..92576d33e 100644
--- a/keyboards/keebio/viterbi/rules.mk
+++ b/keyboards/keebio/viterbi/rules.mk
@@ -19,16 +19,16 @@ BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration
19MOUSEKEY_ENABLE = yes # Mouse keys 19MOUSEKEY_ENABLE = yes # Mouse keys
20EXTRAKEY_ENABLE = yes # Audio control and System control 20EXTRAKEY_ENABLE = yes # Audio control and System control
21CONSOLE_ENABLE = no # Console for debug 21CONSOLE_ENABLE = no # Console for debug
22COMMAND_ENABLE = yes # Commands for debug and configuration 22COMMAND_ENABLE = no # Commands for debug and configuration
23NKRO_ENABLE = no # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work 23NKRO_ENABLE = no # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
24BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality 24BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
25MIDI_ENABLE = no # MIDI controls 25MIDI_ENABLE = no # MIDI controls
26AUDIO_ENABLE = no # Audio output on port C6 26AUDIO_ENABLE = no # Audio output on port C6
27UNICODE_ENABLE = no # Unicode 27UNICODE_ENABLE = no # Unicode
28BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID 28BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID
29RGBLIGHT_ENABLE = no # Enable WS2812 RGB underlight. 29RGBLIGHT_ENABLE = no # Enable WS2812 RGB underlight.
30# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE 30# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
31SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend 31SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
32 32
33SPLIT_KEYBOARD = yes 33SPLIT_KEYBOARD = yes
34 34
diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c
index 2cf7b7058..56d91b07f 100644
--- a/quantum/split_common/matrix.c
+++ b/quantum/split_common/matrix.c
@@ -23,9 +23,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
23#include "quantum.h" 23#include "quantum.h"
24#include "split_util.h" 24#include "split_util.h"
25#include "config.h" 25#include "config.h"
26#include "transport.h" 26#include "transactions.h"
27 27
28#define ERROR_DISCONNECT_COUNT 5 28#ifndef ERROR_DISCONNECT_COUNT
29# define ERROR_DISCONNECT_COUNT 5
30#endif // ERROR_DISCONNECT_COUNT
29 31
30#define ROWS_PER_HAND (MATRIX_ROWS / 2) 32#define ROWS_PER_HAND (MATRIX_ROWS / 2)
31 33
diff --git a/quantum/split_common/post_config.h b/quantum/split_common/post_config.h
index 4ae1d5273..a4c0a1956 100644
--- a/quantum/split_common/post_config.h
+++ b/quantum/split_common/post_config.h
@@ -7,13 +7,4 @@
7# ifndef F_SCL 7# ifndef F_SCL
8# define F_SCL 100000UL // SCL frequency 8# define F_SCL 100000UL // SCL frequency
9# endif 9# endif
10
11#else // use serial
12// When using serial, the user must define RGBLIGHT_SPLIT explicitly
13// in config.h as needed.
14// see quantum/rgblight_post_config.h
15# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
16// When using serial and RGBLIGHT_SPLIT need separate transaction
17# define SERIAL_USE_MULTI_TRANSACTION
18# endif
19#endif 10#endif
diff --git a/quantum/split_common/transaction_id_define.h b/quantum/split_common/transaction_id_define.h
new file mode 100644
index 000000000..464c73478
--- /dev/null
+++ b/quantum/split_common/transaction_id_define.h
@@ -0,0 +1,94 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19enum serial_transaction_id {
20#ifdef USE_I2C
21 I2C_EXECUTE_CALLBACK,
22#endif // USE_I2C
23
24 GET_SLAVE_MATRIX_CHECKSUM,
25 GET_SLAVE_MATRIX_DATA,
26
27#ifdef SPLIT_TRANSPORT_MIRROR
28 PUT_MASTER_MATRIX,
29#endif // SPLIT_TRANSPORT_MIRROR
30
31#ifdef ENCODER_ENABLE
32 GET_ENCODERS_CHECKSUM,
33 GET_ENCODERS_DATA,
34#endif // ENCODER_ENABLE
35
36#ifndef DISABLE_SYNC_TIMER
37 PUT_SYNC_TIMER,
38#endif // DISABLE_SYNC_TIMER
39
40#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
41 PUT_LAYER_STATE,
42 PUT_DEFAULT_LAYER_STATE,
43#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
44
45#ifdef SPLIT_LED_STATE_ENABLE
46 PUT_LED_STATE,
47#endif // SPLIT_LED_STATE_ENABLE
48
49#ifdef SPLIT_MODS_ENABLE
50 PUT_MODS,
51#endif // SPLIT_MODS_ENABLE
52
53#ifdef BACKLIGHT_ENABLE
54 PUT_BACKLIGHT,
55#endif // BACKLIGHT_ENABLE
56
57#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
58 PUT_RGBLIGHT,
59#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
60
61#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
62 PUT_LED_MATRIX,
63#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
64
65#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
66 PUT_RGB_MATRIX,
67#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
68
69#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
70 PUT_WPM,
71#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
72
73#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
74 PUT_RPC_INFO,
75 PUT_RPC_REQ_DATA,
76 EXECUTE_RPC,
77 GET_RPC_RESP_DATA,
78#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
79
80// keyboard-specific
81#ifdef SPLIT_TRANSACTION_IDS_KB
82 SPLIT_TRANSACTION_IDS_KB,
83#endif // SPLIT_TRANSACTION_IDS_KB
84
85// user/keymap-specific
86#ifdef SPLIT_TRANSACTION_IDS_USER
87 SPLIT_TRANSACTION_IDS_USER,
88#endif // SPLIT_TRANSACTION_IDS_USER
89
90 NUM_TOTAL_TRANSACTIONS
91};
92
93// Ensure we only use 5 bits for transaction
94_Static_assert(NUM_TOTAL_TRANSACTIONS <= (1 << 5), "Max number of usable transactions exceeded");
diff --git a/quantum/split_common/transactions.c b/quantum/split_common/transactions.c
new file mode 100644
index 000000000..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
46static 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
64void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
65void 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
71bool 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
101inline 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
116inline 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
127inline 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
135static 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
150static 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
168static 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
173static 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
195static 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
204static 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
234static 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
248static 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
273static 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
284static 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
310static 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
316static 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
338static 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
370static 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
395static 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
401static 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
420static 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
432static 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
457static 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
465static 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
487static 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
495static 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
517static 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), &current_wpm, sizeof(current_wpm));
521}
522
523static 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
539uint8_t dummy;
540split_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
571bool 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
588void 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
605void 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
615bool 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
649void 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
658void 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)
diff --git a/quantum/split_common/transactions.h b/quantum/split_common/transactions.h
new file mode 100644
index 000000000..4306ba1d8
--- /dev/null
+++ b/quantum/split_common/transactions.h
@@ -0,0 +1,54 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "stdint.h"
20#include "stdbool.h"
21
22#include "matrix.h"
23#include "transaction_id_define.h"
24#include "transport.h"
25
26typedef void (*slave_callback_t)(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
27
28// Split transaction Descriptor
29typedef struct _split_transaction_desc_t {
30 uint8_t * status;
31 uint8_t initiator2target_buffer_size;
32 uint16_t initiator2target_offset;
33 uint8_t target2initiator_buffer_size;
34 uint16_t target2initiator_offset;
35 slave_callback_t slave_callback;
36} split_transaction_desc_t;
37
38// Forward declaration for the split transactions
39extern split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS];
40
41#define split_shmem_offset_ptr(offset) ((void *)(((uint8_t *)split_shmem) + (offset)))
42#define split_trans_initiator2target_buffer(trans) (split_shmem_offset_ptr((trans)->initiator2target_offset))
43#define split_trans_target2initiator_buffer(trans) (split_shmem_offset_ptr((trans)->target2initiator_offset))
44
45// returns false if valid data not received from slave
46bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
47void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
48
49void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback);
50
51bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
52
53#define transaction_rpc_send(transaction_id, initiator2target_buffer_size, initiator2target_buffer) transaction_rpc_exec(transaction_id, initiator2target_buffer_size, initiator2target_buffer, 0, NULL)
54#define transaction_rpc_recv(transaction_id, target2initiator_buffer_size, target2initiator_buffer) transaction_rpc_exec(transaction_id, 0, NULL, target2initiator_buffer_size, target2initiator_buffer)
diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c
index 9ed0f7591..a711ef85f 100644
--- a/quantum/split_common/transport.c
+++ b/quantum/split_common/transport.c
@@ -1,452 +1,118 @@
1#include <string.h> 1/* Copyright 2021 QMK
2#include <stddef.h> 2 *
3 3 * This program is free software: you can redistribute it and/or modify
4#include "config.h" 4 * it under the terms of the GNU General Public License as published by
5#include "matrix.h" 5 * the Free Software Foundation, either version 2 of the License, or
6#include "quantum.h" 6 * (at your option) any later version.
7 7 *
8#define ROWS_PER_HAND (MATRIX_ROWS / 2) 8 * This program is distributed in the hope that it will be useful,
9#define SYNC_TIMER_OFFSET 2 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11#ifdef RGBLIGHT_ENABLE 11 * GNU General Public License for more details.
12# include "rgblight.h" 12 *
13#endif 13 * You should have received a copy of the GNU General Public License
14 14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15#ifdef BACKLIGHT_ENABLE 15 */
16# include "backlight.h"
17#endif
18
19#ifdef ENCODER_ENABLE
20# include "encoder.h"
21static pin_t encoders_pad[] = ENCODERS_PAD_A;
22# define NUMBER_OF_ENCODERS (sizeof(encoders_pad) / sizeof(pin_t))
23#endif
24
25#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
26# include "led_matrix.h"
27#endif
28#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
29# include "rgb_matrix.h"
30#endif
31
32#if defined(USE_I2C)
33 16
34# include "i2c_master.h" 17#include <string.h>
35# include "i2c_slave.h" 18#include <debug.h>
36
37typedef struct _I2C_slave_buffer_t {
38# ifndef DISABLE_SYNC_TIMER
39 uint32_t sync_timer;
40# endif
41# ifdef SPLIT_TRANSPORT_MIRROR
42 matrix_row_t mmatrix[ROWS_PER_HAND];
43# endif
44 matrix_row_t smatrix[ROWS_PER_HAND];
45# ifdef SPLIT_MODS_ENABLE
46 uint8_t real_mods;
47 uint8_t weak_mods;
48# ifndef NO_ACTION_ONESHOT
49 uint8_t oneshot_mods;
50# endif
51# endif
52# ifdef BACKLIGHT_ENABLE
53 uint8_t backlight_level;
54# endif
55# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
56 rgblight_syncinfo_t rgblight_sync;
57# endif
58# ifdef ENCODER_ENABLE
59 uint8_t encoder_state[NUMBER_OF_ENCODERS];
60# endif
61# ifdef WPM_ENABLE
62 uint8_t current_wpm;
63# endif
64# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
65 led_eeconfig_t led_matrix;
66 bool led_suspend_state;
67# endif
68# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
69 rgb_config_t rgb_matrix;
70 bool rgb_suspend_state;
71# endif
72} I2C_slave_buffer_t;
73 19
74static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg; 20#include "transactions.h"
21#include "transport.h"
22#include "transaction_id_define.h"
23#include "atomic_util.h"
75 24
76# define I2C_SYNC_TIME_START offsetof(I2C_slave_buffer_t, sync_timer) 25#ifdef USE_I2C
77# define I2C_KEYMAP_MASTER_START offsetof(I2C_slave_buffer_t, mmatrix)
78# define I2C_KEYMAP_SLAVE_START offsetof(I2C_slave_buffer_t, smatrix)
79# define I2C_REAL_MODS_START offsetof(I2C_slave_buffer_t, real_mods)
80# define I2C_WEAK_MODS_START offsetof(I2C_slave_buffer_t, weak_mods)
81# define I2C_ONESHOT_MODS_START offsetof(I2C_slave_buffer_t, oneshot_mods)
82# define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level)
83# define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync)
84# define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state)
85# define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm)
86# define I2C_LED_MATRIX_START offsetof(I2C_slave_buffer_t, led_matrix)
87# define I2C_LED_SUSPEND_START offsetof(I2C_slave_buffer_t, led_suspend_state)
88# define I2C_RGB_MATRIX_START offsetof(I2C_slave_buffer_t, rgb_matrix)
89# define I2C_RGB_SUSPEND_START offsetof(I2C_slave_buffer_t, rgb_suspend_state)
90 26
91# define TIMEOUT 100 27# ifndef SLAVE_I2C_TIMEOUT
28# define SLAVE_I2C_TIMEOUT 100
29# endif // SLAVE_I2C_TIMEOUT
92 30
93# ifndef SLAVE_I2C_ADDRESS 31# ifndef SLAVE_I2C_ADDRESS
94# define SLAVE_I2C_ADDRESS 0x32 32# define SLAVE_I2C_ADDRESS 0x32
95# endif 33# endif
96 34
97// Get rows from other half over i2c 35# include "i2c_master.h"
98bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { 36# include "i2c_slave.h"
99 i2c_readReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_SLAVE_START, (void *)slave_matrix, sizeof(i2c_buffer->smatrix), TIMEOUT);
100# ifdef SPLIT_TRANSPORT_MIRROR
101 i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_MASTER_START, (void *)master_matrix, sizeof(i2c_buffer->mmatrix), TIMEOUT);
102# endif
103 37
104 // write backlight info 38// Ensure the I2C buffer has enough space
105# ifdef BACKLIGHT_ENABLE 39_Static_assert(sizeof(split_shared_memory_t) <= I2C_SLAVE_REG_COUNT, "split_shared_memory_t too large for I2C_SLAVE_REG_COUNT");
106 uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0;
107 if (level != i2c_buffer->backlight_level) {
108 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_BACKLIGHT_START, (void *)&level, sizeof(level), TIMEOUT) >= 0) {
109 i2c_buffer->backlight_level = level;
110 }
111 }
112# endif
113 40
114# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) 41split_shared_memory_t *const split_shmem = (split_shared_memory_t *)i2c_slave_reg;
115 if (rgblight_get_change_flags()) {
116 rgblight_syncinfo_t rgblight_sync;
117 rgblight_get_syncinfo(&rgblight_sync);
118 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_START, (void *)&rgblight_sync, sizeof(rgblight_sync), TIMEOUT) >= 0) {
119 rgblight_clear_change_flags();
120 }
121 }
122# endif
123 42
124# ifdef ENCODER_ENABLE 43void transport_master_init(void) { i2c_init(); }
125 i2c_readReg(SLAVE_I2C_ADDRESS, I2C_ENCODER_START, (void *)i2c_buffer->encoder_state, sizeof(i2c_buffer->encoder_state), TIMEOUT); 44void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); }
126 encoder_update_raw(i2c_buffer->encoder_state);
127# endif
128 45
129# ifdef WPM_ENABLE 46i2c_status_t transport_trigger_callback(int8_t id) {
130 uint8_t current_wpm = get_current_wpm(); 47 // If there's no callback, indicate that we were successful
131 if (current_wpm != i2c_buffer->current_wpm) { 48 if (!split_transaction_table[id].slave_callback) {
132 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WPM_START, (void *)&current_wpm, sizeof(current_wpm), TIMEOUT) >= 0) { 49 return I2C_STATUS_SUCCESS;
133 i2c_buffer->current_wpm = current_wpm;
134 }
135 } 50 }
136# endif
137 51
138# ifdef SPLIT_MODS_ENABLE 52 // Kick off the "callback executor", now that data has been written to the slave
139 uint8_t real_mods = get_mods(); 53 split_shmem->transaction_id = id;
140 if (real_mods != i2c_buffer->real_mods) { 54 split_transaction_desc_t *trans = &split_transaction_table[I2C_EXECUTE_CALLBACK];
141 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_REAL_MODS_START, (void *)&real_mods, sizeof(real_mods), TIMEOUT) >= 0) { 55 return i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, SLAVE_I2C_TIMEOUT);
142 i2c_buffer->real_mods = real_mods; 56}
57
58bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) {
59 i2c_status_t status;
60 split_transaction_desc_t *trans = &split_transaction_table[id];
61 if (initiator2target_length > 0) {
62 size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length;
63 memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len);
64 if ((status = i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) {
65 return false;
143 } 66 }
144 } 67 }
145 68
146 uint8_t weak_mods = get_weak_mods(); 69 // If we need to execute a callback on the slave, do so
147 if (weak_mods != i2c_buffer->weak_mods) { 70 if ((status = transport_trigger_callback(id)) < 0) {
148 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WEAK_MODS_START, (void *)&weak_mods, sizeof(weak_mods), TIMEOUT) >= 0) { 71 return false;
149 i2c_buffer->weak_mods = weak_mods;
150 }
151 } 72 }
152 73
153# ifndef NO_ACTION_ONESHOT 74 if (target2initiator_length > 0) {
154 uint8_t oneshot_mods = get_oneshot_mods(); 75 size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length;
155 if (oneshot_mods != i2c_buffer->oneshot_mods) { 76 if ((status = i2c_readReg(SLAVE_I2C_ADDRESS, trans->target2initiator_offset, split_trans_target2initiator_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) {
156 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_ONESHOT_MODS_START, (void *)&oneshot_mods, sizeof(oneshot_mods), TIMEOUT) >= 0) { 77 return false;
157 i2c_buffer->oneshot_mods = oneshot_mods;
158 } 78 }
79 memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len);
159 } 80 }
160# endif
161# endif
162 81
163# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
164 i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_MATRIX_START, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix), TIMEOUT);
165 bool suspend_state = led_matrix_get_suspend_state();
166 i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->led_suspend_state), TIMEOUT);
167# endif
168# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
169 i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_MATRIX_START, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix), TIMEOUT);
170 bool suspend_state = rgb_matrix_get_suspend_state();
171 i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->rgb_suspend_state), TIMEOUT);
172# endif
173
174# ifndef DISABLE_SYNC_TIMER
175 i2c_buffer->sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
176 i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_SYNC_TIME_START, (void *)&i2c_buffer->sync_timer, sizeof(i2c_buffer->sync_timer), TIMEOUT);
177# endif
178 return true; 82 return true;
179} 83}
180 84
181void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { 85#else // USE_I2C
182# ifndef DISABLE_SYNC_TIMER
183 sync_timer_update(i2c_buffer->sync_timer);
184# endif
185 // Copy matrix to I2C buffer
186 memcpy((void *)i2c_buffer->smatrix, (void *)slave_matrix, sizeof(i2c_buffer->smatrix));
187# ifdef SPLIT_TRANSPORT_MIRROR
188 memcpy((void *)master_matrix, (void *)i2c_buffer->mmatrix, sizeof(i2c_buffer->mmatrix));
189# endif
190
191// Read Backlight Info
192# ifdef BACKLIGHT_ENABLE
193 backlight_set(i2c_buffer->backlight_level);
194# endif
195
196# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
197 // Update the RGB with the new data
198 if (i2c_buffer->rgblight_sync.status.change_flags != 0) {
199 rgblight_update_sync(&i2c_buffer->rgblight_sync, false);
200 i2c_buffer->rgblight_sync.status.change_flags = 0;
201 }
202# endif
203
204# ifdef ENCODER_ENABLE
205 encoder_state_raw(i2c_buffer->encoder_state);
206# endif
207
208# ifdef WPM_ENABLE
209 set_current_wpm(i2c_buffer->current_wpm);
210# endif
211
212# ifdef SPLIT_MODS_ENABLE
213 set_mods(i2c_buffer->real_mods);
214 set_weak_mods(i2c_buffer->weak_mods);
215# ifndef NO_ACTION_ONESHOT
216 set_oneshot_mods(i2c_buffer->oneshot_mods);
217# endif
218# endif
219
220# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
221 memcpy((void *)i2c_buffer->led_matrix, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix));
222 led_matrix_set_suspend_state(i2c_buffer->led_suspend_state);
223# endif
224# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
225 memcpy((void *)i2c_buffer->rgb_matrix, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix));
226 rgb_matrix_set_suspend_state(i2c_buffer->rgb_suspend_state);
227# endif
228}
229
230void transport_master_init(void) { i2c_init(); }
231
232void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); }
233
234#else // USE_SERIAL
235 86
236# include "serial.h" 87# include "serial.h"
237 88
238typedef struct _Serial_s2m_buffer_t { 89static split_shared_memory_t shared_memory;
239 // TODO: if MATRIX_COLS > 8 change to uint8_t packed_matrix[] for pack/unpack 90split_shared_memory_t *const split_shmem = &shared_memory;
240 matrix_row_t smatrix[ROWS_PER_HAND];
241
242# ifdef ENCODER_ENABLE
243 uint8_t encoder_state[NUMBER_OF_ENCODERS];
244# endif
245
246} Serial_s2m_buffer_t;
247
248typedef struct _Serial_m2s_buffer_t {
249# ifdef SPLIT_MODS_ENABLE
250 uint8_t real_mods;
251 uint8_t weak_mods;
252# ifndef NO_ACTION_ONESHOT
253 uint8_t oneshot_mods;
254# endif
255# endif
256# ifndef DISABLE_SYNC_TIMER
257 uint32_t sync_timer;
258# endif
259# ifdef SPLIT_TRANSPORT_MIRROR
260 matrix_row_t mmatrix[ROWS_PER_HAND];
261# endif
262# ifdef BACKLIGHT_ENABLE
263 uint8_t backlight_level;
264# endif
265# ifdef WPM_ENABLE
266 uint8_t current_wpm;
267# endif
268# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
269 led_eeconfig_t led_matrix;
270 bool led_suspend_state;
271# endif
272# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
273 rgb_config_t rgb_matrix;
274 bool rgb_suspend_state;
275# endif
276} Serial_m2s_buffer_t;
277
278# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
279// When MCUs on both sides drive their respective RGB LED chains,
280// it is necessary to synchronize, so it is necessary to communicate RGB
281// information. In that case, define RGBLIGHT_SPLIT with info on the number
282// of LEDs on each half.
283//
284// Otherwise, if the master side MCU drives both sides RGB LED chains,
285// there is no need to communicate.
286
287typedef struct _Serial_rgblight_t {
288 rgblight_syncinfo_t rgblight_sync;
289} Serial_rgblight_t;
290 91
291volatile Serial_rgblight_t serial_rgblight = {}; 92void transport_master_init(void) { soft_serial_initiator_init(); }
292uint8_t volatile status_rgblight = 0; 93void transport_slave_init(void) { soft_serial_target_init(); }
293# endif
294
295volatile Serial_s2m_buffer_t serial_s2m_buffer = {};
296volatile Serial_m2s_buffer_t serial_m2s_buffer = {};
297uint8_t volatile status0 = 0;
298
299enum serial_transaction_id {
300 GET_SLAVE_MATRIX = 0,
301# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
302 PUT_RGBLIGHT,
303# endif
304};
305
306SSTD_t transactions[] = {
307 [GET_SLAVE_MATRIX] =
308 {
309 (uint8_t *)&status0,
310 sizeof(serial_m2s_buffer),
311 (uint8_t *)&serial_m2s_buffer,
312 sizeof(serial_s2m_buffer),
313 (uint8_t *)&serial_s2m_buffer,
314 },
315# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
316 [PUT_RGBLIGHT] =
317 {
318 (uint8_t *)&status_rgblight, sizeof(serial_rgblight), (uint8_t *)&serial_rgblight, 0, NULL // no slave to master transfer
319 },
320# endif
321};
322
323void transport_master_init(void) { soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); }
324
325void transport_slave_init(void) { soft_serial_target_init(transactions, TID_LIMIT(transactions)); }
326 94
327# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) 95bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) {
328 96 split_transaction_desc_t *trans = &split_transaction_table[id];
329// rgblight synchronization information communication. 97 if (initiator2target_length > 0) {
330 98 size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length;
331void transport_rgblight_master(void) { 99 memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len);
332 if (rgblight_get_change_flags()) {
333 rgblight_get_syncinfo((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync);
334 if (soft_serial_transaction(PUT_RGBLIGHT) == TRANSACTION_END) {
335 rgblight_clear_change_flags();
336 }
337 }
338}
339
340void transport_rgblight_slave(void) {
341 if (status_rgblight == TRANSACTION_ACCEPTED) {
342 rgblight_update_sync((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync, false);
343 status_rgblight = TRANSACTION_END;
344 } 100 }
345}
346 101
347# else 102 if (soft_serial_transaction(id) != TRANSACTION_END) {
348# define transport_rgblight_master()
349# define transport_rgblight_slave()
350# endif
351
352bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
353# ifndef SERIAL_USE_MULTI_TRANSACTION
354 if (soft_serial_transaction() != TRANSACTION_END) {
355 return false;
356 }
357# else
358 transport_rgblight_master();
359 if (soft_serial_transaction(GET_SLAVE_MATRIX) != TRANSACTION_END) {
360 return false; 103 return false;
361 } 104 }
362# endif
363 105
364 // TODO: if MATRIX_COLS > 8 change to unpack() 106 if (target2initiator_length > 0) {
365 for (int i = 0; i < ROWS_PER_HAND; ++i) { 107 size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length;
366 slave_matrix[i] = serial_s2m_buffer.smatrix[i]; 108 memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len);
367# ifdef SPLIT_TRANSPORT_MIRROR
368 serial_m2s_buffer.mmatrix[i] = master_matrix[i];
369# endif
370 } 109 }
371 110
372# ifdef BACKLIGHT_ENABLE
373 // Write backlight level for slave to read
374 serial_m2s_buffer.backlight_level = is_backlight_enabled() ? get_backlight_level() : 0;
375# endif
376
377# ifdef ENCODER_ENABLE
378 encoder_update_raw((uint8_t *)serial_s2m_buffer.encoder_state);
379# endif
380
381# ifdef WPM_ENABLE
382 // Write wpm to slave
383 serial_m2s_buffer.current_wpm = get_current_wpm();
384# endif
385
386# ifdef SPLIT_MODS_ENABLE
387 serial_m2s_buffer.real_mods = get_mods();
388 serial_m2s_buffer.weak_mods = get_weak_mods();
389# ifndef NO_ACTION_ONESHOT
390 serial_m2s_buffer.oneshot_mods = get_oneshot_mods();
391# endif
392# endif
393
394# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
395 serial_m2s_buffer.led_matrix = led_matrix_eeconfig;
396 serial_m2s_buffer.led_suspend_state = led_matrix_get_suspend_state();
397# endif
398# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
399 serial_m2s_buffer.rgb_matrix = rgb_matrix_config;
400 serial_m2s_buffer.rgb_suspend_state = rgb_matrix_get_suspend_state();
401# endif
402
403# ifndef DISABLE_SYNC_TIMER
404 serial_m2s_buffer.sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
405# endif
406 return true; 111 return true;
407} 112}
408 113
409void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { 114#endif // USE_I2C
410 transport_rgblight_slave();
411# ifndef DISABLE_SYNC_TIMER
412 sync_timer_update(serial_m2s_buffer.sync_timer);
413# endif
414
415 // TODO: if MATRIX_COLS > 8 change to pack()
416 for (int i = 0; i < ROWS_PER_HAND; ++i) {
417 serial_s2m_buffer.smatrix[i] = slave_matrix[i];
418# ifdef SPLIT_TRANSPORT_MIRROR
419 master_matrix[i] = serial_m2s_buffer.mmatrix[i];
420# endif
421 }
422# ifdef BACKLIGHT_ENABLE
423 backlight_set(serial_m2s_buffer.backlight_level);
424# endif
425
426# ifdef ENCODER_ENABLE
427 encoder_state_raw((uint8_t *)serial_s2m_buffer.encoder_state);
428# endif
429 115
430# ifdef WPM_ENABLE 116bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { return transactions_master(master_matrix, slave_matrix); }
431 set_current_wpm(serial_m2s_buffer.current_wpm);
432# endif
433
434# ifdef SPLIT_MODS_ENABLE
435 set_mods(serial_m2s_buffer.real_mods);
436 set_weak_mods(serial_m2s_buffer.weak_mods);
437# ifndef NO_ACTION_ONESHOT
438 set_oneshot_mods(serial_m2s_buffer.oneshot_mods);
439# endif
440# endif
441
442# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
443 led_matrix_eeconfig = serial_m2s_buffer.led_matrix;
444 led_matrix_set_suspend_state(serial_m2s_buffer.led_suspend_state);
445# endif
446# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
447 rgb_matrix_config = serial_m2s_buffer.rgb_matrix;
448 rgb_matrix_set_suspend_state(serial_m2s_buffer.rgb_suspend_state);
449# endif
450}
451 117
452#endif 118void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { transactions_slave(master_matrix, slave_matrix); } \ No newline at end of file
diff --git a/quantum/split_common/transport.h b/quantum/split_common/transport.h
index a9f66301b..2e07f6b25 100644
--- a/quantum/split_common/transport.h
+++ b/quantum/split_common/transport.h
@@ -1,10 +1,175 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
1#pragma once 17#pragma once
2 18
19#include "stdint.h"
20#include "stdbool.h"
21
22#include "progmem.h"
23#include "action_layer.h"
3#include "matrix.h" 24#include "matrix.h"
4 25
26#ifndef RPC_M2S_BUFFER_SIZE
27# define RPC_M2S_BUFFER_SIZE 32
28#endif // RPC_M2S_BUFFER_SIZE
29
30#ifndef RPC_S2M_BUFFER_SIZE
31# define RPC_S2M_BUFFER_SIZE 32
32#endif // RPC_S2M_BUFFER_SIZE
33
5void transport_master_init(void); 34void transport_master_init(void);
6void transport_slave_init(void); 35void transport_slave_init(void);
7 36
8// returns false if valid data not received from slave 37// returns false if valid data not received from slave
9bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); 38bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
10void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); 39void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
40
41bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length);
42
43#ifdef ENCODER_ENABLE
44# include "encoder.h"
45# define NUMBER_OF_ENCODERS (sizeof((pin_t[])ENCODERS_PAD_A) / sizeof(pin_t))
46#endif // ENCODER_ENABLE
47
48#ifdef BACKLIGHT_ENABLE
49# include "backlight.h"
50#endif // BACKLIGHT_ENABLE
51
52#ifdef RGBLIGHT_ENABLE
53# include "rgblight.h"
54#endif // RGBLIGHT_ENABLE
55
56typedef struct _split_slave_matrix_sync_t {
57 uint8_t checksum;
58 matrix_row_t matrix[(MATRIX_ROWS) / 2];
59} split_slave_matrix_sync_t;
60
61#ifdef SPLIT_TRANSPORT_MIRROR
62typedef struct _split_master_matrix_sync_t {
63 matrix_row_t matrix[(MATRIX_ROWS) / 2];
64} split_master_matrix_sync_t;
65#endif // SPLIT_TRANSPORT_MIRROR
66
67#ifdef ENCODER_ENABLE
68typedef struct _split_slave_encoder_sync_t {
69 uint8_t checksum;
70 uint8_t state[NUMBER_OF_ENCODERS];
71} split_slave_encoder_sync_t;
72#endif // ENCODER_ENABLE
73
74#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
75typedef struct _split_layers_sync_t {
76 layer_state_t layer_state;
77 layer_state_t default_layer_state;
78} split_layers_sync_t;
79#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
80
81#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
82# include "led_matrix.h"
83
84typedef struct _led_matrix_sync_t {
85 led_eeconfig_t led_matrix;
86 bool led_suspend_state;
87} led_matrix_sync_t;
88#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
89
90#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
91# include "rgb_matrix.h"
92
93typedef struct _rgb_matrix_sync_t {
94 rgb_config_t rgb_matrix;
95 bool rgb_suspend_state;
96} rgb_matrix_sync_t;
97#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
98
99#ifdef SPLIT_MODS_ENABLE
100typedef struct _split_mods_sync_t {
101 uint8_t real_mods;
102 uint8_t weak_mods;
103# ifndef NO_ACTION_ONESHOT
104 uint8_t oneshot_mods;
105# endif // NO_ACTION_ONESHOT
106} split_mods_sync_t;
107#endif // SPLIT_MODS_ENABLE
108
109#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
110typedef struct _rpc_sync_info_t {
111 int8_t transaction_id;
112 uint8_t m2s_length;
113 uint8_t s2m_length;
114} rpc_sync_info_t;
115#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
116
117typedef struct _split_shared_memory_t {
118#ifdef USE_I2C
119 int8_t transaction_id;
120#endif // USE_I2C
121
122 split_slave_matrix_sync_t smatrix;
123
124#ifdef SPLIT_TRANSPORT_MIRROR
125 split_master_matrix_sync_t mmatrix;
126#endif // SPLIT_TRANSPORT_MIRROR
127
128#ifdef ENCODER_ENABLE
129 split_slave_encoder_sync_t encoders;
130#endif // ENCODER_ENABLE
131
132#ifndef DISABLE_SYNC_TIMER
133 uint32_t sync_timer;
134#endif // DISABLE_SYNC_TIMER
135
136#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
137 split_layers_sync_t layers;
138#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
139
140#ifdef SPLIT_LED_STATE_ENABLE
141 uint8_t led_state;
142#endif // SPLIT_LED_STATE_ENABLE
143
144#ifdef SPLIT_MODS_ENABLE
145 split_mods_sync_t mods;
146#endif // SPLIT_MODS_ENABLE
147
148#ifdef BACKLIGHT_ENABLE
149 uint8_t backlight_level;
150#endif // BACKLIGHT_ENABLE
151
152#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
153 rgblight_syncinfo_t rgblight_sync;
154#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
155
156#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
157 led_matrix_sync_t led_matrix_sync;
158#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
159
160#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
161 rgb_matrix_sync_t rgb_matrix_sync;
162#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
163
164#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
165 uint8_t current_wpm;
166#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
167
168#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
169 rpc_sync_info_t rpc_info;
170 uint8_t rpc_m2s_buffer[RPC_M2S_BUFFER_SIZE];
171 uint8_t rpc_s2m_buffer[RPC_S2M_BUFFER_SIZE];
172#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
173} split_shared_memory_t;
174
175extern split_shared_memory_t *const split_shmem; \ No newline at end of file
diff --git a/tmk_core/common/host.c b/tmk_core/common/host.c
index e7d92cfac..a8b391e89 100644
--- a/tmk_core/common/host.c
+++ b/tmk_core/common/host.c
@@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
17 17
18#include <stdint.h> 18#include <stdint.h>
19//#include <avr/interrupt.h> 19//#include <avr/interrupt.h>
20#include "keyboard.h"
20#include "keycode.h" 21#include "keycode.h"
21#include "host.h" 22#include "host.h"
22#include "util.h" 23#include "util.h"
@@ -35,15 +36,20 @@ void host_set_driver(host_driver_t *d) { driver = d; }
35 36
36host_driver_t *host_get_driver(void) { return driver; } 37host_driver_t *host_get_driver(void) { return driver; }
37 38
39#ifdef SPLIT_KEYBOARD
40uint8_t split_led_state = 0;
41void set_split_host_keyboard_leds(uint8_t led_state) { split_led_state = led_state; }
42#endif
43
38uint8_t host_keyboard_leds(void) { 44uint8_t host_keyboard_leds(void) {
45#ifdef SPLIT_KEYBOARD
46 if (!is_keyboard_master()) return split_led_state;
47#endif
39 if (!driver) return 0; 48 if (!driver) return 0;
40 return (*driver->keyboard_leds)(); 49 return (*driver->keyboard_leds)();
41} 50}
42 51
43led_t host_keyboard_led_state(void) { 52led_t host_keyboard_led_state(void) { return (led_t)host_keyboard_leds(); }
44 if (!driver) return (led_t){0};
45 return (led_t)((*driver->keyboard_leds)());
46}
47 53
48/* send report */ 54/* send report */
49void host_keyboard_send(report_keyboard_t *report) { 55void host_keyboard_send(report_keyboard_t *report) {