From ff41c22fdccd30cdba44ba840d68261f74f45135 Mon Sep 17 00:00:00 2001 From: XScorpion2 Date: Thu, 25 Mar 2021 06:33:18 -0500 Subject: Adding keyboard level weak function for slave matrix scan (#12317) --- quantum/split_common/matrix.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'quantum/split_common/matrix.c') diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c index d6636b886a..f8de17809d 100644 --- a/quantum/split_common/matrix.c +++ b/quantum/split_common/matrix.c @@ -43,6 +43,7 @@ extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values uint8_t thisHand, thatHand; // user-defined overridable functions +__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); } __attribute__((weak)) void matrix_slave_scan_user(void) {} static inline void setPinOutput_writeLow(pin_t pin) { @@ -284,7 +285,7 @@ bool matrix_post_scan(void) { } else { transport_slave(matrix + thatHand, matrix + thisHand); - matrix_slave_scan_user(); + matrix_slave_scan_kb(); } return changed; -- cgit v1.2.3 From 82aa9ad4a567695d1f7d0b1e36bf8563d2967813 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 19 May 2021 22:43:36 +0200 Subject: matrix: wait for row signal to go HIGH for every row (#12945) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I noticed this discrepancy (last row of the matrix treated differently than the others) when optimizing the input latency of my keyboard controller, see also https://michael.stapelberg.ch/posts/2021-05-08-keyboard-input-latency-qmk-kinesis/ Before this commit, when tuning the delays I noticed ghost key presses when pressing the F2 key, which is on the last row of the keyboard matrix: the dead_grave key, which is on the first row of the keyboard matrix, would be incorrectly detected as pressed. After this commit, all keyboard matrix rows are interpreted correctly. I suspect that my setup is more susceptible to this nuance than others because I use GPIO_INPUT_PIN_DELAY=0 and hence don’t have another delay that might mask the problem. --- quantum/matrix.c | 8 ++------ quantum/split_common/matrix.c | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) (limited to 'quantum/split_common/matrix.c') diff --git a/quantum/matrix.c b/quantum/matrix.c index c027b7bf27..34d6af2e6d 100644 --- a/quantum/matrix.c +++ b/quantum/matrix.c @@ -116,9 +116,7 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) // Unselect row unselect_row(current_row); - if (current_row + 1 < MATRIX_ROWS) { - matrix_output_unselect_delay(); // wait for row signal to go HIGH - } + matrix_output_unselect_delay(); // wait for all Col signals to go HIGH // If the row has changed, store the row and return the changed flag. if (current_matrix[current_row] != current_row_value) { @@ -178,9 +176,7 @@ static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) // Unselect col unselect_col(current_col); - if (current_col + 1 < MATRIX_COLS) { - matrix_output_unselect_delay(); // wait for col signal to go HIGH - } + matrix_output_unselect_delay(); // wait for all Row signals to go HIGH return matrix_changed; } diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c index f8de17809d..039e7d9773 100644 --- a/quantum/split_common/matrix.c +++ b/quantum/split_common/matrix.c @@ -130,9 +130,7 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) // Unselect row unselect_row(current_row); - if (current_row + 1 < MATRIX_ROWS) { - matrix_output_unselect_delay(); // wait for row signal to go HIGH - } + matrix_output_unselect_delay(); // wait for all Col signals to go HIGH // If the row has changed, store the row and return the changed flag. if (current_matrix[current_row] != current_row_value) { @@ -192,9 +190,7 @@ static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) // Unselect col unselect_col(current_col); - if (current_col + 1 < MATRIX_COLS) { - matrix_output_unselect_delay(); // wait for col signal to go HIGH - } + matrix_output_unselect_delay(); // wait for all Row signals to go HIGH return matrix_changed; } -- cgit v1.2.3 From e5d3e5a98969e59d702fefb2690ec582699e129b Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 1 Jun 2021 15:10:39 +1000 Subject: Add weak refs on reading rows/cols. (#13062) --- quantum/matrix.c | 18 +++++++++--------- quantum/split_common/matrix.c | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'quantum/split_common/matrix.c') diff --git a/quantum/matrix.c b/quantum/matrix.c index 34d6af2e6d..9298661ad0 100644 --- a/quantum/matrix.c +++ b/quantum/matrix.c @@ -47,7 +47,7 @@ static inline void setPinInputHigh_atomic(pin_t pin) { #ifdef DIRECT_PINS -static void init_pins(void) { +__attribute__((weak)) void matrix_init_pins(void) { for (int row = 0; row < MATRIX_ROWS; row++) { for (int col = 0; col < MATRIX_COLS; col++) { pin_t pin = direct_pins[row][col]; @@ -58,7 +58,7 @@ static void init_pins(void) { } } -static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { +__attribute__((weak)) bool matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { // Start with a clear matrix row matrix_row_t current_row_value = 0; @@ -90,14 +90,14 @@ static void unselect_rows(void) { } } -static void init_pins(void) { +__attribute__((weak)) void matrix_init_pins(void) { unselect_rows(); for (uint8_t x = 0; x < MATRIX_COLS; x++) { setPinInputHigh_atomic(col_pins[x]); } } -static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { +__attribute__((weak)) bool matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { // Start with a clear matrix row matrix_row_t current_row_value = 0; @@ -138,14 +138,14 @@ static void unselect_cols(void) { } } -static void init_pins(void) { +__attribute__((weak)) void matrix_init_pins(void) { unselect_cols(); for (uint8_t x = 0; x < MATRIX_ROWS; x++) { setPinInputHigh_atomic(row_pins[x]); } } -static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { +__attribute__((weak)) bool matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { bool matrix_changed = false; // Select col @@ -190,7 +190,7 @@ static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) void matrix_init(void) { // initialize key pins - init_pins(); + matrix_init_pins(); // initialize matrix state: all keys off for (uint8_t i = 0; i < MATRIX_ROWS; i++) { @@ -209,12 +209,12 @@ uint8_t matrix_scan(void) { #if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) // Set row, read cols for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) { - changed |= read_cols_on_row(raw_matrix, current_row); + changed |= matrix_read_cols_on_row(raw_matrix, current_row); } #elif (DIODE_DIRECTION == ROW2COL) // Set col, read rows for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { - changed |= read_rows_on_col(raw_matrix, current_col); + changed |= matrix_read_rows_on_col(raw_matrix, current_col); } #endif diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c index 039e7d9773..a54ea90b3f 100644 --- a/quantum/split_common/matrix.c +++ b/quantum/split_common/matrix.c @@ -61,7 +61,7 @@ static inline void setPinInputHigh_atomic(pin_t pin) { #ifdef DIRECT_PINS -static void init_pins(void) { +__attribute__((weak)) void matrix_init_pins(void) { for (int row = 0; row < MATRIX_ROWS; row++) { for (int col = 0; col < MATRIX_COLS; col++) { pin_t pin = direct_pins[row][col]; @@ -72,7 +72,7 @@ static void init_pins(void) { } } -static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { +__attribute__((weak)) bool matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { // Start with a clear matrix row matrix_row_t current_row_value = 0; @@ -104,14 +104,14 @@ static void unselect_rows(void) { } } -static void init_pins(void) { +__attribute__((weak)) void matrix_init_pins(void) { unselect_rows(); for (uint8_t x = 0; x < MATRIX_COLS; x++) { setPinInputHigh_atomic(col_pins[x]); } } -static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { +__attribute__((weak)) bool matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { // Start with a clear matrix row matrix_row_t current_row_value = 0; @@ -152,14 +152,14 @@ static void unselect_cols(void) { } } -static void init_pins(void) { +__attribute__((weak)) void matrix_init_pins(void) { unselect_cols(); for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { setPinInputHigh_atomic(row_pins[x]); } } -static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { +__attribute__((weak)) bool matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { bool matrix_changed = false; // Select col @@ -233,7 +233,7 @@ void matrix_init(void) { thatHand = ROWS_PER_HAND - thisHand; // initialize key pins - init_pins(); + matrix_init_pins(); // initialize matrix state: all keys off for (uint8_t i = 0; i < MATRIX_ROWS; i++) { @@ -293,12 +293,12 @@ uint8_t matrix_scan(void) { #if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) // Set row, read cols for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) { - local_changed |= read_cols_on_row(raw_matrix, current_row); + local_changed |= matrix_read_cols_on_row(raw_matrix, current_row); } #elif (DIODE_DIRECTION == ROW2COL) // Set col, read rows for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { - local_changed |= read_rows_on_col(raw_matrix, current_col); + local_changed |= matrix_read_rows_on_col(raw_matrix, current_col); } #endif -- cgit v1.2.3 From f287597c19dc926c7c28cefa32ffc3f0b4fdba7f Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 9 Jun 2021 17:19:42 +1000 Subject: Use single memcmp to determine if matrix changed. (#13064) * Use memcmp to determine if matrix changed. * Firmware size issues. * Add documentation for the lack of need of MATRIX_ROW_PINS/MATRIX_COL_PINS, when overriding low-level matrix functions. --- docs/config_options.md | 3 + docs/custom_quantum_functions.md | 8 +++ keyboards/anavi/macropad8/keymaps/default/config.h | 19 ++++++ .../frankie_macropad/keymaps/default/config.h | 19 ++++++ .../kbdfans/kbd67/rev1/keymaps/default/config.h | 2 +- keyboards/mt84/keymaps/default/config.h | 19 ++++++ quantum/matrix.c | 71 ++++++++++------------ quantum/split_common/matrix.c | 69 +++++++++------------ 8 files changed, 131 insertions(+), 79 deletions(-) create mode 100644 keyboards/anavi/macropad8/keymaps/default/config.h create mode 100644 keyboards/handwired/frankie_macropad/keymaps/default/config.h create mode 100644 keyboards/mt84/keymaps/default/config.h (limited to 'quantum/split_common/matrix.c') diff --git a/docs/config_options.md b/docs/config_options.md index d0f0b316e0..26fe8cea55 100644 --- a/docs/config_options.md +++ b/docs/config_options.md @@ -51,8 +51,10 @@ This is a C header file that is one of the first things included, and will persi * the number of columns in your keyboard's matrix * `#define MATRIX_ROW_PINS { D0, D5, B5, B6 }` * pins of the rows, from top to bottom + * may be omitted by the keyboard designer if matrix reads are handled in an alternate manner. See [low-level matrix overrides](custom_quantum_functions.md?id=low-level-matrix-overrides) for more information. * `#define MATRIX_COL_PINS { F1, F0, B0, C7, F4, F5, F6, F7, D4, D6, B4, D7 }` * pins of the columns, from left to right + * may be omitted by the keyboard designer if matrix reads are handled in an alternate manner. See [low-level matrix overrides](custom_quantum_functions.md?id=low-level-matrix-overrides) for more information. * `#define MATRIX_IO_DELAY 30` * the delay in microseconds when between changing matrix pin state and reading values * `#define UNUSED_PINS { D1, D2, D3, B1, B2, B3 }` @@ -280,6 +282,7 @@ There are a few different ways to set handedness for split keyboards (listed in * `#define MATRIX_ROW_PINS_RIGHT { }` * `#define MATRIX_COL_PINS_RIGHT { }` * If you want to specify a different pinout for the right half than the left half, you can define `MATRIX_ROW_PINS_RIGHT`/`MATRIX_COL_PINS_RIGHT`. Currently, the size of `MATRIX_ROW_PINS` must be the same as `MATRIX_ROW_PINS_RIGHT` and likewise for the definition of columns. + * may be omitted by the keyboard designer if matrix reads are handled in an alternate manner. See [low-level matrix overrides](custom_quantum_functions.md?id=low-level-matrix-overrides) for more information. * `#define DIRECT_PINS_RIGHT { { F1, F0, B0, C7 }, { F4, F5, F6, F7 } }` * If you want to specify a different direct pinout for the right half than the left half, you can define `DIRECT_PINS_RIGHT`. Currently, the size of `DIRECT_PINS` must be the same as `DIRECT_PINS_RIGHT`. diff --git a/docs/custom_quantum_functions.md b/docs/custom_quantum_functions.md index 694b421e79..30c637bb49 100644 --- a/docs/custom_quantum_functions.md +++ b/docs/custom_quantum_functions.md @@ -144,6 +144,14 @@ This is useful for setting up stuff that you may need elsewhere, but isn't hardw * Keyboard/Revision: `void matrix_init_kb(void)` * Keymap: `void matrix_init_user(void)` +### Low-level Matrix Overrides Function Documentation :id=low-level-matrix-overrides + +* GPIO pin initialisation: `void matrix_init_pins(void)` + * This needs to perform the low-level initialisation of all row and column pins. By default this will initialise the input/output state of each of the GPIO pins listed in `MATRIX_ROW_PINS` and `MATRIX_COL_PINS`, based on whether or not the keyboard is set up for `ROW2COL`, `COL2ROW`, or `DIRECT_PINS`. Should the keyboard designer override this function, no initialisation of pin state will occur within QMK itself, instead deferring to the keyboard's override. +* `COL2ROW`-based row reads: `void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col)` +* `ROW2COL`-based column reads: `void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)` +* `DIRECT_PINS`-based reads: `void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)` + * These three functions need to perform the low-level retrieval of matrix state of relevant input pins, based on the matrix type. Only one of the functions should be implemented, if needed. By default this will iterate through `MATRIX_ROW_PINS` and `MATRIX_COL_PINS`, configuring the inputs and outputs based on whether or not the keyboard is set up for `ROW2COL`, `COL2ROW`, or `DIRECT_PINS`. Should the keyboard designer override this function, no manipulation of matrix GPIO pin state will occur within QMK itself, instead deferring to the keyboard's override. ## Keyboard Post Initialization code diff --git a/keyboards/anavi/macropad8/keymaps/default/config.h b/keyboards/anavi/macropad8/keymaps/default/config.h new file mode 100644 index 0000000000..dd687cad58 --- /dev/null +++ b/keyboards/anavi/macropad8/keymaps/default/config.h @@ -0,0 +1,19 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#define LAYER_STATE_8BIT diff --git a/keyboards/handwired/frankie_macropad/keymaps/default/config.h b/keyboards/handwired/frankie_macropad/keymaps/default/config.h new file mode 100644 index 0000000000..dd687cad58 --- /dev/null +++ b/keyboards/handwired/frankie_macropad/keymaps/default/config.h @@ -0,0 +1,19 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#define LAYER_STATE_8BIT diff --git a/keyboards/kbdfans/kbd67/rev1/keymaps/default/config.h b/keyboards/kbdfans/kbd67/rev1/keymaps/default/config.h index a3ed4f762a..90fb10ebbe 100644 --- a/keyboards/kbdfans/kbd67/rev1/keymaps/default/config.h +++ b/keyboards/kbdfans/kbd67/rev1/keymaps/default/config.h @@ -16,4 +16,4 @@ #pragma once -// place overrides here +#define LAYER_STATE_8BIT diff --git a/keyboards/mt84/keymaps/default/config.h b/keyboards/mt84/keymaps/default/config.h new file mode 100644 index 0000000000..dd687cad58 --- /dev/null +++ b/keyboards/mt84/keymaps/default/config.h @@ -0,0 +1,19 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#define LAYER_STATE_8BIT diff --git a/quantum/matrix.c b/quantum/matrix.c index 9298661ad0..71ef270892 100644 --- a/quantum/matrix.c +++ b/quantum/matrix.c @@ -16,6 +16,7 @@ along with this program. If not, see . */ #include #include +#include #include "util.h" #include "matrix.h" #include "debounce.h" @@ -24,14 +25,23 @@ along with this program. If not, see . #ifdef DIRECT_PINS static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS; #elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW) +# ifdef MATRIX_ROW_PINS static const pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; +# endif // MATRIX_ROW_PINS +# ifdef MATRIX_COL_PINS static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; +# endif // MATRIX_COL_PINS #endif /* matrix state(1:on, 0:off) */ extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values +// user-defined overridable functions +__attribute__((weak)) void matrix_init_pins(void); +__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row); +__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col); + static inline void setPinOutput_writeLow(pin_t pin) { ATOMIC_BLOCK_FORCEON { setPinOutput(pin); @@ -58,7 +68,7 @@ __attribute__((weak)) void matrix_init_pins(void) { } } -__attribute__((weak)) bool matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { +__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { // Start with a clear matrix row matrix_row_t current_row_value = 0; @@ -69,16 +79,13 @@ __attribute__((weak)) bool matrix_read_cols_on_row(matrix_row_t current_matrix[] } } - // If the row has changed, store the row and return the changed flag. - if (current_matrix[current_row] != current_row_value) { - current_matrix[current_row] = current_row_value; - return true; - } - return false; + // Update the matrix + current_matrix[current_row] = current_row_value; } #elif defined(DIODE_DIRECTION) -# if (DIODE_DIRECTION == COL2ROW) +# if defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS) +# if (DIODE_DIRECTION == COL2ROW) static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); } @@ -97,7 +104,7 @@ __attribute__((weak)) void matrix_init_pins(void) { } } -__attribute__((weak)) bool matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { +__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { // Start with a clear matrix row matrix_row_t current_row_value = 0; @@ -118,15 +125,11 @@ __attribute__((weak)) bool matrix_read_cols_on_row(matrix_row_t current_matrix[] unselect_row(current_row); matrix_output_unselect_delay(); // wait for all Col signals to go HIGH - // If the row has changed, store the row and return the changed flag. - if (current_matrix[current_row] != current_row_value) { - current_matrix[current_row] = current_row_value; - return true; - } - return false; + // Update the matrix + current_matrix[current_row] = current_row_value; } -# elif (DIODE_DIRECTION == ROW2COL) +# elif (DIODE_DIRECTION == ROW2COL) static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); } @@ -145,45 +148,32 @@ __attribute__((weak)) void matrix_init_pins(void) { } } -__attribute__((weak)) bool matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { - bool matrix_changed = false; - +__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { // Select col select_col(current_col); matrix_output_select_delay(); // For each row... for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) { - // Store last value of row prior to reading - matrix_row_t last_row_value = current_matrix[row_index]; - matrix_row_t current_row_value = last_row_value; - // Check row pin state if (readPin(row_pins[row_index]) == 0) { // Pin LO, set col bit - current_row_value |= (MATRIX_ROW_SHIFTER << current_col); + current_matrix[row_index] |= (MATRIX_ROW_SHIFTER << current_col); } else { // Pin HI, clear col bit - current_row_value &= ~(MATRIX_ROW_SHIFTER << current_col); - } - - // Determine if the matrix changed state - if ((last_row_value != current_row_value)) { - matrix_changed |= true; - current_matrix[row_index] = current_row_value; + current_matrix[row_index] &= ~(MATRIX_ROW_SHIFTER << current_col); } } // Unselect col unselect_col(current_col); matrix_output_unselect_delay(); // wait for all Row signals to go HIGH - - return matrix_changed; } -# else -# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL! -# endif +# else +# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL! +# endif +# endif // defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS) #else # error DIODE_DIRECTION is not defined! #endif @@ -204,20 +194,23 @@ void matrix_init(void) { } uint8_t matrix_scan(void) { - bool changed = false; + matrix_row_t curr_matrix[MATRIX_ROWS] = {0}; #if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) // Set row, read cols for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) { - changed |= matrix_read_cols_on_row(raw_matrix, current_row); + matrix_read_cols_on_row(curr_matrix, current_row); } #elif (DIODE_DIRECTION == ROW2COL) // Set col, read rows for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { - changed |= matrix_read_rows_on_col(raw_matrix, current_col); + matrix_read_rows_on_col(curr_matrix, current_col); } #endif + bool changed = memcmp(raw_matrix, curr_matrix, sizeof(curr_matrix)) != 0; + if (changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix)); + debounce(raw_matrix, matrix, MATRIX_ROWS, changed); matrix_scan_quantum(); diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c index a54ea90b3f..2cf7b70582 100644 --- a/quantum/split_common/matrix.c +++ b/quantum/split_common/matrix.c @@ -16,6 +16,7 @@ along with this program. If not, see . */ #include #include +#include #include "util.h" #include "matrix.h" #include "debounce.h" @@ -31,8 +32,12 @@ along with this program. If not, see . #ifdef DIRECT_PINS static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS; #elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW) +# ifdef MATRIX_ROW_PINS static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; +# endif // MATRIX_ROW_PINS +# ifdef MATRIX_COL_PINS static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; +# endif // MATRIX_COL_PINS #endif /* matrix state(1:on, 0:off) */ @@ -45,6 +50,9 @@ uint8_t thisHand, thatHand; // user-defined overridable functions __attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); } __attribute__((weak)) void matrix_slave_scan_user(void) {} +__attribute__((weak)) void matrix_init_pins(void); +__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row); +__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col); static inline void setPinOutput_writeLow(pin_t pin) { ATOMIC_BLOCK_FORCEON { @@ -72,7 +80,7 @@ __attribute__((weak)) void matrix_init_pins(void) { } } -__attribute__((weak)) bool matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { +__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { // Start with a clear matrix row matrix_row_t current_row_value = 0; @@ -83,16 +91,13 @@ __attribute__((weak)) bool matrix_read_cols_on_row(matrix_row_t current_matrix[] } } - // If the row has changed, store the row and return the changed flag. - if (current_matrix[current_row] != current_row_value) { - current_matrix[current_row] = current_row_value; - return true; - } - return false; + // Update the matrix + current_matrix[current_row] = current_row_value; } #elif defined(DIODE_DIRECTION) -# if (DIODE_DIRECTION == COL2ROW) +# if defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS) +# if (DIODE_DIRECTION == COL2ROW) static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); } @@ -111,7 +116,7 @@ __attribute__((weak)) void matrix_init_pins(void) { } } -__attribute__((weak)) bool matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { +__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { // Start with a clear matrix row matrix_row_t current_row_value = 0; @@ -132,15 +137,11 @@ __attribute__((weak)) bool matrix_read_cols_on_row(matrix_row_t current_matrix[] unselect_row(current_row); matrix_output_unselect_delay(); // wait for all Col signals to go HIGH - // If the row has changed, store the row and return the changed flag. - if (current_matrix[current_row] != current_row_value) { - current_matrix[current_row] = current_row_value; - return true; - } - return false; + // Update the matrix + current_matrix[current_row] = current_row_value; } -# elif (DIODE_DIRECTION == ROW2COL) +# elif (DIODE_DIRECTION == ROW2COL) static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); } @@ -159,45 +160,32 @@ __attribute__((weak)) void matrix_init_pins(void) { } } -__attribute__((weak)) bool matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { - bool matrix_changed = false; - +__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { // Select col select_col(current_col); matrix_output_select_delay(); // For each row... for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) { - // Store last value of row prior to reading - matrix_row_t last_row_value = current_matrix[row_index]; - matrix_row_t current_row_value = last_row_value; - // Check row pin state if (readPin(row_pins[row_index]) == 0) { // Pin LO, set col bit - current_row_value |= (MATRIX_ROW_SHIFTER << current_col); + current_matrix[row_index] |= (MATRIX_ROW_SHIFTER << current_col); } else { // Pin HI, clear col bit - current_row_value &= ~(MATRIX_ROW_SHIFTER << current_col); - } - - // Determine if the matrix changed state - if ((last_row_value != current_row_value)) { - matrix_changed |= true; - current_matrix[row_index] = current_row_value; + current_matrix[row_index] &= ~(MATRIX_ROW_SHIFTER << current_col); } } // Unselect col unselect_col(current_col); matrix_output_unselect_delay(); // wait for all Row signals to go HIGH - - return matrix_changed; } -# else -# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL! -# endif +# else +# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL! +# endif +# endif // defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS) #else # error DIODE_DIRECTION is not defined! #endif @@ -288,20 +276,23 @@ bool matrix_post_scan(void) { } uint8_t matrix_scan(void) { - bool local_changed = false; + matrix_row_t curr_matrix[MATRIX_ROWS] = {0}; #if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) // Set row, read cols for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) { - local_changed |= matrix_read_cols_on_row(raw_matrix, current_row); + matrix_read_cols_on_row(curr_matrix, current_row); } #elif (DIODE_DIRECTION == ROW2COL) // Set col, read rows for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { - local_changed |= matrix_read_rows_on_col(raw_matrix, current_col); + matrix_read_rows_on_col(curr_matrix, current_col); } #endif + bool local_changed = memcmp(raw_matrix, curr_matrix, sizeof(curr_matrix)) != 0; + if (local_changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix)); + debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, local_changed); bool remote_changed = matrix_post_scan(); -- cgit v1.2.3 From 172e6a703041363decd6fc829542f33180c13beb Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Fri, 18 Jun 2021 09:10:06 +1000 Subject: 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. --- common_features.mk | 6 +- docs/config_options.md | 26 +- docs/feature_split_keyboard.md | 114 ++++- drivers/avr/i2c_master.c | 19 +- drivers/avr/i2c_slave.c | 29 +- drivers/avr/i2c_slave.h | 13 +- drivers/avr/serial.c | 82 +--- drivers/avr/serial.h | 62 --- drivers/chibios/serial.c | 52 +- drivers/chibios/serial.h | 62 --- drivers/chibios/serial_usart.c | 47 +- drivers/serial.h | 46 ++ keyboards/draculad/config.h | 2 + keyboards/handwired/freoduo/rules.mk | 4 +- keyboards/helix/rev2/keymaps/default/rules.mk | 2 +- keyboards/keebio/iris/rev2/rules.mk | 2 + keyboards/keebio/iris/rev3/rules.mk | 1 + keyboards/keebio/iris/rev4/rules.mk | 2 + keyboards/keebio/quefrency/rules.mk | 1 + keyboards/keebio/viterbi/rev2/rules.mk | 2 + keyboards/keebio/viterbi/rules.mk | 8 +- quantum/split_common/matrix.c | 6 +- quantum/split_common/post_config.h | 9 - quantum/split_common/transaction_id_define.h | 94 ++++ quantum/split_common/transactions.c | 670 ++++++++++++++++++++++++++ quantum/split_common/transactions.h | 54 +++ quantum/split_common/transport.c | 484 +++---------------- quantum/split_common/transport.h | 165 +++++++ tmk_core/common/host.c | 14 +- 29 files changed, 1387 insertions(+), 691 deletions(-) delete mode 100644 drivers/avr/serial.h delete mode 100644 drivers/chibios/serial.h create mode 100644 drivers/serial.h create mode 100644 quantum/split_common/transaction_id_define.h create mode 100644 quantum/split_common/transactions.c create mode 100644 quantum/split_common/transactions.h (limited to 'quantum/split_common/matrix.c') diff --git a/common_features.mk b/common_features.mk index 1a079a8df2..15637d7608 100644 --- a/common_features.mk +++ b/common_features.mk @@ -537,7 +537,11 @@ ifeq ($(strip $(SPLIT_KEYBOARD)), yes) # Determine which (if any) transport files are required ifneq ($(strip $(SPLIT_TRANSPORT)), custom) - QUANTUM_LIB_SRC += $(QUANTUM_DIR)/split_common/transport.c + QUANTUM_SRC += $(QUANTUM_DIR)/split_common/transport.c \ + $(QUANTUM_DIR)/split_common/transactions.c + + OPT_DEFS += -DSPLIT_COMMON_TRANSACTIONS + # Functions added via QUANTUM_LIB_SRC are only included in the final binary if they're called. # Unused functions are pruned away, which is why we can add multiple drivers here without bloat. ifeq ($(PLATFORM),AVR) diff --git a/docs/config_options.md b/docs/config_options.md index 26fe8cea55..980195ac68 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 ### Other Options * `#define USE_I2C` - * For using I2C instead of Serial (defaults to serial) + * For using I2C instead of Serial (default is serial; serial transport is supported on ARM -- I2C is AVR-only) * `#define SOFT_SERIAL_PIN D0` * 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 * `#define SPLIT_USB_DETECT` * Detect (with timeout) USB connection when delegating master/slave * Default behavior for ARM - * Required for AVR Teensy + * Required for AVR Teensy (without hardware mods) * `#define SPLIT_USB_TIMEOUT 2000` * 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 * `#define SPLIT_USB_TIMEOUT_POLL 10` * Poll frequency when detecting master/slave when using `SPLIT_USB_DETECT` +* `#define FORCED_SYNC_THROTTLE_MS 100` + * Deadline for synchronizing data from master to slave when using the QMK-provided split transport. + +* `#define SPLIT_TRANSPORT_MIRROR` + * Mirrors the master-side matrix on the slave when using the QMK-provided split transport. + +* `#define SPLIT_LAYER_STATE_ENABLE` + * Ensures the current layer state is available on the slave when using the QMK-provided split transport. + +* `#define SPLIT_LED_STATE_ENABLE` + * Ensures the current host indicator state (caps/num/scroll) is available on the slave when using the QMK-provided split transport. + +* `#define SPLIT_MODS_ENABLE` + * Ensures the current modifier state (normal, weak, and oneshot) is available on the slave when using the QMK-provided split transport. + +* `#define SPLIT_WPM_ENABLE` + * Ensures the current WPM is available on the slave when using the QMK-provided split transport. + +* `#define SPLIT_TRANSACTION_IDS_KB .....` +* `#define SPLIT_TRANSACTION_IDS_USER .....` + * 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. + # The `rules.mk` File This 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 4ebf585f5c..603c387c2d 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 For this, we will mostly be talking about the generic implementation used by the Let's Split and other keyboards. -!> 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. - +!> ARM split supports most QMK subsystems when using the 'serial' and 'serial_usart' drivers. I2C slave is currently unsupported. ## Compatibility Overview @@ -169,7 +168,7 @@ Because not every split keyboard is identical, there are a number of additional #define USE_I2C ``` -This enables I2C support for split keyboards. This isn't strictly for communication, but can be used for OLED or other I2C-based devices. +This configures the use of I2C support for split keyboard transport (AVR only). ```c #define SOFT_SERIAL_PIN D0 @@ -193,20 +192,115 @@ If you're having issues with serial communication, you can change this value, as * **`5`**: about 20kbps ```c -#define SPLIT_MODS_ENABLE +#define FORCED_SYNC_THROTTLE_MS 100 ``` -This enables transmitting modifier state (normal, weak and oneshot) to the non -primary side of the split keyboard. 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 modifer state (e.g. -displaying status on an OLED screen). +This 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. ```c #define SPLIT_TRANSPORT_MIRROR ``` -This 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). +This 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. + +```c +#define SPLIT_LAYER_STATE_ENABLE +``` + +This 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. + +```c +#define SPLIT_LED_STATE_ENABLE +``` + +This 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. + +```c +#define SPLIT_MODS_ENABLE +``` + +This 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. + +```c +#define SPLIT_WPM_ENABLE +``` + +This 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. + +### Custom data sync between sides :id=custom-data-sync + +QMK'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. + +To leverage this, a keyboard or user/keymap can define a comma-separated list of _transaction IDs_: + +```c +// for keyboard-level data sync: +#define SPLIT_TRANSACTION_IDS_KB KEYBOARD_SYNC_A, KEYBOARD_SYNC_B +// or, for user: +#define SPLIT_TRANSACTION_IDS_USER USER_SYNC_A, USER_SYNC_B, USER_SYNC_C +``` + +These _transaction IDs_ then need a slave-side handler function to be registered with the split transport, for example: + +```c +typedef struct _master_to_slave_t { + int m2s_data; +} master_to_slave_t; + +typedef struct _slave_to_master_t { + int s2m_data; +} slave_to_master_t; + +void user_sync_a_slave_handler(uint8_t in_buflen, const void* in_data, uint8_t out_buflen, void* out_data) { + const master_to_slave_t *m2s = (const master_to_slave_t*)in_data; + slave_to_master_t *s2m = (slave_to_master_t*)out_data; + s2m->s2m_data = m2s->m2s_data + 5; // whatever comes in, add 5 so it can be sent back +} + +void keyboard_post_init_user(void) { + transaction_register_rpc(USER_SYNC_A, user_sync_a_slave_handler); +} +``` + +The 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: + +```c +void housekeeping_task_user(void) { + if (is_keyboard_master()) { + // Interact with slave every 500ms + static uint32_t last_sync = 0; + if (timer_elapsed32(last_sync) > 500) { + master_to_slave_t m2s = {6}; + slave_to_master_t s2m = {0}; + if(transaction_rpc_exec(USER_SYNC_A, sizeof(m2s), &m2s, sizeof(s2m), &s2m)) { + last_sync = timer_read32(); + dprintf("Slave value: %d\n", s2m.s2m_data); // this will now be 11, as the slave adds 5 + } else { + dprint("Slave sync failed!\n"); + } + } + } +} +``` + +!> It is recommended that any data sync between halves happens during the master side's _housekeeping task_. This ensures timely retries should failures occur. + +If only one-way data transfer is needed, helper methods are provided: + +```c +bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); +bool transaction_rpc_send(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer); +bool transaction_rpc_recv(int8_t transaction_id, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); +``` + +By default, the inbound and outbound data is limited to a maximum of 32 bytes each. The sizes can be altered if required: + +```c +// Master to slave: +#define RPC_M2S_BUFFER_SIZE 48 +// Slave to master: +#define RPC_S2M_BUFFER_SIZE 48 +``` ### Hardware Configuration Options diff --git a/drivers/avr/i2c_master.c b/drivers/avr/i2c_master.c index b1e4885298..2773e00778 100644 --- a/drivers/avr/i2c_master.c +++ b/drivers/avr/i2c_master.c @@ -28,8 +28,14 @@ # define F_SCL 400000UL // SCL frequency #endif +#ifndef I2C_START_RETRY_COUNT +# define I2C_START_RETRY_COUNT 20 +#endif // I2C_START_RETRY_COUNT + #define TWBR_val (((F_CPU / F_SCL) - 16) / 2) +#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) + void i2c_init(void) { TWSR = 0; /* no prescaler */ TWBR = (uint8_t)TWBR_val; @@ -47,7 +53,7 @@ void i2c_init(void) { #endif } -i2c_status_t i2c_start(uint8_t address, uint16_t timeout) { +static i2c_status_t i2c_start_impl(uint8_t address, uint16_t timeout) { // reset TWI control register TWCR = 0; // transmit START condition @@ -86,6 +92,17 @@ i2c_status_t i2c_start(uint8_t address, uint16_t timeout) { return I2C_STATUS_SUCCESS; } +i2c_status_t i2c_start(uint8_t address, uint16_t timeout) { + // Retry i2c_start_impl a bunch times in case the remote side has interrupts disabled. + uint16_t timeout_timer = timer_read(); + 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 + i2c_status_t status; + do { + status = i2c_start_impl(address, time_slice); + } while ((status < 0) && ((timeout == I2C_TIMEOUT_INFINITE) || (timer_elapsed(timeout_timer) < timeout))); + return status; +} + i2c_status_t i2c_write(uint8_t data, uint16_t timeout) { // load data into data register TWDR = data; diff --git a/drivers/avr/i2c_slave.c b/drivers/avr/i2c_slave.c index 62a378165a..2907f164c0 100644 --- a/drivers/avr/i2c_slave.c +++ b/drivers/avr/i2c_slave.c @@ -17,6 +17,7 @@ * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib */ +#include #include #include #include @@ -24,6 +25,12 @@ #include "i2c_slave.h" +#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +# include "transactions.h" + +static volatile bool is_callback_executor = false; +#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) + volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT]; static volatile uint8_t buffer_address; @@ -48,11 +55,14 @@ ISR(TWI_vect) { case TW_SR_SLA_ACK: // The device is now a slave receiver slave_has_register_set = false; +#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) + is_callback_executor = false; +#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) break; case TW_SR_DATA_ACK: // This device is a slave receiver and has received data - // First byte is the location then the bytes will be writen in buffer with auto-incriment + // First byte is the location then the bytes will be writen in buffer with auto-increment if (!slave_has_register_set) { buffer_address = TWDR; @@ -60,10 +70,25 @@ ISR(TWI_vect) { ack = 0; buffer_address = 0; } - slave_has_register_set = true; // address has been receaved now fill in buffer + slave_has_register_set = true; // address has been received now fill in buffer + +#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) + // Work out if we're attempting to execute a callback + is_callback_executor = buffer_address == split_transaction_table[I2C_EXECUTE_CALLBACK].initiator2target_offset; +#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) } else { i2c_slave_reg[buffer_address] = TWDR; buffer_address++; + +#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) + // If we're intending to execute a transaction callback, do so, as we've just received the transaction ID + if (is_callback_executor) { + split_transaction_desc_t *trans = &split_transaction_table[split_shmem->transaction_id]; + if (trans->slave_callback) { + trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); + } + } +#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) } break; diff --git a/drivers/avr/i2c_slave.h b/drivers/avr/i2c_slave.h index 1cd0625ef4..a8647c9da3 100644 --- a/drivers/avr/i2c_slave.h +++ b/drivers/avr/i2c_slave.h @@ -22,7 +22,18 @@ #pragma once -#define I2C_SLAVE_REG_COUNT 30 +#ifndef I2C_SLAVE_REG_COUNT + +# if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +# include "transport.h" +# define I2C_SLAVE_REG_COUNT sizeof(split_shared_memory_t) +# else // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +# define I2C_SLAVE_REG_COUNT 30 +# endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) + +#endif // I2C_SLAVE_REG_COUNT + +_Static_assert(I2C_SLAVE_REG_COUNT < 256, "I2C target registers must be single byte"); extern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT]; diff --git a/drivers/avr/serial.c b/drivers/avr/serial.c index 3647bee0d3..9a7345a53d 100644 --- a/drivers/avr/serial.c +++ b/drivers/avr/serial.c @@ -224,15 +224,8 @@ # define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2) # define SLAVE_INT_WIDTH_US 1 -# ifndef SERIAL_USE_MULTI_TRANSACTION -# define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY -# else -# define SLAVE_INT_ACK_WIDTH_UNIT 2 -# define SLAVE_INT_ACK_WIDTH 4 -# endif - -static SSTD_t *Transaction_table = NULL; -static uint8_t Transaction_table_size = 0; +# define SLAVE_INT_ACK_WIDTH_UNIT 2 +# define SLAVE_INT_ACK_WIDTH 4 inline static void serial_delay(void) ALWAYS_INLINE; inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); } @@ -259,16 +252,12 @@ inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); } inline static void serial_high(void) ALWAYS_INLINE; inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); } -void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; +void soft_serial_initiator_init(void) { serial_output(); serial_high(); } -void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; +void soft_serial_target_init(void) { serial_input_with_pullup(); // Enable INT0-INT7 @@ -395,19 +384,14 @@ static inline uint8_t nibble_bits_count(uint8_t bits) { // interrupt handle to be used by the target device ISR(SERIAL_PIN_INTERRUPT) { -# ifndef SERIAL_USE_MULTI_TRANSACTION - serial_low(); - serial_output(); - SSTD_t *trans = Transaction_table; -# else // recive transaction table index uint8_t tid, bits; uint8_t pecount = 0; sync_recv(); - bits = serial_read_chunk(&pecount, 7); + bits = serial_read_chunk(&pecount, 8); tid = bits >> 3; - bits = (bits & 7) != nibble_bits_count(tid); - if (bits || pecount > 0 || tid > Transaction_table_size) { + bits = (bits & 7) != (nibble_bits_count(tid) & 7); + if (bits || pecount > 0 || tid > NUM_TOTAL_TRANSACTIONS) { return; } serial_delay_half1(); @@ -415,18 +399,22 @@ ISR(SERIAL_PIN_INTERRUPT) { serial_high(); // response step1 low->high serial_output(); _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH); - SSTD_t *trans = &Transaction_table[tid]; + split_transaction_desc_t *trans = &split_transaction_table[tid]; serial_low(); // response step2 ack high->low -# endif + + // If the transaction has a callback, we can execute it now + if (trans->slave_callback) { + trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); + } // target send phase - if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size); + if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size); // target switch to input change_sender2reciver(); // target recive phase if (trans->initiator2target_buffer_size > 0) { - if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size)) { + if (serial_recive_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) { *trans->status = TRANSACTION_ACCEPTED; } else { *trans->status = TRANSACTION_DATA_ERROR; @@ -448,14 +436,12 @@ ISR(SERIAL_PIN_INTERRUPT) { // TRANSACTION_NO_RESPONSE // TRANSACTION_DATA_ERROR // this code is very time dependent, so we need to disable interrupts -# ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void) { - SSTD_t *trans = Transaction_table; -# else int soft_serial_transaction(int sstd_index) { - if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; - SSTD_t *trans = &Transaction_table[sstd_index]; -# endif + if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR; + split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; + + if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered + cli(); // signal to the target that we want to start a transaction @@ -463,27 +449,11 @@ int soft_serial_transaction(int sstd_index) { serial_low(); _delay_us(SLAVE_INT_WIDTH_US); -# ifndef SERIAL_USE_MULTI_TRANSACTION - // wait for the target response - serial_input_with_pullup(); - _delay_us(SLAVE_INT_RESPONSE_TIME); - - // check if the target is present - if (serial_read_pin()) { - // target failed to pull the line low, assume not present - serial_output(); - serial_high(); - *trans->status = TRANSACTION_NO_RESPONSE; - sei(); - return TRANSACTION_NO_RESPONSE; - } - -# else // send transaction table index int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index)); sync_send(); _delay_sub_us(TID_SEND_ADJUST); - serial_write_chunk(tid, 7); + serial_write_chunk(tid, 8); serial_delay_half1(); // wait for the target response (step1 low->high) @@ -504,12 +474,11 @@ int soft_serial_transaction(int sstd_index) { } _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT); } -# endif // initiator recive phase // if the target is present syncronize with it if (trans->target2initiator_buffer_size > 0) { - if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size)) { + if (!serial_recive_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) { serial_output(); serial_high(); *trans->status = TRANSACTION_DATA_ERROR; @@ -523,7 +492,7 @@ int soft_serial_transaction(int sstd_index) { // initiator send phase if (trans->initiator2target_buffer_size > 0) { - serial_send_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size); + serial_send_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size); } // always, release the line when not in use @@ -534,9 +503,8 @@ int soft_serial_transaction(int sstd_index) { return TRANSACTION_END; } -# ifdef SERIAL_USE_MULTI_TRANSACTION int soft_serial_get_and_clean_status(int sstd_index) { - SSTD_t *trans = &Transaction_table[sstd_index]; + split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; cli(); int retval = *trans->status; *trans->status = 0; @@ -544,8 +512,6 @@ int soft_serial_get_and_clean_status(int sstd_index) { sei(); return retval; } -# endif - #endif // Helix serial.c history diff --git a/drivers/avr/serial.h b/drivers/avr/serial.h deleted file mode 100644 index 53e66cf905..0000000000 --- a/drivers/avr/serial.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include - -// ///////////////////////////////////////////////////////////////// -// Need Soft Serial defines in config.h -// ///////////////////////////////////////////////////////////////// -// ex. -// #define SOFT_SERIAL_PIN ?? // ?? = D0,D1,D2,D3,E6 -// OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5 -// // 1: about 137kbps (default) -// // 2: about 75kbps -// // 3: about 39kbps -// // 4: about 26kbps -// // 5: about 20kbps -// -// //// USE simple API (using signle-type transaction function) -// /* nothing */ -// //// USE flexible API (using multi-type transaction function) -// #define SERIAL_USE_MULTI_TRANSACTION -// -// ///////////////////////////////////////////////////////////////// - -// Soft Serial Transaction Descriptor -typedef struct _SSTD_t { - uint8_t *status; - uint8_t initiator2target_buffer_size; - uint8_t *initiator2target_buffer; - uint8_t target2initiator_buffer_size; - uint8_t *target2initiator_buffer; -} SSTD_t; -#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t)) - -// initiator is transaction start side -void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size); -// target is interrupt accept side -void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size); - -// initiator resullt -#define TRANSACTION_END 0 -#define TRANSACTION_NO_RESPONSE 0x1 -#define TRANSACTION_DATA_ERROR 0x2 -#define TRANSACTION_TYPE_ERROR 0x4 -#ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void); -#else -int soft_serial_transaction(int sstd_index); -#endif - -// target status -// *SSTD_t.status has -// initiator: -// TRANSACTION_END -// or TRANSACTION_NO_RESPONSE -// or TRANSACTION_DATA_ERROR -// target: -// TRANSACTION_DATA_ERROR -// or TRANSACTION_ACCEPTED -#define TRANSACTION_ACCEPTED 0x8 -#ifdef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_get_and_clean_status(int sstd_index); -#endif diff --git a/drivers/chibios/serial.c b/drivers/chibios/serial.c index 54f7e1321f..f54fbcee4e 100644 --- a/drivers/chibios/serial.c +++ b/drivers/chibios/serial.c @@ -74,21 +74,12 @@ static THD_FUNCTION(Thread1, arg) { } } -static SSTD_t *Transaction_table = NULL; -static uint8_t Transaction_table_size = 0; - -void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; - +void soft_serial_initiator_init(void) { serial_output(); serial_high(); } -void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; - +void soft_serial_target_init(void) { serial_input(); 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) { uint8_t checksum_computed = 0; int sstd_index = 0; -#ifdef SERIAL_USE_MULTI_TRANSACTION sstd_index = serial_read_byte(); sync_send(); -#endif - SSTD_t *trans = &Transaction_table[sstd_index]; + split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { - trans->initiator2target_buffer[i] = serial_read_byte(); + split_trans_initiator2target_buffer(trans)[i] = serial_read_byte(); sync_send(); - checksum_computed += trans->initiator2target_buffer[i]; + checksum_computed += split_trans_initiator2target_buffer(trans)[i]; } checksum_computed ^= 7; uint8_t checksum_received = serial_read_byte(); @@ -172,12 +161,17 @@ void interrupt_handler(void *arg) { // wait for the sync to finish sending serial_delay(); + // Allow any slave processing to occur + if (trans->slave_callback) { + trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); + } + uint8_t checksum = 0; for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { - serial_write_byte(trans->target2initiator_buffer[i]); + serial_write_byte(split_trans_target2initiator_buffer(trans)[i]); sync_send(); serial_delay_half(); - checksum += trans->target2initiator_buffer[i]; + checksum += split_trans_target2initiator_buffer(trans)[i]; } serial_write_byte(checksum ^ 7); sync_send(); @@ -206,15 +200,10 @@ void interrupt_handler(void *arg) { // TRANSACTION_NO_RESPONSE // TRANSACTION_DATA_ERROR // this code is very time dependent, so we need to disable interrupts -#ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void) { - int sstd_index = 0; -#else int soft_serial_transaction(int sstd_index) { -#endif - - if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; - SSTD_t *trans = &Transaction_table[sstd_index]; + if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR; + split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; + if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered // TODO: remove extra delay between transactions serial_delay(); @@ -244,14 +233,13 @@ int soft_serial_transaction(int sstd_index) { uint8_t checksum = 0; // send data to the slave -#ifdef SERIAL_USE_MULTI_TRANSACTION serial_write_byte(sstd_index); // first chunk is transaction id sync_recv(); -#endif + for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { - serial_write_byte(trans->initiator2target_buffer[i]); + serial_write_byte(split_trans_initiator2target_buffer(trans)[i]); sync_recv(); - checksum += trans->initiator2target_buffer[i]; + checksum += split_trans_initiator2target_buffer(trans)[i]; } serial_write_byte(checksum ^ 7); sync_recv(); @@ -262,9 +250,9 @@ int soft_serial_transaction(int sstd_index) { // receive data from the slave uint8_t checksum_computed = 0; for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { - trans->target2initiator_buffer[i] = serial_read_byte(); + split_trans_target2initiator_buffer(trans)[i] = serial_read_byte(); sync_recv(); - checksum_computed += trans->target2initiator_buffer[i]; + checksum_computed += split_trans_target2initiator_buffer(trans)[i]; } checksum_computed ^= 7; uint8_t checksum_received = serial_read_byte(); diff --git a/drivers/chibios/serial.h b/drivers/chibios/serial.h deleted file mode 100644 index 0c1857d52e..0000000000 --- a/drivers/chibios/serial.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include - -// ///////////////////////////////////////////////////////////////// -// Need Soft Serial defines in config.h -// ///////////////////////////////////////////////////////////////// -// ex. -// #define SOFT_SERIAL_PIN ?? // ?? = D0,D1,D2,D3,E6 -// OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5 -// // 1: about 137kbps (default) -// // 2: about 75kbps -// // 3: about 39kbps -// // 4: about 26kbps -// // 5: about 20kbps -// -// //// USE simple API (using signle-type transaction function) -// /* nothing */ -// //// USE flexible API (using multi-type transaction function) -// #define SERIAL_USE_MULTI_TRANSACTION -// -// ///////////////////////////////////////////////////////////////// - -// Soft Serial Transaction Descriptor -typedef struct _SSTD_t { - uint8_t *status; - uint8_t initiator2target_buffer_size; - uint8_t *initiator2target_buffer; - uint8_t target2initiator_buffer_size; - uint8_t *target2initiator_buffer; -} SSTD_t; -#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t)) - -// initiator is transaction start side -void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size); -// target is interrupt accept side -void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size); - -// initiator result -#define TRANSACTION_END 0 -#define TRANSACTION_NO_RESPONSE 0x1 -#define TRANSACTION_DATA_ERROR 0x2 -#define TRANSACTION_TYPE_ERROR 0x4 -#ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void); -#else -int soft_serial_transaction(int sstd_index); -#endif - -// target status -// *SSTD_t.status has -// initiator: -// TRANSACTION_END -// or TRANSACTION_NO_RESPONSE -// or TRANSACTION_DATA_ERROR -// target: -// TRANSACTION_DATA_ERROR -// or TRANSACTION_ACCEPTED -#define TRANSACTION_ACCEPTED 0x8 -#ifdef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_get_and_clean_status(int sstd_index); -#endif diff --git a/drivers/chibios/serial_usart.c b/drivers/chibios/serial_usart.c index cae29388c3..9f180d2d74 100644 --- a/drivers/chibios/serial_usart.c +++ b/drivers/chibios/serial_usart.c @@ -113,37 +113,29 @@ void usart_slave_init(void) { chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); } -static SSTD_t* Transaction_table = NULL; -static uint8_t Transaction_table_size = 0; +void soft_serial_initiator_init(void) { usart_master_init(); } -void soft_serial_initiator_init(SSTD_t* sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; - - usart_master_init(); -} - -void soft_serial_target_init(SSTD_t* sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; - - usart_slave_init(); -} +void soft_serial_target_init(void) { usart_slave_init(); } void handle_soft_serial_slave(void) { - uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id - SSTD_t* trans = &Transaction_table[sstd_index]; + uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id + split_transaction_desc_t* trans = &split_transaction_table[sstd_index]; // Always write back the sstd_index as part of a basic handshake sstd_index ^= HANDSHAKE_MAGIC; sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index)); if (trans->initiator2target_buffer_size) { - sdRead(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size); + sdRead(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size); + } + + // Allow any slave processing to occur + if (trans->slave_callback) { + trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); } if (trans->target2initiator_buffer_size) { - sdWrite(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size); + sdWrite(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size); } if (trans->status) { @@ -160,17 +152,14 @@ void handle_soft_serial_slave(void) { // TRANSACTION_END // TRANSACTION_NO_RESPONSE // TRANSACTION_DATA_ERROR -#ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void) { - uint8_t sstd_index = 0; -#else int soft_serial_transaction(int index) { uint8_t sstd_index = index; -#endif - if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; - SSTD_t* trans = &Transaction_table[sstd_index]; - msg_t res = 0; + if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR; + split_transaction_desc_t* trans = &split_transaction_table[sstd_index]; + msg_t res = 0; + + if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered sdClear(&SERIAL_USART_DRIVER); @@ -189,7 +178,7 @@ int soft_serial_transaction(int index) { } if (trans->initiator2target_buffer_size) { - res = sdWriteTimeout(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); + res = sdWriteTimeout(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); if (res < 0) { dprintf("serial::usart_transmit NO_RESPONSE\n"); return TRANSACTION_NO_RESPONSE; @@ -197,7 +186,7 @@ int soft_serial_transaction(int index) { } if (trans->target2initiator_buffer_size) { - res = sdReadTimeout(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); + res = sdReadTimeout(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); if (res < 0) { dprintf("serial::usart_receive NO_RESPONSE\n"); return TRANSACTION_NO_RESPONSE; diff --git a/drivers/serial.h b/drivers/serial.h new file mode 100644 index 0000000000..d9c2a69e96 --- /dev/null +++ b/drivers/serial.h @@ -0,0 +1,46 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#include + +// initiator is transaction start side +void soft_serial_initiator_init(void); +// target is interrupt accept side +void soft_serial_target_init(void); + +// initiator result +#define TRANSACTION_END 0 +#define TRANSACTION_NO_RESPONSE 0x1 +#define TRANSACTION_DATA_ERROR 0x2 +#define TRANSACTION_TYPE_ERROR 0x4 +int soft_serial_transaction(int sstd_index); + +// target status +// *SSTD_t.status has +// initiator: +// TRANSACTION_END +// or TRANSACTION_NO_RESPONSE +// or TRANSACTION_DATA_ERROR +// target: +// TRANSACTION_DATA_ERROR +// or TRANSACTION_ACCEPTED +#define TRANSACTION_ACCEPTED 0x8 +int soft_serial_get_and_clean_status(int sstd_index); diff --git a/keyboards/draculad/config.h b/keyboards/draculad/config.h index 8a27fdea4d..d8a9fbd37c 100644 --- a/keyboards/draculad/config.h +++ b/keyboards/draculad/config.h @@ -65,3 +65,5 @@ along with this program. If not, see . #define UNUSED_PINS #define EE_HANDS + +#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 e3da3753e4..d8923557d6 100644 --- a/keyboards/handwired/freoduo/rules.mk +++ b/keyboards/handwired/freoduo/rules.mk @@ -11,7 +11,7 @@ BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = no # Console for debug -COMMAND_ENABLE = yes # Commands for debug and configuration +COMMAND_ENABLE = no # Commands for debug and configuration # Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend # 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 BLUETOOTH_ENABLE = no # Enable Bluetooth AUDIO_ENABLE = no # Audio output VELOCIKEY_ENABLE = yes -SPLIT_KEYBOARD = yes \ No newline at end of file +SPLIT_KEYBOARD = yes diff --git a/keyboards/helix/rev2/keymaps/default/rules.mk b/keyboards/helix/rev2/keymaps/default/rules.mk index 206e836ec0..c16f3e2b87 100644 --- a/keyboards/helix/rev2/keymaps/default/rules.mk +++ b/keyboards/helix/rev2/keymaps/default/rules.mk @@ -5,7 +5,7 @@ # See TOP/keyboards/helix/rules.mk for a list of options that can be set. # See TOP/docs/config_options.md for more information. # -LTO_ENABLE = no # if firmware size over limit, try this option +LTO_ENABLE = yes # if firmware size over limit, try this option # Helix Spacific Build Options # 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 765fa7f07a..197bad4764 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. SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend SPLIT_KEYBOARD = yes + +LTO_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 ea92bc98f5..b64eab5a4b 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 SPLIT_KEYBOARD = yes ENCODER_ENABLE = yes +LTO_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 ea92bc98f5..9bf5688da0 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 SPLIT_KEYBOARD = yes ENCODER_ENABLE = yes + +LTO_ENABLE = yes \ No newline at end of file diff --git a/keyboards/keebio/quefrency/rules.mk b/keyboards/keebio/quefrency/rules.mk index 2c5ad0c36b..c674e10162 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 SPLIT_KEYBOARD = yes DEFAULT_FOLDER = keebio/quefrency/rev1 +LTO_ENABLE = yes diff --git a/keyboards/keebio/viterbi/rev2/rules.mk b/keyboards/keebio/viterbi/rev2/rules.mk index f95e7ae6a4..829d6a56e0 100644 --- a/keyboards/keebio/viterbi/rev2/rules.mk +++ b/keyboards/keebio/viterbi/rev2/rules.mk @@ -1,3 +1,5 @@ BACKLIGHT_ENABLE = yes LAYOUTS = ortho_5x14 + +LTO_ENABLE = yes \ No newline at end of file diff --git a/keyboards/keebio/viterbi/rules.mk b/keyboards/keebio/viterbi/rules.mk index 0b746d1720..92576d33e8 100644 --- a/keyboards/keebio/viterbi/rules.mk +++ b/keyboards/keebio/viterbi/rules.mk @@ -19,16 +19,16 @@ BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = no # Console for debug -COMMAND_ENABLE = yes # Commands for debug and configuration +COMMAND_ENABLE = no # Commands for debug and configuration NKRO_ENABLE = no # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work -BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality +BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality MIDI_ENABLE = no # MIDI controls AUDIO_ENABLE = no # Audio output on port C6 UNICODE_ENABLE = no # Unicode BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID -RGBLIGHT_ENABLE = no # Enable WS2812 RGB underlight. +RGBLIGHT_ENABLE = no # Enable WS2812 RGB underlight. # Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE -SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend SPLIT_KEYBOARD = yes diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c index 2cf7b70582..56d91b07fe 100644 --- a/quantum/split_common/matrix.c +++ b/quantum/split_common/matrix.c @@ -23,9 +23,11 @@ along with this program. If not, see . #include "quantum.h" #include "split_util.h" #include "config.h" -#include "transport.h" +#include "transactions.h" -#define ERROR_DISCONNECT_COUNT 5 +#ifndef ERROR_DISCONNECT_COUNT +# define ERROR_DISCONNECT_COUNT 5 +#endif // ERROR_DISCONNECT_COUNT #define ROWS_PER_HAND (MATRIX_ROWS / 2) diff --git a/quantum/split_common/post_config.h b/quantum/split_common/post_config.h index 4ae1d52732..a4c0a1956b 100644 --- a/quantum/split_common/post_config.h +++ b/quantum/split_common/post_config.h @@ -7,13 +7,4 @@ # ifndef F_SCL # define F_SCL 100000UL // SCL frequency # endif - -#else // use serial -// When using serial, the user must define RGBLIGHT_SPLIT explicitly -// in config.h as needed. -// see quantum/rgblight_post_config.h -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) -// When using serial and RGBLIGHT_SPLIT need separate transaction -# define SERIAL_USE_MULTI_TRANSACTION -# endif #endif diff --git a/quantum/split_common/transaction_id_define.h b/quantum/split_common/transaction_id_define.h new file mode 100644 index 0000000000..464c73478a --- /dev/null +++ b/quantum/split_common/transaction_id_define.h @@ -0,0 +1,94 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +enum serial_transaction_id { +#ifdef USE_I2C + I2C_EXECUTE_CALLBACK, +#endif // USE_I2C + + GET_SLAVE_MATRIX_CHECKSUM, + GET_SLAVE_MATRIX_DATA, + +#ifdef SPLIT_TRANSPORT_MIRROR + PUT_MASTER_MATRIX, +#endif // SPLIT_TRANSPORT_MIRROR + +#ifdef ENCODER_ENABLE + GET_ENCODERS_CHECKSUM, + GET_ENCODERS_DATA, +#endif // ENCODER_ENABLE + +#ifndef DISABLE_SYNC_TIMER + PUT_SYNC_TIMER, +#endif // DISABLE_SYNC_TIMER + +#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + PUT_LAYER_STATE, + PUT_DEFAULT_LAYER_STATE, +#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +#ifdef SPLIT_LED_STATE_ENABLE + PUT_LED_STATE, +#endif // SPLIT_LED_STATE_ENABLE + +#ifdef SPLIT_MODS_ENABLE + PUT_MODS, +#endif // SPLIT_MODS_ENABLE + +#ifdef BACKLIGHT_ENABLE + PUT_BACKLIGHT, +#endif // BACKLIGHT_ENABLE + +#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + PUT_RGBLIGHT, +#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + PUT_LED_MATRIX, +#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + PUT_RGB_MATRIX, +#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + PUT_WPM, +#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + PUT_RPC_INFO, + PUT_RPC_REQ_DATA, + EXECUTE_RPC, + GET_RPC_RESP_DATA, +#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + +// keyboard-specific +#ifdef SPLIT_TRANSACTION_IDS_KB + SPLIT_TRANSACTION_IDS_KB, +#endif // SPLIT_TRANSACTION_IDS_KB + +// user/keymap-specific +#ifdef SPLIT_TRANSACTION_IDS_USER + SPLIT_TRANSACTION_IDS_USER, +#endif // SPLIT_TRANSACTION_IDS_USER + + NUM_TOTAL_TRANSACTIONS +}; + +// Ensure we only use 5 bits for transaction +_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 0000000000..99a8623b3c --- /dev/null +++ b/quantum/split_common/transactions.c @@ -0,0 +1,670 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "debug.h" +#include "matrix.h" +#include "quantum.h" +#include "transactions.h" +#include "transport.h" +#include "transaction_id_define.h" + +#define SYNC_TIMER_OFFSET 2 + +#ifndef FORCED_SYNC_THROTTLE_MS +# define FORCED_SYNC_THROTTLE_MS 100 +#endif // FORCED_SYNC_THROTTLE_MS + +#define sizeof_member(type, member) sizeof(((type *)NULL)->member) + +#define trans_initiator2target_initializer_cb(member, cb) \ + { &dummy, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), 0, 0, cb } +#define trans_initiator2target_initializer(member) trans_initiator2target_initializer_cb(member, NULL) + +#define trans_target2initiator_initializer_cb(member, cb) \ + { &dummy, 0, 0, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), cb } +#define trans_target2initiator_initializer(member) trans_target2initiator_initializer_cb(member, NULL) + +#define transport_write(id, data, length) transport_execute_transaction(id, data, length, NULL, 0) +#define transport_read(id, data, length) transport_execute_transaction(id, NULL, 0, data, length) + +static uint8_t crc8(const void *data, size_t len) { + const uint8_t *p = (const uint8_t *)data; + uint8_t crc = 0xff; + size_t i, j; + for (i = 0; i < len; i++) { + crc ^= p[i]; + for (j = 0; j < 8; j++) { + if ((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; +} + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) +// Forward-declare the RPC callback handlers +void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); +void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); +#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + +//////////////////////////////////////////////////// +// Helpers + +bool transaction_handler_master(bool okay, matrix_row_t master_matrix[], matrix_row_t slave_matrix[], const char *prefix, bool (*handler)(matrix_row_t master_matrix[], matrix_row_t slave_matrix[])) { + if (okay) { + bool this_okay = true; + for (int iter = 1; iter <= 10; ++iter) { + if (!this_okay) { + for (int i = 0; i < iter * iter; ++i) { + wait_us(10); + } + } + ATOMIC_BLOCK_FORCEON { this_okay = handler(master_matrix, slave_matrix); }; + if (this_okay) break; + } + okay &= this_okay; + if (!okay) { + dprintf("Failed to execute %s\n", prefix); + } + } + return okay; +} + +#define TRANSACTION_HANDLER_MASTER(prefix) \ + do { \ + okay &= transaction_handler_master(okay, master_matrix, slave_matrix, #prefix, &prefix##_master); \ + } while (0) + +#define TRANSACTION_HANDLER_SLAVE(prefix) \ + do { \ + ATOMIC_BLOCK_FORCEON { prefix##_slave(master_matrix, slave_matrix); }; \ + } while (0) + +inline static bool read_if_checksum_mismatch(int8_t trans_id_checksum, int8_t trans_id_retrieve, uint32_t *last_update, void *destination, const void *equiv_shmem, size_t length) { + uint8_t curr_checksum; + bool okay = transport_read(trans_id_checksum, &curr_checksum, sizeof(curr_checksum)); + if (okay && (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || curr_checksum != crc8(equiv_shmem, length))) { + okay &= transport_read(trans_id_retrieve, destination, length); + okay &= curr_checksum == crc8(equiv_shmem, length); + if (okay) { + *last_update = timer_read32(); + } + } else { + memcpy(destination, equiv_shmem, length); + } + return okay; +} + +inline static bool send_if_condition(int8_t trans_id, uint32_t *last_update, bool condition, void *source, size_t length) { + bool okay = true; + if (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || condition) { + okay &= transport_write(trans_id, source, length); + if (okay) { + *last_update = timer_read32(); + } + } + return okay; +} + +inline static bool send_if_data_mismatch(int8_t trans_id, uint32_t *last_update, void *source, const void *equiv_shmem, size_t length) { + // Just run a memcmp to compare the source and equivalent shmem location + return send_if_condition(trans_id, last_update, (memcmp(source, equiv_shmem, length) != 0), source, length); +} + +//////////////////////////////////////////////////// +// Slave matrix + +static bool slave_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + static matrix_row_t last_matrix[(MATRIX_ROWS) / 2] = {0}; // last successfully-read matrix, so we can replicate if there are checksum errors + matrix_row_t temp_matrix[(MATRIX_ROWS) / 2]; // holding area while we test whether or not checksum is correct + + 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)); + if (okay) { + // Checksum matches the received data, save as the last matrix state + memcpy(last_matrix, temp_matrix, sizeof(temp_matrix)); + } + // Copy out the last-known-good matrix state to the slave matrix + memcpy(slave_matrix, last_matrix, sizeof(last_matrix)); + return okay; +} + +static void slave_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + memcpy(split_shmem->smatrix.matrix, slave_matrix, sizeof(split_shmem->smatrix.matrix)); + split_shmem->smatrix.checksum = crc8(split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix)); +} + +// clang-format off +#define TRANSACTIONS_SLAVE_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(slave_matrix_handlers) +#define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(slave_matrix_handlers) +#define TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS \ + [GET_SLAVE_MATRIX_CHECKSUM] = trans_target2initiator_initializer(smatrix.checksum), \ + [GET_SLAVE_MATRIX_DATA] = trans_target2initiator_initializer(smatrix.matrix), +// clang-format on + +//////////////////////////////////////////////////// +// Master matrix + +#ifdef SPLIT_TRANSPORT_MIRROR + +static bool master_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + return send_if_data_mismatch(PUT_MASTER_MATRIX, &last_update, master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix)); +} + +static void master_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + // Always copy to the master matrix + memcpy(master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix)); +} + +# define TRANSACTIONS_MASTER_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(master_matrix_handlers) +# define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(master_matrix_handlers) +# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS [PUT_MASTER_MATRIX] = trans_initiator2target_initializer(mmatrix.matrix), + +#else // SPLIT_TRANSPORT_MIRROR + +# define TRANSACTIONS_MASTER_MATRIX_MASTER() +# define TRANSACTIONS_MASTER_MATRIX_SLAVE() +# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS + +#endif // SPLIT_TRANSPORT_MIRROR + +//////////////////////////////////////////////////// +// Encoders + +#ifdef ENCODER_ENABLE + +static bool encoder_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + uint8_t temp_state[NUMBER_OF_ENCODERS]; + + bool okay = read_if_checksum_mismatch(GET_ENCODERS_CHECKSUM, GET_ENCODERS_DATA, &last_update, temp_state, split_shmem->encoders.state, sizeof(temp_state)); + if (okay) encoder_update_raw(temp_state); + return okay; +} + +static void encoder_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + uint8_t encoder_state[NUMBER_OF_ENCODERS]; + encoder_state_raw(encoder_state); + // Always prepare the encoder state for read. + memcpy(split_shmem->encoders.state, encoder_state, sizeof(encoder_state)); + // Now update the checksum given that the encoders has been written to + split_shmem->encoders.checksum = crc8(encoder_state, sizeof(encoder_state)); +} + +// clang-format off +# define TRANSACTIONS_ENCODERS_MASTER() TRANSACTION_HANDLER_MASTER(encoder_handlers) +# define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE(encoder_handlers) +# define TRANSACTIONS_ENCODERS_REGISTRATIONS \ + [GET_ENCODERS_CHECKSUM] = trans_target2initiator_initializer(encoders.checksum), \ + [GET_ENCODERS_DATA] = trans_target2initiator_initializer(encoders.state), +// clang-format on + +#else // ENCODER_ENABLE + +# define TRANSACTIONS_ENCODERS_MASTER() +# define TRANSACTIONS_ENCODERS_SLAVE() +# define TRANSACTIONS_ENCODERS_REGISTRATIONS + +#endif // ENCODER_ENABLE + +//////////////////////////////////////////////////// +// Sync timer + +#ifndef DISABLE_SYNC_TIMER + +static bool sync_timer_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + + bool okay = true; + if (timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS) { + uint32_t sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; + okay &= transport_write(PUT_SYNC_TIMER, &sync_timer, sizeof(sync_timer)); + if (okay) { + last_update = timer_read32(); + } + } + return okay; +} + +static void sync_timer_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_sync_timer = 0; + if (last_sync_timer != split_shmem->sync_timer) { + last_sync_timer = split_shmem->sync_timer; + sync_timer_update(last_sync_timer); + } +} + +# define TRANSACTIONS_SYNC_TIMER_MASTER() TRANSACTION_HANDLER_MASTER(sync_timer_handlers) +# define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE(sync_timer_handlers) +# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS [PUT_SYNC_TIMER] = trans_initiator2target_initializer(sync_timer), + +#else // DISABLE_SYNC_TIMER + +# define TRANSACTIONS_SYNC_TIMER_MASTER() +# define TRANSACTIONS_SYNC_TIMER_SLAVE() +# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS + +#endif // DISABLE_SYNC_TIMER + +//////////////////////////////////////////////////// +// Layer state + +#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +static bool layer_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_layer_state_update = 0; + static uint32_t last_default_layer_state_update = 0; + + bool okay = send_if_condition(PUT_LAYER_STATE, &last_layer_state_update, (layer_state != split_shmem->layers.layer_state), &layer_state, sizeof(layer_state)); + if (okay) { + 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)); + } + return okay; +} + +static void layer_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + layer_state = split_shmem->layers.layer_state; + default_layer_state = split_shmem->layers.default_layer_state; +} + +// clang-format off +# define TRANSACTIONS_LAYER_STATE_MASTER() TRANSACTION_HANDLER_MASTER(layer_state_handlers) +# define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(layer_state_handlers) +# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS \ + [PUT_LAYER_STATE] = trans_initiator2target_initializer(layers.layer_state), \ + [PUT_DEFAULT_LAYER_STATE] = trans_initiator2target_initializer(layers.default_layer_state), +// clang-format on + +#else // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +# define TRANSACTIONS_LAYER_STATE_MASTER() +# define TRANSACTIONS_LAYER_STATE_SLAVE() +# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS + +#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +//////////////////////////////////////////////////// +// LED state + +#ifdef SPLIT_LED_STATE_ENABLE + +static bool led_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + uint8_t led_state = host_keyboard_leds(); + return send_if_data_mismatch(PUT_LED_STATE, &last_update, &led_state, &split_shmem->led_state, sizeof(led_state)); +} + +static void led_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + void set_split_host_keyboard_leds(uint8_t led_state); + set_split_host_keyboard_leds(split_shmem->led_state); +} + +# define TRANSACTIONS_LED_STATE_MASTER() TRANSACTION_HANDLER_MASTER(led_state_handlers) +# define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(led_state_handlers) +# define TRANSACTIONS_LED_STATE_REGISTRATIONS [PUT_LED_STATE] = trans_initiator2target_initializer(led_state), + +#else // SPLIT_LED_STATE_ENABLE + +# define TRANSACTIONS_LED_STATE_MASTER() +# define TRANSACTIONS_LED_STATE_SLAVE() +# define TRANSACTIONS_LED_STATE_REGISTRATIONS + +#endif // SPLIT_LED_STATE_ENABLE + +//////////////////////////////////////////////////// +// Mods + +#ifdef SPLIT_MODS_ENABLE + +static bool mods_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + bool mods_need_sync = timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS; + split_mods_sync_t new_mods; + new_mods.real_mods = get_mods(); + if (!mods_need_sync && new_mods.real_mods != split_shmem->mods.real_mods) { + mods_need_sync = true; + } + + new_mods.weak_mods = get_weak_mods(); + if (!mods_need_sync && new_mods.weak_mods != split_shmem->mods.weak_mods) { + mods_need_sync = true; + } + +# ifndef NO_ACTION_ONESHOT + new_mods.oneshot_mods = get_oneshot_mods(); + if (!mods_need_sync && new_mods.oneshot_mods != split_shmem->mods.oneshot_mods) { + mods_need_sync = true; + } +# endif // NO_ACTION_ONESHOT + + bool okay = true; + if (mods_need_sync) { + okay &= transport_write(PUT_MODS, &new_mods, sizeof(new_mods)); + if (okay) { + last_update = timer_read32(); + } + } + + return okay; +} + +static void mods_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + set_mods(split_shmem->mods.real_mods); + set_weak_mods(split_shmem->mods.weak_mods); +# ifndef NO_ACTION_ONESHOT + set_oneshot_mods(split_shmem->mods.oneshot_mods); +# endif +} + +# define TRANSACTIONS_MODS_MASTER() TRANSACTION_HANDLER_MASTER(mods_handlers) +# define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods_handlers) +# define TRANSACTIONS_MODS_REGISTRATIONS [PUT_MODS] = trans_initiator2target_initializer(mods), + +#else // SPLIT_MODS_ENABLE + +# define TRANSACTIONS_MODS_MASTER() +# define TRANSACTIONS_MODS_SLAVE() +# define TRANSACTIONS_MODS_REGISTRATIONS + +#endif // SPLIT_MODS_ENABLE + +//////////////////////////////////////////////////// +// Backlight + +#ifdef BACKLIGHT_ENABLE + +static bool backlight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0; + return send_if_condition(PUT_BACKLIGHT, &last_update, (level != split_shmem->backlight_level), &level, sizeof(level)); +} + +static void backlight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { backlight_set(split_shmem->backlight_level); } + +# define TRANSACTIONS_BACKLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(backlight_handlers) +# define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight_handlers) +# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS [PUT_BACKLIGHT] = trans_initiator2target_initializer(backlight_level), + +#else // BACKLIGHT_ENABLE + +# define TRANSACTIONS_BACKLIGHT_MASTER() +# define TRANSACTIONS_BACKLIGHT_SLAVE() +# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS + +#endif // BACKLIGHT_ENABLE + +//////////////////////////////////////////////////// +// RGBLIGHT + +#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +static bool rgblight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + rgblight_syncinfo_t rgblight_sync; + rgblight_get_syncinfo(&rgblight_sync); + if (send_if_condition(PUT_RGBLIGHT, &last_update, (rgblight_sync.status.change_flags != 0), &rgblight_sync, sizeof(rgblight_sync))) { + rgblight_clear_change_flags(); + } else { + return false; + } + return true; +} + +static void rgblight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + // Update the RGB with the new data + if (split_shmem->rgblight_sync.status.change_flags != 0) { + rgblight_update_sync(&split_shmem->rgblight_sync, false); + split_shmem->rgblight_sync.status.change_flags = 0; + } +} + +# define TRANSACTIONS_RGBLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(rgblight_handlers) +# define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight_handlers) +# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS [PUT_RGBLIGHT] = trans_initiator2target_initializer(rgblight_sync), + +#else // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +# define TRANSACTIONS_RGBLIGHT_MASTER() +# define TRANSACTIONS_RGBLIGHT_SLAVE() +# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS + +#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +//////////////////////////////////////////////////// +// LED Matrix + +#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +static bool led_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + led_matrix_sync_t led_matrix_sync; + memcpy(&led_matrix_sync.led_matrix, &led_matrix_eeconfig, sizeof(led_eeconfig_t)); + led_matrix_sync.led_suspend_state = led_matrix_get_suspend_state(); + return send_if_data_mismatch(PUT_LED_MATRIX, &last_update, &led_matrix_sync, &split_shmem->led_matrix_sync, sizeof(led_matrix_sync)); +} + +static void led_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + memcpy(&led_matrix_eeconfig, &split_shmem->led_matrix_sync.led_matrix, sizeof(led_eeconfig_t)); + led_matrix_set_suspend_state(split_shmem->led_matrix_sync.led_suspend_state); +} + +# define TRANSACTIONS_LED_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(led_matrix_handlers) +# define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix_handlers) +# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS [PUT_LED_MATRIX] = trans_initiator2target_initializer(led_matrix_sync), + +#else // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +# define TRANSACTIONS_LED_MATRIX_MASTER() +# define TRANSACTIONS_LED_MATRIX_SLAVE() +# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS + +#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +//////////////////////////////////////////////////// +// RGB Matrix + +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + +static bool rgb_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + rgb_matrix_sync_t rgb_matrix_sync; + memcpy(&rgb_matrix_sync.rgb_matrix, &rgb_matrix_config, sizeof(rgb_config_t)); + rgb_matrix_sync.rgb_suspend_state = rgb_matrix_get_suspend_state(); + return send_if_data_mismatch(PUT_RGB_MATRIX, &last_update, &rgb_matrix_sync, &split_shmem->rgb_matrix_sync, sizeof(rgb_matrix_sync)); +} + +static void rgb_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + memcpy(&rgb_matrix_config, &split_shmem->rgb_matrix_sync.rgb_matrix, sizeof(rgb_config_t)); + rgb_matrix_set_suspend_state(split_shmem->rgb_matrix_sync.rgb_suspend_state); +} + +# define TRANSACTIONS_RGB_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(rgb_matrix_handlers) +# define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix_handlers) +# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS [PUT_RGB_MATRIX] = trans_initiator2target_initializer(rgb_matrix_sync), + +#else // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + +# define TRANSACTIONS_RGB_MATRIX_MASTER() +# define TRANSACTIONS_RGB_MATRIX_SLAVE() +# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS + +#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + +//////////////////////////////////////////////////// +// WPM + +#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + +static bool wpm_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + uint8_t current_wpm = get_current_wpm(); + return send_if_condition(PUT_WPM, &last_update, (current_wpm != split_shmem->current_wpm), ¤t_wpm, sizeof(current_wpm)); +} + +static void wpm_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { set_current_wpm(split_shmem->current_wpm); } + +# define TRANSACTIONS_WPM_MASTER() TRANSACTION_HANDLER_MASTER(wpm_handlers) +# define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE(wpm_handlers) +# define TRANSACTIONS_WPM_REGISTRATIONS [PUT_WPM] = trans_initiator2target_initializer(current_wpm), + +#else // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + +# define TRANSACTIONS_WPM_MASTER() +# define TRANSACTIONS_WPM_SLAVE() +# define TRANSACTIONS_WPM_REGISTRATIONS + +#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + +//////////////////////////////////////////////////// + +uint8_t dummy; +split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = { + // Set defaults + [0 ...(NUM_TOTAL_TRANSACTIONS - 1)] = {NULL, 0, 0, 0, 0, 0}, + +#ifdef USE_I2C + [I2C_EXECUTE_CALLBACK] = trans_initiator2target_initializer(transaction_id), +#endif // USE_I2C + + // clang-format off + TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS + TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS + TRANSACTIONS_ENCODERS_REGISTRATIONS + TRANSACTIONS_SYNC_TIMER_REGISTRATIONS + TRANSACTIONS_LAYER_STATE_REGISTRATIONS + TRANSACTIONS_LED_STATE_REGISTRATIONS + TRANSACTIONS_MODS_REGISTRATIONS + TRANSACTIONS_BACKLIGHT_REGISTRATIONS + TRANSACTIONS_RGBLIGHT_REGISTRATIONS + TRANSACTIONS_LED_MATRIX_REGISTRATIONS + TRANSACTIONS_RGB_MATRIX_REGISTRATIONS + TRANSACTIONS_WPM_REGISTRATIONS +// clang-format on + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + [PUT_RPC_INFO] = trans_initiator2target_initializer_cb(rpc_info, slave_rpc_info_callback), + [PUT_RPC_REQ_DATA] = trans_initiator2target_initializer(rpc_m2s_buffer), + [EXECUTE_RPC] = trans_initiator2target_initializer_cb(rpc_info.transaction_id, slave_rpc_exec_callback), + [GET_RPC_RESP_DATA] = trans_target2initiator_initializer(rpc_s2m_buffer), +#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) +}; + +bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + bool okay = true; + TRANSACTIONS_SLAVE_MATRIX_MASTER(); + TRANSACTIONS_MASTER_MATRIX_MASTER(); + TRANSACTIONS_ENCODERS_MASTER(); + TRANSACTIONS_SYNC_TIMER_MASTER(); + TRANSACTIONS_LAYER_STATE_MASTER(); + TRANSACTIONS_LED_STATE_MASTER(); + TRANSACTIONS_MODS_MASTER(); + TRANSACTIONS_BACKLIGHT_MASTER(); + TRANSACTIONS_RGBLIGHT_MASTER(); + TRANSACTIONS_LED_MATRIX_MASTER(); + TRANSACTIONS_RGB_MATRIX_MASTER(); + TRANSACTIONS_WPM_MASTER(); + return okay; +} + +void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + TRANSACTIONS_SLAVE_MATRIX_SLAVE(); + TRANSACTIONS_MASTER_MATRIX_SLAVE(); + TRANSACTIONS_ENCODERS_SLAVE(); + TRANSACTIONS_SYNC_TIMER_SLAVE(); + TRANSACTIONS_LAYER_STATE_SLAVE(); + TRANSACTIONS_LED_STATE_SLAVE(); + TRANSACTIONS_MODS_SLAVE(); + TRANSACTIONS_BACKLIGHT_SLAVE(); + TRANSACTIONS_RGBLIGHT_SLAVE(); + TRANSACTIONS_LED_MATRIX_SLAVE(); + TRANSACTIONS_RGB_MATRIX_SLAVE(); + TRANSACTIONS_WPM_SLAVE(); +} + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + +void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback) { + // Prevent invoking RPC on QMK core sync data + if (transaction_id <= GET_RPC_RESP_DATA) return; + + // Set the callback + split_transaction_table[transaction_id].slave_callback = callback; + split_transaction_table[transaction_id].initiator2target_offset = offsetof(split_shared_memory_t, rpc_m2s_buffer); + split_transaction_table[transaction_id].target2initiator_offset = offsetof(split_shared_memory_t, rpc_s2m_buffer); +} + +bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) { + // Prevent invoking RPC on QMK core sync data + if (transaction_id <= GET_RPC_RESP_DATA) return false; + // Prevent sizing issues + if (initiator2target_buffer_size > RPC_M2S_BUFFER_SIZE) return false; + if (target2initiator_buffer_size > RPC_S2M_BUFFER_SIZE) return false; + + // Prepare the metadata block + rpc_sync_info_t info = {.transaction_id = transaction_id, .m2s_length = initiator2target_buffer_size, .s2m_length = target2initiator_buffer_size}; + + // Make sure the local side knows that we're not sending the full block of data + split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = initiator2target_buffer_size; + split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = target2initiator_buffer_size; + + // Run through the sequence: + // * set the transaction ID and lengths + // * send the request data + // * execute RPC callback + // * retrieve the response data + if (!transport_write(PUT_RPC_INFO, &info, sizeof(info))) { + return false; + } + if (!transport_write(PUT_RPC_REQ_DATA, initiator2target_buffer, initiator2target_buffer_size)) { + return false; + } + if (!transport_write(EXECUTE_RPC, &transaction_id, sizeof(transaction_id))) { + return false; + } + if (!transport_read(GET_RPC_RESP_DATA, target2initiator_buffer, target2initiator_buffer_size)) { + return false; + } + return true; +} + +void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) { + // The RPC info block contains the intended transaction ID, as well as the sizes for both inbound and outbound data. + // Ignore the args -- the `split_shmem` already has the info, we just need to act upon it. + // We must keep the `split_transaction_table` non-const, so that it is able to be modified at runtime. + + split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = split_shmem->rpc_info.m2s_length; + split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = split_shmem->rpc_info.s2m_length; +} + +void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) { + // We can assume that the buffer lengths are correctly set, now, given that sequentially the rpc_info callback was already executed. + // Go through the rpc_info and execute _that_ transaction's callback, with the scratch buffers as inputs. + int8_t transaction_id = split_shmem->rpc_info.transaction_id; + if (transaction_id < NUM_TOTAL_TRANSACTIONS) { + split_transaction_desc_t *trans = &split_transaction_table[transaction_id]; + if (trans->slave_callback) { + 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); + } + } +} + +#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 0000000000..4306ba1d87 --- /dev/null +++ b/quantum/split_common/transactions.h @@ -0,0 +1,54 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "stdint.h" +#include "stdbool.h" + +#include "matrix.h" +#include "transaction_id_define.h" +#include "transport.h" + +typedef void (*slave_callback_t)(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); + +// Split transaction Descriptor +typedef struct _split_transaction_desc_t { + uint8_t * status; + uint8_t initiator2target_buffer_size; + uint16_t initiator2target_offset; + uint8_t target2initiator_buffer_size; + uint16_t target2initiator_offset; + slave_callback_t slave_callback; +} split_transaction_desc_t; + +// Forward declaration for the split transactions +extern split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS]; + +#define split_shmem_offset_ptr(offset) ((void *)(((uint8_t *)split_shmem) + (offset))) +#define split_trans_initiator2target_buffer(trans) (split_shmem_offset_ptr((trans)->initiator2target_offset)) +#define split_trans_target2initiator_buffer(trans) (split_shmem_offset_ptr((trans)->target2initiator_offset)) + +// returns false if valid data not received from slave +bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); +void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); + +void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback); + +bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); + +#define transaction_rpc_send(transaction_id, initiator2target_buffer_size, initiator2target_buffer) transaction_rpc_exec(transaction_id, initiator2target_buffer_size, initiator2target_buffer, 0, NULL) +#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 9ed0f7591b..a711ef85f0 100644 --- a/quantum/split_common/transport.c +++ b/quantum/split_common/transport.c @@ -1,452 +1,118 @@ -#include -#include - -#include "config.h" -#include "matrix.h" -#include "quantum.h" - -#define ROWS_PER_HAND (MATRIX_ROWS / 2) -#define SYNC_TIMER_OFFSET 2 - -#ifdef RGBLIGHT_ENABLE -# include "rgblight.h" -#endif - -#ifdef BACKLIGHT_ENABLE -# include "backlight.h" -#endif - -#ifdef ENCODER_ENABLE -# include "encoder.h" -static pin_t encoders_pad[] = ENCODERS_PAD_A; -# define NUMBER_OF_ENCODERS (sizeof(encoders_pad) / sizeof(pin_t)) -#endif - -#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) -# include "led_matrix.h" -#endif -#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) -# include "rgb_matrix.h" -#endif - -#if defined(USE_I2C) +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ -# include "i2c_master.h" -# include "i2c_slave.h" - -typedef struct _I2C_slave_buffer_t { -# ifndef DISABLE_SYNC_TIMER - uint32_t sync_timer; -# endif -# ifdef SPLIT_TRANSPORT_MIRROR - matrix_row_t mmatrix[ROWS_PER_HAND]; -# endif - matrix_row_t smatrix[ROWS_PER_HAND]; -# ifdef SPLIT_MODS_ENABLE - uint8_t real_mods; - uint8_t weak_mods; -# ifndef NO_ACTION_ONESHOT - uint8_t oneshot_mods; -# endif -# endif -# ifdef BACKLIGHT_ENABLE - uint8_t backlight_level; -# endif -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) - rgblight_syncinfo_t rgblight_sync; -# endif -# ifdef ENCODER_ENABLE - uint8_t encoder_state[NUMBER_OF_ENCODERS]; -# endif -# ifdef WPM_ENABLE - uint8_t current_wpm; -# endif -# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) - led_eeconfig_t led_matrix; - bool led_suspend_state; -# endif -# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - rgb_config_t rgb_matrix; - bool rgb_suspend_state; -# endif -} I2C_slave_buffer_t; +#include +#include -static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg; +#include "transactions.h" +#include "transport.h" +#include "transaction_id_define.h" +#include "atomic_util.h" -# define I2C_SYNC_TIME_START offsetof(I2C_slave_buffer_t, sync_timer) -# define I2C_KEYMAP_MASTER_START offsetof(I2C_slave_buffer_t, mmatrix) -# define I2C_KEYMAP_SLAVE_START offsetof(I2C_slave_buffer_t, smatrix) -# define I2C_REAL_MODS_START offsetof(I2C_slave_buffer_t, real_mods) -# define I2C_WEAK_MODS_START offsetof(I2C_slave_buffer_t, weak_mods) -# define I2C_ONESHOT_MODS_START offsetof(I2C_slave_buffer_t, oneshot_mods) -# define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level) -# define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync) -# define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state) -# define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm) -# define I2C_LED_MATRIX_START offsetof(I2C_slave_buffer_t, led_matrix) -# define I2C_LED_SUSPEND_START offsetof(I2C_slave_buffer_t, led_suspend_state) -# define I2C_RGB_MATRIX_START offsetof(I2C_slave_buffer_t, rgb_matrix) -# define I2C_RGB_SUSPEND_START offsetof(I2C_slave_buffer_t, rgb_suspend_state) +#ifdef USE_I2C -# define TIMEOUT 100 +# ifndef SLAVE_I2C_TIMEOUT +# define SLAVE_I2C_TIMEOUT 100 +# endif // SLAVE_I2C_TIMEOUT # ifndef SLAVE_I2C_ADDRESS # define SLAVE_I2C_ADDRESS 0x32 # endif -// Get rows from other half over i2c -bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { - i2c_readReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_SLAVE_START, (void *)slave_matrix, sizeof(i2c_buffer->smatrix), TIMEOUT); -# ifdef SPLIT_TRANSPORT_MIRROR - i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_MASTER_START, (void *)master_matrix, sizeof(i2c_buffer->mmatrix), TIMEOUT); -# endif +# include "i2c_master.h" +# include "i2c_slave.h" - // write backlight info -# ifdef BACKLIGHT_ENABLE - uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0; - if (level != i2c_buffer->backlight_level) { - if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_BACKLIGHT_START, (void *)&level, sizeof(level), TIMEOUT) >= 0) { - i2c_buffer->backlight_level = level; - } - } -# endif +// Ensure the I2C buffer has enough space +_Static_assert(sizeof(split_shared_memory_t) <= I2C_SLAVE_REG_COUNT, "split_shared_memory_t too large for I2C_SLAVE_REG_COUNT"); -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) - if (rgblight_get_change_flags()) { - rgblight_syncinfo_t rgblight_sync; - rgblight_get_syncinfo(&rgblight_sync); - if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_START, (void *)&rgblight_sync, sizeof(rgblight_sync), TIMEOUT) >= 0) { - rgblight_clear_change_flags(); - } - } -# endif +split_shared_memory_t *const split_shmem = (split_shared_memory_t *)i2c_slave_reg; -# ifdef ENCODER_ENABLE - i2c_readReg(SLAVE_I2C_ADDRESS, I2C_ENCODER_START, (void *)i2c_buffer->encoder_state, sizeof(i2c_buffer->encoder_state), TIMEOUT); - encoder_update_raw(i2c_buffer->encoder_state); -# endif +void transport_master_init(void) { i2c_init(); } +void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); } -# ifdef WPM_ENABLE - uint8_t current_wpm = get_current_wpm(); - if (current_wpm != i2c_buffer->current_wpm) { - if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WPM_START, (void *)¤t_wpm, sizeof(current_wpm), TIMEOUT) >= 0) { - i2c_buffer->current_wpm = current_wpm; - } +i2c_status_t transport_trigger_callback(int8_t id) { + // If there's no callback, indicate that we were successful + if (!split_transaction_table[id].slave_callback) { + return I2C_STATUS_SUCCESS; } -# endif -# ifdef SPLIT_MODS_ENABLE - uint8_t real_mods = get_mods(); - if (real_mods != i2c_buffer->real_mods) { - if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_REAL_MODS_START, (void *)&real_mods, sizeof(real_mods), TIMEOUT) >= 0) { - i2c_buffer->real_mods = real_mods; + // Kick off the "callback executor", now that data has been written to the slave + split_shmem->transaction_id = id; + split_transaction_desc_t *trans = &split_transaction_table[I2C_EXECUTE_CALLBACK]; + return i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, SLAVE_I2C_TIMEOUT); +} + +bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) { + i2c_status_t status; + split_transaction_desc_t *trans = &split_transaction_table[id]; + if (initiator2target_length > 0) { + size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length; + memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len); + if ((status = i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) { + return false; } } - uint8_t weak_mods = get_weak_mods(); - if (weak_mods != i2c_buffer->weak_mods) { - if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WEAK_MODS_START, (void *)&weak_mods, sizeof(weak_mods), TIMEOUT) >= 0) { - i2c_buffer->weak_mods = weak_mods; - } + // If we need to execute a callback on the slave, do so + if ((status = transport_trigger_callback(id)) < 0) { + return false; } -# ifndef NO_ACTION_ONESHOT - uint8_t oneshot_mods = get_oneshot_mods(); - if (oneshot_mods != i2c_buffer->oneshot_mods) { - if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_ONESHOT_MODS_START, (void *)&oneshot_mods, sizeof(oneshot_mods), TIMEOUT) >= 0) { - i2c_buffer->oneshot_mods = oneshot_mods; + if (target2initiator_length > 0) { + size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length; + if ((status = i2c_readReg(SLAVE_I2C_ADDRESS, trans->target2initiator_offset, split_trans_target2initiator_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) { + return false; } + memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len); } -# endif -# endif -# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) - i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_MATRIX_START, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix), TIMEOUT); - bool suspend_state = led_matrix_get_suspend_state(); - i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->led_suspend_state), TIMEOUT); -# endif -# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_MATRIX_START, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix), TIMEOUT); - bool suspend_state = rgb_matrix_get_suspend_state(); - i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->rgb_suspend_state), TIMEOUT); -# endif - -# ifndef DISABLE_SYNC_TIMER - i2c_buffer->sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; - i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_SYNC_TIME_START, (void *)&i2c_buffer->sync_timer, sizeof(i2c_buffer->sync_timer), TIMEOUT); -# endif return true; } -void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { -# ifndef DISABLE_SYNC_TIMER - sync_timer_update(i2c_buffer->sync_timer); -# endif - // Copy matrix to I2C buffer - memcpy((void *)i2c_buffer->smatrix, (void *)slave_matrix, sizeof(i2c_buffer->smatrix)); -# ifdef SPLIT_TRANSPORT_MIRROR - memcpy((void *)master_matrix, (void *)i2c_buffer->mmatrix, sizeof(i2c_buffer->mmatrix)); -# endif - -// Read Backlight Info -# ifdef BACKLIGHT_ENABLE - backlight_set(i2c_buffer->backlight_level); -# endif - -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) - // Update the RGB with the new data - if (i2c_buffer->rgblight_sync.status.change_flags != 0) { - rgblight_update_sync(&i2c_buffer->rgblight_sync, false); - i2c_buffer->rgblight_sync.status.change_flags = 0; - } -# endif - -# ifdef ENCODER_ENABLE - encoder_state_raw(i2c_buffer->encoder_state); -# endif - -# ifdef WPM_ENABLE - set_current_wpm(i2c_buffer->current_wpm); -# endif - -# ifdef SPLIT_MODS_ENABLE - set_mods(i2c_buffer->real_mods); - set_weak_mods(i2c_buffer->weak_mods); -# ifndef NO_ACTION_ONESHOT - set_oneshot_mods(i2c_buffer->oneshot_mods); -# endif -# endif - -# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) - memcpy((void *)i2c_buffer->led_matrix, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix)); - led_matrix_set_suspend_state(i2c_buffer->led_suspend_state); -# endif -# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - memcpy((void *)i2c_buffer->rgb_matrix, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix)); - rgb_matrix_set_suspend_state(i2c_buffer->rgb_suspend_state); -# endif -} - -void transport_master_init(void) { i2c_init(); } - -void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); } - -#else // USE_SERIAL +#else // USE_I2C # include "serial.h" -typedef struct _Serial_s2m_buffer_t { - // TODO: if MATRIX_COLS > 8 change to uint8_t packed_matrix[] for pack/unpack - matrix_row_t smatrix[ROWS_PER_HAND]; - -# ifdef ENCODER_ENABLE - uint8_t encoder_state[NUMBER_OF_ENCODERS]; -# endif - -} Serial_s2m_buffer_t; - -typedef struct _Serial_m2s_buffer_t { -# ifdef SPLIT_MODS_ENABLE - uint8_t real_mods; - uint8_t weak_mods; -# ifndef NO_ACTION_ONESHOT - uint8_t oneshot_mods; -# endif -# endif -# ifndef DISABLE_SYNC_TIMER - uint32_t sync_timer; -# endif -# ifdef SPLIT_TRANSPORT_MIRROR - matrix_row_t mmatrix[ROWS_PER_HAND]; -# endif -# ifdef BACKLIGHT_ENABLE - uint8_t backlight_level; -# endif -# ifdef WPM_ENABLE - uint8_t current_wpm; -# endif -# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) - led_eeconfig_t led_matrix; - bool led_suspend_state; -# endif -# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - rgb_config_t rgb_matrix; - bool rgb_suspend_state; -# endif -} Serial_m2s_buffer_t; - -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) -// When MCUs on both sides drive their respective RGB LED chains, -// it is necessary to synchronize, so it is necessary to communicate RGB -// information. In that case, define RGBLIGHT_SPLIT with info on the number -// of LEDs on each half. -// -// Otherwise, if the master side MCU drives both sides RGB LED chains, -// there is no need to communicate. - -typedef struct _Serial_rgblight_t { - rgblight_syncinfo_t rgblight_sync; -} Serial_rgblight_t; +static split_shared_memory_t shared_memory; +split_shared_memory_t *const split_shmem = &shared_memory; -volatile Serial_rgblight_t serial_rgblight = {}; -uint8_t volatile status_rgblight = 0; -# endif - -volatile Serial_s2m_buffer_t serial_s2m_buffer = {}; -volatile Serial_m2s_buffer_t serial_m2s_buffer = {}; -uint8_t volatile status0 = 0; - -enum serial_transaction_id { - GET_SLAVE_MATRIX = 0, -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) - PUT_RGBLIGHT, -# endif -}; - -SSTD_t transactions[] = { - [GET_SLAVE_MATRIX] = - { - (uint8_t *)&status0, - sizeof(serial_m2s_buffer), - (uint8_t *)&serial_m2s_buffer, - sizeof(serial_s2m_buffer), - (uint8_t *)&serial_s2m_buffer, - }, -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) - [PUT_RGBLIGHT] = - { - (uint8_t *)&status_rgblight, sizeof(serial_rgblight), (uint8_t *)&serial_rgblight, 0, NULL // no slave to master transfer - }, -# endif -}; - -void transport_master_init(void) { soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); } - -void transport_slave_init(void) { soft_serial_target_init(transactions, TID_LIMIT(transactions)); } +void transport_master_init(void) { soft_serial_initiator_init(); } +void transport_slave_init(void) { soft_serial_target_init(); } -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) - -// rgblight synchronization information communication. - -void transport_rgblight_master(void) { - if (rgblight_get_change_flags()) { - rgblight_get_syncinfo((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync); - if (soft_serial_transaction(PUT_RGBLIGHT) == TRANSACTION_END) { - rgblight_clear_change_flags(); - } - } -} - -void transport_rgblight_slave(void) { - if (status_rgblight == TRANSACTION_ACCEPTED) { - rgblight_update_sync((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync, false); - status_rgblight = TRANSACTION_END; +bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) { + split_transaction_desc_t *trans = &split_transaction_table[id]; + if (initiator2target_length > 0) { + size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length; + memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len); } -} -# else -# define transport_rgblight_master() -# define transport_rgblight_slave() -# endif - -bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { -# ifndef SERIAL_USE_MULTI_TRANSACTION - if (soft_serial_transaction() != TRANSACTION_END) { - return false; - } -# else - transport_rgblight_master(); - if (soft_serial_transaction(GET_SLAVE_MATRIX) != TRANSACTION_END) { + if (soft_serial_transaction(id) != TRANSACTION_END) { return false; } -# endif - // TODO: if MATRIX_COLS > 8 change to unpack() - for (int i = 0; i < ROWS_PER_HAND; ++i) { - slave_matrix[i] = serial_s2m_buffer.smatrix[i]; -# ifdef SPLIT_TRANSPORT_MIRROR - serial_m2s_buffer.mmatrix[i] = master_matrix[i]; -# endif + if (target2initiator_length > 0) { + size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length; + memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len); } -# ifdef BACKLIGHT_ENABLE - // Write backlight level for slave to read - serial_m2s_buffer.backlight_level = is_backlight_enabled() ? get_backlight_level() : 0; -# endif - -# ifdef ENCODER_ENABLE - encoder_update_raw((uint8_t *)serial_s2m_buffer.encoder_state); -# endif - -# ifdef WPM_ENABLE - // Write wpm to slave - serial_m2s_buffer.current_wpm = get_current_wpm(); -# endif - -# ifdef SPLIT_MODS_ENABLE - serial_m2s_buffer.real_mods = get_mods(); - serial_m2s_buffer.weak_mods = get_weak_mods(); -# ifndef NO_ACTION_ONESHOT - serial_m2s_buffer.oneshot_mods = get_oneshot_mods(); -# endif -# endif - -# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) - serial_m2s_buffer.led_matrix = led_matrix_eeconfig; - serial_m2s_buffer.led_suspend_state = led_matrix_get_suspend_state(); -# endif -# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - serial_m2s_buffer.rgb_matrix = rgb_matrix_config; - serial_m2s_buffer.rgb_suspend_state = rgb_matrix_get_suspend_state(); -# endif - -# ifndef DISABLE_SYNC_TIMER - serial_m2s_buffer.sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; -# endif return true; } -void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { - transport_rgblight_slave(); -# ifndef DISABLE_SYNC_TIMER - sync_timer_update(serial_m2s_buffer.sync_timer); -# endif - - // TODO: if MATRIX_COLS > 8 change to pack() - for (int i = 0; i < ROWS_PER_HAND; ++i) { - serial_s2m_buffer.smatrix[i] = slave_matrix[i]; -# ifdef SPLIT_TRANSPORT_MIRROR - master_matrix[i] = serial_m2s_buffer.mmatrix[i]; -# endif - } -# ifdef BACKLIGHT_ENABLE - backlight_set(serial_m2s_buffer.backlight_level); -# endif - -# ifdef ENCODER_ENABLE - encoder_state_raw((uint8_t *)serial_s2m_buffer.encoder_state); -# endif +#endif // USE_I2C -# ifdef WPM_ENABLE - set_current_wpm(serial_m2s_buffer.current_wpm); -# endif - -# ifdef SPLIT_MODS_ENABLE - set_mods(serial_m2s_buffer.real_mods); - set_weak_mods(serial_m2s_buffer.weak_mods); -# ifndef NO_ACTION_ONESHOT - set_oneshot_mods(serial_m2s_buffer.oneshot_mods); -# endif -# endif - -# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) - led_matrix_eeconfig = serial_m2s_buffer.led_matrix; - led_matrix_set_suspend_state(serial_m2s_buffer.led_suspend_state); -# endif -# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - rgb_matrix_config = serial_m2s_buffer.rgb_matrix; - rgb_matrix_set_suspend_state(serial_m2s_buffer.rgb_suspend_state); -# endif -} +bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { return transactions_master(master_matrix, slave_matrix); } -#endif +void 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 a9f66301bf..2e07f6b25c 100644 --- a/quantum/split_common/transport.h +++ b/quantum/split_common/transport.h @@ -1,10 +1,175 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once +#include "stdint.h" +#include "stdbool.h" + +#include "progmem.h" +#include "action_layer.h" #include "matrix.h" +#ifndef RPC_M2S_BUFFER_SIZE +# define RPC_M2S_BUFFER_SIZE 32 +#endif // RPC_M2S_BUFFER_SIZE + +#ifndef RPC_S2M_BUFFER_SIZE +# define RPC_S2M_BUFFER_SIZE 32 +#endif // RPC_S2M_BUFFER_SIZE + void transport_master_init(void); void transport_slave_init(void); // returns false if valid data not received from slave bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); + +bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length); + +#ifdef ENCODER_ENABLE +# include "encoder.h" +# define NUMBER_OF_ENCODERS (sizeof((pin_t[])ENCODERS_PAD_A) / sizeof(pin_t)) +#endif // ENCODER_ENABLE + +#ifdef BACKLIGHT_ENABLE +# include "backlight.h" +#endif // BACKLIGHT_ENABLE + +#ifdef RGBLIGHT_ENABLE +# include "rgblight.h" +#endif // RGBLIGHT_ENABLE + +typedef struct _split_slave_matrix_sync_t { + uint8_t checksum; + matrix_row_t matrix[(MATRIX_ROWS) / 2]; +} split_slave_matrix_sync_t; + +#ifdef SPLIT_TRANSPORT_MIRROR +typedef struct _split_master_matrix_sync_t { + matrix_row_t matrix[(MATRIX_ROWS) / 2]; +} split_master_matrix_sync_t; +#endif // SPLIT_TRANSPORT_MIRROR + +#ifdef ENCODER_ENABLE +typedef struct _split_slave_encoder_sync_t { + uint8_t checksum; + uint8_t state[NUMBER_OF_ENCODERS]; +} split_slave_encoder_sync_t; +#endif // ENCODER_ENABLE + +#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) +typedef struct _split_layers_sync_t { + layer_state_t layer_state; + layer_state_t default_layer_state; +} split_layers_sync_t; +#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) +# include "led_matrix.h" + +typedef struct _led_matrix_sync_t { + led_eeconfig_t led_matrix; + bool led_suspend_state; +} led_matrix_sync_t; +#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) +# include "rgb_matrix.h" + +typedef struct _rgb_matrix_sync_t { + rgb_config_t rgb_matrix; + bool rgb_suspend_state; +} rgb_matrix_sync_t; +#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + +#ifdef SPLIT_MODS_ENABLE +typedef struct _split_mods_sync_t { + uint8_t real_mods; + uint8_t weak_mods; +# ifndef NO_ACTION_ONESHOT + uint8_t oneshot_mods; +# endif // NO_ACTION_ONESHOT +} split_mods_sync_t; +#endif // SPLIT_MODS_ENABLE + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) +typedef struct _rpc_sync_info_t { + int8_t transaction_id; + uint8_t m2s_length; + uint8_t s2m_length; +} rpc_sync_info_t; +#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + +typedef struct _split_shared_memory_t { +#ifdef USE_I2C + int8_t transaction_id; +#endif // USE_I2C + + split_slave_matrix_sync_t smatrix; + +#ifdef SPLIT_TRANSPORT_MIRROR + split_master_matrix_sync_t mmatrix; +#endif // SPLIT_TRANSPORT_MIRROR + +#ifdef ENCODER_ENABLE + split_slave_encoder_sync_t encoders; +#endif // ENCODER_ENABLE + +#ifndef DISABLE_SYNC_TIMER + uint32_t sync_timer; +#endif // DISABLE_SYNC_TIMER + +#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + split_layers_sync_t layers; +#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +#ifdef SPLIT_LED_STATE_ENABLE + uint8_t led_state; +#endif // SPLIT_LED_STATE_ENABLE + +#ifdef SPLIT_MODS_ENABLE + split_mods_sync_t mods; +#endif // SPLIT_MODS_ENABLE + +#ifdef BACKLIGHT_ENABLE + uint8_t backlight_level; +#endif // BACKLIGHT_ENABLE + +#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + rgblight_syncinfo_t rgblight_sync; +#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + led_matrix_sync_t led_matrix_sync; +#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + rgb_matrix_sync_t rgb_matrix_sync; +#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + +#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + uint8_t current_wpm; +#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + rpc_sync_info_t rpc_info; + uint8_t rpc_m2s_buffer[RPC_M2S_BUFFER_SIZE]; + uint8_t rpc_s2m_buffer[RPC_S2M_BUFFER_SIZE]; +#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) +} split_shared_memory_t; + +extern 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 e7d92cfac6..a8b391e893 100644 --- a/tmk_core/common/host.c +++ b/tmk_core/common/host.c @@ -17,6 +17,7 @@ along with this program. If not, see . #include //#include +#include "keyboard.h" #include "keycode.h" #include "host.h" #include "util.h" @@ -35,15 +36,20 @@ void host_set_driver(host_driver_t *d) { driver = d; } host_driver_t *host_get_driver(void) { return driver; } +#ifdef SPLIT_KEYBOARD +uint8_t split_led_state = 0; +void set_split_host_keyboard_leds(uint8_t led_state) { split_led_state = led_state; } +#endif + uint8_t host_keyboard_leds(void) { +#ifdef SPLIT_KEYBOARD + if (!is_keyboard_master()) return split_led_state; +#endif if (!driver) return 0; return (*driver->keyboard_leds)(); } -led_t host_keyboard_led_state(void) { - if (!driver) return (led_t){0}; - return (led_t)((*driver->keyboard_leds)()); -} +led_t host_keyboard_led_state(void) { return (led_t)host_keyboard_leds(); } /* send report */ void host_keyboard_send(report_keyboard_t *report) { -- cgit v1.2.3 From 3ab805fc67e95dfb23188bf63c7f5fbb2edcf038 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Sat, 3 Jul 2021 00:20:11 -0700 Subject: Add support for NO_PIN to all matrix types (#12238) Co-authored-by: Nick Brassel --- quantum/matrix.c | 66 ++++++++++++++++++++++++++++++++++--------- quantum/split_common/matrix.c | 66 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 104 insertions(+), 28 deletions(-) (limited to 'quantum/split_common/matrix.c') diff --git a/quantum/matrix.c b/quantum/matrix.c index 71ef270892..566d9ff340 100644 --- a/quantum/matrix.c +++ b/quantum/matrix.c @@ -53,6 +53,14 @@ static inline void setPinInputHigh_atomic(pin_t pin) { ATOMIC_BLOCK_FORCEON { setPinInputHigh(pin); } } +static inline uint8_t readMatrixPin(pin_t pin) { + if (pin != NO_PIN) { + return readPin(pin); + } else { + return 1; + } +} + // matrix code #ifdef DIRECT_PINS @@ -87,20 +95,34 @@ __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[] # if defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS) # if (DIODE_DIRECTION == COL2ROW) -static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); } +static bool select_row(uint8_t row) { + pin_t pin = row_pins[row]; + if (pin != NO_PIN) { + setPinOutput_writeLow(pin); + return true; + } + return false; +} -static void unselect_row(uint8_t row) { setPinInputHigh_atomic(row_pins[row]); } +static void unselect_row(uint8_t row) { + pin_t pin = row_pins[row]; + if (pin != NO_PIN) { + setPinInputHigh_atomic(pin); + } +} static void unselect_rows(void) { for (uint8_t x = 0; x < MATRIX_ROWS; x++) { - setPinInputHigh_atomic(row_pins[x]); + unselect_row(x); } } __attribute__((weak)) void matrix_init_pins(void) { unselect_rows(); for (uint8_t x = 0; x < MATRIX_COLS; x++) { - setPinInputHigh_atomic(col_pins[x]); + if (col_pins[x] != NO_PIN) { + setPinInputHigh_atomic(col_pins[x]); + } } } @@ -108,14 +130,14 @@ __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[] // Start with a clear matrix row matrix_row_t current_row_value = 0; - // Select row - select_row(current_row); + if (!select_row(current_row)) { // Select row + return; // skip NO_PIN row + } matrix_output_select_delay(); // For each col... for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) { - // Select the col pin to read (active low) - uint8_t pin_state = readPin(col_pins[col_index]); + uint8_t pin_state = readMatrixPin(col_pins[col_index]); // Populate the matrix row with the state of the col pin current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index); @@ -131,32 +153,48 @@ __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[] # elif (DIODE_DIRECTION == ROW2COL) -static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); } +static bool select_col(uint8_t col) { + pin_t pin = col_pins[col]; + if (pin != NO_PIN) { + setPinOutput_writeLow(pin); + return true; + } + return false; +} -static void unselect_col(uint8_t col) { setPinInputHigh_atomic(col_pins[col]); } +static void unselect_col(uint8_t col) { + pin_t pin = col_pins[col]; + if (pin != NO_PIN) { + setPinInputHigh_atomic(pin); + } +} static void unselect_cols(void) { for (uint8_t x = 0; x < MATRIX_COLS; x++) { - setPinInputHigh_atomic(col_pins[x]); + unselect_col(x); } } __attribute__((weak)) void matrix_init_pins(void) { unselect_cols(); for (uint8_t x = 0; x < MATRIX_ROWS; x++) { - setPinInputHigh_atomic(row_pins[x]); + if (row_pins[x] != NO_PIN) { + setPinInputHigh_atomic(row_pins[x]); + } } } __attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { // Select col - select_col(current_col); + if (!select_col(current_col)) { // select col + return; // skip NO_PIN col + } matrix_output_select_delay(); // For each row... for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) { // Check row pin state - if (readPin(row_pins[row_index]) == 0) { + if (readMatrixPin(row_pins[row_index]) == 0) { // Pin LO, set col bit current_matrix[row_index] |= (MATRIX_ROW_SHIFTER << current_col); } else { diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c index 56d91b07fe..d8e078e9bb 100644 --- a/quantum/split_common/matrix.c +++ b/quantum/split_common/matrix.c @@ -67,6 +67,14 @@ static inline void setPinInputHigh_atomic(pin_t pin) { ATOMIC_BLOCK_FORCEON { setPinInputHigh(pin); } } +static inline uint8_t readMatrixPin(pin_t pin) { + if (pin != NO_PIN) { + return readPin(pin); + } else { + return 1; + } +} + // matrix code #ifdef DIRECT_PINS @@ -101,20 +109,34 @@ __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[] # if defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS) # if (DIODE_DIRECTION == COL2ROW) -static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); } +static bool select_row(uint8_t row) { + pin_t pin = row_pins[row]; + if (pin != NO_PIN) { + setPinOutput_writeLow(pin); + return true; + } + return false; +} -static void unselect_row(uint8_t row) { setPinInputHigh_atomic(row_pins[row]); } +static void unselect_row(uint8_t row) { + pin_t pin = row_pins[row]; + if (pin != NO_PIN) { + setPinInputHigh_atomic(pin); + } +} static void unselect_rows(void) { for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { - setPinInputHigh_atomic(row_pins[x]); + unselect_row(x); } } __attribute__((weak)) void matrix_init_pins(void) { unselect_rows(); for (uint8_t x = 0; x < MATRIX_COLS; x++) { - setPinInputHigh_atomic(col_pins[x]); + if (col_pins[x] != NO_PIN) { + setPinInputHigh_atomic(col_pins[x]); + } } } @@ -122,14 +144,14 @@ __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[] // Start with a clear matrix row matrix_row_t current_row_value = 0; - // Select row - select_row(current_row); + if (!select_row(current_row)) { // Select row + return; // skip NO_PIN row + } matrix_output_select_delay(); // For each col... for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) { - // Select the col pin to read (active low) - uint8_t pin_state = readPin(col_pins[col_index]); + uint8_t pin_state = readMatrixPin(col_pins[col_index]); // Populate the matrix row with the state of the col pin current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index); @@ -145,32 +167,48 @@ __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[] # elif (DIODE_DIRECTION == ROW2COL) -static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); } +static bool select_col(uint8_t col) { + pin_t pin = col_pins[col]; + if (pin != NO_PIN) { + setPinOutput_writeLow(pin); + return true; + } + return false; +} -static void unselect_col(uint8_t col) { setPinInputHigh_atomic(col_pins[col]); } +static void unselect_col(uint8_t col) { + pin_t pin = col_pins[col]; + if (pin != NO_PIN) { + setPinInputHigh_atomic(pin); + } +} static void unselect_cols(void) { for (uint8_t x = 0; x < MATRIX_COLS; x++) { - setPinInputHigh_atomic(col_pins[x]); + unselect_col(x); } } __attribute__((weak)) void matrix_init_pins(void) { unselect_cols(); for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { - setPinInputHigh_atomic(row_pins[x]); + if (row_pins[x] != NO_PIN) { + setPinInputHigh_atomic(row_pins[x]); + } } } __attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { // Select col - select_col(current_col); + if (!select_col(current_col)) { // select col + return; // skip NO_PIN col + } matrix_output_select_delay(); // For each row... for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) { // Check row pin state - if (readPin(row_pins[row_index]) == 0) { + if (readMatrixPin(row_pins[row_index]) == 0) { // Pin LO, set col bit current_matrix[row_index] |= (MATRIX_ROW_SHIFTER << current_col); } else { -- cgit v1.2.3 From ccc0b23a75f30f0ee9e1c6440dd3d56c99d38ea2 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Sun, 11 Jul 2021 14:31:35 -0700 Subject: Unify matrix for split common and regular matrix (#13330) --- common_features.mk | 6 +- quantum/matrix.c | 133 ++++++++++++++++- quantum/split_common/matrix.c | 340 ------------------------------------------ 3 files changed, 126 insertions(+), 353 deletions(-) delete mode 100644 quantum/split_common/matrix.c (limited to 'quantum/split_common/matrix.c') diff --git a/common_features.mk b/common_features.mk index f7bdedf2ff..8080113efd 100644 --- a/common_features.mk +++ b/common_features.mk @@ -511,11 +511,7 @@ ifneq ($(strip $(CUSTOM_MATRIX)), yes) # if 'lite' then skip the actual matrix implementation ifneq ($(strip $(CUSTOM_MATRIX)), lite) # Include the standard or split matrix code if needed - ifeq ($(strip $(SPLIT_KEYBOARD)), yes) - QUANTUM_SRC += $(QUANTUM_DIR)/split_common/matrix.c - else - QUANTUM_SRC += $(QUANTUM_DIR)/matrix.c - endif + QUANTUM_SRC += $(QUANTUM_DIR)/matrix.c endif endif diff --git a/quantum/matrix.c b/quantum/matrix.c index 566d9ff340..ed4643f813 100644 --- a/quantum/matrix.c +++ b/quantum/matrix.c @@ -21,15 +21,43 @@ along with this program. If not, see . #include "matrix.h" #include "debounce.h" #include "quantum.h" +#ifdef SPLIT_KEYBOARD +# include "split_common/split_util.h" +# include "split_common/transactions.h" + +# ifndef ERROR_DISCONNECT_COUNT +# define ERROR_DISCONNECT_COUNT 5 +# endif // ERROR_DISCONNECT_COUNT + +# define ROWS_PER_HAND (MATRIX_ROWS / 2) +#else +# define ROWS_PER_HAND (MATRIX_ROWS) +#endif + +#ifdef DIRECT_PINS_RIGHT +# define SPLIT_MUTABLE +#else +# define SPLIT_MUTABLE const +#endif +#ifdef MATRIX_ROW_PINS_RIGHT +# define SPLIT_MUTABLE_ROW +#else +# define SPLIT_MUTABLE_ROW const +#endif +#ifdef MATRIX_COL_PINS_RIGHT +# define SPLIT_MUTABLE_COL +#else +# define SPLIT_MUTABLE_COL const +#endif #ifdef DIRECT_PINS -static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS; +static SPLIT_MUTABLE pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS; #elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW) # ifdef MATRIX_ROW_PINS -static const pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; +static SPLIT_MUTABLE_ROW pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; # endif // MATRIX_ROW_PINS # ifdef MATRIX_COL_PINS -static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; +static SPLIT_MUTABLE_COL pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; # endif // MATRIX_COL_PINS #endif @@ -37,10 +65,19 @@ static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values +#ifdef SPLIT_KEYBOARD +// row offsets for each hand +uint8_t thisHand, thatHand; +#endif + // user-defined overridable functions __attribute__((weak)) void matrix_init_pins(void); __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row); __attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col); +#ifdef SPLIT_KEYBOARD +__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); } +__attribute__((weak)) void matrix_slave_scan_user(void) {} +#endif static inline void setPinOutput_writeLow(pin_t pin) { ATOMIC_BLOCK_FORCEON { @@ -192,7 +229,7 @@ __attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[] matrix_output_select_delay(); // For each row... - for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) { + for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) { // Check row pin state if (readMatrixPin(row_pins[row_index]) == 0) { // Pin LO, set col bit @@ -217,6 +254,37 @@ __attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[] #endif void matrix_init(void) { +#ifdef SPLIT_KEYBOARD + split_pre_init(); + + // Set pinout for right half if pinout for that half is defined + if (!isLeftHand) { +# ifdef DIRECT_PINS_RIGHT + const pin_t direct_pins_right[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS_RIGHT; + for (uint8_t i = 0; i < MATRIX_ROWS; i++) { + for (uint8_t j = 0; j < MATRIX_COLS; j++) { + direct_pins[i][j] = direct_pins_right[i][j]; + } + } +# endif +# ifdef MATRIX_ROW_PINS_RIGHT + const pin_t row_pins_right[MATRIX_ROWS] = MATRIX_ROW_PINS_RIGHT; + for (uint8_t i = 0; i < MATRIX_ROWS; i++) { + row_pins[i] = row_pins_right[i]; + } +# endif +# ifdef MATRIX_COL_PINS_RIGHT + const pin_t col_pins_right[MATRIX_COLS] = MATRIX_COL_PINS_RIGHT; + for (uint8_t i = 0; i < MATRIX_COLS; i++) { + col_pins[i] = col_pins_right[i]; + } +# endif + } + + thisHand = isLeftHand ? 0 : (ROWS_PER_HAND); + thatHand = ROWS_PER_HAND - thisHand; +#endif + // initialize key pins matrix_init_pins(); @@ -226,17 +294,62 @@ void matrix_init(void) { matrix[i] = 0; } - debounce_init(MATRIX_ROWS); + debounce_init(ROWS_PER_HAND); matrix_init_quantum(); + +#ifdef SPLIT_KEYBOARD + split_post_init(); +#endif } +#ifdef SPLIT_KEYBOARD +bool matrix_post_scan(void) { + bool changed = false; + if (is_keyboard_master()) { + static uint8_t error_count; + + matrix_row_t slave_matrix[ROWS_PER_HAND] = {0}; + if (!transport_master(matrix + thisHand, slave_matrix)) { + error_count++; + + if (error_count > ERROR_DISCONNECT_COUNT) { + // reset other half if disconnected + for (int i = 0; i < ROWS_PER_HAND; ++i) { + matrix[thatHand + i] = 0; + slave_matrix[i] = 0; + } + + changed = true; + } + } else { + error_count = 0; + + for (int i = 0; i < ROWS_PER_HAND; ++i) { + if (matrix[thatHand + i] != slave_matrix[i]) { + matrix[thatHand + i] = slave_matrix[i]; + changed = true; + } + } + } + + matrix_scan_quantum(); + } else { + transport_slave(matrix + thatHand, matrix + thisHand); + + matrix_slave_scan_kb(); + } + + return changed; +} +#endif + uint8_t matrix_scan(void) { matrix_row_t curr_matrix[MATRIX_ROWS] = {0}; #if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) // Set row, read cols - for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) { + for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) { matrix_read_cols_on_row(curr_matrix, current_row); } #elif (DIODE_DIRECTION == ROW2COL) @@ -249,8 +362,12 @@ uint8_t matrix_scan(void) { bool changed = memcmp(raw_matrix, curr_matrix, sizeof(curr_matrix)) != 0; if (changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix)); - debounce(raw_matrix, matrix, MATRIX_ROWS, changed); - +#ifdef SPLIT_KEYBOARD + debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed); + changed = (changed || matrix_post_scan()); +#else + debounce(raw_matrix, matrix, ROWS_PER_HAND, changed); matrix_scan_quantum(); +#endif return (uint8_t)changed; } diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c deleted file mode 100644 index d8e078e9bb..0000000000 --- a/quantum/split_common/matrix.c +++ /dev/null @@ -1,340 +0,0 @@ -/* -Copyright 2012 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#include -#include -#include -#include "util.h" -#include "matrix.h" -#include "debounce.h" -#include "quantum.h" -#include "split_util.h" -#include "config.h" -#include "transactions.h" - -#ifndef ERROR_DISCONNECT_COUNT -# define ERROR_DISCONNECT_COUNT 5 -#endif // ERROR_DISCONNECT_COUNT - -#define ROWS_PER_HAND (MATRIX_ROWS / 2) - -#ifdef DIRECT_PINS -static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS; -#elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW) -# ifdef MATRIX_ROW_PINS -static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; -# endif // MATRIX_ROW_PINS -# ifdef MATRIX_COL_PINS -static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; -# endif // MATRIX_COL_PINS -#endif - -/* matrix state(1:on, 0:off) */ -extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values -extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values - -// row offsets for each hand -uint8_t thisHand, thatHand; - -// user-defined overridable functions -__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); } -__attribute__((weak)) void matrix_slave_scan_user(void) {} -__attribute__((weak)) void matrix_init_pins(void); -__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row); -__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col); - -static inline void setPinOutput_writeLow(pin_t pin) { - ATOMIC_BLOCK_FORCEON { - setPinOutput(pin); - writePinLow(pin); - } -} - -static inline void setPinInputHigh_atomic(pin_t pin) { - ATOMIC_BLOCK_FORCEON { setPinInputHigh(pin); } -} - -static inline uint8_t readMatrixPin(pin_t pin) { - if (pin != NO_PIN) { - return readPin(pin); - } else { - return 1; - } -} - -// matrix code - -#ifdef DIRECT_PINS - -__attribute__((weak)) void matrix_init_pins(void) { - for (int row = 0; row < MATRIX_ROWS; row++) { - for (int col = 0; col < MATRIX_COLS; col++) { - pin_t pin = direct_pins[row][col]; - if (pin != NO_PIN) { - setPinInputHigh(pin); - } - } - } -} - -__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { - // Start with a clear matrix row - matrix_row_t current_row_value = 0; - - for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) { - pin_t pin = direct_pins[current_row][col_index]; - if (pin != NO_PIN) { - current_row_value |= readPin(pin) ? 0 : (MATRIX_ROW_SHIFTER << col_index); - } - } - - // Update the matrix - current_matrix[current_row] = current_row_value; -} - -#elif defined(DIODE_DIRECTION) -# if defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS) -# if (DIODE_DIRECTION == COL2ROW) - -static bool select_row(uint8_t row) { - pin_t pin = row_pins[row]; - if (pin != NO_PIN) { - setPinOutput_writeLow(pin); - return true; - } - return false; -} - -static void unselect_row(uint8_t row) { - pin_t pin = row_pins[row]; - if (pin != NO_PIN) { - setPinInputHigh_atomic(pin); - } -} - -static void unselect_rows(void) { - for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { - unselect_row(x); - } -} - -__attribute__((weak)) void matrix_init_pins(void) { - unselect_rows(); - for (uint8_t x = 0; x < MATRIX_COLS; x++) { - if (col_pins[x] != NO_PIN) { - setPinInputHigh_atomic(col_pins[x]); - } - } -} - -__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { - // Start with a clear matrix row - matrix_row_t current_row_value = 0; - - if (!select_row(current_row)) { // Select row - return; // skip NO_PIN row - } - matrix_output_select_delay(); - - // For each col... - for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) { - uint8_t pin_state = readMatrixPin(col_pins[col_index]); - - // Populate the matrix row with the state of the col pin - current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index); - } - - // Unselect row - unselect_row(current_row); - matrix_output_unselect_delay(); // wait for all Col signals to go HIGH - - // Update the matrix - current_matrix[current_row] = current_row_value; -} - -# elif (DIODE_DIRECTION == ROW2COL) - -static bool select_col(uint8_t col) { - pin_t pin = col_pins[col]; - if (pin != NO_PIN) { - setPinOutput_writeLow(pin); - return true; - } - return false; -} - -static void unselect_col(uint8_t col) { - pin_t pin = col_pins[col]; - if (pin != NO_PIN) { - setPinInputHigh_atomic(pin); - } -} - -static void unselect_cols(void) { - for (uint8_t x = 0; x < MATRIX_COLS; x++) { - unselect_col(x); - } -} - -__attribute__((weak)) void matrix_init_pins(void) { - unselect_cols(); - for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { - if (row_pins[x] != NO_PIN) { - setPinInputHigh_atomic(row_pins[x]); - } - } -} - -__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { - // Select col - if (!select_col(current_col)) { // select col - return; // skip NO_PIN col - } - matrix_output_select_delay(); - - // For each row... - for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) { - // Check row pin state - if (readMatrixPin(row_pins[row_index]) == 0) { - // Pin LO, set col bit - current_matrix[row_index] |= (MATRIX_ROW_SHIFTER << current_col); - } else { - // Pin HI, clear col bit - current_matrix[row_index] &= ~(MATRIX_ROW_SHIFTER << current_col); - } - } - - // Unselect col - unselect_col(current_col); - matrix_output_unselect_delay(); // wait for all Row signals to go HIGH -} - -# else -# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL! -# endif -# endif // defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS) -#else -# error DIODE_DIRECTION is not defined! -#endif - -void matrix_init(void) { - split_pre_init(); - - // Set pinout for right half if pinout for that half is defined - if (!isLeftHand) { -#ifdef DIRECT_PINS_RIGHT - const pin_t direct_pins_right[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS_RIGHT; - for (uint8_t i = 0; i < MATRIX_ROWS; i++) { - for (uint8_t j = 0; j < MATRIX_COLS; j++) { - direct_pins[i][j] = direct_pins_right[i][j]; - } - } -#endif -#ifdef MATRIX_ROW_PINS_RIGHT - const pin_t row_pins_right[MATRIX_ROWS] = MATRIX_ROW_PINS_RIGHT; - for (uint8_t i = 0; i < MATRIX_ROWS; i++) { - row_pins[i] = row_pins_right[i]; - } -#endif -#ifdef MATRIX_COL_PINS_RIGHT - const pin_t col_pins_right[MATRIX_COLS] = MATRIX_COL_PINS_RIGHT; - for (uint8_t i = 0; i < MATRIX_COLS; i++) { - col_pins[i] = col_pins_right[i]; - } -#endif - } - - thisHand = isLeftHand ? 0 : (ROWS_PER_HAND); - thatHand = ROWS_PER_HAND - thisHand; - - // initialize key pins - matrix_init_pins(); - - // initialize matrix state: all keys off - for (uint8_t i = 0; i < MATRIX_ROWS; i++) { - raw_matrix[i] = 0; - matrix[i] = 0; - } - - debounce_init(ROWS_PER_HAND); - - matrix_init_quantum(); - - split_post_init(); -} - -bool matrix_post_scan(void) { - bool changed = false; - if (is_keyboard_master()) { - static uint8_t error_count; - - matrix_row_t slave_matrix[ROWS_PER_HAND] = {0}; - if (!transport_master(matrix + thisHand, slave_matrix)) { - error_count++; - - if (error_count > ERROR_DISCONNECT_COUNT) { - // reset other half if disconnected - for (int i = 0; i < ROWS_PER_HAND; ++i) { - matrix[thatHand + i] = 0; - slave_matrix[i] = 0; - } - - changed = true; - } - } else { - error_count = 0; - - for (int i = 0; i < ROWS_PER_HAND; ++i) { - if (matrix[thatHand + i] != slave_matrix[i]) { - matrix[thatHand + i] = slave_matrix[i]; - changed = true; - } - } - } - - matrix_scan_quantum(); - } else { - transport_slave(matrix + thatHand, matrix + thisHand); - - matrix_slave_scan_kb(); - } - - return changed; -} - -uint8_t matrix_scan(void) { - matrix_row_t curr_matrix[MATRIX_ROWS] = {0}; - -#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) - // Set row, read cols - for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) { - matrix_read_cols_on_row(curr_matrix, current_row); - } -#elif (DIODE_DIRECTION == ROW2COL) - // Set col, read rows - for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { - matrix_read_rows_on_col(curr_matrix, current_col); - } -#endif - - bool local_changed = memcmp(raw_matrix, curr_matrix, sizeof(curr_matrix)) != 0; - if (local_changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix)); - - debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, local_changed); - - bool remote_changed = matrix_post_scan(); - return (uint8_t)(local_changed || remote_changed); -} -- cgit v1.2.3