zephyr/drivers/serial/uart_rtt.c
Jordan Yates 60442a221a modules: segger: remove mutex locking
Remove mutex locking in favour of the standard IRQ locking mechanism.
The primary problem with the mutex implementation is that mutex locking
is forbidden in ISR's. This means that any logging from an interrupt
context (e.g. LOG_PANIC in an exception handler), will itself trigger
another assertion due its attempt to use a mutex.

Furthermore, mutexes are a relatively heavyweight locking scheme, which
doesn't necessarily make sense in the context of extremely short locking
periods that would be expected from RTT.

This change aligns Zephyr with the default RTT locking scheme, which
uses interrupt masking to perform access control.

Resolves #79403.

Signed-off-by: Jordan Yates <jordan@embeint.com>
2024-11-07 11:06:45 -08:00

220 lines
5.4 KiB
C

/*
* Copyright (c) 2019 omSquare s.r.o.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/uart.h>
#include <zephyr/kernel.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 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 (dev->config) {
const struct uart_rtt_config *cfg = dev->config;
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)
{
const struct uart_rtt_config *config = dev->config;
unsigned int ch = config ? config->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)
{
const struct uart_rtt_config *config = dev->config;
unsigned int ch = config ? config->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)
{
struct uart_rtt_data *data = dev->data;
data->callback = callback;
data->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 = dev->config;
struct uart_rtt_data *data = dev->data;
unsigned int ch = cfg ? cfg->channel : 0;
ARG_UNUSED(timeout);
/* Output the buffer */
SEGGER_RTT_Write(ch, buf, len);
/* 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), \
.channel = idx, \
}
#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_SERIAL_INIT_PRIORITY, \
&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