Skip to content

Commit 0b453c6

Browse files
pdgendtfabiobaltieri
authored andcommitted
tests: drivers: uart: uart_emul: Add device emulation test
Add an emulated UART device test case. Demonstrate using the zephyr,uart-emul bus for passing data to an emulated implementation for a UART device driver. Signed-off-by: Pieter De Gendt <[email protected]>
1 parent 8bbb889 commit 0b453c6

File tree

5 files changed

+327
-1
lines changed

5 files changed

+327
-1
lines changed

tests/drivers/uart/uart_emul/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
55
project(uart_emul)
66

77
target_sources(app PRIVATE
8-
src/main.c
8+
src/bus.c
9+
src/device.c
910
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2024 Basalte bv
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Properties for dummy emulated driver.
5+
6+
compatible: "uart-dummy"
7+
8+
include: uart-device.yaml
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
/*
2+
* Copyright 2024 Basalte bv
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
*/
7+
8+
#define DT_DRV_COMPAT uart_dummy
9+
10+
#include <zephyr/device.h>
11+
#include <zephyr/devicetree.h>
12+
#include <zephyr/drivers/uart.h>
13+
#include <zephyr/drivers/uart_emul.h>
14+
#include <zephyr/drivers/serial/uart_emul.h>
15+
#include <zephyr/drivers/emul_stub_device.h>
16+
#include <zephyr/ztest.h>
17+
18+
#define UART_DUMMY_NODE DT_NODELABEL(dummy)
19+
#define EMUL_UART_NODE DT_PARENT(UART_DUMMY_NODE)
20+
#define EMUL_UART_RX_FIFO_SIZE DT_PROP(EMUL_UART_NODE, rx_fifo_size)
21+
#define EMUL_UART_TX_FIFO_SIZE DT_PROP(EMUL_UART_NODE, tx_fifo_size)
22+
23+
/*
24+
* Leave one byte left in tx to avoid filling it completely which will block the UART
25+
* tx ready IRQ event.
26+
*/
27+
#define SAMPLE_DATA_SIZE MIN(EMUL_UART_RX_FIFO_SIZE, EMUL_UART_TX_FIFO_SIZE) - 1
28+
29+
struct uart_emul_device_fixture {
30+
const struct device *dev;
31+
uint8_t sample_data[SAMPLE_DATA_SIZE];
32+
uint8_t rx_content[SAMPLE_DATA_SIZE];
33+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
34+
struct k_sem tx_done_sem;
35+
struct k_sem rx_done_sem;
36+
size_t tx_remaining;
37+
size_t rx_remaining;
38+
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
39+
#ifdef CONFIG_UART_ASYNC_API
40+
struct k_event async_events;
41+
#endif /* CONFIG_UART_ASYNC_API */
42+
};
43+
44+
static void *uart_emul_device_setup(void)
45+
{
46+
static struct uart_emul_device_fixture fixture = {.dev = DEVICE_DT_GET(EMUL_UART_NODE)};
47+
48+
for (size_t i = 0; i < SAMPLE_DATA_SIZE; i++) {
49+
fixture.sample_data[i] = i;
50+
}
51+
52+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
53+
k_sem_init(&fixture.tx_done_sem, 0, 1);
54+
k_sem_init(&fixture.rx_done_sem, 0, 1);
55+
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
56+
57+
#ifdef CONFIG_UART_ASYNC_API
58+
k_event_init(&fixture.async_events);
59+
#endif /* CONFIG_UART_ASYNC_API */
60+
61+
zassert_not_null(fixture.dev);
62+
return &fixture;
63+
}
64+
65+
static void uart_emul_device_before(void *f)
66+
{
67+
struct uart_emul_device_fixture *fixture = f;
68+
69+
uart_emul_flush_rx_data(fixture->dev);
70+
uart_emul_flush_tx_data(fixture->dev);
71+
72+
uart_err_check(fixture->dev);
73+
74+
memset(fixture->rx_content, 0, sizeof(fixture->rx_content));
75+
76+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
77+
uart_irq_tx_disable(fixture->dev);
78+
uart_irq_rx_disable(fixture->dev);
79+
80+
k_sem_reset(&fixture->tx_done_sem);
81+
k_sem_reset(&fixture->rx_done_sem);
82+
83+
fixture->tx_remaining = SAMPLE_DATA_SIZE;
84+
fixture->rx_remaining = SAMPLE_DATA_SIZE;
85+
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
86+
87+
#ifdef CONFIG_UART_ASYNC_API
88+
uart_tx_abort(fixture->dev);
89+
uart_rx_disable(fixture->dev);
90+
91+
k_event_set(&fixture->async_events, 0);
92+
#endif /* CONFIG_UART_ASYNC_API */
93+
}
94+
95+
ZTEST_F(uart_emul_device, test_polling)
96+
{
97+
uint32_t len;
98+
uint8_t byte;
99+
int ret;
100+
101+
for (size_t i = 0; i < SAMPLE_DATA_SIZE; ++i) {
102+
uart_poll_out(fixture->dev, i);
103+
}
104+
105+
len = uart_emul_get_tx_data(fixture->dev, NULL, UINT32_MAX);
106+
zassert_equal(len, 0, "TX buffer should be empty");
107+
108+
for (size_t i = 0; i < SAMPLE_DATA_SIZE; ++i) {
109+
ret = uart_poll_in(fixture->dev, &byte);
110+
zassert_equal(ret, 0);
111+
zassert_equal(byte, i);
112+
}
113+
114+
ret = uart_poll_in(fixture->dev, &byte);
115+
zassert_equal(ret, -1, "RX buffer should be empty");
116+
}
117+
118+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
119+
static void uart_emul_device_isr_handle_tx_ready(struct uart_emul_device_fixture *fixture)
120+
{
121+
uint32_t sample_data_it;
122+
int ret;
123+
124+
if (fixture->tx_remaining) {
125+
sample_data_it = sizeof(fixture->sample_data) - fixture->tx_remaining;
126+
ret = uart_fifo_fill(fixture->dev, &fixture->sample_data[sample_data_it],
127+
fixture->tx_remaining);
128+
fixture->tx_remaining -= (size_t)ret;
129+
}
130+
131+
if (fixture->tx_remaining == 0) {
132+
uart_irq_tx_disable(fixture->dev);
133+
k_sem_give(&fixture->tx_done_sem);
134+
}
135+
}
136+
137+
static void uart_emul_device_isr_handle_rx_ready(struct uart_emul_device_fixture *fixture)
138+
{
139+
uint32_t rx_content_it;
140+
int ret;
141+
142+
if (fixture->rx_remaining) {
143+
rx_content_it = sizeof(fixture->rx_content) - fixture->rx_remaining;
144+
ret = uart_fifo_read(fixture->dev, &fixture->rx_content[rx_content_it],
145+
fixture->rx_remaining);
146+
fixture->rx_remaining -= (size_t)ret;
147+
}
148+
149+
if (fixture->rx_remaining == 0) {
150+
k_sem_give(&fixture->rx_done_sem);
151+
}
152+
}
153+
154+
static void uart_emul_device_isr(const struct device *dev, void *user_data)
155+
{
156+
struct uart_emul_device_fixture *fixture = user_data;
157+
158+
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
159+
if (uart_irq_tx_ready(fixture->dev)) {
160+
uart_emul_device_isr_handle_tx_ready(fixture);
161+
}
162+
if (uart_irq_rx_ready(fixture->dev)) {
163+
uart_emul_device_isr_handle_rx_ready(fixture);
164+
}
165+
}
166+
}
167+
168+
ZTEST_F(uart_emul_device, test_irq)
169+
{
170+
size_t tx_len;
171+
int rc;
172+
173+
uart_irq_callback_user_data_set(fixture->dev, uart_emul_device_isr, fixture);
174+
/* enabling the rx irq will call the callback, if set */
175+
uart_irq_rx_enable(fixture->dev);
176+
/* enabling the tx irq will call the callback, if set */
177+
uart_irq_tx_enable(fixture->dev);
178+
179+
/* Wait for all data to be received in full */
180+
zassert_ok(k_sem_take(&fixture->tx_done_sem, K_SECONDS(1)),
181+
"Timeout waiting for UART TX ISR");
182+
183+
tx_len = uart_emul_get_tx_data(fixture->dev, NULL, SAMPLE_DATA_SIZE);
184+
zassert_equal(tx_len, 0, "TX buffer should be empty");
185+
186+
zassert_ok(k_sem_take(&fixture->rx_done_sem, K_SECONDS(1)),
187+
"Timeout waiting for UART RX ISR");
188+
zassert_mem_equal(fixture->rx_content, fixture->sample_data, SAMPLE_DATA_SIZE);
189+
190+
/* No more data in RX buffer */
191+
rc = uart_poll_in(fixture->dev, &fixture->rx_content[0]);
192+
zassert_equal(rc, -1, "RX buffer should be empty");
193+
194+
uart_irq_rx_disable(fixture->dev);
195+
}
196+
#endif
197+
198+
#ifdef CONFIG_UART_ASYNC_API
199+
static void uart_emul_callback(const struct device *dev, struct uart_event *evt, void *user_data)
200+
{
201+
struct uart_emul_device_fixture *fixture = user_data;
202+
203+
zassert_not_null(evt);
204+
k_event_post(&fixture->async_events, ((uint32_t)1 << evt->type));
205+
206+
switch (evt->type) {
207+
case UART_TX_DONE:
208+
zassert_equal(evt->data.tx.len, sizeof(fixture->sample_data));
209+
zassert_equal(evt->data.tx.buf, fixture->sample_data);
210+
break;
211+
case UART_RX_RDY:
212+
zassert_equal(evt->data.rx.len, sizeof(fixture->sample_data));
213+
zassert_mem_equal(&evt->data.rx.buf[evt->data.rx.offset], fixture->sample_data,
214+
sizeof(fixture->sample_data));
215+
break;
216+
case UART_RX_BUF_RELEASED:
217+
zassert_equal(evt->data.rx_buf.buf, fixture->rx_content);
218+
break;
219+
case UART_TX_ABORTED:
220+
case UART_RX_BUF_REQUEST:
221+
case UART_RX_DISABLED:
222+
case UART_RX_STOPPED:
223+
break;
224+
}
225+
}
226+
227+
static bool uart_emul_device_wait_for_event(struct uart_emul_device_fixture *fixture,
228+
enum uart_event_type event)
229+
{
230+
return k_event_wait(&fixture->async_events, ((uint32_t)1 << event), false, K_SECONDS(1)) !=
231+
0;
232+
}
233+
234+
ZTEST_F(uart_emul_device, test_async)
235+
{
236+
size_t tx_len;
237+
238+
uart_emul_set_release_buffer_on_timeout(fixture->dev, true);
239+
240+
zassert_ok(uart_callback_set(fixture->dev, uart_emul_callback, fixture));
241+
zassert_ok(uart_tx(fixture->dev, fixture->sample_data, sizeof(fixture->sample_data),
242+
SYS_FOREVER_US));
243+
zassert_ok(uart_rx_enable(fixture->dev, fixture->rx_content, sizeof(fixture->rx_content),
244+
SYS_FOREVER_US));
245+
246+
/* Wait for all data to be received in full */
247+
zexpect_true(uart_emul_device_wait_for_event(fixture, UART_TX_DONE),
248+
"UART_TX_DONE event expected");
249+
250+
tx_len = uart_emul_get_tx_data(fixture->dev, NULL, SAMPLE_DATA_SIZE);
251+
zassert_equal(tx_len, 0, "TX buffer should be empty");
252+
253+
zexpect_true(uart_emul_device_wait_for_event(fixture, UART_RX_BUF_REQUEST),
254+
"UART_RX_BUF_REQUEST event expected");
255+
zexpect_true(uart_emul_device_wait_for_event(fixture, UART_RX_RDY),
256+
"UART_RX_RDY event expected");
257+
zassert_mem_equal(fixture->rx_content, fixture->sample_data, SAMPLE_DATA_SIZE);
258+
zexpect_true(uart_emul_device_wait_for_event(fixture, UART_RX_BUF_RELEASED),
259+
"UART_RX_BUF_RELEASED event expected");
260+
zexpect_true(uart_emul_device_wait_for_event(fixture, UART_RX_DISABLED),
261+
"UART_RX_DISABLED event expected");
262+
}
263+
#endif /* CONFIG_UART_ASYNC_API */
264+
265+
ZTEST_SUITE(uart_emul_device, NULL, uart_emul_device_setup, uart_emul_device_before, NULL, NULL);
266+
267+
/* Driver details */
268+
269+
/* Our dummy device echoes all data received */
270+
static void uart_dummy_emul_tx_ready(const struct device *dev, size_t size,
271+
const struct emul *target)
272+
{
273+
uint32_t ret;
274+
uint8_t byte;
275+
276+
zassert_equal(target->bus_type, EMUL_BUS_TYPE_UART, "UART bus required");
277+
278+
for (size_t i = 0; i < size; ++i) {
279+
ret = uart_emul_get_tx_data(dev, &byte, 1);
280+
zassert_equal(ret, 1);
281+
282+
ret = uart_emul_put_rx_data(dev, &byte, 1);
283+
zassert_equal(ret, 1);
284+
}
285+
}
286+
287+
static const struct uart_emul_device_api dummy_api = {
288+
.tx_data_ready = uart_dummy_emul_tx_ready,
289+
};
290+
291+
static int uart_dummy_emul_init(const struct emul *target, const struct device *parent)
292+
{
293+
ARG_UNUSED(target);
294+
ARG_UNUSED(parent);
295+
296+
return 0;
297+
}
298+
299+
#define UART_DUMMY_DEFINE(inst) \
300+
EMUL_DT_INST_DEFINE(inst, uart_dummy_emul_init, NULL, NULL, &dummy_api, NULL)
301+
302+
/* Define both device and emulated driver */
303+
DT_INST_FOREACH_STATUS_OKAY(EMUL_STUB_DEVICE)
304+
DT_INST_FOREACH_STATUS_OKAY(UART_DUMMY_DEFINE)

tests/drivers/uart/uart_emul/uart_emul.overlay

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,17 @@
1212
rx-fifo-size = <256>;
1313
tx-fifo-size = <256>;
1414
};
15+
16+
euart1: uart-dummy-bus {
17+
compatible = "zephyr,uart-emul";
18+
status = "okay";
19+
current-speed = <0>;
20+
rx-fifo-size = <256>;
21+
tx-fifo-size = <256>;
22+
23+
dummy: uart-dummy {
24+
compatible = "uart-dummy";
25+
status = "okay";
26+
};
27+
};
1528
};

0 commit comments

Comments
 (0)