diff --git a/tests/drivers/uart/uart_emul/CMakeLists.txt b/tests/drivers/uart/uart_emul/CMakeLists.txt index 78fc07f2383..fd2e4011eec 100644 --- a/tests/drivers/uart/uart_emul/CMakeLists.txt +++ b/tests/drivers/uart/uart_emul/CMakeLists.txt @@ -5,5 +5,6 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(uart_emul) target_sources(app PRIVATE - src/main.c + src/bus.c + src/device.c ) diff --git a/tests/drivers/uart/uart_emul/dts/bindings/uart-dummy.yml b/tests/drivers/uart/uart_emul/dts/bindings/uart-dummy.yml new file mode 100644 index 00000000000..70f47ea1d56 --- /dev/null +++ b/tests/drivers/uart/uart_emul/dts/bindings/uart-dummy.yml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Basalte bv +# SPDX-License-Identifier: Apache-2.0 + +description: Properties for dummy emulated driver. + +compatible: "uart-dummy" + +include: uart-device.yaml diff --git a/tests/drivers/uart/uart_emul/src/main.c b/tests/drivers/uart/uart_emul/src/bus.c similarity index 100% rename from tests/drivers/uart/uart_emul/src/main.c rename to tests/drivers/uart/uart_emul/src/bus.c diff --git a/tests/drivers/uart/uart_emul/src/device.c b/tests/drivers/uart/uart_emul/src/device.c new file mode 100644 index 00000000000..73570f86f65 --- /dev/null +++ b/tests/drivers/uart/uart_emul/src/device.c @@ -0,0 +1,304 @@ +/* + * Copyright 2024 Basalte bv + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#define DT_DRV_COMPAT uart_dummy + +#include +#include +#include +#include +#include +#include +#include + +#define UART_DUMMY_NODE DT_NODELABEL(dummy) +#define EMUL_UART_NODE DT_PARENT(UART_DUMMY_NODE) +#define EMUL_UART_RX_FIFO_SIZE DT_PROP(EMUL_UART_NODE, rx_fifo_size) +#define EMUL_UART_TX_FIFO_SIZE DT_PROP(EMUL_UART_NODE, tx_fifo_size) + +/* + * Leave one byte left in tx to avoid filling it completely which will block the UART + * tx ready IRQ event. + */ +#define SAMPLE_DATA_SIZE MIN(EMUL_UART_RX_FIFO_SIZE, EMUL_UART_TX_FIFO_SIZE) - 1 + +struct uart_emul_device_fixture { + const struct device *dev; + uint8_t sample_data[SAMPLE_DATA_SIZE]; + uint8_t rx_content[SAMPLE_DATA_SIZE]; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + struct k_sem tx_done_sem; + struct k_sem rx_done_sem; + size_t tx_remaining; + size_t rx_remaining; +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +#ifdef CONFIG_UART_ASYNC_API + struct k_event async_events; +#endif /* CONFIG_UART_ASYNC_API */ +}; + +static void *uart_emul_device_setup(void) +{ + static struct uart_emul_device_fixture fixture = {.dev = DEVICE_DT_GET(EMUL_UART_NODE)}; + + for (size_t i = 0; i < SAMPLE_DATA_SIZE; i++) { + fixture.sample_data[i] = i; + } + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + k_sem_init(&fixture.tx_done_sem, 0, 1); + k_sem_init(&fixture.rx_done_sem, 0, 1); +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +#ifdef CONFIG_UART_ASYNC_API + k_event_init(&fixture.async_events); +#endif /* CONFIG_UART_ASYNC_API */ + + zassert_not_null(fixture.dev); + return &fixture; +} + +static void uart_emul_device_before(void *f) +{ + struct uart_emul_device_fixture *fixture = f; + + uart_emul_flush_rx_data(fixture->dev); + uart_emul_flush_tx_data(fixture->dev); + + uart_err_check(fixture->dev); + + memset(fixture->rx_content, 0, sizeof(fixture->rx_content)); + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_tx_disable(fixture->dev); + uart_irq_rx_disable(fixture->dev); + + k_sem_reset(&fixture->tx_done_sem); + k_sem_reset(&fixture->rx_done_sem); + + fixture->tx_remaining = SAMPLE_DATA_SIZE; + fixture->rx_remaining = SAMPLE_DATA_SIZE; +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +#ifdef CONFIG_UART_ASYNC_API + uart_tx_abort(fixture->dev); + uart_rx_disable(fixture->dev); + + k_event_set(&fixture->async_events, 0); +#endif /* CONFIG_UART_ASYNC_API */ +} + +ZTEST_F(uart_emul_device, test_polling) +{ + uint32_t len; + uint8_t byte; + int ret; + + for (size_t i = 0; i < SAMPLE_DATA_SIZE; ++i) { + uart_poll_out(fixture->dev, i); + } + + len = uart_emul_get_tx_data(fixture->dev, NULL, UINT32_MAX); + zassert_equal(len, 0, "TX buffer should be empty"); + + for (size_t i = 0; i < SAMPLE_DATA_SIZE; ++i) { + ret = uart_poll_in(fixture->dev, &byte); + zassert_equal(ret, 0); + zassert_equal(byte, i); + } + + ret = uart_poll_in(fixture->dev, &byte); + zassert_equal(ret, -1, "RX buffer should be empty"); +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static void uart_emul_device_isr_handle_tx_ready(struct uart_emul_device_fixture *fixture) +{ + uint32_t sample_data_it; + int ret; + + if (fixture->tx_remaining) { + sample_data_it = sizeof(fixture->sample_data) - fixture->tx_remaining; + ret = uart_fifo_fill(fixture->dev, &fixture->sample_data[sample_data_it], + fixture->tx_remaining); + fixture->tx_remaining -= (size_t)ret; + } + + if (fixture->tx_remaining == 0) { + uart_irq_tx_disable(fixture->dev); + k_sem_give(&fixture->tx_done_sem); + } +} + +static void uart_emul_device_isr_handle_rx_ready(struct uart_emul_device_fixture *fixture) +{ + uint32_t rx_content_it; + int ret; + + if (fixture->rx_remaining) { + rx_content_it = sizeof(fixture->rx_content) - fixture->rx_remaining; + ret = uart_fifo_read(fixture->dev, &fixture->rx_content[rx_content_it], + fixture->rx_remaining); + fixture->rx_remaining -= (size_t)ret; + } + + if (fixture->rx_remaining == 0) { + k_sem_give(&fixture->rx_done_sem); + } +} + +static void uart_emul_device_isr(const struct device *dev, void *user_data) +{ + struct uart_emul_device_fixture *fixture = user_data; + + while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { + if (uart_irq_tx_ready(fixture->dev)) { + uart_emul_device_isr_handle_tx_ready(fixture); + } + if (uart_irq_rx_ready(fixture->dev)) { + uart_emul_device_isr_handle_rx_ready(fixture); + } + } +} + +ZTEST_F(uart_emul_device, test_irq) +{ + size_t tx_len; + int rc; + + uart_irq_callback_user_data_set(fixture->dev, uart_emul_device_isr, fixture); + /* enabling the rx irq will call the callback, if set */ + uart_irq_rx_enable(fixture->dev); + /* enabling the tx irq will call the callback, if set */ + uart_irq_tx_enable(fixture->dev); + + /* Wait for all data to be received in full */ + zassert_ok(k_sem_take(&fixture->tx_done_sem, K_SECONDS(1)), + "Timeout waiting for UART TX ISR"); + + tx_len = uart_emul_get_tx_data(fixture->dev, NULL, SAMPLE_DATA_SIZE); + zassert_equal(tx_len, 0, "TX buffer should be empty"); + + zassert_ok(k_sem_take(&fixture->rx_done_sem, K_SECONDS(1)), + "Timeout waiting for UART RX ISR"); + zassert_mem_equal(fixture->rx_content, fixture->sample_data, SAMPLE_DATA_SIZE); + + /* No more data in RX buffer */ + rc = uart_poll_in(fixture->dev, &fixture->rx_content[0]); + zassert_equal(rc, -1, "RX buffer should be empty"); + + uart_irq_rx_disable(fixture->dev); +} +#endif + +#ifdef CONFIG_UART_ASYNC_API +static void uart_emul_callback(const struct device *dev, struct uart_event *evt, void *user_data) +{ + struct uart_emul_device_fixture *fixture = user_data; + + zassert_not_null(evt); + k_event_post(&fixture->async_events, ((uint32_t)1 << evt->type)); + + switch (evt->type) { + case UART_TX_DONE: + zassert_equal(evt->data.tx.len, sizeof(fixture->sample_data)); + zassert_equal(evt->data.tx.buf, fixture->sample_data); + break; + case UART_RX_RDY: + zassert_equal(evt->data.rx.len, sizeof(fixture->sample_data)); + zassert_mem_equal(&evt->data.rx.buf[evt->data.rx.offset], fixture->sample_data, + sizeof(fixture->sample_data)); + break; + case UART_RX_BUF_RELEASED: + zassert_equal(evt->data.rx_buf.buf, fixture->rx_content); + break; + case UART_TX_ABORTED: + case UART_RX_BUF_REQUEST: + case UART_RX_DISABLED: + case UART_RX_STOPPED: + break; + } +} + +static bool uart_emul_device_wait_for_event(struct uart_emul_device_fixture *fixture, + enum uart_event_type event) +{ + return k_event_wait(&fixture->async_events, ((uint32_t)1 << event), false, K_SECONDS(1)) != + 0; +} + +ZTEST_F(uart_emul_device, test_async) +{ + size_t tx_len; + + uart_emul_set_release_buffer_on_timeout(fixture->dev, true); + + zassert_ok(uart_callback_set(fixture->dev, uart_emul_callback, fixture)); + zassert_ok(uart_tx(fixture->dev, fixture->sample_data, sizeof(fixture->sample_data), + SYS_FOREVER_US)); + zassert_ok(uart_rx_enable(fixture->dev, fixture->rx_content, sizeof(fixture->rx_content), + SYS_FOREVER_US)); + + /* Wait for all data to be received in full */ + zexpect_true(uart_emul_device_wait_for_event(fixture, UART_TX_DONE), + "UART_TX_DONE event expected"); + + tx_len = uart_emul_get_tx_data(fixture->dev, NULL, SAMPLE_DATA_SIZE); + zassert_equal(tx_len, 0, "TX buffer should be empty"); + + zexpect_true(uart_emul_device_wait_for_event(fixture, UART_RX_BUF_REQUEST), + "UART_RX_BUF_REQUEST event expected"); + zexpect_true(uart_emul_device_wait_for_event(fixture, UART_RX_RDY), + "UART_RX_RDY event expected"); + zassert_mem_equal(fixture->rx_content, fixture->sample_data, SAMPLE_DATA_SIZE); + zexpect_true(uart_emul_device_wait_for_event(fixture, UART_RX_BUF_RELEASED), + "UART_RX_BUF_RELEASED event expected"); + zexpect_true(uart_emul_device_wait_for_event(fixture, UART_RX_DISABLED), + "UART_RX_DISABLED event expected"); +} +#endif /* CONFIG_UART_ASYNC_API */ + +ZTEST_SUITE(uart_emul_device, NULL, uart_emul_device_setup, uart_emul_device_before, NULL, NULL); + +/* Driver details */ + +/* Our dummy device echoes all data received */ +static void uart_dummy_emul_tx_ready(const struct device *dev, size_t size, + const struct emul *target) +{ + uint32_t ret; + uint8_t byte; + + zassert_equal(target->bus_type, EMUL_BUS_TYPE_UART, "UART bus required"); + + for (size_t i = 0; i < size; ++i) { + ret = uart_emul_get_tx_data(dev, &byte, 1); + zassert_equal(ret, 1); + + ret = uart_emul_put_rx_data(dev, &byte, 1); + zassert_equal(ret, 1); + } +} + +static const struct uart_emul_device_api dummy_api = { + .tx_data_ready = uart_dummy_emul_tx_ready, +}; + +static int uart_dummy_emul_init(const struct emul *target, const struct device *parent) +{ + ARG_UNUSED(target); + ARG_UNUSED(parent); + + return 0; +} + +#define UART_DUMMY_DEFINE(inst) \ + EMUL_DT_INST_DEFINE(inst, uart_dummy_emul_init, NULL, NULL, &dummy_api, NULL) + +/* Define both device and emulated driver */ +DT_INST_FOREACH_STATUS_OKAY(EMUL_STUB_DEVICE) +DT_INST_FOREACH_STATUS_OKAY(UART_DUMMY_DEFINE) diff --git a/tests/drivers/uart/uart_emul/uart_emul.overlay b/tests/drivers/uart/uart_emul/uart_emul.overlay index 93e580fd41a..03ff62cdd25 100644 --- a/tests/drivers/uart/uart_emul/uart_emul.overlay +++ b/tests/drivers/uart/uart_emul/uart_emul.overlay @@ -12,4 +12,17 @@ rx-fifo-size = <256>; tx-fifo-size = <256>; }; + + euart1: uart-dummy-bus { + compatible = "zephyr,uart-emul"; + status = "okay"; + current-speed = <0>; + rx-fifo-size = <256>; + tx-fifo-size = <256>; + + dummy: uart-dummy { + compatible = "uart-dummy"; + status = "okay"; + }; + }; };