From 117bff17ba89a70dd85163b499c262b879f52afd Mon Sep 17 00:00:00 2001
From: Stefan Kerkmann <karlk90@pm.me>
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 <nick@tzarc.org>
---
 drivers/chibios/serial_usart.c        | 337 +++++++++++++++++++++++-----------
 drivers/chibios/serial_usart.h        |  40 +++-
 drivers/chibios/serial_usart_duplex.c | 261 --------------------------
 3 files changed, 262 insertions(+), 376 deletions(-)
 delete mode 100644 drivers/chibios/serial_usart_duplex.c

(limited to 'drivers/chibios')

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 <ch.h>
 #include <hal.h>
 
-#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 <http://www.gnu.org/licenses/>.
- */
-
-#include "serial_usart.h"
-
-#include <stdatomic.h>
-
-#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;
-}
-- 
cgit v1.2.3