Browse Source

modem: backend: uart: Add hw-flow-control for UART

Add Asynchronous UART implementation, which does not drop data
when automatic hardware-flow-control is set in the device tree.

With automatic hardware flow control, the CTS pin will be
automatically deactivated when there are no more asynchronous
UART RX buffers available. After buffer space becomes available,
and UART RX is restarted, the CTS pin will be activated.

Signed-off-by: Markus Lassila <markus.lassila@nordicsemi.no>
pull/89495/head
Markus Lassila 4 months ago committed by Benjamin Cabé
parent
commit
10bd2de235
  1. 35
      include/zephyr/modem/backend/uart.h
  2. 5
      subsys/modem/backends/CMakeLists.txt
  3. 15
      subsys/modem/backends/Kconfig
  4. 4
      subsys/modem/backends/modem_backend_uart.c
  5. 63
      subsys/modem/backends/modem_backend_uart_async.c
  6. 2
      subsys/modem/backends/modem_backend_uart_async.h
  7. 439
      subsys/modem/backends/modem_backend_uart_async_hwfc.c
  8. 6
      subsys/modem/modem_cmux.c
  9. 15
      tests/subsys/modem/backends/uart/testcase.yaml

35
include/zephyr/modem/backend/uart.h

@ -30,17 +30,42 @@ struct modem_backend_uart_isr { @@ -30,17 +30,42 @@ struct modem_backend_uart_isr {
uint32_t transmit_buf_put_limit;
};
struct modem_backend_uart_async_common {
uint8_t *transmit_buf;
uint32_t transmit_buf_size;
struct k_work rx_disabled_work;
atomic_t state;
};
#ifdef CONFIG_MODEM_BACKEND_UART_ASYNC_HWFC
struct rx_queue_event {
uint8_t *buf;
size_t len;
};
struct modem_backend_uart_async {
struct modem_backend_uart_async_common common;
struct k_mem_slab rx_slab;
struct k_msgq rx_queue;
struct rx_queue_event rx_event;
struct rx_queue_event rx_queue_buf[CONFIG_MODEM_BACKEND_UART_ASYNC_HWFC_BUFFER_COUNT];
uint32_t rx_buf_size;
uint8_t rx_buf_count;
};
#else
struct modem_backend_uart_async {
struct modem_backend_uart_async_common common;
uint8_t *receive_bufs[2];
uint32_t receive_buf_size;
struct ring_buf receive_rb;
struct k_spinlock receive_rb_lock;
uint8_t *transmit_buf;
uint32_t transmit_buf_size;
struct k_work rx_disabled_work;
atomic_t state;
};
#endif /* CONFIG_MODEM_BACKEND_UART_ASYNC_HWFC */
struct modem_backend_uart {
const struct device *uart;
struct modem_pipe pipe;
@ -60,7 +85,7 @@ struct modem_backend_uart { @@ -60,7 +85,7 @@ struct modem_backend_uart {
struct modem_backend_uart_config {
const struct device *uart;
uint8_t *receive_buf;
uint8_t *receive_buf __aligned(sizeof(uint32_t));
uint32_t receive_buf_size;
uint8_t *transmit_buf;
uint32_t transmit_buf_size;

5
subsys/modem/backends/CMakeLists.txt

@ -6,4 +6,7 @@ zephyr_library() @@ -6,4 +6,7 @@ zephyr_library()
zephyr_library_sources_ifdef(CONFIG_MODEM_BACKEND_TTY modem_backend_tty.c)
zephyr_library_sources_ifdef(CONFIG_MODEM_BACKEND_UART modem_backend_uart.c)
zephyr_library_sources_ifdef(CONFIG_MODEM_BACKEND_UART_ISR modem_backend_uart_isr.c)
zephyr_library_sources_ifdef(CONFIG_MODEM_BACKEND_UART_ASYNC modem_backend_uart_async.c)
if(CONFIG_MODEM_BACKEND_UART_ASYNC)
zephyr_library_sources_ifdef(CONFIG_MODEM_BACKEND_UART_ASYNC_HWFC modem_backend_uart_async_hwfc.c)
zephyr_library_sources_ifndef(CONFIG_MODEM_BACKEND_UART_ASYNC_HWFC modem_backend_uart_async.c)
endif()

15
subsys/modem/backends/Kconfig

@ -48,6 +48,19 @@ config MODEM_BACKEND_UART_ASYNC_RECEIVE_IDLE_TIMEOUT_MS @@ -48,6 +48,19 @@ config MODEM_BACKEND_UART_ASYNC_RECEIVE_IDLE_TIMEOUT_MS
int "Modem async UART receive idle timeout in milliseconds"
default 30
endif
config MODEM_BACKEND_UART_ASYNC_HWFC
bool "Hardware flow control (HWFC) for the modem async UART backend"
select EXPERIMENTAL
if MODEM_BACKEND_UART_ASYNC_HWFC
config MODEM_BACKEND_UART_ASYNC_HWFC_BUFFER_COUNT
int "Modem async UART HWFC buffer count"
range 2 4
default 3
endif # MODEM_BACKEND_UART_ASYNC_HWFC
endif # MODEM_BACKEND_UART_ASYNC
endif # MODEM_BACKEND_UART

4
subsys/modem/backends/modem_backend_uart.c

@ -45,7 +45,9 @@ struct modem_pipe *modem_backend_uart_init(struct modem_backend_uart *backend, @@ -45,7 +45,9 @@ struct modem_pipe *modem_backend_uart_init(struct modem_backend_uart *backend,
#ifdef CONFIG_MODEM_BACKEND_UART_ASYNC
if (modem_backend_uart_async_is_supported(backend)) {
modem_backend_uart_async_init(backend, config);
if (modem_backend_uart_async_init(backend, config)) {
return NULL;
}
return &backend->pipe;
}
#endif /* CONFIG_MODEM_BACKEND_UART_ASYNC */

63
subsys/modem/backends/modem_backend_uart_async.c

@ -22,13 +22,13 @@ enum { @@ -22,13 +22,13 @@ enum {
static bool modem_backend_uart_async_is_uart_stopped(struct modem_backend_uart *backend)
{
if (!atomic_test_bit(&backend->async.state,
if (!atomic_test_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_TRANSMITTING_BIT) &&
!atomic_test_bit(&backend->async.state,
!atomic_test_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RECEIVING_BIT) &&
!atomic_test_bit(&backend->async.state,
!atomic_test_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT) &&
!atomic_test_bit(&backend->async.state,
!atomic_test_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF1_USED_BIT)) {
return true;
}
@ -38,7 +38,7 @@ static bool modem_backend_uart_async_is_uart_stopped(struct modem_backend_uart * @@ -38,7 +38,7 @@ static bool modem_backend_uart_async_is_uart_stopped(struct modem_backend_uart *
static bool modem_backend_uart_async_is_open(struct modem_backend_uart *backend)
{
return atomic_test_bit(&backend->async.state,
return atomic_test_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT);
}
@ -56,7 +56,7 @@ static void modem_backend_uart_async_event_handler(const struct device *dev, @@ -56,7 +56,7 @@ static void modem_backend_uart_async_event_handler(const struct device *dev,
switch (evt->type) {
case UART_TX_DONE:
atomic_clear_bit(&backend->async.state,
atomic_clear_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_TRANSMITTING_BIT);
k_work_submit(&backend->transmit_idle_work);
break;
@ -65,14 +65,14 @@ static void modem_backend_uart_async_event_handler(const struct device *dev, @@ -65,14 +65,14 @@ static void modem_backend_uart_async_event_handler(const struct device *dev,
if (modem_backend_uart_async_is_open(backend)) {
LOG_WRN("Transmit aborted (%zu sent)", evt->data.tx.len);
}
atomic_clear_bit(&backend->async.state,
atomic_clear_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_TRANSMITTING_BIT);
k_work_submit(&backend->transmit_idle_work);
break;
case UART_RX_BUF_REQUEST:
if (!atomic_test_and_set_bit(&backend->async.state,
if (!atomic_test_and_set_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT)) {
uart_rx_buf_rsp(backend->uart, backend->async.receive_bufs[0],
backend->async.receive_buf_size);
@ -80,7 +80,7 @@ static void modem_backend_uart_async_event_handler(const struct device *dev, @@ -80,7 +80,7 @@ static void modem_backend_uart_async_event_handler(const struct device *dev,
break;
}
if (!atomic_test_and_set_bit(&backend->async.state,
if (!atomic_test_and_set_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF1_USED_BIT)) {
uart_rx_buf_rsp(backend->uart, backend->async.receive_bufs[1],
backend->async.receive_buf_size);
@ -93,14 +93,14 @@ static void modem_backend_uart_async_event_handler(const struct device *dev, @@ -93,14 +93,14 @@ static void modem_backend_uart_async_event_handler(const struct device *dev,
case UART_RX_BUF_RELEASED:
if (evt->data.rx_buf.buf == backend->async.receive_bufs[0]) {
atomic_clear_bit(&backend->async.state,
atomic_clear_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT);
break;
}
if (evt->data.rx_buf.buf == backend->async.receive_bufs[1]) {
atomic_clear_bit(&backend->async.state,
atomic_clear_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF1_USED_BIT);
break;
@ -131,7 +131,7 @@ static void modem_backend_uart_async_event_handler(const struct device *dev, @@ -131,7 +131,7 @@ static void modem_backend_uart_async_event_handler(const struct device *dev,
break;
case UART_RX_DISABLED:
atomic_clear_bit(&backend->async.state,
atomic_clear_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RECEIVING_BIT);
break;
@ -144,7 +144,7 @@ static void modem_backend_uart_async_event_handler(const struct device *dev, @@ -144,7 +144,7 @@ static void modem_backend_uart_async_event_handler(const struct device *dev,
}
if (modem_backend_uart_async_is_uart_stopped(backend)) {
k_work_submit(&backend->async.rx_disabled_work);
k_work_submit(&backend->async.common.rx_disabled_work);
}
}
@ -153,12 +153,13 @@ static int modem_backend_uart_async_open(void *data) @@ -153,12 +153,13 @@ static int modem_backend_uart_async_open(void *data)
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
int ret;
atomic_clear(&backend->async.state);
atomic_clear(&backend->async.common.state);
ring_buf_reset(&backend->async.receive_rb);
atomic_set_bit(&backend->async.state, MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT);
atomic_set_bit(&backend->async.state, MODEM_BACKEND_UART_ASYNC_STATE_RECEIVING_BIT);
atomic_set_bit(&backend->async.state, MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT);
atomic_set_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT);
atomic_set_bit(&backend->async.common.state, MODEM_BACKEND_UART_ASYNC_STATE_RECEIVING_BIT);
atomic_set_bit(&backend->async.common.state, MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT);
/* Receive buffers are used internally by UART, receive ring buffer is
* used to store received data.
@ -167,7 +168,7 @@ static int modem_backend_uart_async_open(void *data) @@ -167,7 +168,7 @@ static int modem_backend_uart_async_open(void *data)
backend->async.receive_buf_size,
CONFIG_MODEM_BACKEND_UART_ASYNC_RECEIVE_IDLE_TIMEOUT_MS * 1000L);
if (ret < 0) {
atomic_clear(&backend->async.state);
atomic_clear(&backend->async.common.state);
return ret;
}
@ -197,7 +198,7 @@ static void advertise_receive_buf_stats(struct modem_backend_uart *backend) @@ -197,7 +198,7 @@ static void advertise_receive_buf_stats(struct modem_backend_uart *backend)
static uint32_t get_transmit_buf_size(struct modem_backend_uart *backend)
{
return backend->async.transmit_buf_size;
return backend->async.common.transmit_buf_size;
}
static int modem_backend_uart_async_transmit(void *data, const uint8_t *buf, size_t size)
@ -207,7 +208,7 @@ static int modem_backend_uart_async_transmit(void *data, const uint8_t *buf, siz @@ -207,7 +208,7 @@ static int modem_backend_uart_async_transmit(void *data, const uint8_t *buf, siz
uint32_t bytes_to_transmit;
int ret;
transmitting = atomic_test_and_set_bit(&backend->async.state,
transmitting = atomic_test_and_set_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_TRANSMITTING_BIT);
if (transmitting) {
return 0;
@ -217,9 +218,9 @@ static int modem_backend_uart_async_transmit(void *data, const uint8_t *buf, siz @@ -217,9 +218,9 @@ static int modem_backend_uart_async_transmit(void *data, const uint8_t *buf, siz
bytes_to_transmit = MIN(size, get_transmit_buf_size(backend));
/* Copy buf to transmit buffer which is passed to UART */
memcpy(backend->async.transmit_buf, buf, bytes_to_transmit);
memcpy(backend->async.common.transmit_buf, buf, bytes_to_transmit);
ret = uart_tx(backend->uart, backend->async.transmit_buf, bytes_to_transmit,
ret = uart_tx(backend->uart, backend->async.common.transmit_buf, bytes_to_transmit,
CONFIG_MODEM_BACKEND_UART_ASYNC_TRANSMIT_TIMEOUT_MS * 1000L);
#if CONFIG_MODEM_STATS
@ -263,7 +264,7 @@ static int modem_backend_uart_async_close(void *data) @@ -263,7 +264,7 @@ static int modem_backend_uart_async_close(void *data)
{
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
atomic_clear_bit(&backend->async.state, MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT);
atomic_clear_bit(&backend->async.common.state, MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT);
uart_tx_abort(backend->uart);
uart_rx_disable(backend->uart);
return 0;
@ -284,8 +285,11 @@ bool modem_backend_uart_async_is_supported(struct modem_backend_uart *backend) @@ -284,8 +285,11 @@ bool modem_backend_uart_async_is_supported(struct modem_backend_uart *backend)
static void modem_backend_uart_async_notify_closed(struct k_work *item)
{
struct modem_backend_uart_async_common *common =
CONTAINER_OF(item, struct modem_backend_uart_async_common, rx_disabled_work);
struct modem_backend_uart_async *async =
CONTAINER_OF(item, struct modem_backend_uart_async, rx_disabled_work);
CONTAINER_OF(common, struct modem_backend_uart_async, common);
struct modem_backend_uart *backend =
CONTAINER_OF(async, struct modem_backend_uart, async);
@ -310,7 +314,7 @@ static void init_stats(struct modem_backend_uart *backend) @@ -310,7 +314,7 @@ static void init_stats(struct modem_backend_uart *backend)
}
#endif
void modem_backend_uart_async_init(struct modem_backend_uart *backend,
int modem_backend_uart_async_init(struct modem_backend_uart *backend,
const struct modem_backend_uart_config *config)
{
uint32_t receive_buf_size_quarter = config->receive_buf_size / 4;
@ -324,12 +328,15 @@ void modem_backend_uart_async_init(struct modem_backend_uart *backend, @@ -324,12 +328,15 @@ void modem_backend_uart_async_init(struct modem_backend_uart *backend,
ring_buf_init(&backend->async.receive_rb, (receive_buf_size_quarter * 2),
&config->receive_buf[receive_buf_size_quarter * 2]);
backend->async.transmit_buf = config->transmit_buf;
backend->async.transmit_buf_size = config->transmit_buf_size;
k_work_init(&backend->async.rx_disabled_work, modem_backend_uart_async_notify_closed);
backend->async.common.transmit_buf = config->transmit_buf;
backend->async.common.transmit_buf_size = config->transmit_buf_size;
k_work_init(&backend->async.common.rx_disabled_work,
modem_backend_uart_async_notify_closed);
modem_pipe_init(&backend->pipe, backend, &modem_backend_uart_async_api);
#if CONFIG_MODEM_STATS
init_stats(backend);
#endif
return 0;
}

2
subsys/modem/backends/modem_backend_uart_async.h

@ -15,7 +15,7 @@ extern "C" { @@ -15,7 +15,7 @@ extern "C" {
bool modem_backend_uart_async_is_supported(struct modem_backend_uart *backend);
void modem_backend_uart_async_init(struct modem_backend_uart *backend,
int modem_backend_uart_async_init(struct modem_backend_uart *backend,
const struct modem_backend_uart_config *config);
#ifdef __cplusplus

439
subsys/modem/backends/modem_backend_uart_async_hwfc.c

@ -0,0 +1,439 @@ @@ -0,0 +1,439 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "modem_backend_uart_async.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(modem_backend_uart_async_hwfc, CONFIG_MODEM_MODULES_LOG_LEVEL);
#include <zephyr/kernel.h>
#include <string.h>
struct rx_buf_t {
atomic_t ref_counter;
uint8_t buf[];
};
static inline struct rx_buf_t *block_start_get(struct modem_backend_uart_async *async, uint8_t *buf)
{
size_t block_num;
/* Find the correct block. */
block_num = (((size_t)buf - sizeof(struct rx_buf_t) - (size_t)async->rx_slab.buffer) /
async->rx_buf_size);
return (struct rx_buf_t *) &async->rx_slab.buffer[block_num * async->rx_buf_size];
}
static struct rx_buf_t *rx_buf_alloc(struct modem_backend_uart_async *async)
{
struct rx_buf_t *buf;
if (k_mem_slab_alloc(&async->rx_slab, (void **) &buf, K_NO_WAIT)) {
return NULL;
}
atomic_set(&buf->ref_counter, 1);
return buf;
}
static void rx_buf_ref(struct modem_backend_uart_async *async, void *buf)
{
atomic_inc(&(block_start_get(async, buf)->ref_counter));
}
static void rx_buf_unref(struct modem_backend_uart_async *async, void *buf)
{
struct rx_buf_t *uart_buf = block_start_get(async, buf);
atomic_t ref_counter = atomic_dec(&uart_buf->ref_counter);
if (ref_counter == 1) {
k_mem_slab_free(&async->rx_slab, (void *)uart_buf);
}
}
enum {
MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT,
MODEM_BACKEND_UART_ASYNC_STATE_TRANSMIT_BIT,
MODEM_BACKEND_UART_ASYNC_STATE_RECOVERY_BIT,
};
static int modem_backend_uart_async_hwfc_rx_enable(struct modem_backend_uart *backend)
{
int ret;
struct rx_buf_t *buf = rx_buf_alloc(&backend->async);
if (!buf) {
return -ENOMEM;
}
ret = uart_rx_enable(backend->uart, buf->buf,
backend->async.rx_buf_size - sizeof(struct rx_buf_t),
CONFIG_MODEM_BACKEND_UART_ASYNC_RECEIVE_IDLE_TIMEOUT_MS * 1000);
if (ret) {
rx_buf_unref(&backend->async, buf->buf);
return ret;
}
return 0;
}
static void modem_backend_uart_async_hwfc_rx_recovery(struct modem_backend_uart *backend)
{
int err;
if (!atomic_test_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RECOVERY_BIT)) {
return;
}
err = modem_backend_uart_async_hwfc_rx_enable(backend);
if (err) {
LOG_DBG("RX recovery failed: %d", err);
return;
}
if (!atomic_test_and_clear_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RECOVERY_BIT)) {
/* Closed during recovery. */
uart_rx_disable(backend->uart);
} else {
LOG_DBG("RX recovery success");
}
}
static bool modem_backend_uart_async_hwfc_is_uart_stopped(struct modem_backend_uart *backend)
{
if (!atomic_test_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT) &&
!atomic_test_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RECOVERY_BIT) &&
!atomic_test_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_TRANSMIT_BIT)) {
return true;
}
return false;
}
static bool modem_backend_uart_async_hwfc_is_open(struct modem_backend_uart *backend)
{
return atomic_test_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT);
}
static void modem_backend_uart_async_hwfc_event_handler(const struct device *dev,
struct uart_event *evt, void *user_data)
{
struct modem_backend_uart *backend = (struct modem_backend_uart *) user_data;
struct rx_queue_event rx_event;
int err;
switch (evt->type) {
case UART_TX_DONE:
atomic_clear_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_TRANSMIT_BIT);
k_work_submit(&backend->transmit_idle_work);
break;
case UART_TX_ABORTED:
if (modem_backend_uart_async_hwfc_is_open(backend)) {
LOG_WRN("Transmit aborted (%zu sent)", evt->data.tx.len);
}
atomic_clear_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_TRANSMIT_BIT);
k_work_submit(&backend->transmit_idle_work);
break;
case UART_RX_BUF_REQUEST:
struct rx_buf_t *buf = rx_buf_alloc(&backend->async);
if (!buf) {
LOG_DBG("No receive buffer, disabling RX");
break;
}
err = uart_rx_buf_rsp(backend->uart, buf->buf,
backend->async.rx_buf_size - sizeof(struct rx_buf_t));
if (err) {
LOG_ERR("uart_rx_buf_rsp: %d", err);
rx_buf_unref(&backend->async, buf->buf);
}
break;
case UART_RX_BUF_RELEASED:
if (evt->data.rx_buf.buf) {
rx_buf_unref(&backend->async, evt->data.rx_buf.buf);
}
break;
case UART_RX_RDY:
if (evt->data.rx.buf) {
rx_buf_ref(&backend->async, evt->data.rx.buf);
rx_event.buf = &evt->data.rx.buf[evt->data.rx.offset];
rx_event.len = evt->data.rx.len;
err = k_msgq_put(&backend->async.rx_queue, &rx_event, K_NO_WAIT);
if (err) {
LOG_WRN("RX queue overflow: %d (dropped %u)", err,
evt->data.rx.len);
rx_buf_unref(&backend->async, evt->data.rx.buf);
break;
}
k_work_schedule(&backend->receive_ready_work, K_NO_WAIT);
}
break;
case UART_RX_DISABLED:
if (atomic_test_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT)) {
if (!atomic_test_and_set_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RECOVERY_BIT)) {
k_work_schedule(&backend->receive_ready_work, K_NO_WAIT);
LOG_DBG("RX recovery started");
}
}
break;
case UART_RX_STOPPED:
LOG_WRN("Receive stopped for reasons: %u", (uint8_t)evt->data.rx_stop.reason);
break;
default:
break;
}
if (modem_backend_uart_async_hwfc_is_uart_stopped(backend)) {
k_work_submit(&backend->async.common.rx_disabled_work);
}
}
static int modem_backend_uart_async_hwfc_open(void *data)
{
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
struct rx_buf_t *buf = rx_buf_alloc(&backend->async);
int ret;
if (!buf) {
return -ENOMEM;
}
atomic_clear(&backend->async.common.state);
atomic_set_bit(&backend->async.common.state, MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT);
ret = uart_rx_enable(backend->uart, buf->buf,
backend->async.rx_buf_size - sizeof(struct rx_buf_t),
CONFIG_MODEM_BACKEND_UART_ASYNC_RECEIVE_IDLE_TIMEOUT_MS * 1000L);
if (ret < 0) {
rx_buf_unref(&backend->async, buf->buf);
atomic_clear(&backend->async.common.state);
return ret;
}
modem_pipe_notify_opened(&backend->pipe);
return 0;
}
#if CONFIG_MODEM_STATS
static uint32_t get_receive_buf_size(struct modem_backend_uart *backend)
{
return (backend->async.rx_buf_size - sizeof(struct rx_buf_t)) * backend->async.rx_buf_count;
}
static void advertise_transmit_buf_stats(struct modem_backend_uart *backend, uint32_t length)
{
modem_stats_buffer_advertise_length(&backend->transmit_buf_stats, length);
}
static void advertise_receive_buf_stats(struct modem_backend_uart *backend, uint32_t reserved)
{
modem_stats_buffer_advertise_length(&backend->receive_buf_stats, reserved);
}
#endif
static uint32_t get_transmit_buf_size(struct modem_backend_uart *backend)
{
return backend->async.common.transmit_buf_size;
}
static int modem_backend_uart_async_hwfc_transmit(void *data, const uint8_t *buf, size_t size)
{
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
bool transmitting;
uint32_t bytes_to_transmit;
int ret;
transmitting = atomic_test_and_set_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_TRANSMIT_BIT);
if (transmitting) {
return 0;
}
/* Determine amount of bytes to transmit */
bytes_to_transmit = MIN(size, get_transmit_buf_size(backend));
/* Copy buf to transmit buffer which is passed to UART */
memcpy(backend->async.common.transmit_buf, buf, bytes_to_transmit);
ret = uart_tx(backend->uart, backend->async.common.transmit_buf, bytes_to_transmit,
CONFIG_MODEM_BACKEND_UART_ASYNC_TRANSMIT_TIMEOUT_MS * 1000L);
#if CONFIG_MODEM_STATS
advertise_transmit_buf_stats(backend, bytes_to_transmit);
#endif
if (ret != 0) {
LOG_ERR("Failed to %s %u bytes. (%d)",
"start async transmit for", bytes_to_transmit, ret);
return ret;
}
return (int)bytes_to_transmit;
}
static int modem_backend_uart_async_hwfc_receive(void *data, uint8_t *buf, size_t size)
{
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
size_t received = 0;
size_t copy_size = 0;
#if CONFIG_MODEM_STATS
struct rx_queue_event rx_event;
size_t reserved = backend->async.rx_event.len;
for (int i = 0; i < k_msgq_num_used_get(&backend->async.rx_queue); i++) {
if (k_msgq_peek_at(&backend->async.rx_queue, &rx_event, i)) {
break;
}
reserved += rx_event.len;
}
advertise_receive_buf_stats(backend, reserved);
#endif
while (size > received) {
/* Keeping track of the async.rx_event allows us to receive less than what the event
* indicates.
*/
if (backend->async.rx_event.len == 0) {
if (k_msgq_get(&backend->async.rx_queue, &backend->async.rx_event,
K_NO_WAIT)) {
break;
}
}
copy_size = MIN(size - received, backend->async.rx_event.len);
memcpy(buf, backend->async.rx_event.buf, copy_size);
buf += copy_size;
received += copy_size;
backend->async.rx_event.buf += copy_size;
backend->async.rx_event.len -= copy_size;
if (backend->async.rx_event.len == 0) {
rx_buf_unref(&backend->async, backend->async.rx_event.buf);
}
}
if (backend->async.rx_event.len != 0 ||
k_msgq_num_used_get(&backend->async.rx_queue) != 0) {
k_work_schedule(&backend->receive_ready_work, K_NO_WAIT);
}
modem_backend_uart_async_hwfc_rx_recovery(backend);
return (int)received;
}
static int modem_backend_uart_async_hwfc_close(void *data)
{
struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
atomic_clear_bit(&backend->async.common.state, MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT);
uart_tx_abort(backend->uart);
if (!atomic_test_and_clear_bit(&backend->async.common.state,
MODEM_BACKEND_UART_ASYNC_STATE_RECOVERY_BIT)) {
/* Disable the RX, if recovery is not ongoing. */
uart_rx_disable(backend->uart);
}
return 0;
}
static const struct modem_pipe_api modem_backend_uart_async_api = {
.open = modem_backend_uart_async_hwfc_open,
.transmit = modem_backend_uart_async_hwfc_transmit,
.receive = modem_backend_uart_async_hwfc_receive,
.close = modem_backend_uart_async_hwfc_close,
};
bool modem_backend_uart_async_is_supported(struct modem_backend_uart *backend)
{
return uart_callback_set(backend->uart, modem_backend_uart_async_hwfc_event_handler,
backend) == 0;
}
static void modem_backend_uart_async_hwfc_notify_closed(struct k_work *item)
{
struct modem_backend_uart_async_common *common =
CONTAINER_OF(item, struct modem_backend_uart_async_common, rx_disabled_work);
struct modem_backend_uart_async *async =
CONTAINER_OF(common, struct modem_backend_uart_async, common);
struct modem_backend_uart *backend =
CONTAINER_OF(async, struct modem_backend_uart, async);
modem_pipe_notify_closed(&backend->pipe);
}
#if CONFIG_MODEM_STATS
static void init_stats(struct modem_backend_uart *backend)
{
char name[CONFIG_MODEM_STATS_BUFFER_NAME_SIZE];
uint32_t receive_buf_size;
uint32_t transmit_buf_size;
receive_buf_size = get_receive_buf_size(backend);
transmit_buf_size = get_transmit_buf_size(backend);
snprintk(name, sizeof(name), "%s_%s", backend->uart->name, "rx");
modem_stats_buffer_init(&backend->receive_buf_stats, name, receive_buf_size);
snprintk(name, sizeof(name), "%s_%s", backend->uart->name, "tx");
modem_stats_buffer_init(&backend->transmit_buf_stats, name, transmit_buf_size);
}
#endif
int modem_backend_uart_async_init(struct modem_backend_uart *backend,
const struct modem_backend_uart_config *config)
{
int32_t buf_size = (int32_t)config->receive_buf_size;
int err;
backend->async.rx_buf_count = CONFIG_MODEM_BACKEND_UART_ASYNC_HWFC_BUFFER_COUNT;
/* Make sure all the buffers will be aligned. */
buf_size -= (config->receive_buf_size % (sizeof(uint32_t) * backend->async.rx_buf_count));
backend->async.rx_buf_size = buf_size / backend->async.rx_buf_count;
__ASSERT_NO_MSG(backend->async.rx_buf_size > sizeof(struct rx_buf_t));
/* Initialize the RX buffers and event queue. */
err = k_mem_slab_init(&backend->async.rx_slab, config->receive_buf,
backend->async.rx_buf_size, backend->async.rx_buf_count);
if (err) {
return err;
}
k_msgq_init(&backend->async.rx_queue, (char *)&backend->async.rx_queue_buf,
sizeof(struct rx_queue_event), CONFIG_MODEM_BACKEND_UART_ASYNC_HWFC_BUFFER_COUNT);
backend->async.common.transmit_buf = config->transmit_buf;
backend->async.common.transmit_buf_size = config->transmit_buf_size;
k_work_init(&backend->async.common.rx_disabled_work,
modem_backend_uart_async_hwfc_notify_closed);
modem_pipe_init(&backend->pipe, backend, &modem_backend_uart_async_api);
#if CONFIG_MODEM_STATS
init_stats(backend);
#endif
return 0;
}

6
subsys/modem/modem_cmux.c

@ -1108,7 +1108,7 @@ static int modem_cmux_dlci_pipe_api_transmit(void *data, const uint8_t *buf, siz @@ -1108,7 +1108,7 @@ static int modem_cmux_dlci_pipe_api_transmit(void *data, const uint8_t *buf, siz
{
struct modem_cmux_dlci *dlci = (struct modem_cmux_dlci *)data;
struct modem_cmux *cmux = dlci->cmux;
int ret;
int ret = 0;
K_SPINLOCK(&cmux->work_lock) {
if (!cmux->attached) {
@ -1345,7 +1345,7 @@ int modem_cmux_connect(struct modem_cmux *cmux) @@ -1345,7 +1345,7 @@ int modem_cmux_connect(struct modem_cmux *cmux)
int modem_cmux_connect_async(struct modem_cmux *cmux)
{
int ret;
int ret = 0;
if (k_event_test(&cmux->event, MODEM_CMUX_EVENT_CONNECTED_BIT)) {
return -EALREADY;
@ -1360,8 +1360,6 @@ int modem_cmux_connect_async(struct modem_cmux *cmux) @@ -1360,8 +1360,6 @@ int modem_cmux_connect_async(struct modem_cmux *cmux)
if (k_work_delayable_is_pending(&cmux->connect_work) == false) {
k_work_schedule(&cmux->connect_work, K_NO_WAIT);
}
ret = 0;
}
return ret;

15
tests/subsys/modem/backends/uart/testcase.yaml

@ -5,15 +5,26 @@ common: @@ -5,15 +5,26 @@ common:
harness: ztest
harness_config:
fixture: gpio_loopback
tests:
modem.backends.uart.async:
extra_configs:
- CONFIG_UART_ASYNC_API=y
platform_allow:
- b_u585i_iot02a
- nrf5340dk/nrf5340/cpuapp
tests:
modem.backends.uart.async:
modem.backends.uart.async.hwfc:
extra_configs:
- CONFIG_UART_ASYNC_API=y
- CONFIG_MODEM_BACKEND_UART_ASYNC_HWFC=y
- CONFIG_TEST_HW_FLOW_CONTROL=y
platform_allow:
- nrf5340dk/nrf5340/cpuapp
modem.backends.uart.isr:
extra_configs:
- CONFIG_UART_INTERRUPT_DRIVEN=y
platform_allow:
- b_u585i_iot02a
- nrf5340dk/nrf5340/cpuapp

Loading…
Cancel
Save