/* * Copyright (c) 2019 omSquare s.r.o. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #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_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