2022-06-17 22:06:44 +02:00
// Copyright 2021 QMK
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
2020-05-21 19:00:21 +02:00
2021-05-27 05:37:54 +02:00
# include "serial_usart.h"
2022-06-18 00:04:17 +02:00
# include "serial_protocol.h"
2022-04-19 12:56:16 +02:00
# include "synchronization_util.h"
2020-05-21 19:00:21 +02:00
2021-07-02 00:24:08 +02:00
# if defined(SERIAL_USART_CONFIG)
2022-06-17 22:06:44 +02:00
static QMKSerialConfig serial_config = SERIAL_USART_CONFIG ;
2021-07-02 00:24:08 +02:00
# else
2022-06-17 22:06:44 +02:00
static QMKSerialConfig serial_config = {
# if HAL_USE_SERIAL
. speed = ( SERIAL_USART_SPEED ) , /* baudrate - mandatory */
# else
. baud = ( SERIAL_USART_SPEED ) , /* baudrate - mandatory */
# endif
2021-07-02 00:24:08 +02:00
. 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
2022-06-17 22:06:44 +02:00
. cr3 = ( SERIAL_USART_CR3 )
2020-05-21 19:00:21 +02:00
# endif
2021-07-02 00:24:08 +02:00
} ;
2020-05-21 19:00:21 +02:00
# endif
2022-06-17 22:06:44 +02:00
static QMKSerialDriver * serial_driver = ( QMKSerialDriver * ) & SERIAL_USART_DRIVER ;
2020-05-21 19:00:21 +02:00
2022-06-17 22:06:44 +02:00
# if HAL_USE_SERIAL
/**
* @ brief SERIAL Driver startup routine .
*/
static inline void usart_driver_start ( void ) {
sdStart ( serial_driver , & serial_config ) ;
}
2020-05-21 19:00:21 +02:00
2022-06-18 00:04:17 +02:00
inline void serial_transport_driver_clear ( void ) {
2021-07-02 00:24:08 +02:00
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 ( ) ;
}
2020-05-21 19:00:21 +02:00
}
2022-06-17 22:06:44 +02:00
# elif HAL_USE_SIO
void clear_rx_evt_cb ( SIODriver * siop ) {
osalSysLockFromISR ( ) ;
/* If errors occured during transactions this callback is invoked. We just
* clear the error sources and move on . We rely on the fact that we check
* for the success of the transaction by comparing the received / send bytes
* with the actual received / send bytes in the send / receive functions . */
sioGetAndClearEventsI ( serial_driver ) ;
osalSysUnlockFromISR ( ) ;
}
static const SIOOperation serial_usart_operation = { . rx_cb = NULL , . rx_idle_cb = NULL , . tx_cb = NULL , . tx_end_cb = NULL , . rx_evt_cb = & clear_rx_evt_cb } ;
/**
* @ brief SIO Driver startup routine .
*/
static inline void usart_driver_start ( void ) {
sioStart ( serial_driver , & serial_config ) ;
sioStartOperation ( serial_driver , & serial_usart_operation ) ;
}
2022-06-18 00:04:17 +02:00
inline void serial_transport_driver_clear ( void ) {
2022-06-17 22:06:44 +02:00
osalSysLock ( ) ;
while ( ! sioIsRXEmptyX ( serial_driver ) ) {
( void ) sioGetX ( serial_driver ) ;
}
osalSysUnlock ( ) ;
}
# else
# error Either the SERIAL or SIO driver has to be activated to use the usart driver for split keyboards.
# endif
2022-06-18 00:04:17 +02:00
inline bool serial_transport_send ( const uint8_t * source , const size_t size ) {
2022-06-17 22:06:44 +02:00
bool success = ( size_t ) chnWriteTimeout ( serial_driver , source , size , TIME_MS2I ( SERIAL_USART_TIMEOUT ) ) = = size ;
2021-07-02 00:24:08 +02:00
# if !defined(SERIAL_USART_FULL_DUPLEX)
2022-06-17 22:06:44 +02:00
/* Half duplex fills the input queue with the data we wrote - just throw it away. */
if ( likely ( success ) ) {
size_t bytes_left = size ;
# if HAL_USE_SERIAL
/* The SERIAL driver uses large soft FIFOs that are filled from an IRQ
* context , so there is a delay between receiving the data and it
* becoming actually available , therefore we have to apply a timeout
* mechanism . Under the right circumstances ( e . g . bad cables paired with
* high baud rates ) less bytes can be present in the input queue as
* well . */
uint8_t dump [ 64 ] ;
while ( unlikely ( bytes_left > = 64 ) ) {
2022-06-18 00:04:17 +02:00
if ( unlikely ( ! serial_transport_receive ( dump , 64 ) ) ) {
2022-06-17 22:06:44 +02:00
return false ;
}
bytes_left - = 64 ;
}
2022-06-18 00:04:17 +02:00
return serial_transport_receive ( dump , bytes_left ) ;
2022-06-17 22:06:44 +02:00
# else
/* The SIO driver directly accesses the hardware FIFOs of the USART
* peripheral . As these are limited in depth , the RX FIFO might have been
* overflowed by a large that we just send . Therefore we attempt to read
* back all the data we send or until the FIFO runs empty in case it
* overflowed and data was truncated . */
if ( unlikely ( sioSynchronizeTXEnd ( serial_driver , TIME_MS2I ( SERIAL_USART_TIMEOUT ) ) < MSG_OK ) ) {
return false ;
}
osalSysLock ( ) ;
while ( bytes_left > 0 & & ! sioIsRXEmptyX ( serial_driver ) ) {
( void ) sioGetX ( serial_driver ) ;
bytes_left - - ;
}
osalSysUnlock ( ) ;
# endif
2021-07-02 00:24:08 +02:00
}
# endif
2020-05-21 19:00:21 +02:00
2021-07-02 00:24:08 +02:00
return success ;
2020-05-21 19:00:21 +02:00
}
2022-06-18 00:04:17 +02:00
inline bool serial_transport_receive ( uint8_t * destination , const size_t size ) {
2022-06-17 22:06:44 +02:00
bool success = ( size_t ) chnReadTimeout ( serial_driver , destination , size , TIME_MS2I ( SERIAL_USART_TIMEOUT ) ) = = size ;
return success ;
}
2022-06-18 00:04:17 +02:00
inline bool serial_transport_receive_blocking ( uint8_t * destination , const size_t size ) {
2022-06-17 22:06:44 +02:00
bool success = ( size_t ) chnRead ( serial_driver , destination , size ) = = size ;
2021-07-02 00:24:08 +02:00
return success ;
2020-05-21 19:00:21 +02:00
}
2021-07-02 00:24:08 +02:00
# if !defined(SERIAL_USART_FULL_DUPLEX)
2020-05-21 19:00:21 +02:00
2021-07-02 00:24:08 +02:00
/**
* @ brief Initiate pins for USART peripheral . Half - duplex configuration .
2020-05-21 19:00:21 +02:00
*/
2021-07-02 00:24:08 +02:00
__attribute__ ( ( weak ) ) void usart_init ( void ) {
# if defined(MCU_STM32)
# if defined(USE_GPIOV1)
2021-09-15 03:19:51 +02:00
palSetLineMode ( SERIAL_USART_TX_PIN , PAL_MODE_ALTERNATE_OPENDRAIN ) ;
2021-07-02 00:24:08 +02:00
# else
2021-09-16 00:18:58 +02:00
palSetLineMode ( SERIAL_USART_TX_PIN , PAL_MODE_ALTERNATE ( SERIAL_USART_TX_PAL_MODE ) | PAL_OUTPUT_TYPE_OPENDRAIN ) ;
2021-07-02 00:24:08 +02:00
# endif
2020-05-21 19:00:21 +02:00
2021-07-02 00:24:08 +02:00
# 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
2020-05-21 19:00:21 +02:00
}
# else
2021-05-27 05:37:54 +02:00
2021-07-02 00:24:08 +02:00
/**
* @ brief Initiate pins for USART peripheral . Full - duplex configuration .
*/
__attribute__ ( ( weak ) ) void usart_init ( void ) {
# if defined(MCU_STM32)
# if defined(USE_GPIOV1)
2021-09-15 03:19:51 +02:00
palSetLineMode ( SERIAL_USART_TX_PIN , PAL_MODE_ALTERNATE_PUSHPULL ) ;
2021-07-02 00:24:08 +02:00
palSetLineMode ( SERIAL_USART_RX_PIN , PAL_MODE_INPUT ) ;
# else
2021-09-16 00:18:58 +02:00
palSetLineMode ( SERIAL_USART_TX_PIN , PAL_MODE_ALTERNATE ( SERIAL_USART_TX_PAL_MODE ) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST ) ;
palSetLineMode ( SERIAL_USART_RX_PIN , PAL_MODE_ALTERNATE ( SERIAL_USART_RX_PAL_MODE ) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST ) ;
2021-07-02 00:24:08 +02:00
# endif
# if defined(USART_REMAP)
2021-05-27 05:37:54 +02:00
USART_REMAP ;
2021-07-02 00:24:08 +02:00
# 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
}
2021-05-27 05:37:54 +02:00
# endif
2021-07-02 00:24:08 +02:00
/**
* @ brief Overridable master specific initializations .
*/
2022-06-17 22:06:44 +02:00
__attribute__ ( ( weak , nonnull ) ) void usart_master_init ( QMKSerialDriver * * driver ) {
2021-07-02 00:24:08 +02:00
( void ) driver ;
usart_init ( ) ;
2020-05-21 19:00:21 +02:00
}
2021-07-02 00:24:08 +02:00
/**
* @ brief Overridable slave specific initializations .
*/
2022-06-17 22:06:44 +02:00
__attribute__ ( ( weak , nonnull ) ) void usart_slave_init ( QMKSerialDriver * * driver ) {
2021-07-02 00:24:08 +02:00
( void ) driver ;
2020-05-21 19:00:21 +02:00
usart_init ( ) ;
2021-07-02 00:24:08 +02:00
}
2022-06-18 00:04:17 +02:00
void serial_transport_driver_slave_init ( void ) {
2021-07-02 00:24:08 +02:00
usart_slave_init ( & serial_driver ) ;
2022-06-17 22:06:44 +02:00
usart_driver_start ( ) ;
2021-07-02 00:24:08 +02:00
}
2022-06-18 00:04:17 +02:00
void serial_transport_driver_master_init ( void ) {
2021-07-02 00:24:08 +02:00
usart_master_init ( & serial_driver ) ;
# if defined(MCU_STM32) && defined(SERIAL_USART_PIN_SWAP)
2022-02-12 19:29:31 +01:00
serial_config . cr2 | = USART_CR2_SWAP ; // master has swapped TX/RX pins
2021-07-02 00:24:08 +02:00
# endif
2022-06-17 22:06:44 +02:00
usart_driver_start ( ) ;
2020-05-21 19:00:21 +02:00
}