From d9610120de0452bc6a548bd36b1d4fdfba56d071 Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Thu, 27 May 2021 05:37:54 +0200 Subject: Add Full-duplex serial driver for ARM boards (#9842) --- docs/serial_driver.md | 154 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 147 insertions(+), 7 deletions(-) (limited to 'docs/serial_driver.md') diff --git a/docs/serial_driver.md b/docs/serial_driver.md index c98f4c1176..359fc59551 100644 --- a/docs/serial_driver.md +++ b/docs/serial_driver.md @@ -3,16 +3,18 @@ This driver powers the [Split Keyboard](feature_split_keyboard.md) feature. ?> Serial in this context should be read as **sending information one bit at a time**, rather than implementing UART/USART/RS485/RS232 standards. -All drivers in this category have the following characteristics: -* Provides data and signaling over a single conductor -* Limited to single master, single slave +Drivers in this category have the following characteristics: +* bit bang and USART Half-duplex provide data and signaling over a single conductor +* USART Full-duplex provide data and signaling over two conductors +* They are all limited to single master and single slave communication scheme ## Supported Driver Types | | AVR | ARM | -|-------------------|--------------------|--------------------| +| ----------------- | ------------------ | ------------------ | | bit bang | :heavy_check_mark: | :heavy_check_mark: | | USART Half-duplex | | :heavy_check_mark: | +| USART Full-duplex | | :heavy_check_mark: | ## Driver configuration @@ -42,7 +44,7 @@ Configure the driver via your config.h: Along with the generic options above, you must also turn on the `PAL_USE_CALLBACKS` feature in your halconf.h. ### USART Half-duplex -Targeting STM32 boards where communication is offloaded to a USART hardware device. The advantage is that this provides fast and accurate timings. `SOFT_SERIAL_PIN` for this driver is the configured USART TX pin. **The TX pin must have appropriate pull-up resistors**. To configure it, add this to your rules.mk: +Targeting STM32 boards where communication is offloaded to a USART hardware device. The advantage over bitbang is that this provides fast and accurate timings. `SERIAL_PIN_TX` for this driver is the configured USART TX pin. As this Pin is configured in open-drain mode an **external pull-up resistor is needed to keep the line high** (resistor values of 1.5k to 8.2k are known to work). To configure it, add this to your rules.mk: ```make SERIAL_DRIVER = usart @@ -50,7 +52,8 @@ SERIAL_DRIVER = usart Configure the hardware via your config.h: ```c -#define SOFT_SERIAL_PIN B6 // USART TX pin +#define SOFT_SERIAL_PIN B6 // USART TX pin +//#define USART1_REMAP // Remap USART TX and RX pins on STM32F103 MCUs, see table below. #define SELECT_SOFT_SERIAL_SPEED 1 // or 0, 2, 3, 4, 5 // 0: about 460800 baud // 1: about 230400 baud (default) @@ -58,7 +61,7 @@ Configure the hardware via your config.h: // 3: about 57600 baud // 4: about 38400 baud // 5: about 19200 baud -#define SERIAL_USART_DRIVER SD1 // USART driver of TX pin. default: SD1 +#define SERIAL_USART_DRIVER SD1 // USART driver of TX pin. default: SD1 #define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 #define SERIAL_USART_TIMEOUT 100 // USART driver timeout. default 100 ``` @@ -68,3 +71,140 @@ You must also enable the ChibiOS `SERIAL` feature: * In your board's mcuconf.h: `#define STM32_SERIAL_USE_USARTn TRUE` (where 'n' matches the peripheral number of your selected USART on the MCU) Do note that the configuration required is for the `SERIAL` peripheral, not the `UART` peripheral. + +### USART Full-duplex +Targeting STM32 boards where communication is offloaded to a USART hardware device. The advantage over bitbang is that this provides fast and accurate timings. USART Full-Duplex requires two conductors **without** pull-up resistors instead of one conductor with a pull-up resistor unlike the Half-duplex driver, but it is more efficent as it uses DMA transfers, which can result in even faster transmission speeds. + +#### Pin configuration + +`SERIAL_USART_TX_PIN` is the USART `TX` pin, `SERIAL_USART_RX_PIN` is the USART `RX` pin. No external pull-up resistors are needed as the `TX` pin operates in push-pull mode. To use this driver the usart peripherals `TX` and `RX` pins must be configured with the correct Alternate-functions. If you are using a Proton-C everything is already setup, same is true for STM32F103 MCUs. For MCUs which are using a modern flexible GPIO configuration you have to specify these by setting `SERIAL_USART_TX_PAL_MODE` and `SERIAL_USART_RX_PAL_MODE`. Refeer to the corresponding datasheets of your MCU or find those settings in the table below. + +#### Connecting the halves and Pin Swap +Please note that `TX` of the master half has to be connected with the `RX` pin of the slave half and `RX` of the master half has to be connected with the `TX` pin of the slave half! Usually this pin swap has to be done outside of the MCU e.g. with cables or on the pcb. Some MCUs like the STM32F303 used on the Proton-C allow this pin swap directly inside the MCU, this feature can be enabled using `#define SERIAL_USART_PIN_SWAP` in your config.h. + +#### Setup +To use the driver, add this to your rules.mk: + +```make +SERIAL_DRIVER = usart_duplex +``` + +Next configure the hardware via your config.h: + +```c +#define SERIAL_USART_TX_PIN B6 // USART TX pin +#define SERIAL_USART_RX_PIN B7 // USART RX pin +//#define USART1_REMAP // Remap USART TX and RX pins on STM32F103 MCUs, see table below. +//#define SERIAL_USART_PIN_SWAP // Swap TX and RX pins if keyboard is master halve. + // Check if this feature is necessary with your keyboard design and available on the mcu. +#define SELECT_SOFT_SERIAL_SPEED 1 // or 0, 2, 3, 4, 5 + // 0: 460800 baud + // 1: 230400 baud (default) + // 2: 115200 baud + // 3: 57600 baud + // 4: 38400 baud + // 5: 19200 baud +#define SERIAL_USART_DRIVER UARTD1 // USART driver of TX and RX pin. default: UARTD1 +#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 +#define SERIAL_USART_RX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 +#define SERIAL_USART_TIMEOUT 100 // USART driver timeout. default 100 +``` + +You must also enable the ChibiOS `UART` with blocking api feature: +* In your board's halconf.h: `#define HAL_USE_UART TRUE` and `#define UART_USE_WAIT TRUE` +* In your board's mcuconf.h: `#define STM32_UART_USE_USARTn TRUE` (where 'n' matches the peripheral number of your selected USART on the MCU) + +Do note that the configuration required is for the `UART` peripheral, not the `SERIAL` peripheral. + +#### Pins for USART Peripherals with Alternate Functions for selected STM32 MCUs + +##### STM32F303 / Proton-C [Datasheet](https://www.st.com/resource/en/datasheet/stm32f303cc.pdf) + +Pin Swap available: :heavy_check_mark: + +| Pin | Function | Mode | +| ---------- | -------- | ---- | +| **USART1** | | | +| PA9 | TX | AF7 | +| PA10 | RX | AF7 | +| PB6 | TX | AF7 | +| PB7 | RX | AF7 | +| PC4 | TX | AF7 | +| PC5 | RX | AF7 | +| PE0 | TX | AF7 | +| PE1 | RX | AF7 | +| **USART2** | | | +| PA2 | TX | AF7 | +| PA3 | RX | AF7 | +| PA14 | TX | AF7 | +| PA15 | RX | AF7 | +| PB3 | TX | AF7 | +| PB4 | RX | AF7 | +| PD5 | TX | AF7 | +| PD6 | RX | AF7 | +| **USART3** | | | +| PB10 | TX | AF7 | +| PB11 | RX | AF7 | +| PC10 | TX | AF7 | +| PC11 | RX | AF7 | +| PD8 | TX | AF7 | +| PD9 | RX | AF7 | + +##### STM32F072 [Datasheet](https://www.st.com/resource/en/datasheet/stm32f072c8.pdf) + +Pin Swap available: :heavy_check_mark: + +| Pin | Function | Mode | +| ------ | -------- | ---- | +| USART1 | | | +| PA9 | TX | AF1 | +| PA10 | RX | AF1 | +| PB6 | TX | AF0 | +| PB7 | RX | AF0 | +| USART2 | | | +| PA2 | TX | AF1 | +| PA3 | RX | AF1 | +| PA14 | TX | AF1 | +| PA15 | RX | AF1 | +| USART3 | | | +| PB10 | TX | AF4 | +| PB11 | RX | AF4 | +| PC4 | TX | AF1 | +| PC5 | RX | AF1 | +| PC10 | TX | AF1 | +| PC11 | RX | AF1 | +| PD8 | TX | AF0 | +| PD9 | RX | AF0 | +| USART4 | | | +| PA0 | TX | AF4 | +| PA1 | RX | AF4 | + +##### STM32F103 Medium Density (C8-CB) [Datasheet](https://www.st.com/resource/en/datasheet/stm32f103c8.pdf) + +Pin Swap available: N/A + +TX Pin is always Alternate Function Push-Pull, RX Pin is always regular input pin for any USART peripheral. **For STM32F103 no additional Alternate Function configuration is necessary. QMK is already configured.** + +Pin remapping: + +The pins of USART Peripherals use default Pins that can be remapped to use other pins using the AFIO registers. Default pins are marked **bold**. Add the appropriate defines to your config.h file. + +| Pin | Function | Mode | USART_REMAP | +| ---------- | -------- | ---- | ------------------- | +| **USART1** | | | | +| **PA9** | TX | AFPP | | +| **PA10** | RX | IN | | +| PB6 | TX | AFPP | USART1_REMAP | +| PB7 | RX | IN | USART1_REMAP | +| **USART2** | | | | +| **PA2** | TX | AFPP | | +| **PA3** | RX | IN | | +| PD5 | TX | AFPP | USART2_REMAP | +| PD6 | RX | IN | USART2_REMAP | +| **USART3** | | | | +| **PB10** | TX | AFPP | | +| **PB11** | RX | IN | | +| PC10 | TX | AFPP | USART3_PARTIALREMAP | +| PC11 | RX | IN | USART3_PARTIALREMAP | +| PD8 | TX | AFPP | USART3_FULLREMAP | +| PD9 | RX | IN | USART3_FULLREMAP | -- cgit v1.2.3 From 117bff17ba89a70dd85163b499c262b879f52afd Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Fri, 2 Jul 2021 00:24:08 +0200 Subject: [Core] Unite half-duplex and full-duplex serial drivers (#13081) * Unite half-duplex and full-duplex serial driver. * Add full duplex operation mode to the interrupt based driver * Delete DMA UART based full duplex driver * The new driver targets #11930 * Fix freezes with failing transactions in half-duplex * Increase default serial TX/RX buffer size to 128 bytes * Correctly use bool instead of size_t Co-authored-by: Nick Brassel --- build_keyboard.mk | 1 + docs/serial_driver.md | 15 +- drivers/chibios/serial_usart.c | 337 +++++++++++++++-------- drivers/chibios/serial_usart.h | 40 ++- drivers/chibios/serial_usart_duplex.c | 261 ------------------ platforms/chibios/QMK_PROTON_C/configs/halconf.h | 2 +- platforms/chibios/common/configs/halconf.h | 2 +- 7 files changed, 273 insertions(+), 385 deletions(-) delete mode 100644 drivers/chibios/serial_usart_duplex.c (limited to 'docs/serial_driver.md') diff --git a/build_keyboard.mk b/build_keyboard.mk index 4c4ff3b5fd..38ca2aaa99 100644 --- a/build_keyboard.mk +++ b/build_keyboard.mk @@ -237,6 +237,7 @@ ifdef MCU_FAMILY PLATFORM=CHIBIOS PLATFORM_KEY=chibios FIRMWARE_FORMAT?=bin + OPT_DEFS += -DMCU_$(MCU_FAMILY) else ifdef ARM_ATSAM PLATFORM=ARM_ATSAM PLATFORM_KEY=arm_atsam diff --git a/docs/serial_driver.md b/docs/serial_driver.md index 359fc59551..ed989b0a15 100644 --- a/docs/serial_driver.md +++ b/docs/serial_driver.md @@ -73,7 +73,7 @@ You must also enable the ChibiOS `SERIAL` feature: Do note that the configuration required is for the `SERIAL` peripheral, not the `UART` peripheral. ### USART Full-duplex -Targeting STM32 boards where communication is offloaded to a USART hardware device. The advantage over bitbang is that this provides fast and accurate timings. USART Full-Duplex requires two conductors **without** pull-up resistors instead of one conductor with a pull-up resistor unlike the Half-duplex driver, but it is more efficent as it uses DMA transfers, which can result in even faster transmission speeds. +Targeting STM32 boards where communication is offloaded to a USART hardware device. The advantage over bitbang is that this provides fast and accurate timings. USART Full-Duplex requires two conductors **without** pull-up resistors instead of one conductor with a pull-up resistor unlike the Half-duplex driver. Due to its internal design it is more efficent, which can result in even faster transmission speeds. #### Pin configuration @@ -86,12 +86,13 @@ Please note that `TX` of the master half has to be connected with the `RX` pin o To use the driver, add this to your rules.mk: ```make -SERIAL_DRIVER = usart_duplex +SERIAL_DRIVER = usart ``` Next configure the hardware via your config.h: ```c +#define SERIAL_USART_FULL_DUPLEX // Enable full duplex operation mode. #define SERIAL_USART_TX_PIN B6 // USART TX pin #define SERIAL_USART_RX_PIN B7 // USART RX pin //#define USART1_REMAP // Remap USART TX and RX pins on STM32F103 MCUs, see table below. @@ -104,17 +105,17 @@ Next configure the hardware via your config.h: // 3: 57600 baud // 4: 38400 baud // 5: 19200 baud -#define SERIAL_USART_DRIVER UARTD1 // USART driver of TX and RX pin. default: UARTD1 +#define SERIAL_USART_DRIVER SD1 // USART driver of TX and RX pin. default: SD1 #define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 #define SERIAL_USART_RX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 #define SERIAL_USART_TIMEOUT 100 // USART driver timeout. default 100 ``` -You must also enable the ChibiOS `UART` with blocking api feature: -* In your board's halconf.h: `#define HAL_USE_UART TRUE` and `#define UART_USE_WAIT TRUE` -* In your board's mcuconf.h: `#define STM32_UART_USE_USARTn TRUE` (where 'n' matches the peripheral number of your selected USART on the MCU) +You must also enable the ChibiOS `SERIAL` feature: +* In your board's halconf.h: `#define HAL_USE_SERIAL TRUE` +* In your board's mcuconf.h: `#define STM32_SERIAL_USE_USARTn TRUE` (where 'n' matches the peripheral number of your selected USART on the MCU) -Do note that the configuration required is for the `UART` peripheral, not the `SERIAL` peripheral. +Do note that the configuration required is for the `SERIAL` peripheral, not the `UART` peripheral. #### Pins for USART Peripherals with Alternate Functions for selected STM32 MCUs diff --git a/drivers/chibios/serial_usart.c b/drivers/chibios/serial_usart.c index 9f180d2d74..ea4473791c 100644 --- a/drivers/chibios/serial_usart.c +++ b/drivers/chibios/serial_usart.c @@ -16,179 +16,300 @@ #include "serial_usart.h" -#ifndef USE_GPIOV1 -// The default PAL alternate modes are used to signal that the pins are used for USART -# ifndef SERIAL_USART_TX_PAL_MODE -# define SERIAL_USART_TX_PAL_MODE 7 +#if defined(SERIAL_USART_CONFIG) +static SerialConfig serial_config = SERIAL_USART_CONFIG; +#else +static SerialConfig serial_config = { + .speed = (SERIAL_USART_SPEED), /* speed - mandatory */ + .cr1 = (SERIAL_USART_CR1), + .cr2 = (SERIAL_USART_CR2), +# if !defined(SERIAL_USART_FULL_DUPLEX) + .cr3 = ((SERIAL_USART_CR3) | USART_CR3_HDSEL) /* activate half-duplex mode */ +# else + .cr3 = (SERIAL_USART_CR3) # endif +}; #endif -#ifndef SERIAL_USART_DRIVER -# define SERIAL_USART_DRIVER SD1 -#endif - -#ifdef SOFT_SERIAL_PIN -# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN -#endif - -static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) { - msg_t ret = sdWrite(driver, data, size); +static SerialDriver* serial_driver = &SERIAL_USART_DRIVER; - // Half duplex requires us to read back the data we just wrote - just throw it away - uint8_t dump[size]; - sdRead(driver, dump, size); +static inline bool react_to_transactions(void); +static inline bool __attribute__((nonnull)) receive(uint8_t* destination, const size_t size); +static inline bool __attribute__((nonnull)) send(const uint8_t* source, const size_t size); +static inline int initiate_transaction(uint8_t sstd_index); +static inline void usart_clear(void); - return ret; +/** + * @brief Clear the receive input queue. + */ +static inline void usart_clear(void) { + osalSysLock(); + bool volatile queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue); + osalSysUnlock(); + + while (queue_not_empty) { + osalSysLock(); + /* Hard reset the input queue. */ + iqResetI(&serial_driver->iqueue); + osalSysUnlock(); + /* Allow pending interrupts to preempt. + * Do not merge the lock/unlock blocks into one + * or the code will not work properly. + * The empty read adds a tiny amount of delay. */ + (void)queue_not_empty; + osalSysLock(); + queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue); + osalSysUnlock(); + } } -#undef sdWrite -#define sdWrite sdWriteHalfDuplex - -static inline msg_t sdWriteTimeoutHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size, uint32_t timeout) { - msg_t ret = sdWriteTimeout(driver, data, size, timeout); - // Half duplex requires us to read back the data we just wrote - just throw it away - uint8_t dump[size]; - sdReadTimeout(driver, dump, size, timeout); +/** + * @brief Blocking send of buffer with timeout. + * + * @return true Send success. + * @return false Send failed. + */ +static inline bool send(const uint8_t* source, const size_t size) { + bool success = (size_t)sdWriteTimeout(serial_driver, source, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size; + +#if !defined(SERIAL_USART_FULL_DUPLEX) + if (success) { + /* Half duplex fills the input queue with the data we wrote - just throw it away. + Under the right circumstances (e.g. bad cables paired with high baud rates) + less bytes can be present in the input queue, therefore a timeout is needed. */ + uint8_t dump[size]; + return receive(dump, size); + } +#endif - return ret; + return success; } -#undef sdWriteTimeout -#define sdWriteTimeout sdWriteTimeoutHalfDuplex -static inline void sdClear(SerialDriver* driver) { - while (sdGetTimeout(driver, TIME_IMMEDIATE) != MSG_TIMEOUT) { - // Do nothing with the data - } +/** + * @brief Blocking receive of size * bytes with timeout. + * + * @return true Receive success. + * @return false Receive failed. + */ +static inline bool receive(uint8_t* destination, const size_t size) { + bool success = (size_t)sdReadTimeout(serial_driver, destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size; + return success; } -static SerialConfig sdcfg = { - (SERIAL_USART_SPEED), // speed - mandatory - (SERIAL_USART_CR1), // CR1 - (SERIAL_USART_CR2), // CR2 - (SERIAL_USART_CR3) // CR3 -}; - -void handle_soft_serial_slave(void); +#if !defined(SERIAL_USART_FULL_DUPLEX) -/* - * This thread runs on the slave and responds to transactions initiated - * by the master +/** + * @brief Initiate pins for USART peripheral. Half-duplex configuration. */ -static THD_WORKING_AREA(waSlaveThread, 2048); -static THD_FUNCTION(SlaveThread, arg) { - (void)arg; - chRegSetThreadName("slave_transport"); +__attribute__((weak)) void usart_init(void) { +# if defined(MCU_STM32) +# if defined(USE_GPIOV1) + palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN); +# else + palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); +# endif - while (true) { - handle_soft_serial_slave(); - } +# if defined(USART_REMAP) + USART_REMAP; +# endif +# else +# pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files." +# endif } -__attribute__((weak)) void usart_init(void) { -#if defined(USE_GPIOV1) - palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN); #else - palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); -#endif -#if defined(USART_REMAP) +/** + * @brief Initiate pins for USART peripheral. Full-duplex configuration. + */ +__attribute__((weak)) void usart_init(void) { +# if defined(MCU_STM32) +# if defined(USE_GPIOV1) + palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL); + palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT); +# else + palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); +# endif + +# if defined(USART_REMAP) USART_REMAP; +# endif +# else +# pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files." +# endif +} + #endif + +/** + * @brief Overridable master specific initializations. + */ +__attribute__((weak, nonnull)) void usart_master_init(SerialDriver** driver) { + (void)driver; + usart_init(); } -void usart_master_init(void) { +/** + * @brief Overridable slave specific initializations. + */ +__attribute__((weak, nonnull)) void usart_slave_init(SerialDriver** driver) { + (void)driver; usart_init(); +} + +/** + * @brief This thread runs on the slave and responds to transactions initiated + * by the master. + */ +static THD_WORKING_AREA(waSlaveThread, 1024); +static THD_FUNCTION(SlaveThread, arg) { + (void)arg; + chRegSetThreadName("usart_tx_rx"); - sdcfg.cr3 |= USART_CR3_HDSEL; - sdStart(&SERIAL_USART_DRIVER, &sdcfg); + while (true) { + if (!react_to_transactions()) { + /* Clear the receive queue, to start with a clean slate. + * Parts of failed transactions or spurious bytes could still be in it. */ + usart_clear(); + } + } } -void usart_slave_init(void) { - usart_init(); +/** + * @brief Slave specific initializations. + */ +void soft_serial_target_init(void) { + usart_slave_init(&serial_driver); - sdcfg.cr3 |= USART_CR3_HDSEL; - sdStart(&SERIAL_USART_DRIVER, &sdcfg); + sdStart(serial_driver, &serial_config); - // Start transport thread + /* Start transport thread. */ chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); } -void soft_serial_initiator_init(void) { usart_master_init(); } +/** + * @brief React to transactions started by the master. + */ +static inline bool react_to_transactions(void) { + /* Wait until there is a transaction for us. */ + uint8_t sstd_index = (uint8_t)sdGet(serial_driver); -void soft_serial_target_init(void) { usart_slave_init(); } + /* Sanity check that we are actually responding to a valid transaction. */ + if (sstd_index >= NUM_TOTAL_TRANSACTIONS) { + return false; + } -void handle_soft_serial_slave(void) { - uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id - split_transaction_desc_t* trans = &split_transaction_table[sstd_index]; + split_transaction_desc_t* trans = &split_transaction_table[sstd_index]; - // Always write back the sstd_index as part of a basic handshake + /* Send back the handshake which is XORed as a simple checksum, + to signal that the slave is ready to receive possible transaction buffers */ sstd_index ^= HANDSHAKE_MAGIC; - sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index)); + if (!send(&sstd_index, sizeof(sstd_index))) { + *trans->status = TRANSACTION_DATA_ERROR; + return false; + } + /* Receive transaction buffer from the master. If this transaction requires it.*/ if (trans->initiator2target_buffer_size) { - sdRead(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size); + if (!receive(split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) { + *trans->status = TRANSACTION_DATA_ERROR; + return false; + } } - // Allow any slave processing to occur + /* 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)); + trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, split_trans_target2initiator_buffer(trans)); } + /* Send transaction buffer to the master. If this transaction requires it. */ if (trans->target2initiator_buffer_size) { - sdWrite(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size); + if (!send(split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) { + *trans->status = TRANSACTION_DATA_ERROR; + return false; + } } - if (trans->status) { - *trans->status = TRANSACTION_ACCEPTED; - } + *trans->status = TRANSACTION_ACCEPTED; + return true; +} + +/** + * @brief Master specific initializations. + */ +void soft_serial_initiator_init(void) { + usart_master_init(&serial_driver); + +#if defined(MCU_STM32) && defined(SERIAL_USART_PIN_SWAP) + serial_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins +#endif + + sdStart(serial_driver, &serial_config); } -///////// -// start transaction by initiator -// -// int soft_serial_transaction(int sstd_index) -// -// Returns: -// TRANSACTION_END -// TRANSACTION_NO_RESPONSE -// TRANSACTION_DATA_ERROR +/** + * @brief Start transaction from the master half to the slave half. + * + * @param index Transaction Table index of the transaction to start. + * @return int TRANSACTION_NO_RESPONSE in case of Timeout. + * TRANSACTION_TYPE_ERROR in case of invalid transaction index. + * TRANSACTION_END in case of success. + */ int soft_serial_transaction(int index) { - uint8_t sstd_index = index; + /* Clear the receive queue, to start with a clean slate. + * Parts of failed transactions or spurious bytes could still be in it. */ + usart_clear(); + return initiate_transaction((uint8_t)index); +} - if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR; - split_transaction_desc_t* trans = &split_transaction_table[sstd_index]; - msg_t res = 0; +/** + * @brief Initiate transaction to slave half. + */ +static inline int initiate_transaction(uint8_t sstd_index) { + /* Sanity check that we are actually starting a valid transaction. */ + if (sstd_index >= NUM_TOTAL_TRANSACTIONS) { + dprintln("USART: Illegal transaction Id."); + return TRANSACTION_TYPE_ERROR; + } - if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered + split_transaction_desc_t* trans = &split_transaction_table[sstd_index]; - sdClear(&SERIAL_USART_DRIVER); + /* Transaction is not registered. Abort. */ + if (!trans->status) { + dprintln("USART: Transaction not registered."); + return TRANSACTION_TYPE_ERROR; + } - // First chunk is always transaction id - sdWriteTimeout(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index), TIME_MS2I(SERIAL_USART_TIMEOUT)); + /* Send transaction table index to the slave, which doubles as basic handshake token. */ + if (!send(&sstd_index, sizeof(sstd_index))) { + dprintln("USART: Send Handshake failed."); + return TRANSACTION_TYPE_ERROR; + } uint8_t sstd_index_shake = 0xFF; - // Which we always read back first so that we can error out correctly - // - due to the half duplex limitations on return codes, we always have to read *something* - // - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready - res = sdReadTimeout(&SERIAL_USART_DRIVER, &sstd_index_shake, sizeof(sstd_index_shake), TIME_MS2I(SERIAL_USART_TIMEOUT)); - if (res < 0 || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) { - dprintf("serial::usart_shake NO_RESPONSE\n"); + /* Which we always read back first so that we can error out correctly. + * - due to the half duplex limitations on return codes, we always have to read *something*. + * - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready. + */ + if (!receive(&sstd_index_shake, sizeof(sstd_index_shake)) || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) { + dprintln("USART: Handshake failed."); return TRANSACTION_NO_RESPONSE; } + /* Send transaction buffer to the slave. If this transaction requires it. */ if (trans->initiator2target_buffer_size) { - 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"); + if (!send(split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) { + dprintln("USART: Send failed."); return TRANSACTION_NO_RESPONSE; } } + /* Receive transaction buffer from the slave. If this transaction requires it. */ if (trans->target2initiator_buffer_size) { - 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"); + if (!receive(split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) { + dprintln("USART: Receive failed."); return TRANSACTION_NO_RESPONSE; } } diff --git a/drivers/chibios/serial_usart.h b/drivers/chibios/serial_usart.h index fee7b4d159..c64e15566f 100644 --- a/drivers/chibios/serial_usart.h +++ b/drivers/chibios/serial_usart.h @@ -23,19 +23,45 @@ #include #include -#ifndef USART_CR1_M0 +#if !defined(SERIAL_USART_DRIVER) +# define SERIAL_USART_DRIVER SD1 +#endif + +#if !defined(USE_GPIOV1) +/* The default PAL alternate modes are used to signal that the pins are used for USART. */ +# if !defined(SERIAL_USART_TX_PAL_MODE) +# define SERIAL_USART_TX_PAL_MODE 7 +# endif +# if !defined(SERIAL_USART_RX_PAL_MODE) +# define SERIAL_USART_RX_PAL_MODE 7 +# endif +#endif + +#if defined(SOFT_SERIAL_PIN) +# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN +#endif + +#if !defined(SERIAL_USART_TX_PIN) +# define SERIAL_USART_TX_PIN A9 +#endif + +#if !defined(SERIAL_USART_RX_PIN) +# define SERIAL_USART_RX_PIN A10 +#endif + +#if !defined(USART_CR1_M0) # define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so #endif -#ifndef SERIAL_USART_CR1 +#if !defined(SERIAL_USART_CR1) # define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length #endif -#ifndef SERIAL_USART_CR2 +#if !defined(SERIAL_USART_CR2) # define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits #endif -#ifndef SERIAL_USART_CR3 +#if !defined(SERIAL_USART_CR3) # define SERIAL_USART_CR3 0 #endif @@ -61,11 +87,11 @@ } while (0) #endif -#ifndef SELECT_SOFT_SERIAL_SPEED +#if !defined(SELECT_SOFT_SERIAL_SPEED) # define SELECT_SOFT_SERIAL_SPEED 1 #endif -#ifdef SERIAL_USART_SPEED +#if defined(SERIAL_USART_SPEED) // Allow advanced users to directly set SERIAL_USART_SPEED #elif SELECT_SOFT_SERIAL_SPEED == 0 # define SERIAL_USART_SPEED 460800 @@ -83,7 +109,7 @@ # error invalid SELECT_SOFT_SERIAL_SPEED value #endif -#ifndef SERIAL_USART_TIMEOUT +#if !defined(SERIAL_USART_TIMEOUT) # define SERIAL_USART_TIMEOUT 100 #endif diff --git a/drivers/chibios/serial_usart_duplex.c b/drivers/chibios/serial_usart_duplex.c deleted file mode 100644 index cc9b889ac2..0000000000 --- a/drivers/chibios/serial_usart_duplex.c +++ /dev/null @@ -1,261 +0,0 @@ -/* 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 3 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 "serial_usart.h" - -#include - -#if !defined(USE_GPIOV1) -// The default PAL alternate modes are used to signal that the pins are used for USART -# if !defined(SERIAL_USART_TX_PAL_MODE) -# define SERIAL_USART_TX_PAL_MODE 7 -# endif -# if !defined(SERIAL_USART_RX_PAL_MODE) -# define SERIAL_USART_RX_PAL_MODE 7 -# endif -#endif - -#if !defined(SERIAL_USART_DRIVER) -# define SERIAL_USART_DRIVER UARTD1 -#endif - -#if !defined(SERIAL_USART_TX_PIN) -# define SERIAL_USART_TX_PIN A9 -#endif - -#if !defined(SERIAL_USART_RX_PIN) -# define SERIAL_USART_RX_PIN A10 -#endif - -#define SIGNAL_HANDSHAKE_RECEIVED 0x1 - -void handle_transactions_slave(uint8_t sstd_index); -static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake); - -/* - * UART driver configuration structure. We use the blocking DMA enabled API and - * the rxchar callback to receive handshake tokens but only on the slave halve. - */ -// clang-format off -static UARTConfig uart_config = { - .txend1_cb = NULL, - .txend2_cb = NULL, - .rxend_cb = NULL, - .rxchar_cb = NULL, - .rxerr_cb = NULL, - .timeout_cb = NULL, - .speed = (SERIAL_USART_SPEED), - .cr1 = (SERIAL_USART_CR1), - .cr2 = (SERIAL_USART_CR2), - .cr3 = (SERIAL_USART_CR3) -}; -// clang-format on - -static SSTD_t* Transaction_table = NULL; -static uint8_t Transaction_table_size = 0; -static atomic_uint_least8_t handshake = 0xFF; -static thread_reference_t tp_target = NULL; - -/* - * This callback is invoked when a character is received but the application - * was not ready to receive it, the character is passed as parameter. - * Receive transaction table index from initiator, which doubles as basic handshake token. */ -static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake) { - /* Check if received handshake is not a valid transaction id. - * Please note that we can still catch a seemingly valid handshake - * i.e. a byte from a ongoing transfer which is in the allowed range. - * So this check mainly prevents any obviously wrong handshakes and - * subsequent wakeups of the receiving thread, which is a costly operation. */ - if (received_handshake > Transaction_table_size) { - return; - } - - handshake = (uint8_t)received_handshake; - chSysLockFromISR(); - /* Wakeup receiving thread to start a transaction. */ - chEvtSignalI(tp_target, (eventmask_t)SIGNAL_HANDSHAKE_RECEIVED); - chSysUnlockFromISR(); -} - -__attribute__((weak)) void usart_init(void) { -#if defined(USE_GPIOV1) - palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL); - palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT); -#else - palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); - palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); -#endif -} - -/* - * This thread runs on the slave half and reacts to transactions initiated from the master. - */ -static THD_WORKING_AREA(waSlaveThread, 1024); -static THD_FUNCTION(SlaveThread, arg) { - (void)arg; - chRegSetThreadName("slave_usart_tx_rx"); - - while (true) { - /* We sleep as long as there is no handshake waiting for us. */ - chEvtWaitAny((eventmask_t)SIGNAL_HANDSHAKE_RECEIVED); - handle_transactions_slave(handshake); - } -} - -void soft_serial_target_init(SSTD_t* const sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; - usart_init(); - -#if defined(USART_REMAP) - USART_REMAP; -#endif - - tp_target = chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); - - // Start receiving handshake tokens on slave halve - uart_config.rxchar_cb = receive_transaction_handshake; - uartStart(&SERIAL_USART_DRIVER, &uart_config); -} - -/** - * @brief React to transactions started by the master. - * This version uses duplex send and receive usart pheriphals and DMA backed transfers. - */ -void inline handle_transactions_slave(uint8_t sstd_index) { - size_t buffer_size = 0; - msg_t msg = 0; - SSTD_t* trans = &Transaction_table[sstd_index]; - - /* Send back the handshake which is XORed as a simple checksum, - to signal that the slave is ready to receive possible transaction buffers */ - sstd_index ^= HANDSHAKE_MAGIC; - buffer_size = (size_t)sizeof(sstd_index); - msg = uartSendTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT)); - - if (msg != MSG_OK) { - if (trans->status) { - *trans->status = TRANSACTION_NO_RESPONSE; - } - return; - } - - /* Receive transaction buffer from the master. If this transaction requires it.*/ - buffer_size = (size_t)trans->initiator2target_buffer_size; - if (buffer_size) { - msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT)); - if (msg != MSG_OK) { - if (trans->status) { - *trans->status = TRANSACTION_NO_RESPONSE; - } - return; - } - } - - /* Send transaction buffer to the master. If this transaction requires it. */ - buffer_size = (size_t)trans->target2initiator_buffer_size; - if (buffer_size) { - msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT)); - if (msg != MSG_OK) { - if (trans->status) { - *trans->status = TRANSACTION_NO_RESPONSE; - } - return; - } - } - - if (trans->status) { - *trans->status = TRANSACTION_ACCEPTED; - } -} - -void soft_serial_initiator_init(SSTD_t* const sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; - usart_init(); - -#if defined(SERIAL_USART_PIN_SWAP) - uart_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins -#endif - -#if defined(USART_REMAP) - USART_REMAP; -#endif - - uartStart(&SERIAL_USART_DRIVER, &uart_config); -} - -/** - * @brief Start transaction from the master to the slave. - * This version uses duplex send and receive usart pheriphals and DMA backed transfers. - * - * @param index Transaction Table index of the transaction to start. - * @return int TRANSACTION_NO_RESPONSE in case of Timeout. - * TRANSACTION_TYPE_ERROR in case of invalid transaction index. - * TRANSACTION_END in case of success. - */ -#if !defined(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* const trans = &Transaction_table[sstd_index]; - msg_t msg = 0; - size_t buffer_size = (size_t)sizeof(sstd_index); - - /* Send transaction table index to the slave, which doubles as basic handshake token. */ - uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT)); - - uint8_t sstd_index_shake = 0xFF; - buffer_size = (size_t)sizeof(sstd_index_shake); - - /* Receive the handshake token from the slave. The token was XORed by the slave as a simple checksum. - If the tokens match, the master will start to send and receive possible transaction buffers. */ - msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index_shake, TIME_MS2I(SERIAL_USART_TIMEOUT)); - if (msg != MSG_OK || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) { - dprintln("USART: Handshake Failed"); - return TRANSACTION_NO_RESPONSE; - } - - /* Send transaction buffer to the slave. If this transaction requires it. */ - buffer_size = (size_t)trans->initiator2target_buffer_size; - if (buffer_size) { - msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT)); - if (msg != MSG_OK) { - dprintln("USART: Send Failed"); - return TRANSACTION_NO_RESPONSE; - } - } - - /* Receive transaction buffer from the slave. If this transaction requires it. */ - buffer_size = (size_t)trans->target2initiator_buffer_size; - if (buffer_size) { - msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT)); - if (msg != MSG_OK) { - dprintln("USART: Receive Failed"); - return TRANSACTION_NO_RESPONSE; - } - } - - return TRANSACTION_END; -} diff --git a/platforms/chibios/QMK_PROTON_C/configs/halconf.h b/platforms/chibios/QMK_PROTON_C/configs/halconf.h index 41fbac29e0..d7a639a6d0 100644 --- a/platforms/chibios/QMK_PROTON_C/configs/halconf.h +++ b/platforms/chibios/QMK_PROTON_C/configs/halconf.h @@ -412,7 +412,7 @@ * buffers. */ #if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__) -#define SERIAL_BUFFERS_SIZE 16 +#define SERIAL_BUFFERS_SIZE 128 #endif /*===========================================================================*/ diff --git a/platforms/chibios/common/configs/halconf.h b/platforms/chibios/common/configs/halconf.h index 264ae4e6c1..c80f67ee27 100644 --- a/platforms/chibios/common/configs/halconf.h +++ b/platforms/chibios/common/configs/halconf.h @@ -412,7 +412,7 @@ * buffers. */ #if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__) -#define SERIAL_BUFFERS_SIZE 16 +#define SERIAL_BUFFERS_SIZE 128 #endif /*===========================================================================*/ -- cgit v1.2.3 From 0ae20e74578ae4420af3e684f7ad9fcbd7beee94 Mon Sep 17 00:00:00 2001 From: Joakim Tufvegren Date: Sun, 22 Aug 2021 02:51:17 +0200 Subject: 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 Co-authored-by: Joel Challis --- docs/feature_split_keyboard.md | 16 +++++ docs/serial_driver.md | 6 +- keyboards/ergodox_infinity/config.h | 1 - platforms/chibios/drivers/serial_usart.h | 2 +- quantum/matrix.c | 36 +++++------ quantum/split_common/split_util.c | 51 ++++++++++++++++ quantum/split_common/split_util.h | 5 ++ quantum/split_common/transactions.c | 102 +++++++++++++++---------------- quantum/split_common/transport.c | 2 +- 9 files changed, 143 insertions(+), 78 deletions(-) (limited to 'docs/serial_driver.md') diff --git a/docs/feature_split_keyboard.md b/docs/feature_split_keyboard.md index a84d384601..27df46a82a 100644 --- a/docs/feature_split_keyboard.md +++ b/docs/feature_split_keyboard.md @@ -203,6 +203,22 @@ If you're having issues with serial communication, you can change this value, as 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_MAX_CONNECTION_ERRORS 10 +``` +This sets the maximum number of failed communication attempts (one per scan cycle) from the master part before it assumes that no slave part is connected. This makes it possible to use a master part without the slave part connected. + +Set to 0 to disable the disconnection check altogether. + +```c +#define SPLIT_CONNECTION_CHECK_TIMEOUT 500 +``` +How long (in milliseconds) the master part should block all connection attempts to the slave after the communication has been flagged as disconnected (see `SPLIT_MAX_CONNECTION_ERRORS` above). + +One communication attempt will be allowed everytime this amount of time has passed since the last attempt. If that attempt succeeds, the communication is seen as working again. + +Set to 0 to disable this throttling of communications while disconnected. This can save you a couple of bytes of firmware size. + ```c #define SPLIT_TRANSPORT_MIRROR ``` diff --git a/docs/serial_driver.md b/docs/serial_driver.md index ed989b0a15..3e89deffad 100644 --- a/docs/serial_driver.md +++ b/docs/serial_driver.md @@ -63,7 +63,7 @@ Configure the hardware via your config.h: // 5: about 19200 baud #define SERIAL_USART_DRIVER SD1 // USART driver of TX pin. default: SD1 #define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 -#define SERIAL_USART_TIMEOUT 100 // USART driver timeout. default 100 +#define SERIAL_USART_TIMEOUT 20 // USART driver timeout. default 20 ``` You must also enable the ChibiOS `SERIAL` feature: @@ -105,10 +105,10 @@ Next configure the hardware via your config.h: // 3: 57600 baud // 4: 38400 baud // 5: 19200 baud -#define SERIAL_USART_DRIVER SD1 // USART driver of TX and RX pin. default: SD1 +#define SERIAL_USART_DRIVER SD1 // USART driver of TX and RX pin. default: SD1 #define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 #define SERIAL_USART_RX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 -#define SERIAL_USART_TIMEOUT 100 // USART driver timeout. default 100 +#define SERIAL_USART_TIMEOUT 20 // USART driver timeout. default 20 ``` You must also enable the ChibiOS `SERIAL` feature: diff --git a/keyboards/ergodox_infinity/config.h b/keyboards/ergodox_infinity/config.h index e0208ba4d3..4bee8c3d93 100644 --- a/keyboards/ergodox_infinity/config.h +++ b/keyboards/ergodox_infinity/config.h @@ -67,7 +67,6 @@ along with this program. If not, see . #define SERIAL_USART_DRIVER SD1 // Only true for the master half #define SERIAL_USART_CONFIG { (SERIAL_USART_SPEED), } // Only field is speed #define SERIAL_USART_FULL_DUPLEX -#define SERIAL_USART_TIMEOUT 50 /* number of backlight levels */ #define BACKLIGHT_LEVELS 3 diff --git a/platforms/chibios/drivers/serial_usart.h b/platforms/chibios/drivers/serial_usart.h index c64e15566f..7b135b31e0 100644 --- a/platforms/chibios/drivers/serial_usart.h +++ b/platforms/chibios/drivers/serial_usart.h @@ -110,7 +110,7 @@ #endif #if !defined(SERIAL_USART_TIMEOUT) -# define SERIAL_USART_TIMEOUT 100 +# define SERIAL_USART_TIMEOUT 20 #endif #define HANDSHAKE_MAGIC 7 diff --git a/quantum/matrix.c b/quantum/matrix.c index d22817bf48..33586c431b 100644 --- a/quantum/matrix.c +++ b/quantum/matrix.c @@ -25,10 +25,6 @@ along with this program. If not, see . # 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) @@ -307,33 +303,31 @@ void matrix_init(void) { } #ifdef SPLIT_KEYBOARD +// Fallback implementation for keyboards not using the standard split_util.c +__attribute__((weak)) bool transport_master_if_connected(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + transport_master(master_matrix, slave_matrix); + return true; // Treat the transport as always connected +} + 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; - + if (transport_master_if_connected(matrix + thisHand, slave_matrix)) { for (int i = 0; i < ROWS_PER_HAND; ++i) { if (matrix[thatHand + i] != slave_matrix[i]) { matrix[thatHand + i] = slave_matrix[i]; changed = true; } } + } else { + // reset other half if disconnected + for (int i = 0; i < ROWS_PER_HAND; ++i) { + matrix[thatHand + i] = 0; + slave_matrix[i] = 0; + } + + changed = true; } matrix_scan_quantum(); diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c index 8d414f6fe6..35f0a9d181 100644 --- a/quantum/split_common/split_util.c +++ b/quantum/split_common/split_util.c @@ -39,6 +39,21 @@ # define SPLIT_USB_TIMEOUT_POLL 10 #endif +// Max number of consecutive failed communications (one per scan cycle) before the communication is seen as disconnected. +// Set to 0 to disable the disconnection check altogether. +#ifndef SPLIT_MAX_CONNECTION_ERRORS +# define SPLIT_MAX_CONNECTION_ERRORS 10 +#endif // SPLIT_MAX_CONNECTION_ERRORS + +// How long (in milliseconds) to block all connection attempts after the communication has been flagged as disconnected. +// One communication attempt will be allowed everytime this amount of time has passed since the last attempt. If that attempt succeeds, the communication is seen as working again. +// Set to 0 to disable communication throttling while disconnected +#ifndef SPLIT_CONNECTION_CHECK_TIMEOUT +# define SPLIT_CONNECTION_CHECK_TIMEOUT 500 +#endif // SPLIT_CONNECTION_CHECK_TIMEOUT + +static uint8_t connection_errors = 0; + volatile bool isLeftHand = true; #if defined(SPLIT_USB_DETECT) @@ -142,3 +157,39 @@ void split_post_init(void) { transport_slave_init(); } } + +bool is_transport_connected(void) { return connection_errors < SPLIT_MAX_CONNECTION_ERRORS; } + +bool transport_master_if_connected(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { +#if SPLIT_MAX_CONNECTION_ERRORS > 0 && SPLIT_CONNECTION_CHECK_TIMEOUT > 0 + // Throttle transaction attempts if target doesn't seem to be connected + // Without this, a solo half becomes unusable due to constant read timeouts + static uint16_t connection_check_timer = 0; + const bool is_disconnected = !is_transport_connected(); + if (is_disconnected && timer_elapsed(connection_check_timer) < SPLIT_CONNECTION_CHECK_TIMEOUT) { + return false; + } +#endif // SPLIT_MAX_CONNECTION_ERRORS > 0 && SPLIT_CONNECTION_CHECK_TIMEOUT > 0 + + __attribute__((unused)) bool okay = transport_master(master_matrix, slave_matrix); +#if SPLIT_MAX_CONNECTION_ERRORS > 0 + if (!okay) { + if (connection_errors < UINT8_MAX) { + connection_errors++; + } +# if SPLIT_CONNECTION_CHECK_TIMEOUT > 0 + bool connected = is_transport_connected(); + if (!connected) { + connection_check_timer = timer_read(); + dprintln("Target disconnected, throttling connection attempts"); + } + return connected; + } else if (is_disconnected) { + dprintln("Target connected"); +# endif // SPLIT_CONNECTION_CHECK_TIMEOUT > 0 + } + + connection_errors = 0; +#endif // SPLIT_MAX_CONNECTION_ERRORS > 0 + return true; +} diff --git a/quantum/split_common/split_util.h b/quantum/split_common/split_util.h index a4c12519e0..ef72043bb7 100644 --- a/quantum/split_common/split_util.h +++ b/quantum/split_common/split_util.h @@ -5,8 +5,13 @@ #include #include +#include "matrix.h" + extern volatile bool isLeftHand; void matrix_master_OLED_init(void); void split_pre_init(void); void split_post_init(void); + +bool transport_master_if_connected(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); +bool is_transport_connected(void); diff --git a/quantum/split_common/transactions.c b/quantum/split_common/transactions.c index 7eb3eca3e7..28ea4ef6d8 100644 --- a/quantum/split_common/transactions.c +++ b/quantum/split_common/transactions.c @@ -23,6 +23,7 @@ #include "quantum.h" #include "transactions.h" #include "transport.h" +#include "split_util.h" #include "transaction_id_define.h" #define SYNC_TIMER_OFFSET 2 @@ -53,34 +54,30 @@ void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *i //////////////////////////////////////////////////// // 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); - } +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[])) { + int num_retries = is_transport_connected() ? 10 : 1; + for (int iter = 1; iter <= num_retries; ++iter) { + if (iter > 1) { + 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); } + bool this_okay = true; + ATOMIC_BLOCK_FORCEON { this_okay = handler(master_matrix, slave_matrix); }; + if (this_okay) return true; } - return okay; + dprintf("Failed to execute %s\n", prefix); + return false; } -#define TRANSACTION_HANDLER_MASTER(prefix) \ - do { \ - okay &= transaction_handler_master(okay, master_matrix, slave_matrix, #prefix, &prefix##_master); \ +#define TRANSACTION_HANDLER_MASTER(prefix) \ + do { \ + if (!transaction_handler_master(master_matrix, slave_matrix, #prefix, &prefix##_handlers_master)) return false; \ } while (0) -#define TRANSACTION_HANDLER_SLAVE(prefix) \ - do { \ - ATOMIC_BLOCK_FORCEON { prefix##_slave(master_matrix, slave_matrix); }; \ +#define TRANSACTION_HANDLER_SLAVE(prefix) \ + do { \ + ATOMIC_BLOCK_FORCEON { prefix##_handlers_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) { @@ -138,8 +135,8 @@ static void slave_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row } // 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_MASTER() TRANSACTION_HANDLER_MASTER(slave_matrix) +#define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(slave_matrix) #define TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS \ [GET_SLAVE_MATRIX_CHECKSUM] = trans_target2initiator_initializer(smatrix.checksum), \ [GET_SLAVE_MATRIX_DATA] = trans_target2initiator_initializer(smatrix.matrix), @@ -160,8 +157,8 @@ static void master_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_ro 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_MASTER() TRANSACTION_HANDLER_MASTER(master_matrix) +# define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(master_matrix) # define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS [PUT_MASTER_MATRIX] = trans_initiator2target_initializer(mmatrix.matrix), #else // SPLIT_TRANSPORT_MIRROR @@ -196,8 +193,8 @@ static void encoder_handlers_slave(matrix_row_t master_matrix[], matrix_row_t sl } // clang-format off -# define TRANSACTIONS_ENCODERS_MASTER() TRANSACTION_HANDLER_MASTER(encoder_handlers) -# define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE(encoder_handlers) +# define TRANSACTIONS_ENCODERS_MASTER() TRANSACTION_HANDLER_MASTER(encoder) +# define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE(encoder) # define TRANSACTIONS_ENCODERS_REGISTRATIONS \ [GET_ENCODERS_CHECKSUM] = trans_target2initiator_initializer(encoders.checksum), \ [GET_ENCODERS_DATA] = trans_target2initiator_initializer(encoders.state), @@ -238,8 +235,8 @@ static void sync_timer_handlers_slave(matrix_row_t master_matrix[], matrix_row_t } } -# 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_MASTER() TRANSACTION_HANDLER_MASTER(sync_timer) +# define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE(sync_timer) # define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS [PUT_SYNC_TIMER] = trans_initiator2target_initializer(sync_timer), #else // DISABLE_SYNC_TIMER @@ -272,8 +269,8 @@ static void layer_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_ } // 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_MASTER() TRANSACTION_HANDLER_MASTER(layer_state) +# define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(layer_state) # 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), @@ -303,8 +300,8 @@ static void led_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t 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_MASTER() TRANSACTION_HANDLER_MASTER(led_state) +# define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(led_state) # define TRANSACTIONS_LED_STATE_REGISTRATIONS [PUT_LED_STATE] = trans_initiator2target_initializer(led_state), #else // SPLIT_LED_STATE_ENABLE @@ -360,8 +357,8 @@ static void mods_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave # endif } -# define TRANSACTIONS_MODS_MASTER() TRANSACTION_HANDLER_MASTER(mods_handlers) -# define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods_handlers) +# define TRANSACTIONS_MODS_MASTER() TRANSACTION_HANDLER_MASTER(mods) +# define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods) # define TRANSACTIONS_MODS_REGISTRATIONS [PUT_MODS] = trans_initiator2target_initializer(mods), #else // SPLIT_MODS_ENABLE @@ -385,8 +382,8 @@ static bool backlight_handlers_master(matrix_row_t master_matrix[], matrix_row_t 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_MASTER() TRANSACTION_HANDLER_MASTER(backlight) +# define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight) # define TRANSACTIONS_BACKLIGHT_REGISTRATIONS [PUT_BACKLIGHT] = trans_initiator2target_initializer(backlight_level), #else // BACKLIGHT_ENABLE @@ -422,8 +419,8 @@ static void rgblight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t s } } -# define TRANSACTIONS_RGBLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(rgblight_handlers) -# define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight_handlers) +# define TRANSACTIONS_RGBLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(rgblight) +# define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight) # define TRANSACTIONS_RGBLIGHT_REGISTRATIONS [PUT_RGBLIGHT] = trans_initiator2target_initializer(rgblight_sync), #else // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) @@ -452,8 +449,8 @@ static void led_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_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_MASTER() TRANSACTION_HANDLER_MASTER(led_matrix) +# define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix) # define TRANSACTIONS_LED_MATRIX_REGISTRATIONS [PUT_LED_MATRIX] = trans_initiator2target_initializer(led_matrix_sync), #else // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) @@ -482,8 +479,8 @@ static void rgb_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_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_MASTER() TRANSACTION_HANDLER_MASTER(rgb_matrix) +# define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix) # define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS [PUT_RGB_MATRIX] = trans_initiator2target_initializer(rgb_matrix_sync), #else // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) @@ -507,8 +504,8 @@ static bool wpm_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave 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_MASTER() TRANSACTION_HANDLER_MASTER(wpm) +# define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE(wpm) # define TRANSACTIONS_WPM_REGISTRATIONS [PUT_WPM] = trans_initiator2target_initializer(current_wpm), #else // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) @@ -538,8 +535,8 @@ static void oled_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave } } -# define TRANSACTIONS_OLED_MASTER() TRANSACTION_HANDLER_MASTER(oled_handlers) -# define TRANSACTIONS_OLED_SLAVE() TRANSACTION_HANDLER_SLAVE(oled_handlers) +# define TRANSACTIONS_OLED_MASTER() TRANSACTION_HANDLER_MASTER(oled) +# define TRANSACTIONS_OLED_SLAVE() TRANSACTION_HANDLER_SLAVE(oled) # define TRANSACTIONS_OLED_REGISTRATIONS [PUT_OLED] = trans_initiator2target_initializer(current_oled_state), #else // defined(OLED_DRIVER_ENABLE) && defined(SPLIT_OLED_ENABLE) @@ -569,8 +566,8 @@ static void st7565_handlers_slave(matrix_row_t master_matrix[], matrix_row_t sla } } -# define TRANSACTIONS_ST7565_MASTER() TRANSACTION_HANDLER_MASTER(st7565_handlers) -# define TRANSACTIONS_ST7565_SLAVE() TRANSACTION_HANDLER_SLAVE(st7565_handlers) +# define TRANSACTIONS_ST7565_MASTER() TRANSACTION_HANDLER_MASTER(st7565) +# define TRANSACTIONS_ST7565_SLAVE() TRANSACTION_HANDLER_SLAVE(st7565) # define TRANSACTIONS_ST7565_REGISTRATIONS [PUT_ST7565] = trans_initiator2target_initializer(current_st7565_state), #else // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE) @@ -618,7 +615,6 @@ split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = { }; 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(); @@ -633,7 +629,7 @@ bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix TRANSACTIONS_WPM_MASTER(); TRANSACTIONS_OLED_MASTER(); TRANSACTIONS_ST7565_MASTER(); - return okay; + return true; } void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { @@ -666,6 +662,10 @@ 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) { + // Prevent transaction attempts while transport is disconnected + if (!is_transport_connected()) { + return false; + } // Prevent invoking RPC on QMK core sync data if (transaction_id <= GET_RPC_RESP_DATA) return false; // Prevent sizing issues diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c index a711ef85f0..bcc0261417 100644 --- a/quantum/split_common/transport.c +++ b/quantum/split_common/transport.c @@ -115,4 +115,4 @@ bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { return transactions_master(master_matrix, slave_matrix); } -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 +void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { transactions_slave(master_matrix, slave_matrix); } -- cgit v1.2.3