blob: d5116dee2c0edef370ccc9cdf1d3b12395b8b39d [file] [log] [blame]
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_SERIAL_UART_ASYNC_TO_IRQ_H_
#define ZEPHYR_DRIVERS_SERIAL_UART_ASYNC_TO_IRQ_H_
#include <zephyr/drivers/uart.h>
#include <zephyr/logging/log.h>
#include <zephyr/spinlock.h>
#include <zephyr/sys/util.h>
#include <zephyr/drivers/serial/uart_async_rx.h>
/**
* @brief UART Asynchronous to Interrupt driven API adaptation layer
* @ingroup uart_interface
* @{
*/
#ifdef __cplusplus
extern "C" {
#endif
/* Forward declarations. */
/** @brief Data structure used by the adaptation layer.
*
* Pointer to that data must be the first element of the UART device data structure.
*/
struct uart_async_to_irq_data;
/** @brief Configuration structure used by the adaptation layer.
*
* Pointer to this data must be the first element of the UART device configuration structure.
*/
struct uart_async_to_irq_config;
/* @brief Function that triggers trampoline to the interrupt context.
*
* This context is used to call user UART interrupt handler. It is to used to
* fulfill the requirement that UART interrupt driven API shall be called from
* the UART interrupt. Trampoline context shall have the same priority as UART.
*
* One option may be to use k_timer configured to expire immediately.
*/
typedef void (*uart_async_to_irq_trampoline)(const struct device *dev);
/** @brief Callback to be called from trampoline context.
*
* @param dev UART device.
*/
void uart_async_to_irq_trampoline_cb(const struct device *dev);
/** @brief Interrupt driven API initializer.
*
* It should be used in the initialization of the UART API structure in the
* driver to provide interrupt driven API functions.
*/
#define UART_ASYNC_TO_IRQ_API_INIT() \
.fifo_fill = z_uart_async_to_irq_fifo_fill, \
.fifo_read = z_uart_async_to_irq_fifo_read, \
.irq_tx_enable = z_uart_async_to_irq_irq_tx_enable, \
.irq_tx_disable = z_uart_async_to_irq_irq_tx_disable, \
.irq_tx_ready = z_uart_async_to_irq_irq_tx_ready, \
.irq_rx_enable = z_uart_async_to_irq_irq_rx_enable, \
.irq_rx_disable = z_uart_async_to_irq_irq_rx_disable, \
.irq_tx_complete = z_uart_async_to_irq_irq_tx_complete,\
.irq_rx_ready = z_uart_async_to_irq_irq_rx_ready, \
.irq_err_enable = z_uart_async_to_irq_irq_err_enable, \
.irq_err_disable = z_uart_async_to_irq_irq_err_disable,\
.irq_is_pending = z_uart_async_to_irq_irq_is_pending, \
.irq_update = z_uart_async_to_irq_irq_update, \
.irq_callback_set = z_uart_async_to_irq_irq_callback_set
/** @brief Configuration structure initializer.
*
* @param _api Structure with UART asynchronous API.
* @param _trampoline Function that trampolines to the interrupt context.
* @param _baudrate UART baudrate.
* @param _tx_buf TX buffer.
* @param _tx_len TX buffer length.
* @param _rx_buf RX buffer.
* @param _rx_len RX buffer length.
* @param _rx_cnt Number of chunks into which RX buffer is divided.
* @param _log Logging instance, if not provided (empty) then default is used.
*/
#define UART_ASYNC_TO_IRQ_API_CONFIG_INITIALIZER(_api, _trampoline, _baudrate, _tx_buf, \
_tx_len, _rx_buf, _rx_len, _rx_cnt, _log) \
{ \
.tx_buf = _tx_buf, \
.tx_len = _tx_len, \
.async_rx = { \
.buffer = _rx_buf, \
.length = _rx_len, \
.buf_cnt = _rx_cnt \
}, \
.api = _api, \
.trampoline = _trampoline, \
.baudrate = _baudrate, \
LOG_OBJECT_PTR_INIT(log, \
COND_CODE_1(IS_EMPTY(_log), \
(LOG_OBJECT_PTR(UART_ASYNC_TO_IRQ_LOG_NAME)), \
(_log) \
) \
) \
}
/** @brief Initialize the adaptation layer.
*
* @param data Data associated with the given adaptation layer instance.
* @param config Configuration structure. Must be persistent.
*
* @retval 0 On successful initialization.
*/
int uart_async_to_irq_init(struct uart_async_to_irq_data *data,
const struct uart_async_to_irq_config *config);
/* @brief Enable RX for interrupt driven API.
*
* @param dev UART device. Device must support asynchronous API.
*
* @retval 0 on successful operation.
* @retval -EINVAL if adaption layer has wrong configuration.
* @retval negative value Error reported by the UART API.
*/
int uart_async_to_irq_rx_enable(const struct device *dev);
/* @brief Disable RX for interrupt driven API.
*
* @param dev UART device. Device must support asynchronous API.
*
* @retval 0 on successful operation.
* @retval -EINVAL if adaption layer has wrong configuration.
* @retval negative value Error reported by the UART API.
*/
int uart_async_to_irq_rx_disable(const struct device *dev);
/* Starting from here API is internal only. */
/** @cond INTERNAL_HIDDEN
* @brief Structure used by the adaptation layer.
*/
struct uart_async_to_irq_config {
/** Pointer to the TX buffer. */
uint8_t *tx_buf;
/** TX buffer length. */
size_t tx_len;
/** UART Asynchronous RX helper configuration. */
struct uart_async_rx_config async_rx;
/** Async API used by the a2i layer. */
const struct uart_async_to_irq_async_api *api;
/** Trampoline callback. */
uart_async_to_irq_trampoline trampoline;
/** Initial baudrate. */
uint32_t baudrate;
/** Instance logging handler. */
LOG_INSTANCE_PTR_DECLARE(log);
};
/** @brief Asynchronous API used by the adaptation layer. */
struct uart_async_to_irq_async_api {
int (*callback_set)(const struct device *dev,
uart_callback_t callback,
void *user_data);
int (*tx)(const struct device *dev, const uint8_t *buf, size_t len,
int32_t timeout);
int (*tx_abort)(const struct device *dev);
int (*rx_enable)(const struct device *dev, uint8_t *buf, size_t len,
int32_t timeout);
int (*rx_buf_rsp)(const struct device *dev, uint8_t *buf, size_t len);
int (*rx_disable)(const struct device *dev);
};
/** @brief Structure holding receiver data. */
struct uart_async_to_irq_rx_data {
/** Asynchronous RX helper data. */
struct uart_async_rx async_rx;
/** Semaphore for pending on RX disable. */
struct k_sem sem;
/** Number of pending buffer requests which weren't handled because lack of free buffers. */
atomic_t pending_buf_req;
};
/** @brief Structure holding transmitter data. */
struct uart_async_to_irq_tx_data {
/** TX buffer. */
uint8_t *buf;
/** Length of the buffer. */
size_t len;
};
/** @briref Data associated with the asynchronous to the interrupt driven API adaptation layer. */
struct uart_async_to_irq_data {
/** User callback for interrupt driven API. */
uart_irq_callback_user_data_t callback;
/** User data. */
void *user_data;
/** Interrupt request counter. */
atomic_t irq_req;
/** RX specific data. */
struct uart_async_to_irq_rx_data rx;
/** TX specific data. */
struct uart_async_to_irq_tx_data tx;
/** Spinlock. */
struct k_spinlock lock;
/** Internally used flags for holding the state of the a2i layer. */
atomic_t flags;
};
/** Interrupt driven FIFO fill function. */
int z_uart_async_to_irq_fifo_fill(const struct device *dev,
const uint8_t *buf,
int len);
/** Interrupt driven FIFO read function. */
int z_uart_async_to_irq_fifo_read(const struct device *dev,
uint8_t *buf,
const int len);
/** Interrupt driven transfer enabling function. */
void z_uart_async_to_irq_irq_tx_enable(const struct device *dev);
/** Interrupt driven transfer disabling function */
void z_uart_async_to_irq_irq_tx_disable(const struct device *dev);
/** Interrupt driven transfer ready function */
int z_uart_async_to_irq_irq_tx_ready(const struct device *dev);
/** Interrupt driven receiver enabling function */
void z_uart_async_to_irq_irq_rx_enable(const struct device *dev);
/** Interrupt driven receiver disabling function */
void z_uart_async_to_irq_irq_rx_disable(const struct device *dev);
/** Interrupt driven transfer complete function */
int z_uart_async_to_irq_irq_tx_complete(const struct device *dev);
/** Interrupt driven receiver ready function */
int z_uart_async_to_irq_irq_rx_ready(const struct device *dev);
/** Interrupt driven error enabling function */
void z_uart_async_to_irq_irq_err_enable(const struct device *dev);
/** Interrupt driven error disabling function */
void z_uart_async_to_irq_irq_err_disable(const struct device *dev);
/** Interrupt driven pending status function */
int z_uart_async_to_irq_irq_is_pending(const struct device *dev);
/** Interrupt driven interrupt update function */
int z_uart_async_to_irq_irq_update(const struct device *dev);
/** Set the irq callback function */
void z_uart_async_to_irq_irq_callback_set(const struct device *dev,
uart_irq_callback_user_data_t cb,
void *user_data);
/** @endcond */
#ifdef __cplusplus
}
#endif
/** @} */
#endif /* ZEPHYR_DRIVERS_SERIAL_UART_ASYNC_TO_IRQ_H_ */