Skip to content

Commit 92019b1

Browse files
pdgendtfabiobaltieri
authored andcommitted
emul: Support UART device emulation
Add support to the existing UART emulated bus, to have a client device emulator. Signed-off-by: Pieter De Gendt <[email protected]>
1 parent 804d383 commit 92019b1

File tree

9 files changed

+178
-26
lines changed

9 files changed

+178
-26
lines changed

drivers/serial/Kconfig.emul

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ config UART_EMUL
77
bool "Emulated UART driver [EXPERIMENTAL]"
88
default y
99
depends on DT_HAS_ZEPHYR_UART_EMUL_ENABLED
10+
depends on EMUL
11+
select SERIAL_HAS_DRIVER
1012
select SERIAL_SUPPORT_INTERRUPT
1113
select SERIAL_SUPPORT_ASYNC
1214
select RING_BUFFER
@@ -16,6 +18,12 @@ config UART_EMUL
1618

1719
if UART_EMUL
1820

21+
config UART_EMUL_DEVICE_INIT_PRIORITY
22+
int "UART emulated devices' init priority"
23+
default 0
24+
help
25+
The init priority of emulated driver on the UART bus.
26+
1927
config UART_EMUL_WORK_Q_STACK_SIZE
2028
int "UART emulator work queue stack size"
2129
default 2048

drivers/serial/uart_emul.c

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99

1010
#include <errno.h>
1111

12-
#include <zephyr/drivers/serial/uart_emul.h>
12+
#include <zephyr/drivers/emul.h>
1313
#include <zephyr/drivers/uart.h>
14+
#include <zephyr/drivers/serial/uart_emul.h>
1415
#include <zephyr/kernel.h>
1516
#include <zephyr/logging/log.h>
1617
#include <zephyr/sys/ring_buffer.h>
@@ -19,12 +20,20 @@
1920
LOG_MODULE_REGISTER(uart_emul, CONFIG_UART_LOG_LEVEL);
2021

2122
struct uart_emul_config {
23+
/* emul_list has to be the first member */
24+
struct emul_list_for_bus emul_list;
25+
2226
bool loopback;
2327
size_t latch_buffer_size;
2428
};
2529

30+
BUILD_ASSERT(offsetof(struct uart_emul_config, emul_list) == 0);
31+
2632
/* Device run time data */
2733
struct uart_emul_data {
34+
/* List of struct uart_emul associated with the device */
35+
sys_slist_t emuls;
36+
2837
const struct device *dev;
2938

3039
struct uart_config cfg;
@@ -98,6 +107,24 @@ int uart_emul_init_work_q(void)
98107

99108
SYS_INIT(uart_emul_init_work_q, POST_KERNEL, 0);
100109

110+
static void uart_emul_tx_data_ready(const struct device *dev)
111+
{
112+
struct uart_emul_data *data = dev->data;
113+
sys_snode_t *node;
114+
115+
if (data->tx_data_ready_cb) {
116+
(data->tx_data_ready_cb)(dev, ring_buf_size_get(data->tx_rb), data->user_data);
117+
}
118+
SYS_SLIST_FOR_EACH_NODE(&data->emuls, node) {
119+
struct uart_emul *emul = CONTAINER_OF(node, struct uart_emul, node);
120+
121+
__ASSERT_NO_MSG(emul->api != NULL);
122+
__ASSERT_NO_MSG(emul->api->tx_data_ready != NULL);
123+
124+
emul->api->tx_data_ready(dev, ring_buf_size_get(data->tx_rb), emul->target);
125+
}
126+
}
127+
101128
static int uart_emul_poll_in(const struct device *dev, unsigned char *p_char)
102129
{
103130
struct uart_emul_data *drv_data = dev->data;
@@ -135,10 +162,8 @@ static void uart_emul_poll_out(const struct device *dev, unsigned char out_char)
135162
if (drv_cfg->loopback) {
136163
uart_emul_put_rx_data(dev, &out_char, 1);
137164
}
138-
if (drv_data->tx_data_ready_cb) {
139-
(drv_data->tx_data_ready_cb)(dev, ring_buf_size_get(drv_data->tx_rb),
140-
drv_data->user_data);
141-
}
165+
166+
uart_emul_tx_data_ready(dev);
142167
}
143168

144169
static int uart_emul_err_check(const struct device *dev)
@@ -183,9 +208,8 @@ static int uart_emul_fifo_fill(const struct device *dev, const uint8_t *tx_data,
183208
if (config->loopback) {
184209
uart_emul_put_rx_data(dev, (uint8_t *)tx_data, put_size);
185210
}
186-
if (data->tx_data_ready_cb) {
187-
data->tx_data_ready_cb(dev, ring_buf_size_get(data->tx_rb), data->user_data);
188-
}
211+
212+
uart_emul_tx_data_ready(dev);
189213

190214
return ret;
191215
}
@@ -597,9 +621,8 @@ static void uart_emul_async_tx_handler(struct k_work *work)
597621
LOG_WRN("Lost %" PRIu32 " bytes on loopback", written - loop_written);
598622
}
599623
}
600-
if (data->tx_data_ready_cb) {
601-
(data->tx_data_ready_cb)(dev, ring_buf_size_get(data->tx_rb), data->user_data);
602-
}
624+
625+
uart_emul_tx_data_ready(dev);
603626

604627
if ((config->loopback && written) || !written) {
605628
/* When using the loopback fixture, just allow to drop all bytes in the ring buffer
@@ -973,19 +996,40 @@ void uart_emul_set_release_buffer_on_timeout(const struct device *dev, bool rele
973996
IF_ENABLED(CONFIG_UART_ASYNC_API, (drv_data->rx_release_on_timeout = release_on_timeout;));
974997
}
975998

999+
int uart_emul_register(const struct device *dev, struct uart_emul *emul)
1000+
{
1001+
struct uart_emul_data *data = dev->data;
1002+
1003+
sys_slist_append(&data->emuls, &emul->node);
1004+
1005+
return 0;
1006+
}
1007+
9761008
#define UART_EMUL_RX_FIFO_SIZE(inst) (DT_INST_PROP(inst, rx_fifo_size))
9771009
#define UART_EMUL_TX_FIFO_SIZE(inst) (DT_INST_PROP(inst, tx_fifo_size))
9781010

1011+
#define EMUL_LINK_AND_COMMA(node_id) \
1012+
{ \
1013+
.dev = DEVICE_DT_GET(node_id), \
1014+
},
1015+
9791016
#define DEFINE_UART_EMUL(inst) \
1017+
static const struct emul_link_for_bus emuls_##inst[] = { \
1018+
DT_FOREACH_CHILD_STATUS_OKAY(DT_DRV_INST(inst), EMUL_LINK_AND_COMMA)}; \
9801019
\
9811020
RING_BUF_DECLARE(uart_emul_##inst##_rx_rb, UART_EMUL_RX_FIFO_SIZE(inst)); \
9821021
RING_BUF_DECLARE(uart_emul_##inst##_tx_rb, UART_EMUL_TX_FIFO_SIZE(inst)); \
9831022
\
984-
static struct uart_emul_config uart_emul_cfg_##inst = { \
1023+
static const struct uart_emul_config uart_emul_cfg_##inst = { \
9851024
.loopback = DT_INST_PROP(inst, loopback), \
9861025
.latch_buffer_size = DT_INST_PROP(inst, latch_buffer_size), \
1026+
.emul_list = { \
1027+
.children = emuls_##inst, \
1028+
.num_children = ARRAY_SIZE(emuls_##inst), \
1029+
}, \
9871030
}; \
9881031
static struct uart_emul_data uart_emul_data_##inst = { \
1032+
.emuls = SYS_SLIST_STATIC_INIT(&_CONCAT(uart_emul_data_, inst).emuls), \
9891033
.dev = DEVICE_DT_INST_GET(inst), \
9901034
.rx_rb = &uart_emul_##inst##_rx_rb, \
9911035
.tx_rb = &uart_emul_##inst##_tx_rb, \
@@ -1000,6 +1044,12 @@ void uart_emul_set_release_buffer_on_timeout(const struct device *dev, bool rele
10001044
uart_emul_async_rx_disable_handler),)) \
10011045
}; \
10021046
\
1047+
static int uart_emul_post_init_##inst(void) \
1048+
{ \
1049+
return emul_init_for_bus(DEVICE_DT_INST_GET(inst)); \
1050+
} \
1051+
SYS_INIT(uart_emul_post_init_##inst, POST_KERNEL, CONFIG_UART_EMUL_DEVICE_INIT_PRIORITY); \
1052+
\
10031053
DEVICE_DT_INST_DEFINE(inst, NULL, NULL, &uart_emul_data_##inst, &uart_emul_cfg_##inst, \
10041054
PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &uart_emul_api);
10051055

include/zephyr/drivers/emul.h

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ struct emul;
2828
#include <zephyr/drivers/i2c_emul.h>
2929
#include <zephyr/drivers/spi_emul.h>
3030
#include <zephyr/drivers/mspi_emul.h>
31+
#include <zephyr/drivers/uart_emul.h>
3132
#include <zephyr/sys/iterable_sections.h>
3233

3334
/**
@@ -38,6 +39,7 @@ enum emul_bus_type {
3839
EMUL_BUS_TYPE_ESPI,
3940
EMUL_BUS_TYPE_SPI,
4041
EMUL_BUS_TYPE_MSPI,
42+
EMUL_BUS_TYPE_UART,
4143
EMUL_BUS_TYPE_NONE,
4244
};
4345

@@ -94,6 +96,7 @@ struct emul {
9496
struct espi_emul *espi;
9597
struct spi_emul *spi;
9698
struct mspi_emul *mspi;
99+
struct uart_emul *uart;
97100
struct no_bus_emul *none;
98101
} bus;
99102
/** Address of the API structure exposed by the emulator instance */
@@ -114,12 +117,14 @@ struct emul {
114117
#define Z_EMUL_REG_BUS_IDENTIFIER(_dev_node_id) (_CONCAT(_CONCAT(__emulreg_, _dev_node_id), _bus))
115118

116119
/* Conditionally places text based on what bus _dev_node_id is on. */
117-
#define Z_EMUL_BUS(_dev_node_id, _i2c, _espi, _spi, _mspi, _none) \
120+
#define Z_EMUL_BUS(_dev_node_id, _i2c, _espi, _spi, _mspi, _uart, _none) \
118121
COND_CODE_1(DT_ON_BUS(_dev_node_id, i2c), (_i2c), \
119-
(COND_CODE_1(DT_ON_BUS(_dev_node_id, espi), (_espi), \
120-
(COND_CODE_1(DT_ON_BUS(_dev_node_id, spi), (_spi), \
121-
(COND_CODE_1(DT_ON_BUS(_dev_node_id, mspi), (_mspi), \
122-
(_none))))))))
122+
(COND_CODE_1(DT_ON_BUS(_dev_node_id, espi), (_espi), \
123+
(COND_CODE_1(DT_ON_BUS(_dev_node_id, spi), (_spi), \
124+
(COND_CODE_1(DT_ON_BUS(_dev_node_id, mspi), (_mspi), \
125+
(COND_CODE_1(DT_ON_BUS(_dev_node_id, uart), (_uart), \
126+
(_none))))))))))
127+
123128
/**
124129
* @brief Define a new emulator
125130
*
@@ -135,20 +140,21 @@ struct emul {
135140
* @param _backend_api emulator-specific backend api
136141
*/
137142
#define EMUL_DT_DEFINE(node_id, init_fn, data_ptr, cfg_ptr, bus_api, _backend_api) \
138-
static struct Z_EMUL_BUS(node_id, i2c_emul, espi_emul, spi_emul, mspi_emul, no_bus_emul) \
139-
Z_EMUL_REG_BUS_IDENTIFIER(node_id) = { \
140-
.api = bus_api, \
141-
.Z_EMUL_BUS(node_id, addr, chipsel, chipsel, dev_idx, addr) = \
142-
DT_REG_ADDR(node_id), \
143-
}; \
143+
static struct Z_EMUL_BUS(node_id, i2c_emul, espi_emul, spi_emul, mspi_emul, uart_emul, \
144+
no_bus_emul) Z_EMUL_REG_BUS_IDENTIFIER(node_id) = { \
145+
.api = bus_api, \
146+
IF_ENABLED(DT_NODE_HAS_PROP(node_id, reg), \
147+
(.Z_EMUL_BUS(node_id, addr, chipsel, chipsel, dev_idx, dummy, addr) = \
148+
DT_REG_ADDR(node_id),))}; \
144149
const STRUCT_SECTION_ITERABLE(emul, EMUL_DT_NAME_GET(node_id)) __used = { \
145150
.init = (init_fn), \
146151
.dev = DEVICE_DT_GET(node_id), \
147152
.cfg = (cfg_ptr), \
148153
.data = (data_ptr), \
149154
.bus_type = Z_EMUL_BUS(node_id, EMUL_BUS_TYPE_I2C, EMUL_BUS_TYPE_ESPI, \
150-
EMUL_BUS_TYPE_SPI, EMUL_BUS_TYPE_MSPI, EMUL_BUS_TYPE_NONE), \
151-
.bus = {.Z_EMUL_BUS(node_id, i2c, espi, spi, mspi, none) = \
155+
EMUL_BUS_TYPE_SPI, EMUL_BUS_TYPE_MSPI, EMUL_BUS_TYPE_UART, \
156+
EMUL_BUS_TYPE_NONE), \
157+
.bus = {.Z_EMUL_BUS(node_id, i2c, espi, spi, mspi, uart, none) = \
152158
&(Z_EMUL_REG_BUS_IDENTIFIER(node_id))}, \
153159
.backend_api = (_backend_api), \
154160
};

include/zephyr/drivers/uart_emul.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2024 Basalte bv
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_DRIVERS_UART_EMUL_H_
8+
#define ZEPHYR_INCLUDE_DRIVERS_UART_EMUL_H_
9+
10+
#include <zephyr/device.h>
11+
#include <zephyr/drivers/emul.h>
12+
#include <zephyr/drivers/uart.h>
13+
#include <zephyr/sys/slist.h>
14+
#include <zephyr/types.h>
15+
16+
/**
17+
* @file
18+
*
19+
* @brief Public APIs for the UART device emulation drivers.
20+
*/
21+
22+
/**
23+
* @brief UART Emulation Interface
24+
* @defgroup uart_emul_interface UART Emulation Interface
25+
* @ingroup io_emulators
26+
* @{
27+
*/
28+
29+
#ifdef __cplusplus
30+
extern "C" {
31+
#endif
32+
33+
struct uart_emul_device_api;
34+
35+
/**
36+
* @brief Define the emulation callback function signature
37+
*
38+
* @param dev UART device instance
39+
* @param size Number of available bytes in TX buffer
40+
* @param target pointer to emulation context
41+
*/
42+
typedef void (*uart_emul_device_tx_data_ready_t)(const struct device *dev, size_t size,
43+
const struct emul *target);
44+
45+
/** Node in a linked list of emulators for UART devices */
46+
struct uart_emul {
47+
sys_snode_t node;
48+
/** Target emulator - REQUIRED for all emulated bus nodes of any type */
49+
const struct emul *target;
50+
/** API provided for this device */
51+
const struct uart_emul_device_api *api;
52+
};
53+
54+
/** Definition of the emulator API */
55+
struct uart_emul_device_api {
56+
uart_emul_device_tx_data_ready_t tx_data_ready;
57+
};
58+
59+
/**
60+
* Register an emulated device on the controller
61+
*
62+
* @param dev Device that will use the emulator
63+
* @param emul UART emulator to use
64+
* @return 0 indicating success
65+
*/
66+
int uart_emul_register(const struct device *dev, struct uart_emul *emul);
67+
68+
#ifdef __cplusplus
69+
}
70+
#endif
71+
72+
/**
73+
* @}
74+
*/
75+
76+
#endif /* ZEPHYR_INCLUDE_DRIVERS_UART_EMUL_H_ */

subsys/emul/emul.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ int emul_init_for_bus(const struct device *dev)
5858
case EMUL_BUS_TYPE_MSPI:
5959
emul->bus.mspi->target = emul;
6060
break;
61+
case EMUL_BUS_TYPE_UART:
62+
emul->bus.uart->target = emul;
63+
break;
6164
case EMUL_BUS_TYPE_NONE:
6265
break;
6366
}
@@ -89,6 +92,11 @@ int emul_init_for_bus(const struct device *dev)
8992
rc = mspi_emul_register(dev, emul->bus.mspi);
9093
break;
9194
#endif /* CONFIG_MSPI_EMUL */
95+
#ifdef CONFIG_UART_EMUL
96+
case EMUL_BUS_TYPE_UART:
97+
rc = uart_emul_register(dev, emul->bus.uart);
98+
break;
99+
#endif /* CONFIG_UART_EMUL */
92100
default:
93101
rc = -EINVAL;
94102
LOG_WRN("Found no emulated bus enabled to register emulator %s",

tests/drivers/console_switching/prj.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ CONFIG_CONSOLE_SUBSYS=y
55
CONFIG_CONSOLE_GETLINE=y
66
CONFIG_DEVICE_MUTABLE=y
77
CONFIG_DEVMUX=y
8-
CONFIG_UART_EMUL=y
8+
CONFIG_EMUL=y

tests/drivers/uart/uart_emul/prj.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
CONFIG_ZTEST=y
22
CONFIG_SERIAL=y
3+
CONFIG_EMUL=y

tests/subsys/logging/log_backend_uart/prj.conf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
CONFIG_ZTEST=y
55
CONFIG_ASSERT=y
66
CONFIG_TEST_LOGGING_DEFAULTS=n
7+
CONFIG_SERIAL=y
8+
CONFIG_EMUL=y
79

810
CONFIG_LOG=y
911
CONFIG_LOG_BACKEND_UART=y

tests/subsys/shell/shell_backend_uart/prj.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ CONFIG_SHELL_PRINTF_BUFF_SIZE=15
66
CONFIG_SHELL_METAKEYS=n
77
CONFIG_LOG=n
88
CONFIG_ZTEST=y
9+
CONFIG_EMUL=y

0 commit comments

Comments
 (0)