| /* |
| * 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 |