241 lines
5.7 KiB
C
241 lines
5.7 KiB
C
/*
|
|
* Copyright (c) 2019 omSquare s.r.o.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <drivers/uart.h>
|
|
#include <SEGGER_RTT.h>
|
|
|
|
#define DT_DRV_COMPAT segger_rtt_uart
|
|
|
|
struct uart_rtt_config {
|
|
void *up_buffer;
|
|
size_t up_size;
|
|
void *down_buffer;
|
|
size_t down_size;
|
|
uint8_t channel;
|
|
};
|
|
|
|
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)
|
|
{
|
|
/*
|
|
* Channel 0 is initialized at compile-time, Kconfig ensures that
|
|
* it is configured in correct, non-blocking mode. Other channels
|
|
* need to be configured at run-time.
|
|
*/
|
|
if (get_dev_config(dev)) {
|
|
const struct uart_rtt_config *cfg = get_dev_config(dev);
|
|
|
|
SEGGER_RTT_ConfigUpBuffer(cfg->channel, dev->name,
|
|
cfg->up_buffer, cfg->up_size,
|
|
SEGGER_RTT_MODE_NO_BLOCK_SKIP);
|
|
SEGGER_RTT_ConfigDownBuffer(cfg->channel, dev->name,
|
|
cfg->down_buffer, cfg->down_size,
|
|
SEGGER_RTT_MODE_NO_BLOCK_SKIP);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Poll the device for input.
|
|
*
|
|
* @param dev UART device struct
|
|
* @param c Pointer to character
|
|
*
|
|
* @return 0 if a character arrived, -1 if the input buffer if empty.
|
|
*/
|
|
|
|
static int uart_rtt_poll_in(const struct device *dev, unsigned char *c)
|
|
{
|
|
unsigned int ch =
|
|
get_dev_config(dev) ? get_dev_config(dev)->channel : 0;
|
|
unsigned int ret = SEGGER_RTT_Read(ch, c, 1);
|
|
|
|
return ret ? 0 : -1;
|
|
}
|
|
|
|
/**
|
|
* @brief Output a character in polled mode.
|
|
*
|
|
* @param dev UART device struct
|
|
* @param c Character to send
|
|
*/
|
|
static void uart_rtt_poll_out(const struct device *dev, unsigned char c)
|
|
{
|
|
unsigned int ch =
|
|
get_dev_config(dev) ? get_dev_config(dev)->channel : 0;
|
|
|
|
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)
|
|
#define UART_RTT_PROP(idx, prop) DT_PROP(UART_RTT(idx), prop)
|
|
#define UART_RTT_CONFIG_NAME(idx) uart_rtt##idx##_config
|
|
|
|
#define UART_RTT_CONFIG(idx) \
|
|
static \
|
|
uint8_t uart_rtt##idx##_tx_buf[UART_RTT_PROP(idx, tx_buffer_size)]; \
|
|
static \
|
|
uint8_t uart_rtt##idx##_rx_buf[UART_RTT_PROP(idx, rx_buffer_size)]; \
|
|
\
|
|
static const struct uart_rtt_config UART_RTT_CONFIG_NAME(idx) = { \
|
|
.up_buffer = uart_rtt##idx##_tx_buf, \
|
|
.up_size = sizeof(uart_rtt##idx##_tx_buf), \
|
|
.down_buffer = uart_rtt##idx##_rx_buf, \
|
|
.down_size = sizeof(uart_rtt##idx##_rx_buf), \
|
|
}
|
|
|
|
#define UART_RTT_INIT(idx, config) \
|
|
struct uart_rtt_data uart_rtt##idx##_data; \
|
|
\
|
|
DEVICE_DT_DEFINE(UART_RTT(idx), uart_rtt_init, NULL, \
|
|
&uart_rtt##idx##_data, config, \
|
|
PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
|
&uart_rtt_driver_api)
|
|
|
|
#ifdef CONFIG_UART_RTT_0
|
|
UART_RTT_INIT(0, NULL);
|
|
#endif
|
|
|
|
#ifdef CONFIG_UART_RTT_1
|
|
UART_RTT_CONFIG(1);
|
|
UART_RTT_INIT(1, &UART_RTT_CONFIG_NAME(1));
|
|
#endif
|
|
|
|
#ifdef CONFIG_UART_RTT_2
|
|
UART_RTT_CONFIG(2);
|
|
UART_RTT_INIT(2, &UART_RTT_CONFIG_NAME(2));
|
|
#endif
|
|
|
|
#ifdef CONFIG_UART_RTT_3
|
|
UART_RTT_CONFIG(3);
|
|
UART_RTT_INIT(3, &UART_RTT_CONFIG_NAME(3));
|
|
#endif
|