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>
This commit is contained in:
parent
acaaa9e2c5
commit
aeb85db627
3 changed files with 146 additions and 4 deletions
|
@ -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
|
|||
#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
|
|||
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)
|
|||
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)
|
|||
|
||||
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)
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
|||
}
|
||||
}
|
||||
|
||||
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 = {
|
|||
#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,2 +1,3 @@
|
|||
CONFIG_ZTEST=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_UART_INTERRUPT_DRIVEN=y
|
||||
|
|
|
@ -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)
|
|||
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…
Add table
Add a link
Reference in a new issue