drivers: serial: uart_rtt async support

Add support for `CONFIG_UART_ASYNC_API` to the uart_rtt driver.
As RTT provides no mechanism for knowing when new data has arrived,
the reception commands all return errors.

This fixes hard faults when asynchronous calls are made on the uart_rtt
driver, in addition to providing a small level of functionality.

Signed-off-by: Jordan Yates <jordan.yates@data61.csiro.au>
This commit is contained in:
Jordan Yates 2020-08-21 15:38:46 +10:00 committed by Carles Cufí
commit 1d0acd2bb8

View file

@ -17,11 +17,25 @@ struct uart_rtt_config {
uint8_t channel;
};
static inline const struct uart_rtt_config *get_dev_config(const struct device *dev)
struct uart_rtt_data {
#ifdef CONFIG_UART_ASYNC_API
uart_callback_t callback;
void *user_data;
#endif /* CONFIG_UART_ASYNC_API */
};
static inline
const struct uart_rtt_config *get_dev_config(const struct device *dev)
{
return dev->config;
}
static inline
struct uart_rtt_data *get_dev_data(const struct device *dev)
{
return dev->data;
}
static int uart_rtt_init(const struct device *dev)
{
/*
@ -74,9 +88,111 @@ static void uart_rtt_poll_out(const struct device *dev, unsigned char c)
SEGGER_RTT_Write(ch, &c, 1);
}
#ifdef CONFIG_UART_ASYNC_API
static int uart_rtt_callback_set(const struct device *dev,
uart_callback_t callback, void *user_data)
{
get_dev_data(dev)->callback = callback;
get_dev_data(dev)->user_data = user_data;
return 0;
}
static int uart_rtt_tx(const struct device *dev,
const uint8_t *buf, size_t len, int32_t timeout)
{
const struct uart_rtt_config *cfg = get_dev_config(dev);
struct uart_rtt_data *data = get_dev_data(dev);
unsigned int ch = cfg ? cfg->channel : 0;
ARG_UNUSED(timeout);
/* RTT mutex cannot be claimed in ISRs */
if (k_is_in_isr()) {
return -ENOTSUP;
}
/* Claim the RTT lock */
if (k_mutex_lock(&rtt_term_mutex, K_NO_WAIT) != 0) {
return -EBUSY;
}
/* Output the buffer */
SEGGER_RTT_WriteNoLock(ch, buf, len);
/* Return RTT lock */
SEGGER_RTT_UNLOCK();
/* Send the TX complete callback */
if (data->callback) {
struct uart_event evt = {
.type = UART_TX_DONE,
.data.tx.buf = buf,
.data.tx.len = len
};
data->callback(dev, &evt, data->user_data);
}
return 0;
}
static int uart_rtt_tx_abort(const struct device *dev)
{
/* RTT TX is a memcpy, there is never a transmission to abort */
ARG_UNUSED(dev);
return -EFAULT;
}
static int uart_rtt_rx_enable(const struct device *dev,
uint8_t *buf, size_t len, int32_t timeout)
{
/* SEGGER RTT reception is implemented as a direct memory write to RAM
* by a connected debugger. As such there is no hardware interrupt
* or other mechanism to know when the debugger has added data to be
* read. Asynchronous RX does not make sense in such a context, and is
* therefore not supported.
*/
ARG_UNUSED(dev);
ARG_UNUSED(buf);
ARG_UNUSED(len);
ARG_UNUSED(timeout);
return -ENOTSUP;
}
static int uart_rtt_rx_disable(const struct device *dev)
{
/* Asynchronous RX not supported, see uart_rtt_rx_enable */
ARG_UNUSED(dev);
return -EFAULT;
}
static int uart_rtt_rx_buf_rsp(const struct device *dev,
uint8_t *buf, size_t len)
{
/* Asynchronous RX not supported, see uart_rtt_rx_enable */
ARG_UNUSED(dev);
ARG_UNUSED(buf);
ARG_UNUSED(len);
return -ENOTSUP;
}
#endif /* CONFIG_UART_ASYNC_API */
static const struct uart_driver_api uart_rtt_driver_api = {
.poll_in = uart_rtt_poll_in,
.poll_out = uart_rtt_poll_out,
#ifdef CONFIG_UART_ASYNC_API
.callback_set = uart_rtt_callback_set,
.tx = uart_rtt_tx,
.tx_abort = uart_rtt_tx_abort,
.rx_enable = uart_rtt_rx_enable,
.rx_buf_rsp = uart_rtt_rx_buf_rsp,
.rx_disable = uart_rtt_rx_disable,
#endif /* CONFIG_UART_ASYNC_API */
};
#define UART_RTT(idx) DT_NODELABEL(rtt##idx)
@ -97,8 +213,10 @@ static const struct uart_driver_api uart_rtt_driver_api = {
}
#define UART_RTT_INIT(idx, config) \
struct uart_rtt_data uart_rtt##idx##_data; \
\
DEVICE_AND_API_INIT(uart_rtt##idx, DT_LABEL(UART_RTT(idx)), \
uart_rtt_init, NULL, config, \
uart_rtt_init, &uart_rtt##idx##_data, config, \
PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&uart_rtt_driver_api)