You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

723 lines
31 KiB

Make solo half of split keyboards (more) usable. (#13523) * Make solo half of split keyboards (more) usable. Using only one half of a split keyboard (that's using the split_common framework to communicate) is not a great experience, since several read timeouts per scan cycle cause an unusably slow scan rate. This change blocks all split communication attempts for 500 ms (configurable) after an error occurs, causing the scan rate to become at least _more_ usable, but might need some tweaking to work fully on most keyboards. One read timeout still needs to occur after the 500 ms has passed, and if that timeout isn't low enough, some scan cycles may still be too slow. * Fix lint complaint. * Require 25 consecutive comm errors to see comms as disconnected. The number of max errors can be overridden by defining `SPLIT_MAX_CONNECTION_ERRORS`. * Add comments to new defines, and ability to disable disconnection check. Also increase `SPLIT_MAX_CONNECTION_ERRORS` to 40, since it's divisible by most relevant numbers for the description. * Make lint happy ...again * Only update `connection_check_timer` when needed. * Add new defines to split keyboard documentation. * Move connection timeout logic to transport.c, add `is_transport_connected`. * Use split_common disconnection logic in matrix.c. Instead of doing more or less the same thing twice. * Move disconnection logic to `transport_master`. Is a cleaner implementation, and causes the scan rate while disconnected to increase instead of decrease. * Lint fixes. * Lower default `SERIAL_USART_TIMEOUT` to 20 ms. The read timeout must be low enough to not cause exessively long scan cycles when using a solo split half. 10 ms was determined from testing to work fine even with the slowest defined baudrate of 19200 (5 ms was too low for that case), so 20 ms should be fine for most cases. * Remove `SERIAL_USART_TIMEOUT` from ergodox_infinity/config.h Was somewhat mistakenly included in an earlier PR. * Fix building with `USE_I2C`. * Reduce built firmware size. Not really sure why this works, the idea was taken from tzarc's work on split disconnection. * Tweak and improve opt-out for split disconnection logic. There are now two ways to opt out from this feature: * Set `SPLIT_MAX_CONNECTION_ERRORS` to 0. This will completely disable the connection status checks (also affects the slave matrix reset logic in matrix.c, though). * Set `SPLIT_CONNECTION_CHECK_TIMEOUT` to 0. This will only disable the communication throttling while disconnected. Will make the firmware smaller. * Make split disconnection logic work with custom transports. Includes a fallback implementation for keyboards using a custom split_util.c but not a custom matrix.c (currently no such keyboard seems to be merged, though). * Remove unnecessary include of timer.h Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Joel Challis <git@zvecr.com>
2 years ago
Make solo half of split keyboards (more) usable. (#13523) * Make solo half of split keyboards (more) usable. Using only one half of a split keyboard (that's using the split_common framework to communicate) is not a great experience, since several read timeouts per scan cycle cause an unusably slow scan rate. This change blocks all split communication attempts for 500 ms (configurable) after an error occurs, causing the scan rate to become at least _more_ usable, but might need some tweaking to work fully on most keyboards. One read timeout still needs to occur after the 500 ms has passed, and if that timeout isn't low enough, some scan cycles may still be too slow. * Fix lint complaint. * Require 25 consecutive comm errors to see comms as disconnected. The number of max errors can be overridden by defining `SPLIT_MAX_CONNECTION_ERRORS`. * Add comments to new defines, and ability to disable disconnection check. Also increase `SPLIT_MAX_CONNECTION_ERRORS` to 40, since it's divisible by most relevant numbers for the description. * Make lint happy ...again * Only update `connection_check_timer` when needed. * Add new defines to split keyboard documentation. * Move connection timeout logic to transport.c, add `is_transport_connected`. * Use split_common disconnection logic in matrix.c. Instead of doing more or less the same thing twice. * Move disconnection logic to `transport_master`. Is a cleaner implementation, and causes the scan rate while disconnected to increase instead of decrease. * Lint fixes. * Lower default `SERIAL_USART_TIMEOUT` to 20 ms. The read timeout must be low enough to not cause exessively long scan cycles when using a solo split half. 10 ms was determined from testing to work fine even with the slowest defined baudrate of 19200 (5 ms was too low for that case), so 20 ms should be fine for most cases. * Remove `SERIAL_USART_TIMEOUT` from ergodox_infinity/config.h Was somewhat mistakenly included in an earlier PR. * Fix building with `USE_I2C`. * Reduce built firmware size. Not really sure why this works, the idea was taken from tzarc's work on split disconnection. * Tweak and improve opt-out for split disconnection logic. There are now two ways to opt out from this feature: * Set `SPLIT_MAX_CONNECTION_ERRORS` to 0. This will completely disable the connection status checks (also affects the slave matrix reset logic in matrix.c, though). * Set `SPLIT_CONNECTION_CHECK_TIMEOUT` to 0. This will only disable the communication throttling while disconnected. Will make the firmware smaller. * Make split disconnection logic work with custom transports. Includes a fallback implementation for keyboards using a custom split_util.c but not a custom matrix.c (currently no such keyboard seems to be merged, though). * Remove unnecessary include of timer.h Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Joel Challis <git@zvecr.com>
2 years ago
Make solo half of split keyboards (more) usable. (#13523) * Make solo half of split keyboards (more) usable. Using only one half of a split keyboard (that's using the split_common framework to communicate) is not a great experience, since several read timeouts per scan cycle cause an unusably slow scan rate. This change blocks all split communication attempts for 500 ms (configurable) after an error occurs, causing the scan rate to become at least _more_ usable, but might need some tweaking to work fully on most keyboards. One read timeout still needs to occur after the 500 ms has passed, and if that timeout isn't low enough, some scan cycles may still be too slow. * Fix lint complaint. * Require 25 consecutive comm errors to see comms as disconnected. The number of max errors can be overridden by defining `SPLIT_MAX_CONNECTION_ERRORS`. * Add comments to new defines, and ability to disable disconnection check. Also increase `SPLIT_MAX_CONNECTION_ERRORS` to 40, since it's divisible by most relevant numbers for the description. * Make lint happy ...again * Only update `connection_check_timer` when needed. * Add new defines to split keyboard documentation. * Move connection timeout logic to transport.c, add `is_transport_connected`. * Use split_common disconnection logic in matrix.c. Instead of doing more or less the same thing twice. * Move disconnection logic to `transport_master`. Is a cleaner implementation, and causes the scan rate while disconnected to increase instead of decrease. * Lint fixes. * Lower default `SERIAL_USART_TIMEOUT` to 20 ms. The read timeout must be low enough to not cause exessively long scan cycles when using a solo split half. 10 ms was determined from testing to work fine even with the slowest defined baudrate of 19200 (5 ms was too low for that case), so 20 ms should be fine for most cases. * Remove `SERIAL_USART_TIMEOUT` from ergodox_infinity/config.h Was somewhat mistakenly included in an earlier PR. * Fix building with `USE_I2C`. * Reduce built firmware size. Not really sure why this works, the idea was taken from tzarc's work on split disconnection. * Tweak and improve opt-out for split disconnection logic. There are now two ways to opt out from this feature: * Set `SPLIT_MAX_CONNECTION_ERRORS` to 0. This will completely disable the connection status checks (also affects the slave matrix reset logic in matrix.c, though). * Set `SPLIT_CONNECTION_CHECK_TIMEOUT` to 0. This will only disable the communication throttling while disconnected. Will make the firmware smaller. * Make split disconnection logic work with custom transports. Includes a fallback implementation for keyboards using a custom split_util.c but not a custom matrix.c (currently no such keyboard seems to be merged, though). * Remove unnecessary include of timer.h Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Joel Challis <git@zvecr.com>
2 years ago
Make solo half of split keyboards (more) usable. (#13523) * Make solo half of split keyboards (more) usable. Using only one half of a split keyboard (that's using the split_common framework to communicate) is not a great experience, since several read timeouts per scan cycle cause an unusably slow scan rate. This change blocks all split communication attempts for 500 ms (configurable) after an error occurs, causing the scan rate to become at least _more_ usable, but might need some tweaking to work fully on most keyboards. One read timeout still needs to occur after the 500 ms has passed, and if that timeout isn't low enough, some scan cycles may still be too slow. * Fix lint complaint. * Require 25 consecutive comm errors to see comms as disconnected. The number of max errors can be overridden by defining `SPLIT_MAX_CONNECTION_ERRORS`. * Add comments to new defines, and ability to disable disconnection check. Also increase `SPLIT_MAX_CONNECTION_ERRORS` to 40, since it's divisible by most relevant numbers for the description. * Make lint happy ...again * Only update `connection_check_timer` when needed. * Add new defines to split keyboard documentation. * Move connection timeout logic to transport.c, add `is_transport_connected`. * Use split_common disconnection logic in matrix.c. Instead of doing more or less the same thing twice. * Move disconnection logic to `transport_master`. Is a cleaner implementation, and causes the scan rate while disconnected to increase instead of decrease. * Lint fixes. * Lower default `SERIAL_USART_TIMEOUT` to 20 ms. The read timeout must be low enough to not cause exessively long scan cycles when using a solo split half. 10 ms was determined from testing to work fine even with the slowest defined baudrate of 19200 (5 ms was too low for that case), so 20 ms should be fine for most cases. * Remove `SERIAL_USART_TIMEOUT` from ergodox_infinity/config.h Was somewhat mistakenly included in an earlier PR. * Fix building with `USE_I2C`. * Reduce built firmware size. Not really sure why this works, the idea was taken from tzarc's work on split disconnection. * Tweak and improve opt-out for split disconnection logic. There are now two ways to opt out from this feature: * Set `SPLIT_MAX_CONNECTION_ERRORS` to 0. This will completely disable the connection status checks (also affects the slave matrix reset logic in matrix.c, though). * Set `SPLIT_CONNECTION_CHECK_TIMEOUT` to 0. This will only disable the communication throttling while disconnected. Will make the firmware smaller. * Make split disconnection logic work with custom transports. Includes a fallback implementation for keyboards using a custom split_util.c but not a custom matrix.c (currently no such keyboard seems to be merged, though). * Remove unnecessary include of timer.h Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Joel Challis <git@zvecr.com>
2 years ago
Make solo half of split keyboards (more) usable. (#13523) * Make solo half of split keyboards (more) usable. Using only one half of a split keyboard (that's using the split_common framework to communicate) is not a great experience, since several read timeouts per scan cycle cause an unusably slow scan rate. This change blocks all split communication attempts for 500 ms (configurable) after an error occurs, causing the scan rate to become at least _more_ usable, but might need some tweaking to work fully on most keyboards. One read timeout still needs to occur after the 500 ms has passed, and if that timeout isn't low enough, some scan cycles may still be too slow. * Fix lint complaint. * Require 25 consecutive comm errors to see comms as disconnected. The number of max errors can be overridden by defining `SPLIT_MAX_CONNECTION_ERRORS`. * Add comments to new defines, and ability to disable disconnection check. Also increase `SPLIT_MAX_CONNECTION_ERRORS` to 40, since it's divisible by most relevant numbers for the description. * Make lint happy ...again * Only update `connection_check_timer` when needed. * Add new defines to split keyboard documentation. * Move connection timeout logic to transport.c, add `is_transport_connected`. * Use split_common disconnection logic in matrix.c. Instead of doing more or less the same thing twice. * Move disconnection logic to `transport_master`. Is a cleaner implementation, and causes the scan rate while disconnected to increase instead of decrease. * Lint fixes. * Lower default `SERIAL_USART_TIMEOUT` to 20 ms. The read timeout must be low enough to not cause exessively long scan cycles when using a solo split half. 10 ms was determined from testing to work fine even with the slowest defined baudrate of 19200 (5 ms was too low for that case), so 20 ms should be fine for most cases. * Remove `SERIAL_USART_TIMEOUT` from ergodox_infinity/config.h Was somewhat mistakenly included in an earlier PR. * Fix building with `USE_I2C`. * Reduce built firmware size. Not really sure why this works, the idea was taken from tzarc's work on split disconnection. * Tweak and improve opt-out for split disconnection logic. There are now two ways to opt out from this feature: * Set `SPLIT_MAX_CONNECTION_ERRORS` to 0. This will completely disable the connection status checks (also affects the slave matrix reset logic in matrix.c, though). * Set `SPLIT_CONNECTION_CHECK_TIMEOUT` to 0. This will only disable the communication throttling while disconnected. Will make the firmware smaller. * Make split disconnection logic work with custom transports. Includes a fallback implementation for keyboards using a custom split_util.c but not a custom matrix.c (currently no such keyboard seems to be merged, though). * Remove unnecessary include of timer.h Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Joel Challis <git@zvecr.com>
2 years ago
Make solo half of split keyboards (more) usable. (#13523) * Make solo half of split keyboards (more) usable. Using only one half of a split keyboard (that's using the split_common framework to communicate) is not a great experience, since several read timeouts per scan cycle cause an unusably slow scan rate. This change blocks all split communication attempts for 500 ms (configurable) after an error occurs, causing the scan rate to become at least _more_ usable, but might need some tweaking to work fully on most keyboards. One read timeout still needs to occur after the 500 ms has passed, and if that timeout isn't low enough, some scan cycles may still be too slow. * Fix lint complaint. * Require 25 consecutive comm errors to see comms as disconnected. The number of max errors can be overridden by defining `SPLIT_MAX_CONNECTION_ERRORS`. * Add comments to new defines, and ability to disable disconnection check. Also increase `SPLIT_MAX_CONNECTION_ERRORS` to 40, since it's divisible by most relevant numbers for the description. * Make lint happy ...again * Only update `connection_check_timer` when needed. * Add new defines to split keyboard documentation. * Move connection timeout logic to transport.c, add `is_transport_connected`. * Use split_common disconnection logic in matrix.c. Instead of doing more or less the same thing twice. * Move disconnection logic to `transport_master`. Is a cleaner implementation, and causes the scan rate while disconnected to increase instead of decrease. * Lint fixes. * Lower default `SERIAL_USART_TIMEOUT` to 20 ms. The read timeout must be low enough to not cause exessively long scan cycles when using a solo split half. 10 ms was determined from testing to work fine even with the slowest defined baudrate of 19200 (5 ms was too low for that case), so 20 ms should be fine for most cases. * Remove `SERIAL_USART_TIMEOUT` from ergodox_infinity/config.h Was somewhat mistakenly included in an earlier PR. * Fix building with `USE_I2C`. * Reduce built firmware size. Not really sure why this works, the idea was taken from tzarc's work on split disconnection. * Tweak and improve opt-out for split disconnection logic. There are now two ways to opt out from this feature: * Set `SPLIT_MAX_CONNECTION_ERRORS` to 0. This will completely disable the connection status checks (also affects the slave matrix reset logic in matrix.c, though). * Set `SPLIT_CONNECTION_CHECK_TIMEOUT` to 0. This will only disable the communication throttling while disconnected. Will make the firmware smaller. * Make split disconnection logic work with custom transports. Includes a fallback implementation for keyboards using a custom split_util.c but not a custom matrix.c (currently no such keyboard seems to be merged, though). * Remove unnecessary include of timer.h Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Joel Challis <git@zvecr.com>
2 years ago
Make solo half of split keyboards (more) usable. (#13523) * Make solo half of split keyboards (more) usable. Using only one half of a split keyboard (that's using the split_common framework to communicate) is not a great experience, since several read timeouts per scan cycle cause an unusably slow scan rate. This change blocks all split communication attempts for 500 ms (configurable) after an error occurs, causing the scan rate to become at least _more_ usable, but might need some tweaking to work fully on most keyboards. One read timeout still needs to occur after the 500 ms has passed, and if that timeout isn't low enough, some scan cycles may still be too slow. * Fix lint complaint. * Require 25 consecutive comm errors to see comms as disconnected. The number of max errors can be overridden by defining `SPLIT_MAX_CONNECTION_ERRORS`. * Add comments to new defines, and ability to disable disconnection check. Also increase `SPLIT_MAX_CONNECTION_ERRORS` to 40, since it's divisible by most relevant numbers for the description. * Make lint happy ...again * Only update `connection_check_timer` when needed. * Add new defines to split keyboard documentation. * Move connection timeout logic to transport.c, add `is_transport_connected`. * Use split_common disconnection logic in matrix.c. Instead of doing more or less the same thing twice. * Move disconnection logic to `transport_master`. Is a cleaner implementation, and causes the scan rate while disconnected to increase instead of decrease. * Lint fixes. * Lower default `SERIAL_USART_TIMEOUT` to 20 ms. The read timeout must be low enough to not cause exessively long scan cycles when using a solo split half. 10 ms was determined from testing to work fine even with the slowest defined baudrate of 19200 (5 ms was too low for that case), so 20 ms should be fine for most cases. * Remove `SERIAL_USART_TIMEOUT` from ergodox_infinity/config.h Was somewhat mistakenly included in an earlier PR. * Fix building with `USE_I2C`. * Reduce built firmware size. Not really sure why this works, the idea was taken from tzarc's work on split disconnection. * Tweak and improve opt-out for split disconnection logic. There are now two ways to opt out from this feature: * Set `SPLIT_MAX_CONNECTION_ERRORS` to 0. This will completely disable the connection status checks (also affects the slave matrix reset logic in matrix.c, though). * Set `SPLIT_CONNECTION_CHECK_TIMEOUT` to 0. This will only disable the communication throttling while disconnected. Will make the firmware smaller. * Make split disconnection logic work with custom transports. Includes a fallback implementation for keyboards using a custom split_util.c but not a custom matrix.c (currently no such keyboard seems to be merged, though). * Remove unnecessary include of timer.h Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Joel Challis <git@zvecr.com>
2 years ago
Make solo half of split keyboards (more) usable. (#13523) * Make solo half of split keyboards (more) usable. Using only one half of a split keyboard (that's using the split_common framework to communicate) is not a great experience, since several read timeouts per scan cycle cause an unusably slow scan rate. This change blocks all split communication attempts for 500 ms (configurable) after an error occurs, causing the scan rate to become at least _more_ usable, but might need some tweaking to work fully on most keyboards. One read timeout still needs to occur after the 500 ms has passed, and if that timeout isn't low enough, some scan cycles may still be too slow. * Fix lint complaint. * Require 25 consecutive comm errors to see comms as disconnected. The number of max errors can be overridden by defining `SPLIT_MAX_CONNECTION_ERRORS`. * Add comments to new defines, and ability to disable disconnection check. Also increase `SPLIT_MAX_CONNECTION_ERRORS` to 40, since it's divisible by most relevant numbers for the description. * Make lint happy ...again * Only update `connection_check_timer` when needed. * Add new defines to split keyboard documentation. * Move connection timeout logic to transport.c, add `is_transport_connected`. * Use split_common disconnection logic in matrix.c. Instead of doing more or less the same thing twice. * Move disconnection logic to `transport_master`. Is a cleaner implementation, and causes the scan rate while disconnected to increase instead of decrease. * Lint fixes. * Lower default `SERIAL_USART_TIMEOUT` to 20 ms. The read timeout must be low enough to not cause exessively long scan cycles when using a solo split half. 10 ms was determined from testing to work fine even with the slowest defined baudrate of 19200 (5 ms was too low for that case), so 20 ms should be fine for most cases. * Remove `SERIAL_USART_TIMEOUT` from ergodox_infinity/config.h Was somewhat mistakenly included in an earlier PR. * Fix building with `USE_I2C`. * Reduce built firmware size. Not really sure why this works, the idea was taken from tzarc's work on split disconnection. * Tweak and improve opt-out for split disconnection logic. There are now two ways to opt out from this feature: * Set `SPLIT_MAX_CONNECTION_ERRORS` to 0. This will completely disable the connection status checks (also affects the slave matrix reset logic in matrix.c, though). * Set `SPLIT_CONNECTION_CHECK_TIMEOUT` to 0. This will only disable the communication throttling while disconnected. Will make the firmware smaller. * Make split disconnection logic work with custom transports. Includes a fallback implementation for keyboards using a custom split_util.c but not a custom matrix.c (currently no such keyboard seems to be merged, though). * Remove unnecessary include of timer.h Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Joel Challis <git@zvecr.com>
2 years ago
Make solo half of split keyboards (more) usable. (#13523) * Make solo half of split keyboards (more) usable. Using only one half of a split keyboard (that's using the split_common framework to communicate) is not a great experience, since several read timeouts per scan cycle cause an unusably slow scan rate. This change blocks all split communication attempts for 500 ms (configurable) after an error occurs, causing the scan rate to become at least _more_ usable, but might need some tweaking to work fully on most keyboards. One read timeout still needs to occur after the 500 ms has passed, and if that timeout isn't low enough, some scan cycles may still be too slow. * Fix lint complaint. * Require 25 consecutive comm errors to see comms as disconnected. The number of max errors can be overridden by defining `SPLIT_MAX_CONNECTION_ERRORS`. * Add comments to new defines, and ability to disable disconnection check. Also increase `SPLIT_MAX_CONNECTION_ERRORS` to 40, since it's divisible by most relevant numbers for the description. * Make lint happy ...again * Only update `connection_check_timer` when needed. * Add new defines to split keyboard documentation. * Move connection timeout logic to transport.c, add `is_transport_connected`. * Use split_common disconnection logic in matrix.c. Instead of doing more or less the same thing twice. * Move disconnection logic to `transport_master`. Is a cleaner implementation, and causes the scan rate while disconnected to increase instead of decrease. * Lint fixes. * Lower default `SERIAL_USART_TIMEOUT` to 20 ms. The read timeout must be low enough to not cause exessively long scan cycles when using a solo split half. 10 ms was determined from testing to work fine even with the slowest defined baudrate of 19200 (5 ms was too low for that case), so 20 ms should be fine for most cases. * Remove `SERIAL_USART_TIMEOUT` from ergodox_infinity/config.h Was somewhat mistakenly included in an earlier PR. * Fix building with `USE_I2C`. * Reduce built firmware size. Not really sure why this works, the idea was taken from tzarc's work on split disconnection. * Tweak and improve opt-out for split disconnection logic. There are now two ways to opt out from this feature: * Set `SPLIT_MAX_CONNECTION_ERRORS` to 0. This will completely disable the connection status checks (also affects the slave matrix reset logic in matrix.c, though). * Set `SPLIT_CONNECTION_CHECK_TIMEOUT` to 0. This will only disable the communication throttling while disconnected. Will make the firmware smaller. * Make split disconnection logic work with custom transports. Includes a fallback implementation for keyboards using a custom split_util.c but not a custom matrix.c (currently no such keyboard seems to be merged, though). * Remove unnecessary include of timer.h Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Joel Challis <git@zvecr.com>
2 years ago
Make solo half of split keyboards (more) usable. (#13523) * Make solo half of split keyboards (more) usable. Using only one half of a split keyboard (that's using the split_common framework to communicate) is not a great experience, since several read timeouts per scan cycle cause an unusably slow scan rate. This change blocks all split communication attempts for 500 ms (configurable) after an error occurs, causing the scan rate to become at least _more_ usable, but might need some tweaking to work fully on most keyboards. One read timeout still needs to occur after the 500 ms has passed, and if that timeout isn't low enough, some scan cycles may still be too slow. * Fix lint complaint. * Require 25 consecutive comm errors to see comms as disconnected. The number of max errors can be overridden by defining `SPLIT_MAX_CONNECTION_ERRORS`. * Add comments to new defines, and ability to disable disconnection check. Also increase `SPLIT_MAX_CONNECTION_ERRORS` to 40, since it's divisible by most relevant numbers for the description. * Make lint happy ...again * Only update `connection_check_timer` when needed. * Add new defines to split keyboard documentation. * Move connection timeout logic to transport.c, add `is_transport_connected`. * Use split_common disconnection logic in matrix.c. Instead of doing more or less the same thing twice. * Move disconnection logic to `transport_master`. Is a cleaner implementation, and causes the scan rate while disconnected to increase instead of decrease. * Lint fixes. * Lower default `SERIAL_USART_TIMEOUT` to 20 ms. The read timeout must be low enough to not cause exessively long scan cycles when using a solo split half. 10 ms was determined from testing to work fine even with the slowest defined baudrate of 19200 (5 ms was too low for that case), so 20 ms should be fine for most cases. * Remove `SERIAL_USART_TIMEOUT` from ergodox_infinity/config.h Was somewhat mistakenly included in an earlier PR. * Fix building with `USE_I2C`. * Reduce built firmware size. Not really sure why this works, the idea was taken from tzarc's work on split disconnection. * Tweak and improve opt-out for split disconnection logic. There are now two ways to opt out from this feature: * Set `SPLIT_MAX_CONNECTION_ERRORS` to 0. This will completely disable the connection status checks (also affects the slave matrix reset logic in matrix.c, though). * Set `SPLIT_CONNECTION_CHECK_TIMEOUT` to 0. This will only disable the communication throttling while disconnected. Will make the firmware smaller. * Make split disconnection logic work with custom transports. Includes a fallback implementation for keyboards using a custom split_util.c but not a custom matrix.c (currently no such keyboard seems to be merged, though). * Remove unnecessary include of timer.h Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Joel Challis <git@zvecr.com>
2 years ago
Make solo half of split keyboards (more) usable. (#13523) * Make solo half of split keyboards (more) usable. Using only one half of a split keyboard (that's using the split_common framework to communicate) is not a great experience, since several read timeouts per scan cycle cause an unusably slow scan rate. This change blocks all split communication attempts for 500 ms (configurable) after an error occurs, causing the scan rate to become at least _more_ usable, but might need some tweaking to work fully on most keyboards. One read timeout still needs to occur after the 500 ms has passed, and if that timeout isn't low enough, some scan cycles may still be too slow. * Fix lint complaint. * Require 25 consecutive comm errors to see comms as disconnected. The number of max errors can be overridden by defining `SPLIT_MAX_CONNECTION_ERRORS`. * Add comments to new defines, and ability to disable disconnection check. Also increase `SPLIT_MAX_CONNECTION_ERRORS` to 40, since it's divisible by most relevant numbers for the description. * Make lint happy ...again * Only update `connection_check_timer` when needed. * Add new defines to split keyboard documentation. * Move connection timeout logic to transport.c, add `is_transport_connected`. * Use split_common disconnection logic in matrix.c. Instead of doing more or less the same thing twice. * Move disconnection logic to `transport_master`. Is a cleaner implementation, and causes the scan rate while disconnected to increase instead of decrease. * Lint fixes. * Lower default `SERIAL_USART_TIMEOUT` to 20 ms. The read timeout must be low enough to not cause exessively long scan cycles when using a solo split half. 10 ms was determined from testing to work fine even with the slowest defined baudrate of 19200 (5 ms was too low for that case), so 20 ms should be fine for most cases. * Remove `SERIAL_USART_TIMEOUT` from ergodox_infinity/config.h Was somewhat mistakenly included in an earlier PR. * Fix building with `USE_I2C`. * Reduce built firmware size. Not really sure why this works, the idea was taken from tzarc's work on split disconnection. * Tweak and improve opt-out for split disconnection logic. There are now two ways to opt out from this feature: * Set `SPLIT_MAX_CONNECTION_ERRORS` to 0. This will completely disable the connection status checks (also affects the slave matrix reset logic in matrix.c, though). * Set `SPLIT_CONNECTION_CHECK_TIMEOUT` to 0. This will only disable the communication throttling while disconnected. Will make the firmware smaller. * Make split disconnection logic work with custom transports. Includes a fallback implementation for keyboards using a custom split_util.c but not a custom matrix.c (currently no such keyboard seems to be merged, though). * Remove unnecessary include of timer.h Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Joel Challis <git@zvecr.com>
2 years ago
  1. /* Copyright 2021 QMK
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <string.h>
  17. #include <stddef.h>
  18. #include "crc.h"
  19. #include "debug.h"
  20. #include "matrix.h"
  21. #include "quantum.h"
  22. #include "transactions.h"
  23. #include "transport.h"
  24. #include "split_util.h"
  25. #include "transaction_id_define.h"
  26. #define SYNC_TIMER_OFFSET 2
  27. #ifndef FORCED_SYNC_THROTTLE_MS
  28. # define FORCED_SYNC_THROTTLE_MS 100
  29. #endif // FORCED_SYNC_THROTTLE_MS
  30. #define sizeof_member(type, member) sizeof(((type *)NULL)->member)
  31. #define trans_initiator2target_initializer_cb(member, cb) \
  32. { &dummy, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), 0, 0, cb }
  33. #define trans_initiator2target_initializer(member) trans_initiator2target_initializer_cb(member, NULL)
  34. #define trans_target2initiator_initializer_cb(member, cb) \
  35. { &dummy, 0, 0, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), cb }
  36. #define trans_target2initiator_initializer(member) trans_target2initiator_initializer_cb(member, NULL)
  37. #define transport_write(id, data, length) transport_execute_transaction(id, data, length, NULL, 0)
  38. #define transport_read(id, data, length) transport_execute_transaction(id, NULL, 0, data, length)
  39. #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
  40. // Forward-declare the RPC callback handlers
  41. void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
  42. void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
  43. #endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
  44. ////////////////////////////////////////////////////
  45. // Helpers
  46. static bool transaction_handler_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[], const char *prefix, bool (*handler)(matrix_row_t master_matrix[], matrix_row_t slave_matrix[])) {
  47. int num_retries = is_transport_connected() ? 10 : 1;
  48. for (int iter = 1; iter <= num_retries; ++iter) {
  49. if (iter > 1) {
  50. for (int i = 0; i < iter * iter; ++i) {
  51. wait_us(10);
  52. }
  53. }
  54. bool this_okay = true;
  55. ATOMIC_BLOCK_FORCEON { this_okay = handler(master_matrix, slave_matrix); };
  56. if (this_okay) return true;
  57. }
  58. dprintf("Failed to execute %s\n", prefix);
  59. return false;
  60. }
  61. #define TRANSACTION_HANDLER_MASTER(prefix) \
  62. do { \
  63. if (!transaction_handler_master(master_matrix, slave_matrix, #prefix, &prefix##_handlers_master)) return false; \
  64. } while (0)
  65. #define TRANSACTION_HANDLER_SLAVE(prefix) \
  66. do { \
  67. ATOMIC_BLOCK_FORCEON { prefix##_handlers_slave(master_matrix, slave_matrix); }; \
  68. } while (0)
  69. 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) {
  70. uint8_t curr_checksum;
  71. bool okay = transport_read(trans_id_checksum, &curr_checksum, sizeof(curr_checksum));
  72. if (okay && (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || curr_checksum != crc8(equiv_shmem, length))) {
  73. okay &= transport_read(trans_id_retrieve, destination, length);
  74. okay &= curr_checksum == crc8(equiv_shmem, length);
  75. if (okay) {
  76. *last_update = timer_read32();
  77. }
  78. } else {
  79. memcpy(destination, equiv_shmem, length);
  80. }
  81. return okay;
  82. }
  83. inline static bool send_if_condition(int8_t trans_id, uint32_t *last_update, bool condition, void *source, size_t length) {
  84. bool okay = true;
  85. if (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || condition) {
  86. okay &= transport_write(trans_id, source, length);
  87. if (okay) {
  88. *last_update = timer_read32();
  89. }
  90. }
  91. return okay;
  92. }
  93. inline static bool send_if_data_mismatch(int8_t trans_id, uint32_t *last_update, void *source, const void *equiv_shmem, size_t length) {
  94. // Just run a memcmp to compare the source and equivalent shmem location
  95. return send_if_condition(trans_id, last_update, (memcmp(source, equiv_shmem, length) != 0), source, length);
  96. }
  97. ////////////////////////////////////////////////////
  98. // Slave matrix
  99. static bool slave_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  100. static uint32_t last_update = 0;
  101. static matrix_row_t last_matrix[(MATRIX_ROWS) / 2] = {0}; // last successfully-read matrix, so we can replicate if there are checksum errors
  102. matrix_row_t temp_matrix[(MATRIX_ROWS) / 2]; // holding area while we test whether or not checksum is correct
  103. 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));
  104. if (okay) {
  105. // Checksum matches the received data, save as the last matrix state
  106. memcpy(last_matrix, temp_matrix, sizeof(temp_matrix));
  107. }
  108. // Copy out the last-known-good matrix state to the slave matrix
  109. memcpy(slave_matrix, last_matrix, sizeof(last_matrix));
  110. return okay;
  111. }
  112. static void slave_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  113. memcpy(split_shmem->smatrix.matrix, slave_matrix, sizeof(split_shmem->smatrix.matrix));
  114. split_shmem->smatrix.checksum = crc8(split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix));
  115. }
  116. // clang-format off
  117. #define TRANSACTIONS_SLAVE_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(slave_matrix)
  118. #define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(slave_matrix)
  119. #define TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS \
  120. [GET_SLAVE_MATRIX_CHECKSUM] = trans_target2initiator_initializer(smatrix.checksum), \
  121. [GET_SLAVE_MATRIX_DATA] = trans_target2initiator_initializer(smatrix.matrix),
  122. // clang-format on
  123. ////////////////////////////////////////////////////
  124. // Master matrix
  125. #ifdef SPLIT_TRANSPORT_MIRROR
  126. static bool master_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  127. static uint32_t last_update = 0;
  128. return send_if_data_mismatch(PUT_MASTER_MATRIX, &last_update, master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));
  129. }
  130. static void master_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  131. // Always copy to the master matrix
  132. memcpy(master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));
  133. }
  134. # define TRANSACTIONS_MASTER_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(master_matrix)
  135. # define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(master_matrix)
  136. # define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS [PUT_MASTER_MATRIX] = trans_initiator2target_initializer(mmatrix.matrix),
  137. #else // SPLIT_TRANSPORT_MIRROR
  138. # define TRANSACTIONS_MASTER_MATRIX_MASTER()
  139. # define TRANSACTIONS_MASTER_MATRIX_SLAVE()
  140. # define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS
  141. #endif // SPLIT_TRANSPORT_MIRROR
  142. ////////////////////////////////////////////////////
  143. // Encoders
  144. #ifdef ENCODER_ENABLE
  145. static bool encoder_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  146. static uint32_t last_update = 0;
  147. uint8_t temp_state[NUMBER_OF_ENCODERS];
  148. bool okay = read_if_checksum_mismatch(GET_ENCODERS_CHECKSUM, GET_ENCODERS_DATA, &last_update, temp_state, split_shmem->encoders.state, sizeof(temp_state));
  149. if (okay) encoder_update_raw(temp_state);
  150. return okay;
  151. }
  152. static void encoder_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  153. uint8_t encoder_state[NUMBER_OF_ENCODERS];
  154. encoder_state_raw(encoder_state);
  155. // Always prepare the encoder state for read.
  156. memcpy(split_shmem->encoders.state, encoder_state, sizeof(encoder_state));
  157. // Now update the checksum given that the encoders has been written to
  158. split_shmem->encoders.checksum = crc8(encoder_state, sizeof(encoder_state));
  159. }
  160. // clang-format off
  161. # define TRANSACTIONS_ENCODERS_MASTER() TRANSACTION_HANDLER_MASTER(encoder)
  162. # define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE(encoder)
  163. # define TRANSACTIONS_ENCODERS_REGISTRATIONS \
  164. [GET_ENCODERS_CHECKSUM] = trans_target2initiator_initializer(encoders.checksum), \
  165. [GET_ENCODERS_DATA] = trans_target2initiator_initializer(encoders.state),
  166. // clang-format on
  167. #else // ENCODER_ENABLE
  168. # define TRANSACTIONS_ENCODERS_MASTER()
  169. # define TRANSACTIONS_ENCODERS_SLAVE()
  170. # define TRANSACTIONS_ENCODERS_REGISTRATIONS
  171. #endif // ENCODER_ENABLE
  172. ////////////////////////////////////////////////////
  173. // Sync timer
  174. #ifndef DISABLE_SYNC_TIMER
  175. static bool sync_timer_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  176. static uint32_t last_update = 0;
  177. bool okay = true;
  178. if (timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS) {
  179. uint32_t sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
  180. okay &= transport_write(PUT_SYNC_TIMER, &sync_timer, sizeof(sync_timer));
  181. if (okay) {
  182. last_update = timer_read32();
  183. }
  184. }
  185. return okay;
  186. }
  187. static void sync_timer_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  188. static uint32_t last_sync_timer = 0;
  189. if (last_sync_timer != split_shmem->sync_timer) {
  190. last_sync_timer = split_shmem->sync_timer;
  191. sync_timer_update(last_sync_timer);
  192. }
  193. }
  194. # define TRANSACTIONS_SYNC_TIMER_MASTER() TRANSACTION_HANDLER_MASTER(sync_timer)
  195. # define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE(sync_timer)
  196. # define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS [PUT_SYNC_TIMER] = trans_initiator2target_initializer(sync_timer),
  197. #else // DISABLE_SYNC_TIMER
  198. # define TRANSACTIONS_SYNC_TIMER_MASTER()
  199. # define TRANSACTIONS_SYNC_TIMER_SLAVE()
  200. # define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS
  201. #endif // DISABLE_SYNC_TIMER
  202. ////////////////////////////////////////////////////
  203. // Layer state
  204. #if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
  205. static bool layer_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  206. static uint32_t last_layer_state_update = 0;
  207. static uint32_t last_default_layer_state_update = 0;
  208. bool okay = send_if_condition(PUT_LAYER_STATE, &last_layer_state_update, (layer_state != split_shmem->layers.layer_state), &layer_state, sizeof(layer_state));
  209. if (okay) {
  210. 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));
  211. }
  212. return okay;
  213. }
  214. static void layer_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  215. layer_state = split_shmem->layers.layer_state;
  216. default_layer_state = split_shmem->layers.default_layer_state;
  217. }
  218. // clang-format off
  219. # define TRANSACTIONS_LAYER_STATE_MASTER() TRANSACTION_HANDLER_MASTER(layer_state)
  220. # define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(layer_state)
  221. # define TRANSACTIONS_LAYER_STATE_REGISTRATIONS \
  222. [PUT_LAYER_STATE] = trans_initiator2target_initializer(layers.layer_state), \
  223. [PUT_DEFAULT_LAYER_STATE] = trans_initiator2target_initializer(layers.default_layer_state),
  224. // clang-format on
  225. #else // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
  226. # define TRANSACTIONS_LAYER_STATE_MASTER()
  227. # define TRANSACTIONS_LAYER_STATE_SLAVE()
  228. # define TRANSACTIONS_LAYER_STATE_REGISTRATIONS
  229. #endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
  230. ////////////////////////////////////////////////////
  231. // LED state
  232. #ifdef SPLIT_LED_STATE_ENABLE
  233. static bool led_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  234. static uint32_t last_update = 0;
  235. uint8_t led_state = host_keyboard_leds();
  236. return send_if_data_mismatch(PUT_LED_STATE, &last_update, &led_state, &split_shmem->led_state, sizeof(led_state));
  237. }
  238. static void led_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  239. void set_split_host_keyboard_leds(uint8_t led_state);
  240. set_split_host_keyboard_leds(split_shmem->led_state);
  241. }
  242. # define TRANSACTIONS_LED_STATE_MASTER() TRANSACTION_HANDLER_MASTER(led_state)
  243. # define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(led_state)
  244. # define TRANSACTIONS_LED_STATE_REGISTRATIONS [PUT_LED_STATE] = trans_initiator2target_initializer(led_state),
  245. #else // SPLIT_LED_STATE_ENABLE
  246. # define TRANSACTIONS_LED_STATE_MASTER()
  247. # define TRANSACTIONS_LED_STATE_SLAVE()
  248. # define TRANSACTIONS_LED_STATE_REGISTRATIONS
  249. #endif // SPLIT_LED_STATE_ENABLE
  250. ////////////////////////////////////////////////////
  251. // Mods
  252. #ifdef SPLIT_MODS_ENABLE
  253. static bool mods_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  254. static uint32_t last_update = 0;
  255. bool mods_need_sync = timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS;
  256. split_mods_sync_t new_mods;
  257. new_mods.real_mods = get_mods();
  258. if (!mods_need_sync && new_mods.real_mods != split_shmem->mods.real_mods) {
  259. mods_need_sync = true;
  260. }
  261. new_mods.weak_mods = get_weak_mods();
  262. if (!mods_need_sync && new_mods.weak_mods != split_shmem->mods.weak_mods) {
  263. mods_need_sync = true;
  264. }
  265. # ifndef NO_ACTION_ONESHOT
  266. new_mods.oneshot_mods = get_oneshot_mods();
  267. if (!mods_need_sync && new_mods.oneshot_mods != split_shmem->mods.oneshot_mods) {
  268. mods_need_sync = true;
  269. }
  270. # endif // NO_ACTION_ONESHOT
  271. bool okay = true;
  272. if (mods_need_sync) {
  273. okay &= transport_write(PUT_MODS, &new_mods, sizeof(new_mods));
  274. if (okay) {
  275. last_update = timer_read32();
  276. }
  277. }
  278. return okay;
  279. }
  280. static void mods_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  281. set_mods(split_shmem->mods.real_mods);
  282. set_weak_mods(split_shmem->mods.weak_mods);
  283. # ifndef NO_ACTION_ONESHOT
  284. set_oneshot_mods(split_shmem->mods.oneshot_mods);
  285. # endif
  286. }
  287. # define TRANSACTIONS_MODS_MASTER() TRANSACTION_HANDLER_MASTER(mods)
  288. # define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods)
  289. # define TRANSACTIONS_MODS_REGISTRATIONS [PUT_MODS] = trans_initiator2target_initializer(mods),
  290. #else // SPLIT_MODS_ENABLE
  291. # define TRANSACTIONS_MODS_MASTER()
  292. # define TRANSACTIONS_MODS_SLAVE()
  293. # define TRANSACTIONS_MODS_REGISTRATIONS
  294. #endif // SPLIT_MODS_ENABLE
  295. ////////////////////////////////////////////////////
  296. // Backlight
  297. #ifdef BACKLIGHT_ENABLE
  298. static bool backlight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  299. static uint32_t last_update = 0;
  300. uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0;
  301. return send_if_condition(PUT_BACKLIGHT, &last_update, (level != split_shmem->backlight_level), &level, sizeof(level));
  302. }
  303. static void backlight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { backlight_set(split_shmem->backlight_level); }
  304. # define TRANSACTIONS_BACKLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(backlight)
  305. # define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight)
  306. # define TRANSACTIONS_BACKLIGHT_REGISTRATIONS [PUT_BACKLIGHT] = trans_initiator2target_initializer(backlight_level),
  307. #else // BACKLIGHT_ENABLE
  308. # define TRANSACTIONS_BACKLIGHT_MASTER()
  309. # define TRANSACTIONS_BACKLIGHT_SLAVE()
  310. # define TRANSACTIONS_BACKLIGHT_REGISTRATIONS
  311. #endif // BACKLIGHT_ENABLE
  312. ////////////////////////////////////////////////////
  313. // RGBLIGHT
  314. #if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
  315. static bool rgblight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  316. static uint32_t last_update = 0;
  317. rgblight_syncinfo_t rgblight_sync;
  318. rgblight_get_syncinfo(&rgblight_sync);
  319. if (send_if_condition(PUT_RGBLIGHT, &last_update, (rgblight_sync.status.change_flags != 0), &rgblight_sync, sizeof(rgblight_sync))) {
  320. rgblight_clear_change_flags();
  321. } else {
  322. return false;
  323. }
  324. return true;
  325. }
  326. static void rgblight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  327. // Update the RGB with the new data
  328. if (split_shmem->rgblight_sync.status.change_flags != 0) {
  329. rgblight_update_sync(&split_shmem->rgblight_sync, false);
  330. split_shmem->rgblight_sync.status.change_flags = 0;
  331. }
  332. }
  333. # define TRANSACTIONS_RGBLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(rgblight)
  334. # define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight)
  335. # define TRANSACTIONS_RGBLIGHT_REGISTRATIONS [PUT_RGBLIGHT] = trans_initiator2target_initializer(rgblight_sync),
  336. #else // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
  337. # define TRANSACTIONS_RGBLIGHT_MASTER()
  338. # define TRANSACTIONS_RGBLIGHT_SLAVE()
  339. # define TRANSACTIONS_RGBLIGHT_REGISTRATIONS
  340. #endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
  341. ////////////////////////////////////////////////////
  342. // LED Matrix
  343. #if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
  344. static bool led_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  345. static uint32_t last_update = 0;
  346. led_matrix_sync_t led_matrix_sync;
  347. memcpy(&led_matrix_sync.led_matrix, &led_matrix_eeconfig, sizeof(led_eeconfig_t));
  348. led_matrix_sync.led_suspend_state = led_matrix_get_suspend_state();
  349. return send_if_data_mismatch(PUT_LED_MATRIX, &last_update, &led_matrix_sync, &split_shmem->led_matrix_sync, sizeof(led_matrix_sync));
  350. }
  351. static void led_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  352. memcpy(&led_matrix_eeconfig, &split_shmem->led_matrix_sync.led_matrix, sizeof(led_eeconfig_t));
  353. led_matrix_set_suspend_state(split_shmem->led_matrix_sync.led_suspend_state);
  354. }
  355. # define TRANSACTIONS_LED_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(led_matrix)
  356. # define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix)
  357. # define TRANSACTIONS_LED_MATRIX_REGISTRATIONS [PUT_LED_MATRIX] = trans_initiator2target_initializer(led_matrix_sync),
  358. #else // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
  359. # define TRANSACTIONS_LED_MATRIX_MASTER()
  360. # define TRANSACTIONS_LED_MATRIX_SLAVE()
  361. # define TRANSACTIONS_LED_MATRIX_REGISTRATIONS
  362. #endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
  363. ////////////////////////////////////////////////////
  364. // RGB Matrix
  365. #if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
  366. static bool rgb_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  367. static uint32_t last_update = 0;
  368. rgb_matrix_sync_t rgb_matrix_sync;
  369. memcpy(&rgb_matrix_sync.rgb_matrix, &rgb_matrix_config, sizeof(rgb_config_t));
  370. rgb_matrix_sync.rgb_suspend_state = rgb_matrix_get_suspend_state();
  371. return send_if_data_mismatch(PUT_RGB_MATRIX, &last_update, &rgb_matrix_sync, &split_shmem->rgb_matrix_sync, sizeof(rgb_matrix_sync));
  372. }
  373. static void rgb_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  374. memcpy(&rgb_matrix_config, &split_shmem->rgb_matrix_sync.rgb_matrix, sizeof(rgb_config_t));
  375. rgb_matrix_set_suspend_state(split_shmem->rgb_matrix_sync.rgb_suspend_state);
  376. }
  377. # define TRANSACTIONS_RGB_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(rgb_matrix)
  378. # define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix)
  379. # define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS [PUT_RGB_MATRIX] = trans_initiator2target_initializer(rgb_matrix_sync),
  380. #else // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
  381. # define TRANSACTIONS_RGB_MATRIX_MASTER()
  382. # define TRANSACTIONS_RGB_MATRIX_SLAVE()
  383. # define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS
  384. #endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
  385. ////////////////////////////////////////////////////
  386. // WPM
  387. #if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
  388. static bool wpm_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  389. static uint32_t last_update = 0;
  390. uint8_t current_wpm = get_current_wpm();
  391. return send_if_condition(PUT_WPM, &last_update, (current_wpm != split_shmem->current_wpm), &current_wpm, sizeof(current_wpm));
  392. }
  393. static void wpm_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { set_current_wpm(split_shmem->current_wpm); }
  394. # define TRANSACTIONS_WPM_MASTER() TRANSACTION_HANDLER_MASTER(wpm)
  395. # define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE(wpm)
  396. # define TRANSACTIONS_WPM_REGISTRATIONS [PUT_WPM] = trans_initiator2target_initializer(current_wpm),
  397. #else // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
  398. # define TRANSACTIONS_WPM_MASTER()
  399. # define TRANSACTIONS_WPM_SLAVE()
  400. # define TRANSACTIONS_WPM_REGISTRATIONS
  401. #endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
  402. ////////////////////////////////////////////////////
  403. // OLED
  404. #if defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
  405. static bool oled_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  406. static uint32_t last_update = 0;
  407. bool current_oled_state = is_oled_on();
  408. return send_if_condition(PUT_OLED, &last_update, (current_oled_state != split_shmem->current_oled_state), &current_oled_state, sizeof(current_oled_state));
  409. }
  410. static void oled_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  411. if (split_shmem->current_oled_state) {
  412. oled_on();
  413. } else {
  414. oled_off();
  415. }
  416. }
  417. # define TRANSACTIONS_OLED_MASTER() TRANSACTION_HANDLER_MASTER(oled)
  418. # define TRANSACTIONS_OLED_SLAVE() TRANSACTION_HANDLER_SLAVE(oled)
  419. # define TRANSACTIONS_OLED_REGISTRATIONS [PUT_OLED] = trans_initiator2target_initializer(current_oled_state),
  420. #else // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
  421. # define TRANSACTIONS_OLED_MASTER()
  422. # define TRANSACTIONS_OLED_SLAVE()
  423. # define TRANSACTIONS_OLED_REGISTRATIONS
  424. #endif // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
  425. ////////////////////////////////////////////////////
  426. // ST7565
  427. #if defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
  428. static bool st7565_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  429. static uint32_t last_update = 0;
  430. bool current_st7565_state = st7565_is_on();
  431. return send_if_condition(PUT_ST7565, &last_update, (current_st7565_state != split_shmem->current_st7565_state), &current_st7565_state, sizeof(current_st7565_state));
  432. }
  433. static void st7565_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  434. if (split_shmem->current_st7565_state) {
  435. st7565_on();
  436. } else {
  437. st7565_off();
  438. }
  439. }
  440. # define TRANSACTIONS_ST7565_MASTER() TRANSACTION_HANDLER_MASTER(st7565)
  441. # define TRANSACTIONS_ST7565_SLAVE() TRANSACTION_HANDLER_SLAVE(st7565)
  442. # define TRANSACTIONS_ST7565_REGISTRATIONS [PUT_ST7565] = trans_initiator2target_initializer(current_st7565_state),
  443. #else // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
  444. # define TRANSACTIONS_ST7565_MASTER()
  445. # define TRANSACTIONS_ST7565_SLAVE()
  446. # define TRANSACTIONS_ST7565_REGISTRATIONS
  447. #endif // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
  448. ////////////////////////////////////////////////////
  449. uint8_t dummy;
  450. split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = {
  451. // Set defaults
  452. [0 ...(NUM_TOTAL_TRANSACTIONS - 1)] = {NULL, 0, 0, 0, 0, 0},
  453. #ifdef USE_I2C
  454. [I2C_EXECUTE_CALLBACK] = trans_initiator2target_initializer(transaction_id),
  455. #endif // USE_I2C
  456. // clang-format off
  457. TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS
  458. TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS
  459. TRANSACTIONS_ENCODERS_REGISTRATIONS
  460. TRANSACTIONS_SYNC_TIMER_REGISTRATIONS
  461. TRANSACTIONS_LAYER_STATE_REGISTRATIONS
  462. TRANSACTIONS_LED_STATE_REGISTRATIONS
  463. TRANSACTIONS_MODS_REGISTRATIONS
  464. TRANSACTIONS_BACKLIGHT_REGISTRATIONS
  465. TRANSACTIONS_RGBLIGHT_REGISTRATIONS
  466. TRANSACTIONS_LED_MATRIX_REGISTRATIONS
  467. TRANSACTIONS_RGB_MATRIX_REGISTRATIONS
  468. TRANSACTIONS_WPM_REGISTRATIONS
  469. TRANSACTIONS_OLED_REGISTRATIONS
  470. TRANSACTIONS_ST7565_REGISTRATIONS
  471. // clang-format on
  472. #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
  473. [PUT_RPC_INFO] = trans_initiator2target_initializer_cb(rpc_info, slave_rpc_info_callback),
  474. [PUT_RPC_REQ_DATA] = trans_initiator2target_initializer(rpc_m2s_buffer),
  475. [EXECUTE_RPC] = trans_initiator2target_initializer_cb(rpc_info.transaction_id, slave_rpc_exec_callback),
  476. [GET_RPC_RESP_DATA] = trans_target2initiator_initializer(rpc_s2m_buffer),
  477. #endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
  478. };
  479. bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  480. TRANSACTIONS_SLAVE_MATRIX_MASTER();
  481. TRANSACTIONS_MASTER_MATRIX_MASTER();
  482. TRANSACTIONS_ENCODERS_MASTER();
  483. TRANSACTIONS_SYNC_TIMER_MASTER();
  484. TRANSACTIONS_LAYER_STATE_MASTER();
  485. TRANSACTIONS_LED_STATE_MASTER();
  486. TRANSACTIONS_MODS_MASTER();
  487. TRANSACTIONS_BACKLIGHT_MASTER();
  488. TRANSACTIONS_RGBLIGHT_MASTER();
  489. TRANSACTIONS_LED_MATRIX_MASTER();
  490. TRANSACTIONS_RGB_MATRIX_MASTER();
  491. TRANSACTIONS_WPM_MASTER();
  492. TRANSACTIONS_OLED_MASTER();
  493. TRANSACTIONS_ST7565_MASTER();
  494. return true;
  495. }
  496. void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
  497. TRANSACTIONS_SLAVE_MATRIX_SLAVE();
  498. TRANSACTIONS_MASTER_MATRIX_SLAVE();
  499. TRANSACTIONS_ENCODERS_SLAVE();
  500. TRANSACTIONS_SYNC_TIMER_SLAVE();
  501. TRANSACTIONS_LAYER_STATE_SLAVE();
  502. TRANSACTIONS_LED_STATE_SLAVE();
  503. TRANSACTIONS_MODS_SLAVE();
  504. TRANSACTIONS_BACKLIGHT_SLAVE();
  505. TRANSACTIONS_RGBLIGHT_SLAVE();
  506. TRANSACTIONS_LED_MATRIX_SLAVE();
  507. TRANSACTIONS_RGB_MATRIX_SLAVE();
  508. TRANSACTIONS_WPM_SLAVE();
  509. TRANSACTIONS_OLED_SLAVE();
  510. TRANSACTIONS_ST7565_SLAVE();
  511. }
  512. #if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
  513. void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback) {
  514. // Prevent invoking RPC on QMK core sync data
  515. if (transaction_id <= GET_RPC_RESP_DATA) return;
  516. // Set the callback
  517. split_transaction_table[transaction_id].slave_callback = callback;
  518. split_transaction_table[transaction_id].initiator2target_offset = offsetof(split_shared_memory_t, rpc_m2s_buffer);
  519. split_transaction_table[transaction_id].target2initiator_offset = offsetof(split_shared_memory_t, rpc_s2m_buffer);
  520. }
  521. 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) {
  522. // Prevent transaction attempts while transport is disconnected
  523. if (!is_transport_connected()) {
  524. return false;
  525. }
  526. // Prevent invoking RPC on QMK core sync data
  527. if (transaction_id <= GET_RPC_RESP_DATA) return false;
  528. // Prevent sizing issues
  529. if (initiator2target_buffer_size > RPC_M2S_BUFFER_SIZE) return false;
  530. if (target2initiator_buffer_size > RPC_S2M_BUFFER_SIZE) return false;
  531. // Prepare the metadata block
  532. rpc_sync_info_t info = {.transaction_id = transaction_id, .m2s_length = initiator2target_buffer_size, .s2m_length = target2initiator_buffer_size};
  533. // Make sure the local side knows that we're not sending the full block of data
  534. split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = initiator2target_buffer_size;
  535. split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = target2initiator_buffer_size;
  536. // Run through the sequence:
  537. // * set the transaction ID and lengths
  538. // * send the request data
  539. // * execute RPC callback
  540. // * retrieve the response data
  541. if (!transport_write(PUT_RPC_INFO, &info, sizeof(info))) {
  542. return false;
  543. }
  544. if (!transport_write(PUT_RPC_REQ_DATA, initiator2target_buffer, initiator2target_buffer_size)) {
  545. return false;
  546. }
  547. if (!transport_write(EXECUTE_RPC, &transaction_id, sizeof(transaction_id))) {
  548. return false;
  549. }
  550. if (!transport_read(GET_RPC_RESP_DATA, target2initiator_buffer, target2initiator_buffer_size)) {
  551. return false;
  552. }
  553. return true;
  554. }
  555. void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
  556. // The RPC info block contains the intended transaction ID, as well as the sizes for both inbound and outbound data.
  557. // Ignore the args -- the `split_shmem` already has the info, we just need to act upon it.
  558. // We must keep the `split_transaction_table` non-const, so that it is able to be modified at runtime.
  559. split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = split_shmem->rpc_info.m2s_length;
  560. split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = split_shmem->rpc_info.s2m_length;
  561. }
  562. void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
  563. // We can assume that the buffer lengths are correctly set, now, given that sequentially the rpc_info callback was already executed.
  564. // Go through the rpc_info and execute _that_ transaction's callback, with the scratch buffers as inputs.
  565. int8_t transaction_id = split_shmem->rpc_info.transaction_id;
  566. if (transaction_id < NUM_TOTAL_TRANSACTIONS) {
  567. split_transaction_desc_t *trans = &split_transaction_table[transaction_id];
  568. if (trans->slave_callback) {
  569. 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);
  570. }
  571. }
  572. }
  573. #endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)