zephyr/drivers/serial/uart_async_rx.c
Krzysztof Chruściński 165bd2a780 drivers: serial: Add uart_async_rx module
Add module which can handle RX path of UART asynchronous RX API. Module
can be utilized in cases where processing of received data is not performed
directly in the event context but it is delayed. At least two use cases
has been identified (shell async UART backend, asynchronous to interrupt
driven adaptation layer).

Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no>
2023-11-14 09:21:46 +01:00

135 lines
3.4 KiB
C

/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/serial/uart_async_rx.h>
static uint8_t inc(struct uart_async_rx *rx_data, uint8_t val)
{
return (val + 1) & (rx_data->config->buf_cnt - 1);
}
static struct uart_async_rx_buf *get_buf(struct uart_async_rx *rx_data, uint8_t idx)
{
uint8_t *p = rx_data->config->buffer;
p += idx * (rx_data->buf_len + sizeof(struct uart_async_rx_buf));
return (struct uart_async_rx_buf *)p;
}
uint8_t *uart_async_rx_buf_req(struct uart_async_rx *rx_data)
{
uint8_t *data = NULL;
if (rx_data->free_buf_cnt != 0) {
struct uart_async_rx_buf *buf = get_buf(rx_data, rx_data->drv_buf_idx);
data = buf->buffer;
rx_data->drv_buf_idx = inc(rx_data, rx_data->drv_buf_idx);
atomic_dec(&rx_data->free_buf_cnt);
}
return data;
}
void uart_async_rx_on_rdy(struct uart_async_rx *rx_data, uint8_t *buffer, size_t length)
{
/* Cannot use CONTAINER_OF because validation fails due to type mismatch:
* uint8_t * vs uint8_t [].
*/
struct uart_async_rx_buf *rx_buf =
(struct uart_async_rx_buf *)(buffer - offsetof(struct uart_async_rx_buf, buffer));
rx_buf->wr_idx += length;
__ASSERT_NO_MSG(rx_buf->wr_idx <= rx_data->buf_len);
atomic_add(&rx_data->pending_bytes, length);
}
static void buf_reset(struct uart_async_rx_buf *buf)
{
buf->rd_idx = 0;
buf->wr_idx = 0;
buf->completed = 0;
}
static void usr_rx_buf_release(struct uart_async_rx *rx_data, struct uart_async_rx_buf *buf)
{
buf_reset(buf);
rx_data->rd_buf_idx = inc(rx_data, rx_data->rd_buf_idx);
atomic_inc(&rx_data->free_buf_cnt);
__ASSERT_NO_MSG(rx_data->free_buf_cnt <= rx_data->config->buf_cnt);
}
void uart_async_rx_on_buf_rel(struct uart_async_rx *rx_data, uint8_t *buffer)
{
/* Cannot use CONTAINER_OF because validation fails due to type mismatch:
* uint8_t * vs uint8_t [].
*/
struct uart_async_rx_buf *rx_buf =
(struct uart_async_rx_buf *)(buffer - offsetof(struct uart_async_rx_buf, buffer));
rx_buf->completed = 1;
rx_data->wr_buf_idx = inc(rx_data, rx_data->wr_buf_idx);
}
size_t uart_async_rx_data_claim(struct uart_async_rx *rx_data, uint8_t **data, size_t length)
{
struct uart_async_rx_buf *buf;
int rem;
if ((rx_data->pending_bytes == 0) || (length == 0)) {
return 0;
}
do {
buf = get_buf(rx_data, rx_data->rd_buf_idx);
if ((buf->rd_idx == buf->wr_idx) && (buf->completed == 1)) {
usr_rx_buf_release(rx_data, buf);
} else {
break;
}
} while (1);
*data = &buf->buffer[buf->rd_idx];
rem = buf->wr_idx - buf->rd_idx;
return MIN(length, rem);
}
void uart_async_rx_data_consume(struct uart_async_rx *rx_data, size_t length)
{
struct uart_async_rx_buf *buf = get_buf(rx_data, rx_data->rd_buf_idx);
buf->rd_idx += length;
atomic_sub(&rx_data->pending_bytes, length);
__ASSERT_NO_MSG(buf->rd_idx <= buf->wr_idx);
}
void uart_async_rx_reset(struct uart_async_rx *rx_data)
{
rx_data->free_buf_cnt = rx_data->config->buf_cnt;
for (uint8_t i = 0; i < rx_data->config->buf_cnt; i++) {
buf_reset(get_buf(rx_data, i));
}
}
int uart_async_rx_init(struct uart_async_rx *rx_data,
const struct uart_async_rx_config *config)
{
__ASSERT_NO_MSG(config->length / config->buf_cnt <= UINT8_MAX);
memset(rx_data, 0, sizeof(*rx_data));
rx_data->config = config;
rx_data->buf_len = (config->length / config->buf_cnt) - UART_ASYNC_RX_BUF_OVERHEAD;
uart_async_rx_reset(rx_data);
return 0;
}