8bc5111c27
Since there is only one consumer of the data stored in the buffers, it is enough to have one read index variable which can be stored in the data associated with the module and not in the buffer space (where there is a read index for each buffer). Additionally, we can safely assume that module works with small buffers so 127 byte limit is enough. Based on that assumption completed flag can be stored on a single byte together with write index. After this change, control data for each buffer takes 1 byte (3 bytes previously). Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no>
149 lines
3.8 KiB
C
149 lines
3.8 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->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_idx = 0;
|
|
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;
|
|
}
|
|
|
|
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);
|
|
/* Even though buffer is released in consume phase it is possible that
|
|
* it is required here as well (e.g. was not completed previously).
|
|
*/
|
|
if ((buf->completed == 1) && (rx_data->rd_idx == buf->wr_idx)) {
|
|
usr_rx_buf_release(rx_data, buf);
|
|
} else {
|
|
break;
|
|
}
|
|
} while (1);
|
|
|
|
*data = &buf->buffer[rx_data->rd_idx];
|
|
rem = buf->wr_idx - rx_data->rd_idx;
|
|
|
|
return MIN(length, rem);
|
|
}
|
|
|
|
bool 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);
|
|
|
|
rx_data->rd_idx += length;
|
|
/* Attempt to release the buffer if it is completed and all data is consumed. */
|
|
if ((buf->completed == 1) && (rx_data->rd_idx == buf->wr_idx)) {
|
|
usr_rx_buf_release(rx_data, buf);
|
|
}
|
|
|
|
atomic_sub(&rx_data->pending_bytes, length);
|
|
|
|
__ASSERT_NO_MSG(rx_data->rd_idx <= buf->wr_idx);
|
|
|
|
return rx_data->free_buf_cnt > 0;
|
|
}
|
|
|
|
void uart_async_rx_reset(struct uart_async_rx *rx_data)
|
|
{
|
|
rx_data->free_buf_cnt = rx_data->config->buf_cnt;
|
|
rx_data->rd_idx = 0;
|
|
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;
|
|
|
|
if (rx_data->buf_len >= BIT(7)) {
|
|
return -EINVAL;
|
|
}
|
|
uart_async_rx_reset(rx_data);
|
|
|
|
return 0;
|
|
}
|