| /* |
| * 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; |
| rx_data->rd_buf_idx = 0; |
| rx_data->drv_buf_idx = 0; |
| rx_data->pending_bytes = 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->buf_cnt > 0); |
| __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; |
| } |