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 <pieter.degendt@basalte.be>
This commit is contained in:
parent
804d383854
commit
92019b1dac
9 changed files with 178 additions and 26 deletions
|
@ -7,6 +7,8 @@ config UART_EMUL
|
|||
bool "Emulated UART driver [EXPERIMENTAL]"
|
||||
default y
|
||||
depends on DT_HAS_ZEPHYR_UART_EMUL_ENABLED
|
||||
depends on EMUL
|
||||
select SERIAL_HAS_DRIVER
|
||||
select SERIAL_SUPPORT_INTERRUPT
|
||||
select SERIAL_SUPPORT_ASYNC
|
||||
select RING_BUFFER
|
||||
|
@ -16,6 +18,12 @@ config UART_EMUL
|
|||
|
||||
if UART_EMUL
|
||||
|
||||
config UART_EMUL_DEVICE_INIT_PRIORITY
|
||||
int "UART emulated devices' init priority"
|
||||
default 0
|
||||
help
|
||||
The init priority of emulated driver on the UART bus.
|
||||
|
||||
config UART_EMUL_WORK_Q_STACK_SIZE
|
||||
int "UART emulator work queue stack size"
|
||||
default 2048
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/drivers/serial/uart_emul.h>
|
||||
#include <zephyr/drivers/emul.h>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/drivers/serial/uart_emul.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/ring_buffer.h>
|
||||
|
@ -19,12 +20,20 @@
|
|||
LOG_MODULE_REGISTER(uart_emul, CONFIG_UART_LOG_LEVEL);
|
||||
|
||||
struct uart_emul_config {
|
||||
/* emul_list has to be the first member */
|
||||
struct emul_list_for_bus emul_list;
|
||||
|
||||
bool loopback;
|
||||
size_t latch_buffer_size;
|
||||
};
|
||||
|
||||
BUILD_ASSERT(offsetof(struct uart_emul_config, emul_list) == 0);
|
||||
|
||||
/* Device run time data */
|
||||
struct uart_emul_data {
|
||||
/* List of struct uart_emul associated with the device */
|
||||
sys_slist_t emuls;
|
||||
|
||||
const struct device *dev;
|
||||
|
||||
struct uart_config cfg;
|
||||
|
@ -98,6 +107,24 @@ int uart_emul_init_work_q(void)
|
|||
|
||||
SYS_INIT(uart_emul_init_work_q, POST_KERNEL, 0);
|
||||
|
||||
static void uart_emul_tx_data_ready(const struct device *dev)
|
||||
{
|
||||
struct uart_emul_data *data = dev->data;
|
||||
sys_snode_t *node;
|
||||
|
||||
if (data->tx_data_ready_cb) {
|
||||
(data->tx_data_ready_cb)(dev, ring_buf_size_get(data->tx_rb), data->user_data);
|
||||
}
|
||||
SYS_SLIST_FOR_EACH_NODE(&data->emuls, node) {
|
||||
struct uart_emul *emul = CONTAINER_OF(node, struct uart_emul, node);
|
||||
|
||||
__ASSERT_NO_MSG(emul->api != NULL);
|
||||
__ASSERT_NO_MSG(emul->api->tx_data_ready != NULL);
|
||||
|
||||
emul->api->tx_data_ready(dev, ring_buf_size_get(data->tx_rb), emul->target);
|
||||
}
|
||||
}
|
||||
|
||||
static int uart_emul_poll_in(const struct device *dev, unsigned char *p_char)
|
||||
{
|
||||
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)
|
|||
if (drv_cfg->loopback) {
|
||||
uart_emul_put_rx_data(dev, &out_char, 1);
|
||||
}
|
||||
if (drv_data->tx_data_ready_cb) {
|
||||
(drv_data->tx_data_ready_cb)(dev, ring_buf_size_get(drv_data->tx_rb),
|
||||
drv_data->user_data);
|
||||
}
|
||||
|
||||
uart_emul_tx_data_ready(dev);
|
||||
}
|
||||
|
||||
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,
|
|||
if (config->loopback) {
|
||||
uart_emul_put_rx_data(dev, (uint8_t *)tx_data, put_size);
|
||||
}
|
||||
if (data->tx_data_ready_cb) {
|
||||
data->tx_data_ready_cb(dev, ring_buf_size_get(data->tx_rb), data->user_data);
|
||||
}
|
||||
|
||||
uart_emul_tx_data_ready(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -597,9 +621,8 @@ static void uart_emul_async_tx_handler(struct k_work *work)
|
|||
LOG_WRN("Lost %" PRIu32 " bytes on loopback", written - loop_written);
|
||||
}
|
||||
}
|
||||
if (data->tx_data_ready_cb) {
|
||||
(data->tx_data_ready_cb)(dev, ring_buf_size_get(data->tx_rb), data->user_data);
|
||||
}
|
||||
|
||||
uart_emul_tx_data_ready(dev);
|
||||
|
||||
if ((config->loopback && written) || !written) {
|
||||
/* 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
|
|||
IF_ENABLED(CONFIG_UART_ASYNC_API, (drv_data->rx_release_on_timeout = release_on_timeout;));
|
||||
}
|
||||
|
||||
int uart_emul_register(const struct device *dev, struct uart_emul *emul)
|
||||
{
|
||||
struct uart_emul_data *data = dev->data;
|
||||
|
||||
sys_slist_append(&data->emuls, &emul->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define UART_EMUL_RX_FIFO_SIZE(inst) (DT_INST_PROP(inst, rx_fifo_size))
|
||||
#define UART_EMUL_TX_FIFO_SIZE(inst) (DT_INST_PROP(inst, tx_fifo_size))
|
||||
|
||||
#define EMUL_LINK_AND_COMMA(node_id) \
|
||||
{ \
|
||||
.dev = DEVICE_DT_GET(node_id), \
|
||||
},
|
||||
|
||||
#define DEFINE_UART_EMUL(inst) \
|
||||
static const struct emul_link_for_bus emuls_##inst[] = { \
|
||||
DT_FOREACH_CHILD_STATUS_OKAY(DT_DRV_INST(inst), EMUL_LINK_AND_COMMA)}; \
|
||||
\
|
||||
RING_BUF_DECLARE(uart_emul_##inst##_rx_rb, UART_EMUL_RX_FIFO_SIZE(inst)); \
|
||||
RING_BUF_DECLARE(uart_emul_##inst##_tx_rb, UART_EMUL_TX_FIFO_SIZE(inst)); \
|
||||
\
|
||||
static struct uart_emul_config uart_emul_cfg_##inst = { \
|
||||
static const struct uart_emul_config uart_emul_cfg_##inst = { \
|
||||
.loopback = DT_INST_PROP(inst, loopback), \
|
||||
.latch_buffer_size = DT_INST_PROP(inst, latch_buffer_size), \
|
||||
.emul_list = { \
|
||||
.children = emuls_##inst, \
|
||||
.num_children = ARRAY_SIZE(emuls_##inst), \
|
||||
}, \
|
||||
}; \
|
||||
static struct uart_emul_data uart_emul_data_##inst = { \
|
||||
.emuls = SYS_SLIST_STATIC_INIT(&_CONCAT(uart_emul_data_, inst).emuls), \
|
||||
.dev = DEVICE_DT_INST_GET(inst), \
|
||||
.rx_rb = &uart_emul_##inst##_rx_rb, \
|
||||
.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
|
|||
uart_emul_async_rx_disable_handler),)) \
|
||||
}; \
|
||||
\
|
||||
static int uart_emul_post_init_##inst(void) \
|
||||
{ \
|
||||
return emul_init_for_bus(DEVICE_DT_INST_GET(inst)); \
|
||||
} \
|
||||
SYS_INIT(uart_emul_post_init_##inst, POST_KERNEL, CONFIG_UART_EMUL_DEVICE_INIT_PRIORITY); \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, NULL, NULL, &uart_emul_data_##inst, &uart_emul_cfg_##inst, \
|
||||
PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &uart_emul_api);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ struct emul;
|
|||
#include <zephyr/drivers/i2c_emul.h>
|
||||
#include <zephyr/drivers/spi_emul.h>
|
||||
#include <zephyr/drivers/mspi_emul.h>
|
||||
#include <zephyr/drivers/uart_emul.h>
|
||||
#include <zephyr/sys/iterable_sections.h>
|
||||
|
||||
/**
|
||||
|
@ -38,6 +39,7 @@ enum emul_bus_type {
|
|||
EMUL_BUS_TYPE_ESPI,
|
||||
EMUL_BUS_TYPE_SPI,
|
||||
EMUL_BUS_TYPE_MSPI,
|
||||
EMUL_BUS_TYPE_UART,
|
||||
EMUL_BUS_TYPE_NONE,
|
||||
};
|
||||
|
||||
|
@ -94,6 +96,7 @@ struct emul {
|
|||
struct espi_emul *espi;
|
||||
struct spi_emul *spi;
|
||||
struct mspi_emul *mspi;
|
||||
struct uart_emul *uart;
|
||||
struct no_bus_emul *none;
|
||||
} bus;
|
||||
/** Address of the API structure exposed by the emulator instance */
|
||||
|
@ -114,12 +117,14 @@ struct emul {
|
|||
#define Z_EMUL_REG_BUS_IDENTIFIER(_dev_node_id) (_CONCAT(_CONCAT(__emulreg_, _dev_node_id), _bus))
|
||||
|
||||
/* Conditionally places text based on what bus _dev_node_id is on. */
|
||||
#define Z_EMUL_BUS(_dev_node_id, _i2c, _espi, _spi, _mspi, _none) \
|
||||
#define Z_EMUL_BUS(_dev_node_id, _i2c, _espi, _spi, _mspi, _uart, _none) \
|
||||
COND_CODE_1(DT_ON_BUS(_dev_node_id, i2c), (_i2c), \
|
||||
(COND_CODE_1(DT_ON_BUS(_dev_node_id, espi), (_espi), \
|
||||
(COND_CODE_1(DT_ON_BUS(_dev_node_id, spi), (_spi), \
|
||||
(COND_CODE_1(DT_ON_BUS(_dev_node_id, mspi), (_mspi), \
|
||||
(_none))))))))
|
||||
(COND_CODE_1(DT_ON_BUS(_dev_node_id, espi), (_espi), \
|
||||
(COND_CODE_1(DT_ON_BUS(_dev_node_id, spi), (_spi), \
|
||||
(COND_CODE_1(DT_ON_BUS(_dev_node_id, mspi), (_mspi), \
|
||||
(COND_CODE_1(DT_ON_BUS(_dev_node_id, uart), (_uart), \
|
||||
(_none))))))))))
|
||||
|
||||
/**
|
||||
* @brief Define a new emulator
|
||||
*
|
||||
|
@ -135,20 +140,21 @@ struct emul {
|
|||
* @param _backend_api emulator-specific backend api
|
||||
*/
|
||||
#define EMUL_DT_DEFINE(node_id, init_fn, data_ptr, cfg_ptr, bus_api, _backend_api) \
|
||||
static struct Z_EMUL_BUS(node_id, i2c_emul, espi_emul, spi_emul, mspi_emul, no_bus_emul) \
|
||||
Z_EMUL_REG_BUS_IDENTIFIER(node_id) = { \
|
||||
.api = bus_api, \
|
||||
.Z_EMUL_BUS(node_id, addr, chipsel, chipsel, dev_idx, addr) = \
|
||||
DT_REG_ADDR(node_id), \
|
||||
}; \
|
||||
static struct Z_EMUL_BUS(node_id, i2c_emul, espi_emul, spi_emul, mspi_emul, uart_emul, \
|
||||
no_bus_emul) Z_EMUL_REG_BUS_IDENTIFIER(node_id) = { \
|
||||
.api = bus_api, \
|
||||
IF_ENABLED(DT_NODE_HAS_PROP(node_id, reg), \
|
||||
(.Z_EMUL_BUS(node_id, addr, chipsel, chipsel, dev_idx, dummy, addr) = \
|
||||
DT_REG_ADDR(node_id),))}; \
|
||||
const STRUCT_SECTION_ITERABLE(emul, EMUL_DT_NAME_GET(node_id)) __used = { \
|
||||
.init = (init_fn), \
|
||||
.dev = DEVICE_DT_GET(node_id), \
|
||||
.cfg = (cfg_ptr), \
|
||||
.data = (data_ptr), \
|
||||
.bus_type = Z_EMUL_BUS(node_id, EMUL_BUS_TYPE_I2C, EMUL_BUS_TYPE_ESPI, \
|
||||
EMUL_BUS_TYPE_SPI, EMUL_BUS_TYPE_MSPI, EMUL_BUS_TYPE_NONE), \
|
||||
.bus = {.Z_EMUL_BUS(node_id, i2c, espi, spi, mspi, none) = \
|
||||
EMUL_BUS_TYPE_SPI, EMUL_BUS_TYPE_MSPI, EMUL_BUS_TYPE_UART, \
|
||||
EMUL_BUS_TYPE_NONE), \
|
||||
.bus = {.Z_EMUL_BUS(node_id, i2c, espi, spi, mspi, uart, none) = \
|
||||
&(Z_EMUL_REG_BUS_IDENTIFIER(node_id))}, \
|
||||
.backend_api = (_backend_api), \
|
||||
};
|
||||
|
|
76
include/zephyr/drivers/uart_emul.h
Normal file
76
include/zephyr/drivers/uart_emul.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2024 Basalte bv
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_UART_EMUL_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_UART_EMUL_H_
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/emul.h>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/sys/slist.h>
|
||||
#include <zephyr/types.h>
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Public APIs for the UART device emulation drivers.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief UART Emulation Interface
|
||||
* @defgroup uart_emul_interface UART Emulation Interface
|
||||
* @ingroup io_emulators
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct uart_emul_device_api;
|
||||
|
||||
/**
|
||||
* @brief Define the emulation callback function signature
|
||||
*
|
||||
* @param dev UART device instance
|
||||
* @param size Number of available bytes in TX buffer
|
||||
* @param target pointer to emulation context
|
||||
*/
|
||||
typedef void (*uart_emul_device_tx_data_ready_t)(const struct device *dev, size_t size,
|
||||
const struct emul *target);
|
||||
|
||||
/** Node in a linked list of emulators for UART devices */
|
||||
struct uart_emul {
|
||||
sys_snode_t node;
|
||||
/** Target emulator - REQUIRED for all emulated bus nodes of any type */
|
||||
const struct emul *target;
|
||||
/** API provided for this device */
|
||||
const struct uart_emul_device_api *api;
|
||||
};
|
||||
|
||||
/** Definition of the emulator API */
|
||||
struct uart_emul_device_api {
|
||||
uart_emul_device_tx_data_ready_t tx_data_ready;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register an emulated device on the controller
|
||||
*
|
||||
* @param dev Device that will use the emulator
|
||||
* @param emul UART emulator to use
|
||||
* @return 0 indicating success
|
||||
*/
|
||||
int uart_emul_register(const struct device *dev, struct uart_emul *emul);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_UART_EMUL_H_ */
|
|
@ -58,6 +58,9 @@ int emul_init_for_bus(const struct device *dev)
|
|||
case EMUL_BUS_TYPE_MSPI:
|
||||
emul->bus.mspi->target = emul;
|
||||
break;
|
||||
case EMUL_BUS_TYPE_UART:
|
||||
emul->bus.uart->target = emul;
|
||||
break;
|
||||
case EMUL_BUS_TYPE_NONE:
|
||||
break;
|
||||
}
|
||||
|
@ -89,6 +92,11 @@ int emul_init_for_bus(const struct device *dev)
|
|||
rc = mspi_emul_register(dev, emul->bus.mspi);
|
||||
break;
|
||||
#endif /* CONFIG_MSPI_EMUL */
|
||||
#ifdef CONFIG_UART_EMUL
|
||||
case EMUL_BUS_TYPE_UART:
|
||||
rc = uart_emul_register(dev, emul->bus.uart);
|
||||
break;
|
||||
#endif /* CONFIG_UART_EMUL */
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
LOG_WRN("Found no emulated bus enabled to register emulator %s",
|
||||
|
|
|
@ -5,4 +5,4 @@ CONFIG_CONSOLE_SUBSYS=y
|
|||
CONFIG_CONSOLE_GETLINE=y
|
||||
CONFIG_DEVICE_MUTABLE=y
|
||||
CONFIG_DEVMUX=y
|
||||
CONFIG_UART_EMUL=y
|
||||
CONFIG_EMUL=y
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
CONFIG_ZTEST=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_EMUL=y
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
CONFIG_ZTEST=y
|
||||
CONFIG_ASSERT=y
|
||||
CONFIG_TEST_LOGGING_DEFAULTS=n
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_EMUL=y
|
||||
|
||||
CONFIG_LOG=y
|
||||
CONFIG_LOG_BACKEND_UART=y
|
||||
|
|
|
@ -6,3 +6,4 @@ CONFIG_SHELL_PRINTF_BUFF_SIZE=15
|
|||
CONFIG_SHELL_METAKEYS=n
|
||||
CONFIG_LOG=n
|
||||
CONFIG_ZTEST=y
|
||||
CONFIG_EMUL=y
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue