Browse Source

drivers: uart_emul: Add IRQ Based TX

Added an interrupt based transmit routine and interrupt based
uart_emul tests.

Signed-off-by: Nick Kraus <nick@nckraus.com>
pull/59720/head
Nick Kraus 2 years ago committed by Carles Cufí
parent
commit
aeb85db627
  1. 87
      drivers/serial/uart_emul.c
  2. 1
      tests/drivers/uart/uart_emul/prj.conf
  3. 62
      tests/drivers/uart/uart_emul/src/main.c

87
drivers/serial/uart_emul.c

@ -41,6 +41,7 @@ struct uart_emul_data { @@ -41,6 +41,7 @@ struct uart_emul_data {
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
bool rx_irq_en;
bool tx_irq_en;
struct uart_emul_work irq_work;
uart_irq_callback_user_data_t irq_cb;
void *irq_cb_udata;
@ -114,6 +115,26 @@ static int uart_emul_config_get(const struct device *dev, struct uart_config *cf @@ -114,6 +115,26 @@ static int uart_emul_config_get(const struct device *dev, struct uart_config *cf
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static int uart_emul_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size)
{
int ret;
struct uart_emul_data *data = dev->data;
const struct uart_emul_config *config = dev->config;
K_SPINLOCK(&data->tx_lock) {
ret = ring_buf_put(data->tx_rb, tx_data, size);
}
if (config->loopback) {
uart_emul_put_rx_data(dev, (uint8_t *)tx_data, ret);
}
if (data->tx_data_ready_cb) {
data->tx_data_ready_cb(dev, ring_buf_size_get(data->tx_rb), data->user_data);
}
return ret;
}
static int uart_emul_fifo_read(const struct device *dev, uint8_t *rx_data, int size)
{
int ret;
@ -131,6 +152,22 @@ static int uart_emul_fifo_read(const struct device *dev, uint8_t *rx_data, int s @@ -131,6 +152,22 @@ static int uart_emul_fifo_read(const struct device *dev, uint8_t *rx_data, int s
return ret;
}
static int uart_emul_irq_tx_ready(const struct device *dev)
{
bool ready = false;
struct uart_emul_data *data = dev->data;
K_SPINLOCK(&data->tx_lock) {
if (!data->tx_irq_en) {
K_SPINLOCK_BREAK;
}
ready = ring_buf_space_get(data->tx_rb) > 0;
}
return ready;
}
static int uart_emul_irq_rx_ready(const struct device *dev)
{
bool ready = false;
@ -163,6 +200,14 @@ static void uart_emul_irq_handler(struct k_work *work) @@ -163,6 +200,14 @@ static void uart_emul_irq_handler(struct k_work *work)
while (true) {
bool have_work = false;
K_SPINLOCK(&data->tx_lock) {
if (!data->tx_irq_en) {
K_SPINLOCK_BREAK;
}
have_work = have_work || ring_buf_space_get(data->tx_rb) > 0;
}
K_SPINLOCK(&data->rx_lock) {
if (!data->rx_irq_en) {
K_SPINLOCK_BREAK;
@ -181,14 +226,22 @@ static void uart_emul_irq_handler(struct k_work *work) @@ -181,14 +226,22 @@ static void uart_emul_irq_handler(struct k_work *work)
static int uart_emul_irq_is_pending(const struct device *dev)
{
bool rx_pending;
return uart_emul_irq_tx_ready(dev) || uart_emul_irq_rx_ready(dev);
}
static void uart_emul_irq_tx_enable(const struct device *dev)
{
bool submit_irq_work;
struct uart_emul_data *const data = dev->data;
K_SPINLOCK(&data->rx_lock) {
rx_pending = !ring_buf_is_empty(data->rx_rb);
K_SPINLOCK(&data->tx_lock) {
data->tx_irq_en = true;
submit_irq_work = ring_buf_space_get(data->tx_rb) > 0;
}
return rx_pending;
if (submit_irq_work) {
(void)k_work_submit(&data->irq_work.work);
}
}
static void uart_emul_irq_rx_enable(const struct device *dev)
@ -206,6 +259,15 @@ static void uart_emul_irq_rx_enable(const struct device *dev) @@ -206,6 +259,15 @@ static void uart_emul_irq_rx_enable(const struct device *dev)
}
}
static void uart_emul_irq_tx_disable(const struct device *dev)
{
struct uart_emul_data *const data = dev->data;
K_SPINLOCK(&data->tx_lock) {
data->tx_irq_en = false;
}
}
static void uart_emul_irq_rx_disable(const struct device *dev)
{
struct uart_emul_data *const data = dev->data;
@ -215,6 +277,18 @@ static void uart_emul_irq_rx_disable(const struct device *dev) @@ -215,6 +277,18 @@ static void uart_emul_irq_rx_disable(const struct device *dev)
}
}
static int uart_emul_irq_tx_complete(const struct device *dev)
{
bool tx_complete = false;
struct uart_emul_data *const data = dev->data;
K_SPINLOCK(&data->tx_lock) {
tx_complete = ring_buf_is_empty(data->tx_rb);
}
return tx_complete;
}
static void uart_emul_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
void *user_data)
{
@ -239,10 +313,15 @@ static const struct uart_driver_api uart_emul_api = { @@ -239,10 +313,15 @@ static const struct uart_driver_api uart_emul_api = {
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
.err_check = uart_emul_err_check,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.fifo_fill = uart_emul_fifo_fill,
.fifo_read = uart_emul_fifo_read,
.irq_tx_enable = uart_emul_irq_tx_enable,
.irq_rx_enable = uart_emul_irq_rx_enable,
.irq_tx_disable = uart_emul_irq_tx_disable,
.irq_rx_disable = uart_emul_irq_rx_disable,
.irq_tx_ready = uart_emul_irq_tx_ready,
.irq_rx_ready = uart_emul_irq_rx_ready,
.irq_tx_complete = uart_emul_irq_tx_complete,
.irq_callback_set = uart_emul_irq_callback_set,
.irq_update = uart_emul_irq_update,
.irq_is_pending = uart_emul_irq_is_pending,

1
tests/drivers/uart/uart_emul/prj.conf

@ -1,2 +1,3 @@ @@ -1,2 +1,3 @@
CONFIG_ZTEST=y
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y

62
tests/drivers/uart/uart_emul/src/main.c

@ -35,6 +35,9 @@ static void uart_emul_before(void *f) @@ -35,6 +35,9 @@ static void uart_emul_before(void *f)
{
struct uart_emul_fixture *fixture = f;
uart_irq_tx_disable(fixture->dev);
uart_irq_rx_disable(fixture->dev);
uart_emul_flush_rx_data(fixture->dev);
uart_emul_flush_tx_data(fixture->dev);
}
@ -75,4 +78,63 @@ ZTEST_F(uart_emul, test_polling_in) @@ -75,4 +78,63 @@ ZTEST_F(uart_emul, test_polling_in)
zassert_equal(rc, -1, "RX buffer should be empty");
}
static void uart_emul_isr(const struct device *dev, void *user_data)
{
/* always of size SAMPLE_DATA_SIZE */
uint8_t *buf = user_data;
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
if (uart_irq_tx_ready(dev)) {
uart_fifo_fill(dev, buf, SAMPLE_DATA_SIZE);
uart_irq_tx_disable(dev);
}
if (uart_irq_rx_ready(dev)) {
uart_fifo_read(dev, buf, SAMPLE_DATA_SIZE);
}
}
}
ZTEST_F(uart_emul, test_irq_tx)
{
uint8_t tx_content[SAMPLE_DATA_SIZE] = {0};
size_t tx_len;
uart_irq_callback_user_data_set(fixture->dev, uart_emul_isr, fixture->sample_data);
/* enabling the tx irq will call the callback, if set */
uart_irq_tx_enable(fixture->dev);
/* allow space for work queue to run */
k_yield();
tx_len = uart_emul_get_tx_data(fixture->dev, tx_content, SAMPLE_DATA_SIZE);
zassert_equal(tx_len, SAMPLE_DATA_SIZE, "TX buffer length does not match");
zassert_mem_equal(tx_content, fixture->sample_data, SAMPLE_DATA_SIZE);
/* No more data in TX buffer */
tx_len = uart_emul_get_tx_data(fixture->dev, tx_content, sizeof(tx_content));
zassert_equal(tx_len, 0, "TX buffer should be empty");
}
ZTEST_F(uart_emul, test_irq_rx)
{
uint8_t rx_content[SAMPLE_DATA_SIZE] = {0};
int rc;
uart_irq_callback_user_data_set(fixture->dev, uart_emul_isr, rx_content);
uart_irq_rx_enable(fixture->dev);
/* putting rx data will call the irq callback, if enabled */
uart_emul_put_rx_data(fixture->dev, fixture->sample_data, SAMPLE_DATA_SIZE);
/* allow space for work queue to run */
k_yield();
zassert_mem_equal(rx_content, fixture->sample_data, SAMPLE_DATA_SIZE);
/* No more data in RX buffer */
rc = uart_poll_in(fixture->dev, &rx_content[0]);
zassert_equal(rc, -1, "RX buffer should be empty");
uart_irq_rx_disable(fixture->dev);
}
ZTEST_SUITE(uart_emul, NULL, uart_emul_setup, uart_emul_before, NULL, NULL);

Loading…
Cancel
Save