blob: 7c51a6cd2519b6f2909e01a5b54ca9d987e9f84a [file] [log] [blame]
/*
* 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;
}