1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-24 14:03:55 +01:00

Merge pull request #14285 from fjmolinas/pr_uart_nb_race

sam0/stm32: fix possible uart_nonblocking deadlock
This commit is contained in:
Alexandre Abadie 2020-06-17 12:14:25 +02:00 committed by GitHub
commit 07c78efc83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 53 additions and 18 deletions

View File

@ -194,8 +194,8 @@ typedef enum {
/**
* @brief Size of the UART TX buffer for non-blocking mode.
*/
#ifndef SAM0_UART_TXBUF_SIZE
#define SAM0_UART_TXBUF_SIZE (64)
#ifndef UART_TXBUF_SIZE
#define UART_TXBUF_SIZE (64)
#endif
/**

View File

@ -41,7 +41,7 @@
#ifdef MODULE_PERIPH_UART_NONBLOCKING
#include "tsrb.h"
static tsrb_t uart_tx_rb[UART_NUMOF];
static uint8_t uart_tx_rb_buf[UART_NUMOF][SAM0_UART_TXBUF_SIZE];
static uint8_t uart_tx_rb_buf[UART_NUMOF][UART_TXBUF_SIZE];
#endif
static uart_isr_ctx_t uart_ctx[UART_NUMOF];
@ -68,7 +68,7 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
#ifdef MODULE_PERIPH_UART_NONBLOCKING
/* set up the TX buffer */
tsrb_init(&uart_tx_rb[uart], uart_tx_rb_buf[uart], SAM0_UART_TXBUF_SIZE);
tsrb_init(&uart_tx_rb[uart], uart_tx_rb_buf[uart], UART_TXBUF_SIZE);
#endif
/* configure pins */
@ -176,7 +176,17 @@ void uart_write(uart_t uart, const uint8_t *data, size_t len)
{
#ifdef MODULE_PERIPH_UART_NONBLOCKING
for (const void* end = data + len; data != end; ++data) {
while (tsrb_add_one(&uart_tx_rb[uart], *data) < 0) {}
if (irq_is_in() || __get_PRIMASK()) {
/* if ring buffer is full free up a spot */
if (tsrb_full(&uart_tx_rb[uart])) {
while (!dev(uart)->INTFLAG.bit.DRE) {}
dev(uart)->DATA.reg = tsrb_get_one(&uart_tx_rb[uart]);
}
tsrb_add_one(&uart_tx_rb[uart], *data);
}
else {
while (tsrb_add_one(&uart_tx_rb[uart], *data) < 0) {}
}
dev(uart)->INTENSET.reg = SERCOM_USART_INTENSET_DRE;
}
#else

View File

@ -550,8 +550,8 @@ typedef enum {
/**
* @brief Size of the UART TX buffer for non-blocking mode.
*/
#ifndef STM32_UART_TXBUF_SIZE
#define STM32_UART_TXBUF_SIZE (64)
#ifndef UART_TXBUF_SIZE
#define UART_TXBUF_SIZE (64)
#endif
#ifndef DOXYGEN

View File

@ -61,7 +61,7 @@
* @brief Allocate for tx ring buffers
*/
static tsrb_t uart_tx_rb[UART_NUMOF];
static uint8_t uart_tx_rb_buf[UART_NUMOF][STM32_UART_TXBUF_SIZE];
static uint8_t uart_tx_rb_buf[UART_NUMOF][UART_TXBUF_SIZE];
#endif
/**
@ -168,7 +168,7 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
#ifdef MODULE_PERIPH_UART_NONBLOCKING
/* set up the TX buffer */
tsrb_init(&uart_tx_rb[uart], uart_tx_rb_buf[uart], STM32_UART_TXBUF_SIZE);
tsrb_init(&uart_tx_rb[uart], uart_tx_rb_buf[uart], UART_TXBUF_SIZE);
#endif
uart_init_pins(uart, rx_cb);
@ -207,6 +207,10 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
dev(uart)->CR1 = (USART_CR1_UE | USART_CR1_TE);
}
#ifdef MODULE_PERIPH_UART_NONBLOCKING
NVIC_EnableIRQ(uart_config[uart].irqn);
#endif
#ifdef MODULE_PERIPH_UART_HW_FC
if (uart_config[uart].cts_pin != GPIO_UNDEF) {
dev(uart)->CR3 |= USART_CR3_CTSE;
@ -318,22 +322,18 @@ static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate)
#endif /* MODULE_PERIPH_LPUART */
#endif /* STM32L0 || STM32L4 || STM32WB */
#ifndef MODULE_PERIPH_UART_NONBLOCKING
static inline void send_byte(uart_t uart, uint8_t byte)
{
while (!(dev(uart)->ISR_REG & ISR_TXE)) {}
dev(uart)->TDR_REG = byte;
}
#endif
#ifndef MODULE_PERIPH_UART_NONBLOCKING
static inline void wait_for_tx_complete(uart_t uart)
{
#ifdef MODULE_PERIPH_UART_NONBLOCKING
(void) uart;
#else
while (!(dev(uart)->ISR_REG & ISR_TC)) {}
#endif
}
#endif
void uart_write(uart_t uart, const uint8_t *data, size_t len)
{
@ -383,17 +383,28 @@ void uart_write(uart_t uart, const uint8_t *data, size_t len)
return;
}
#endif
for (size_t i = 0; i < len; i++) {
#ifdef MODULE_PERIPH_UART_NONBLOCKING
for (size_t i = 0; i < len; i++) {
dev(uart)->CR1 |= (USART_CR1_TCIE);
while (tsrb_add_one(&uart_tx_rb[uart], data[i]) < 0) {}
if (irq_is_in() || __get_PRIMASK()) {
/* if ring buffer is full free up a spot */
if (tsrb_full(&uart_tx_rb[uart])) {
send_byte(uart, tsrb_get_one(&uart_tx_rb[uart]));
}
tsrb_add_one(&uart_tx_rb[uart], data[i]);
}
else {
while (tsrb_add_one(&uart_tx_rb[uart], data[i]) < 0) {}
}
}
#else
for (size_t i = 0; i < len; i++) {
send_byte(uart, data[i]);
#endif
}
/* make sure the function is synchronous by waiting for the transfer to
* finish */
wait_for_tx_complete(uart);
#endif
}
void uart_poweron(uart_t uart)

View File

@ -30,8 +30,21 @@ static inline uint32_t puts_delay(const char* str)
return LINE_DELAY_MS * 1000;
}
static void _irq_disabled_print(void)
{
unsigned state = irq_disable();
/* fill the transmit buffer */
for (uint8_t i = 0; i < UART_TXBUF_SIZE; i++) {
printf(" ");
}
puts("\nputs with disabled interrupts and a full transmit buffer");
irq_restore(state);
}
int main(void)
{
_irq_disabled_print();
uint32_t total_us = 0;
xtimer_ticks32_t counter = xtimer_now();

View File

@ -14,6 +14,7 @@ PRECISION = 1.002
def testfunc(child):
child.expect_exact("puts with disabled interrupts and a full transmit buffer")
child.expect(r'== printed in (\d+)/(\d+) µs ==')
time_actual = int(child.match.group(1))
time_expect = int(child.match.group(2))