1 /*
2 * Copyright (c) 2023 Trackunit Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "modem_backend_uart_async.h"
8
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(modem_backend_uart_async, CONFIG_MODEM_MODULES_LOG_LEVEL);
11
12 #include <zephyr/kernel.h>
13 #include <string.h>
14
15 enum {
16 MODEM_BACKEND_UART_ASYNC_STATE_TRANSMITTING_BIT,
17 MODEM_BACKEND_UART_ASYNC_STATE_RECEIVING_BIT,
18 MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT,
19 MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF1_USED_BIT,
20 MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT,
21 };
22
modem_backend_uart_async_is_uart_stopped(struct modem_backend_uart * backend)23 static bool modem_backend_uart_async_is_uart_stopped(struct modem_backend_uart *backend)
24 {
25 if (!atomic_test_bit(&backend->async.state,
26 MODEM_BACKEND_UART_ASYNC_STATE_TRANSMITTING_BIT) &&
27 !atomic_test_bit(&backend->async.state,
28 MODEM_BACKEND_UART_ASYNC_STATE_RECEIVING_BIT) &&
29 !atomic_test_bit(&backend->async.state,
30 MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT) &&
31 !atomic_test_bit(&backend->async.state,
32 MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF1_USED_BIT)) {
33 return true;
34 }
35
36 return false;
37 }
38
modem_backend_uart_async_is_open(struct modem_backend_uart * backend)39 static bool modem_backend_uart_async_is_open(struct modem_backend_uart *backend)
40 {
41 return atomic_test_bit(&backend->async.state,
42 MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT);
43 }
44
get_receive_buf_length(struct modem_backend_uart * backend)45 static uint32_t get_receive_buf_length(struct modem_backend_uart *backend)
46 {
47 return ring_buf_size_get(&backend->async.receive_rb);
48 }
49
modem_backend_uart_async_event_handler(const struct device * dev,struct uart_event * evt,void * user_data)50 static void modem_backend_uart_async_event_handler(const struct device *dev,
51 struct uart_event *evt, void *user_data)
52 {
53 struct modem_backend_uart *backend = (struct modem_backend_uart *) user_data;
54 k_spinlock_key_t key;
55 uint32_t received;
56
57 switch (evt->type) {
58 case UART_TX_DONE:
59 atomic_clear_bit(&backend->async.state,
60 MODEM_BACKEND_UART_ASYNC_STATE_TRANSMITTING_BIT);
61 k_work_submit(&backend->transmit_idle_work);
62 break;
63
64 case UART_TX_ABORTED:
65 if (modem_backend_uart_async_is_open(backend)) {
66 LOG_WRN("Transmit aborted (%zu sent)", evt->data.tx.len);
67 }
68 atomic_clear_bit(&backend->async.state,
69 MODEM_BACKEND_UART_ASYNC_STATE_TRANSMITTING_BIT);
70 k_work_submit(&backend->transmit_idle_work);
71
72 break;
73
74 case UART_RX_BUF_REQUEST:
75 if (!atomic_test_and_set_bit(&backend->async.state,
76 MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT)) {
77 uart_rx_buf_rsp(backend->uart, backend->async.receive_bufs[0],
78 backend->async.receive_buf_size);
79
80 break;
81 }
82
83 if (!atomic_test_and_set_bit(&backend->async.state,
84 MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF1_USED_BIT)) {
85 uart_rx_buf_rsp(backend->uart, backend->async.receive_bufs[1],
86 backend->async.receive_buf_size);
87
88 break;
89 }
90
91 LOG_WRN("No receive buffer available");
92 break;
93
94 case UART_RX_BUF_RELEASED:
95 if (evt->data.rx_buf.buf == backend->async.receive_bufs[0]) {
96 atomic_clear_bit(&backend->async.state,
97 MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT);
98
99 break;
100 }
101
102 if (evt->data.rx_buf.buf == backend->async.receive_bufs[1]) {
103 atomic_clear_bit(&backend->async.state,
104 MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF1_USED_BIT);
105
106 break;
107 }
108
109 LOG_WRN("Unknown receive buffer released");
110 break;
111
112 case UART_RX_RDY:
113 key = k_spin_lock(&backend->async.receive_rb_lock);
114 received = ring_buf_put(&backend->async.receive_rb,
115 &evt->data.rx.buf[evt->data.rx.offset],
116 evt->data.rx.len);
117
118 if (received < evt->data.rx.len) {
119 const unsigned int buf_size = get_receive_buf_length(backend);
120
121 ring_buf_reset(&backend->async.receive_rb);
122 k_spin_unlock(&backend->async.receive_rb_lock, key);
123
124 LOG_WRN("Receive buffer overrun (dropped %u + %u)",
125 buf_size - received, (unsigned int)evt->data.rx.len);
126 break;
127 }
128
129 k_spin_unlock(&backend->async.receive_rb_lock, key);
130 k_work_schedule(&backend->receive_ready_work, K_NO_WAIT);
131 break;
132
133 case UART_RX_DISABLED:
134 atomic_clear_bit(&backend->async.state,
135 MODEM_BACKEND_UART_ASYNC_STATE_RECEIVING_BIT);
136 break;
137
138 case UART_RX_STOPPED:
139 LOG_WRN("Receive stopped for reasons: %u", (uint8_t)evt->data.rx_stop.reason);
140 break;
141
142 default:
143 break;
144 }
145
146 if (modem_backend_uart_async_is_uart_stopped(backend)) {
147 k_work_submit(&backend->async.rx_disabled_work);
148 }
149 }
150
modem_backend_uart_async_open(void * data)151 static int modem_backend_uart_async_open(void *data)
152 {
153 struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
154 int ret;
155
156 atomic_clear(&backend->async.state);
157 ring_buf_reset(&backend->async.receive_rb);
158
159 atomic_set_bit(&backend->async.state, MODEM_BACKEND_UART_ASYNC_STATE_RX_BUF0_USED_BIT);
160 atomic_set_bit(&backend->async.state, MODEM_BACKEND_UART_ASYNC_STATE_RECEIVING_BIT);
161 atomic_set_bit(&backend->async.state, MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT);
162
163 /* Receive buffers are used internally by UART, receive ring buffer is
164 * used to store received data.
165 */
166 ret = uart_rx_enable(backend->uart, backend->async.receive_bufs[0],
167 backend->async.receive_buf_size,
168 CONFIG_MODEM_BACKEND_UART_ASYNC_RECEIVE_IDLE_TIMEOUT_MS * 1000L);
169 if (ret < 0) {
170 atomic_clear(&backend->async.state);
171 return ret;
172 }
173
174 modem_pipe_notify_opened(&backend->pipe);
175 return 0;
176 }
177
178 #if CONFIG_MODEM_STATS
get_receive_buf_size(struct modem_backend_uart * backend)179 static uint32_t get_receive_buf_size(struct modem_backend_uart *backend)
180 {
181 return ring_buf_capacity_get(&backend->async.receive_rb);
182 }
183
advertise_transmit_buf_stats(struct modem_backend_uart * backend,uint32_t length)184 static void advertise_transmit_buf_stats(struct modem_backend_uart *backend, uint32_t length)
185 {
186 modem_stats_buffer_advertise_length(&backend->transmit_buf_stats, length);
187 }
188
advertise_receive_buf_stats(struct modem_backend_uart * backend)189 static void advertise_receive_buf_stats(struct modem_backend_uart *backend)
190 {
191 uint32_t length;
192
193 length = get_receive_buf_length(backend);
194 modem_stats_buffer_advertise_length(&backend->receive_buf_stats, length);
195 }
196 #endif
197
get_transmit_buf_size(struct modem_backend_uart * backend)198 static uint32_t get_transmit_buf_size(struct modem_backend_uart *backend)
199 {
200 return backend->async.transmit_buf_size;
201 }
202
modem_backend_uart_async_transmit(void * data,const uint8_t * buf,size_t size)203 static int modem_backend_uart_async_transmit(void *data, const uint8_t *buf, size_t size)
204 {
205 struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
206 bool transmitting;
207 uint32_t bytes_to_transmit;
208 int ret;
209
210 transmitting = atomic_test_and_set_bit(&backend->async.state,
211 MODEM_BACKEND_UART_ASYNC_STATE_TRANSMITTING_BIT);
212 if (transmitting) {
213 return 0;
214 }
215
216 /* Determine amount of bytes to transmit */
217 bytes_to_transmit = MIN(size, get_transmit_buf_size(backend));
218
219 /* Copy buf to transmit buffer which is passed to UART */
220 memcpy(backend->async.transmit_buf, buf, bytes_to_transmit);
221
222 ret = uart_tx(backend->uart, backend->async.transmit_buf, bytes_to_transmit,
223 CONFIG_MODEM_BACKEND_UART_ASYNC_TRANSMIT_TIMEOUT_MS * 1000L);
224
225 #if CONFIG_MODEM_STATS
226 advertise_transmit_buf_stats(backend, bytes_to_transmit);
227 #endif
228
229 if (ret != 0) {
230 LOG_ERR("Failed to %s %u bytes. (%d)",
231 "start async transmit for", bytes_to_transmit, ret);
232 return ret;
233 }
234
235 return (int)bytes_to_transmit;
236 }
237
modem_backend_uart_async_receive(void * data,uint8_t * buf,size_t size)238 static int modem_backend_uart_async_receive(void *data, uint8_t *buf, size_t size)
239 {
240 struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
241 k_spinlock_key_t key;
242 uint32_t received;
243 bool empty;
244
245 key = k_spin_lock(&backend->async.receive_rb_lock);
246
247 #if CONFIG_MODEM_STATS
248 advertise_receive_buf_stats(backend);
249 #endif
250
251 received = ring_buf_get(&backend->async.receive_rb, buf, size);
252 empty = ring_buf_is_empty(&backend->async.receive_rb);
253 k_spin_unlock(&backend->async.receive_rb_lock, key);
254
255 if (!empty) {
256 k_work_schedule(&backend->receive_ready_work, K_NO_WAIT);
257 }
258
259 return (int)received;
260 }
261
modem_backend_uart_async_close(void * data)262 static int modem_backend_uart_async_close(void *data)
263 {
264 struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
265
266 atomic_clear_bit(&backend->async.state, MODEM_BACKEND_UART_ASYNC_STATE_OPEN_BIT);
267 uart_tx_abort(backend->uart);
268 uart_rx_disable(backend->uart);
269 return 0;
270 }
271
272 static const struct modem_pipe_api modem_backend_uart_async_api = {
273 .open = modem_backend_uart_async_open,
274 .transmit = modem_backend_uart_async_transmit,
275 .receive = modem_backend_uart_async_receive,
276 .close = modem_backend_uart_async_close,
277 };
278
modem_backend_uart_async_is_supported(struct modem_backend_uart * backend)279 bool modem_backend_uart_async_is_supported(struct modem_backend_uart *backend)
280 {
281 return uart_callback_set(backend->uart, modem_backend_uart_async_event_handler,
282 backend) == 0;
283 }
284
modem_backend_uart_async_notify_closed(struct k_work * item)285 static void modem_backend_uart_async_notify_closed(struct k_work *item)
286 {
287 struct modem_backend_uart_async *async =
288 CONTAINER_OF(item, struct modem_backend_uart_async, rx_disabled_work);
289
290 struct modem_backend_uart *backend =
291 CONTAINER_OF(async, struct modem_backend_uart, async);
292
293 modem_pipe_notify_closed(&backend->pipe);
294 }
295
296 #if CONFIG_MODEM_STATS
init_stats(struct modem_backend_uart * backend)297 static void init_stats(struct modem_backend_uart *backend)
298 {
299 char name[CONFIG_MODEM_STATS_BUFFER_NAME_SIZE];
300 uint32_t receive_buf_size;
301 uint32_t transmit_buf_size;
302
303 receive_buf_size = get_receive_buf_size(backend);
304 transmit_buf_size = get_transmit_buf_size(backend);
305
306 snprintk(name, sizeof(name), "%s_%s", backend->uart->name, "rx");
307 modem_stats_buffer_init(&backend->receive_buf_stats, name, receive_buf_size);
308 snprintk(name, sizeof(name), "%s_%s", backend->uart->name, "tx");
309 modem_stats_buffer_init(&backend->transmit_buf_stats, name, transmit_buf_size);
310 }
311 #endif
312
modem_backend_uart_async_init(struct modem_backend_uart * backend,const struct modem_backend_uart_config * config)313 void modem_backend_uart_async_init(struct modem_backend_uart *backend,
314 const struct modem_backend_uart_config *config)
315 {
316 uint32_t receive_buf_size_quarter = config->receive_buf_size / 4;
317
318 /* Use half the receive buffer for UART receive buffers */
319 backend->async.receive_buf_size = receive_buf_size_quarter;
320 backend->async.receive_bufs[0] = &config->receive_buf[0];
321 backend->async.receive_bufs[1] = &config->receive_buf[receive_buf_size_quarter];
322
323 /* Use half the receive buffer for the received data ring buffer */
324 ring_buf_init(&backend->async.receive_rb, (receive_buf_size_quarter * 2),
325 &config->receive_buf[receive_buf_size_quarter * 2]);
326
327 backend->async.transmit_buf = config->transmit_buf;
328 backend->async.transmit_buf_size = config->transmit_buf_size;
329 k_work_init(&backend->async.rx_disabled_work, modem_backend_uart_async_notify_closed);
330 modem_pipe_init(&backend->pipe, backend, &modem_backend_uart_async_api);
331
332 #if CONFIG_MODEM_STATS
333 init_stats(backend);
334 #endif
335 }
336