1 /*
2 * Copyright (c) 2019 omSquare s.r.o.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/drivers/uart.h>
8 #include <zephyr/kernel.h>
9 #include <SEGGER_RTT.h>
10
11 #define DT_DRV_COMPAT segger_rtt_uart
12
13 extern struct k_mutex rtt_term_mutex;
14
15 struct uart_rtt_config {
16 void *up_buffer;
17 size_t up_size;
18 void *down_buffer;
19 size_t down_size;
20 uint8_t channel;
21 };
22
23 struct uart_rtt_data {
24 #ifdef CONFIG_UART_ASYNC_API
25 uart_callback_t callback;
26 void *user_data;
27 #endif /* CONFIG_UART_ASYNC_API */
28 };
29
uart_rtt_init(const struct device * dev)30 static int uart_rtt_init(const struct device *dev)
31 {
32 /*
33 * Channel 0 is initialized at compile-time, Kconfig ensures that
34 * it is configured in correct, non-blocking mode. Other channels
35 * need to be configured at run-time.
36 */
37 if (dev->config) {
38 const struct uart_rtt_config *cfg = dev->config;
39
40 SEGGER_RTT_ConfigUpBuffer(cfg->channel, dev->name,
41 cfg->up_buffer, cfg->up_size,
42 SEGGER_RTT_MODE_NO_BLOCK_SKIP);
43 SEGGER_RTT_ConfigDownBuffer(cfg->channel, dev->name,
44 cfg->down_buffer, cfg->down_size,
45 SEGGER_RTT_MODE_NO_BLOCK_SKIP);
46 }
47 return 0;
48 }
49
50 /**
51 * @brief Poll the device for input.
52 *
53 * @param dev UART device struct
54 * @param c Pointer to character
55 *
56 * @return 0 if a character arrived, -1 if the input buffer if empty.
57 */
58
uart_rtt_poll_in(const struct device * dev,unsigned char * c)59 static int uart_rtt_poll_in(const struct device *dev, unsigned char *c)
60 {
61 const struct uart_rtt_config *config = dev->config;
62 unsigned int ch = config ? config->channel : 0;
63 unsigned int ret = SEGGER_RTT_Read(ch, c, 1);
64
65 return ret ? 0 : -1;
66 }
67
68 /**
69 * @brief Output a character in polled mode.
70 *
71 * @param dev UART device struct
72 * @param c Character to send
73 */
uart_rtt_poll_out(const struct device * dev,unsigned char c)74 static void uart_rtt_poll_out(const struct device *dev, unsigned char c)
75 {
76 const struct uart_rtt_config *config = dev->config;
77 unsigned int ch = config ? config->channel : 0;
78
79 SEGGER_RTT_Write(ch, &c, 1);
80 }
81
82 #ifdef CONFIG_UART_ASYNC_API
83
uart_rtt_callback_set(const struct device * dev,uart_callback_t callback,void * user_data)84 static int uart_rtt_callback_set(const struct device *dev,
85 uart_callback_t callback, void *user_data)
86 {
87 struct uart_rtt_data *data = dev->data;
88
89 data->callback = callback;
90 data->user_data = user_data;
91 return 0;
92 }
93
uart_rtt_tx(const struct device * dev,const uint8_t * buf,size_t len,int32_t timeout)94 static int uart_rtt_tx(const struct device *dev,
95 const uint8_t *buf, size_t len, int32_t timeout)
96 {
97 const struct uart_rtt_config *cfg = dev->config;
98 struct uart_rtt_data *data = dev->data;
99 unsigned int ch = cfg ? cfg->channel : 0;
100
101 ARG_UNUSED(timeout);
102
103 /* RTT mutex cannot be claimed in ISRs */
104 if (k_is_in_isr()) {
105 return -ENOTSUP;
106 }
107
108 /* Claim the RTT lock */
109 if (k_mutex_lock(&rtt_term_mutex, K_NO_WAIT) != 0) {
110 return -EBUSY;
111 }
112
113 /* Output the buffer */
114 SEGGER_RTT_WriteNoLock(ch, buf, len);
115
116 /* Return RTT lock */
117 SEGGER_RTT_UNLOCK();
118
119 /* Send the TX complete callback */
120 if (data->callback) {
121 struct uart_event evt = {
122 .type = UART_TX_DONE,
123 .data.tx.buf = buf,
124 .data.tx.len = len
125 };
126 data->callback(dev, &evt, data->user_data);
127 }
128
129 return 0;
130 }
131
uart_rtt_tx_abort(const struct device * dev)132 static int uart_rtt_tx_abort(const struct device *dev)
133 {
134 /* RTT TX is a memcpy, there is never a transmission to abort */
135 ARG_UNUSED(dev);
136
137 return -EFAULT;
138 }
139
uart_rtt_rx_enable(const struct device * dev,uint8_t * buf,size_t len,int32_t timeout)140 static int uart_rtt_rx_enable(const struct device *dev,
141 uint8_t *buf, size_t len, int32_t timeout)
142 {
143 /* SEGGER RTT reception is implemented as a direct memory write to RAM
144 * by a connected debugger. As such there is no hardware interrupt
145 * or other mechanism to know when the debugger has added data to be
146 * read. Asynchronous RX does not make sense in such a context, and is
147 * therefore not supported.
148 */
149 ARG_UNUSED(dev);
150 ARG_UNUSED(buf);
151 ARG_UNUSED(len);
152 ARG_UNUSED(timeout);
153
154 return -ENOTSUP;
155 }
156
uart_rtt_rx_disable(const struct device * dev)157 static int uart_rtt_rx_disable(const struct device *dev)
158 {
159 /* Asynchronous RX not supported, see uart_rtt_rx_enable */
160 ARG_UNUSED(dev);
161
162 return -EFAULT;
163 }
164
uart_rtt_rx_buf_rsp(const struct device * dev,uint8_t * buf,size_t len)165 static int uart_rtt_rx_buf_rsp(const struct device *dev,
166 uint8_t *buf, size_t len)
167 {
168 /* Asynchronous RX not supported, see uart_rtt_rx_enable */
169 ARG_UNUSED(dev);
170 ARG_UNUSED(buf);
171 ARG_UNUSED(len);
172
173 return -ENOTSUP;
174 }
175
176 #endif /* CONFIG_UART_ASYNC_API */
177
178 static const struct uart_driver_api uart_rtt_driver_api = {
179 .poll_in = uart_rtt_poll_in,
180 .poll_out = uart_rtt_poll_out,
181 #ifdef CONFIG_UART_ASYNC_API
182 .callback_set = uart_rtt_callback_set,
183 .tx = uart_rtt_tx,
184 .tx_abort = uart_rtt_tx_abort,
185 .rx_enable = uart_rtt_rx_enable,
186 .rx_buf_rsp = uart_rtt_rx_buf_rsp,
187 .rx_disable = uart_rtt_rx_disable,
188 #endif /* CONFIG_UART_ASYNC_API */
189 };
190
191 #define UART_RTT(idx) DT_NODELABEL(rtt##idx)
192 #define UART_RTT_PROP(idx, prop) DT_PROP(UART_RTT(idx), prop)
193 #define UART_RTT_CONFIG_NAME(idx) uart_rtt##idx##_config
194
195 #define UART_RTT_CONFIG(idx) \
196 static \
197 uint8_t uart_rtt##idx##_tx_buf[UART_RTT_PROP(idx, tx_buffer_size)]; \
198 static \
199 uint8_t uart_rtt##idx##_rx_buf[UART_RTT_PROP(idx, rx_buffer_size)]; \
200 \
201 static const struct uart_rtt_config UART_RTT_CONFIG_NAME(idx) = { \
202 .up_buffer = uart_rtt##idx##_tx_buf, \
203 .up_size = sizeof(uart_rtt##idx##_tx_buf), \
204 .down_buffer = uart_rtt##idx##_rx_buf, \
205 .down_size = sizeof(uart_rtt##idx##_rx_buf), \
206 .channel = idx, \
207 }
208
209 #define UART_RTT_INIT(idx, config) \
210 struct uart_rtt_data uart_rtt##idx##_data; \
211 \
212 DEVICE_DT_DEFINE(UART_RTT(idx), uart_rtt_init, NULL, \
213 &uart_rtt##idx##_data, config, \
214 PRE_KERNEL_2, CONFIG_SERIAL_INIT_PRIORITY, \
215 &uart_rtt_driver_api)
216
217 #ifdef CONFIG_UART_RTT_0
218 UART_RTT_INIT(0, NULL);
219 #endif
220
221 #ifdef CONFIG_UART_RTT_1
222 UART_RTT_CONFIG(1);
223 UART_RTT_INIT(1, &UART_RTT_CONFIG_NAME(1));
224 #endif
225
226 #ifdef CONFIG_UART_RTT_2
227 UART_RTT_CONFIG(2);
228 UART_RTT_INIT(2, &UART_RTT_CONFIG_NAME(2));
229 #endif
230
231 #ifdef CONFIG_UART_RTT_3
232 UART_RTT_CONFIG(3);
233 UART_RTT_INIT(3, &UART_RTT_CONFIG_NAME(3));
234 #endif
235