1 /*
2  * Copyright (c) 2023 Trackunit Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "modem_backend_uart_isr.h"
8 
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(modem_backend_uart_isr, CONFIG_MODEM_MODULES_LOG_LEVEL);
11 
12 #include <string.h>
13 
modem_backend_uart_isr_flush(struct modem_backend_uart * backend)14 static void modem_backend_uart_isr_flush(struct modem_backend_uart *backend)
15 {
16 	uint8_t c;
17 
18 	while (uart_fifo_read(backend->uart, &c, 1) > 0) {
19 		continue;
20 	}
21 }
22 
modem_backend_uart_isr_irq_handler_receive_ready(struct modem_backend_uart * backend)23 static void modem_backend_uart_isr_irq_handler_receive_ready(struct modem_backend_uart *backend)
24 {
25 	uint32_t size;
26 	struct ring_buf *receive_rb;
27 	uint8_t *buffer;
28 	int ret;
29 
30 	receive_rb = &backend->isr.receive_rdb[backend->isr.receive_rdb_used];
31 	size = ring_buf_put_claim(receive_rb, &buffer, UINT32_MAX);
32 	if (size == 0) {
33 		/* This can be caused by
34 		 * - a too long CONFIG_MODEM_BACKEND_UART_ISR_RECEIVE_IDLE_TIMEOUT_MS
35 		 * - or a too small receive_buf_size
36 		 * relatively to the (too high) baud rate and amount of incoming data.
37 		 */
38 		LOG_WRN("Receive buffer overrun");
39 		ring_buf_put_finish(receive_rb, 0);
40 		ring_buf_reset(receive_rb);
41 		size = ring_buf_put_claim(receive_rb, &buffer, UINT32_MAX);
42 	}
43 
44 	ret = uart_fifo_read(backend->uart, buffer, size);
45 	if (ret <= 0) {
46 		ring_buf_put_finish(receive_rb, 0);
47 		return;
48 	}
49 	ring_buf_put_finish(receive_rb, (uint32_t)ret);
50 
51 	if (ring_buf_space_get(receive_rb) > ring_buf_capacity_get(receive_rb) / 20) {
52 		/*
53 		 * Avoid having the receiver call modem_pipe_receive() too often (e.g. every byte).
54 		 * It temporarily disables the UART RX IRQ when swapping buffers
55 		 * which can cause byte loss at higher baud rates.
56 		 */
57 		k_work_schedule(&backend->receive_ready_work,
58 			K_MSEC(CONFIG_MODEM_BACKEND_UART_ISR_RECEIVE_IDLE_TIMEOUT_MS));
59 	} else {
60 		/* The buffer is getting full. Run the work item immediately to free up space. */
61 		k_work_reschedule(&backend->receive_ready_work, K_NO_WAIT);
62 	}
63 }
64 
modem_backend_uart_isr_irq_handler_transmit_ready(struct modem_backend_uart * backend)65 static void modem_backend_uart_isr_irq_handler_transmit_ready(struct modem_backend_uart *backend)
66 {
67 	uint32_t size;
68 	uint8_t *buffer;
69 	int ret;
70 
71 	if (ring_buf_is_empty(&backend->isr.transmit_rb) == true) {
72 		uart_irq_tx_disable(backend->uart);
73 		k_work_submit(&backend->transmit_idle_work);
74 		return;
75 	}
76 
77 	size = ring_buf_get_claim(&backend->isr.transmit_rb, &buffer, UINT32_MAX);
78 	ret = uart_fifo_fill(backend->uart, buffer, size);
79 	if (ret < 0) {
80 		ring_buf_get_finish(&backend->isr.transmit_rb, 0);
81 	} else {
82 		ring_buf_get_finish(&backend->isr.transmit_rb, (uint32_t)ret);
83 
84 		/* Update transmit buf capacity tracker */
85 		atomic_sub(&backend->isr.transmit_buf_len, (uint32_t)ret);
86 	}
87 }
88 
modem_backend_uart_isr_irq_handler(const struct device * uart,void * user_data)89 static void modem_backend_uart_isr_irq_handler(const struct device *uart, void *user_data)
90 {
91 	struct modem_backend_uart *backend = (struct modem_backend_uart *)user_data;
92 
93 	if (uart_irq_update(uart) < 1) {
94 		return;
95 	}
96 
97 	if (uart_irq_rx_ready(uart)) {
98 		modem_backend_uart_isr_irq_handler_receive_ready(backend);
99 	}
100 
101 	if (uart_irq_tx_ready(uart)) {
102 		modem_backend_uart_isr_irq_handler_transmit_ready(backend);
103 	}
104 }
105 
modem_backend_uart_isr_open(void * data)106 static int modem_backend_uart_isr_open(void *data)
107 {
108 	struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
109 
110 	ring_buf_reset(&backend->isr.receive_rdb[0]);
111 	ring_buf_reset(&backend->isr.receive_rdb[1]);
112 	ring_buf_reset(&backend->isr.transmit_rb);
113 	atomic_set(&backend->isr.transmit_buf_len, 0);
114 	modem_backend_uart_isr_flush(backend);
115 	uart_irq_rx_enable(backend->uart);
116 	uart_irq_tx_enable(backend->uart);
117 	modem_pipe_notify_opened(&backend->pipe);
118 	return 0;
119 }
120 
get_transmit_buf_length(struct modem_backend_uart * backend)121 static uint32_t get_transmit_buf_length(struct modem_backend_uart *backend)
122 {
123 	return atomic_get(&backend->isr.transmit_buf_len);
124 }
125 
126 #if CONFIG_MODEM_STATS
get_receive_buf_length(struct modem_backend_uart * backend)127 static uint32_t get_receive_buf_length(struct modem_backend_uart *backend)
128 {
129 	return ring_buf_size_get(&backend->isr.receive_rdb[0]) +
130 	       ring_buf_size_get(&backend->isr.receive_rdb[1]);
131 }
132 
get_receive_buf_size(struct modem_backend_uart * backend)133 static uint32_t get_receive_buf_size(struct modem_backend_uart *backend)
134 {
135 	return ring_buf_capacity_get(&backend->isr.receive_rdb[0]) +
136 	       ring_buf_capacity_get(&backend->isr.receive_rdb[1]);
137 }
138 
get_transmit_buf_size(struct modem_backend_uart * backend)139 static uint32_t get_transmit_buf_size(struct modem_backend_uart *backend)
140 {
141 	return ring_buf_capacity_get(&backend->isr.transmit_rb);
142 }
143 
advertise_transmit_buf_stats(struct modem_backend_uart * backend)144 static void advertise_transmit_buf_stats(struct modem_backend_uart *backend)
145 {
146 	uint32_t length;
147 
148 	length = get_transmit_buf_length(backend);
149 	modem_stats_buffer_advertise_length(&backend->transmit_buf_stats, length);
150 }
151 
advertise_receive_buf_stats(struct modem_backend_uart * backend)152 static void advertise_receive_buf_stats(struct modem_backend_uart *backend)
153 {
154 	uint32_t length;
155 
156 	uart_irq_rx_disable(backend->uart);
157 	length = get_receive_buf_length(backend);
158 	uart_irq_rx_enable(backend->uart);
159 	modem_stats_buffer_advertise_length(&backend->receive_buf_stats, length);
160 }
161 #endif
162 
modem_backend_uart_isr_transmit_buf_above_limit(struct modem_backend_uart * backend)163 static bool modem_backend_uart_isr_transmit_buf_above_limit(struct modem_backend_uart *backend)
164 {
165 	return backend->isr.transmit_buf_put_limit < get_transmit_buf_length(backend);
166 }
167 
modem_backend_uart_isr_transmit(void * data,const uint8_t * buf,size_t size)168 static int modem_backend_uart_isr_transmit(void *data, const uint8_t *buf, size_t size)
169 {
170 	struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
171 	int written;
172 
173 	if (modem_backend_uart_isr_transmit_buf_above_limit(backend) == true) {
174 		return 0;
175 	}
176 
177 	uart_irq_tx_disable(backend->uart);
178 	written = ring_buf_put(&backend->isr.transmit_rb, buf, size);
179 	uart_irq_tx_enable(backend->uart);
180 
181 	/* Update transmit buf capacity tracker */
182 	atomic_add(&backend->isr.transmit_buf_len, written);
183 
184 #if CONFIG_MODEM_STATS
185 	advertise_transmit_buf_stats(backend);
186 #endif
187 
188 	return written;
189 }
190 
modem_backend_uart_isr_receive(void * data,uint8_t * buf,size_t size)191 static int modem_backend_uart_isr_receive(void *data, uint8_t *buf, size_t size)
192 {
193 	struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
194 
195 	uint32_t read_bytes;
196 	uint8_t receive_rdb_unused;
197 
198 #if CONFIG_MODEM_STATS
199 	advertise_receive_buf_stats(backend);
200 #endif
201 
202 	read_bytes = 0;
203 	receive_rdb_unused = (backend->isr.receive_rdb_used == 1) ? 0 : 1;
204 
205 	/* Read data from unused ring double buffer first */
206 	read_bytes += ring_buf_get(&backend->isr.receive_rdb[receive_rdb_unused], buf, size);
207 
208 	if (ring_buf_is_empty(&backend->isr.receive_rdb[receive_rdb_unused]) == false) {
209 		return (int)read_bytes;
210 	}
211 
212 	/* Swap receive ring double buffer */
213 	uart_irq_rx_disable(backend->uart);
214 	backend->isr.receive_rdb_used = receive_rdb_unused;
215 	uart_irq_rx_enable(backend->uart);
216 
217 	/* Read data from previously used buffer */
218 	receive_rdb_unused = (backend->isr.receive_rdb_used == 1) ? 0 : 1;
219 
220 	read_bytes += ring_buf_get(&backend->isr.receive_rdb[receive_rdb_unused],
221 				   &buf[read_bytes], (size - read_bytes));
222 
223 	return (int)read_bytes;
224 }
225 
modem_backend_uart_isr_close(void * data)226 static int modem_backend_uart_isr_close(void *data)
227 {
228 	struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
229 
230 	uart_irq_rx_disable(backend->uart);
231 	uart_irq_tx_disable(backend->uart);
232 	modem_pipe_notify_closed(&backend->pipe);
233 	return 0;
234 }
235 
236 static const struct modem_pipe_api modem_backend_uart_isr_api = {
237 	.open = modem_backend_uart_isr_open,
238 	.transmit = modem_backend_uart_isr_transmit,
239 	.receive = modem_backend_uart_isr_receive,
240 	.close = modem_backend_uart_isr_close,
241 };
242 
243 #if CONFIG_MODEM_STATS
init_stats(struct modem_backend_uart * backend)244 static void init_stats(struct modem_backend_uart *backend)
245 {
246 	char name[CONFIG_MODEM_STATS_BUFFER_NAME_SIZE];
247 	uint32_t receive_buf_size;
248 	uint32_t transmit_buf_size;
249 
250 	receive_buf_size = get_receive_buf_size(backend);
251 	transmit_buf_size = get_transmit_buf_size(backend);
252 
253 	snprintk(name, sizeof(name), "%s_%s", backend->uart->name, "rx");
254 	modem_stats_buffer_init(&backend->receive_buf_stats, name, receive_buf_size);
255 	snprintk(name, sizeof(name), "%s_%s", backend->uart->name, "tx");
256 	modem_stats_buffer_init(&backend->transmit_buf_stats, name, transmit_buf_size);
257 }
258 #endif
259 
modem_backend_uart_isr_init(struct modem_backend_uart * backend,const struct modem_backend_uart_config * config)260 void modem_backend_uart_isr_init(struct modem_backend_uart *backend,
261 				 const struct modem_backend_uart_config *config)
262 {
263 	uint32_t receive_double_buf_size;
264 
265 	backend->isr.transmit_buf_put_limit =
266 		config->transmit_buf_size - (config->transmit_buf_size / 4);
267 
268 	receive_double_buf_size = config->receive_buf_size / 2;
269 
270 	ring_buf_init(&backend->isr.receive_rdb[0], receive_double_buf_size,
271 		      &config->receive_buf[0]);
272 
273 	ring_buf_init(&backend->isr.receive_rdb[1], receive_double_buf_size,
274 		      &config->receive_buf[receive_double_buf_size]);
275 
276 	ring_buf_init(&backend->isr.transmit_rb, config->transmit_buf_size,
277 		      config->transmit_buf);
278 
279 	atomic_set(&backend->isr.transmit_buf_len, 0);
280 	uart_irq_rx_disable(backend->uart);
281 	uart_irq_tx_disable(backend->uart);
282 	uart_irq_callback_user_data_set(backend->uart, modem_backend_uart_isr_irq_handler,
283 					backend);
284 
285 	modem_pipe_init(&backend->pipe, backend, &modem_backend_uart_isr_api);
286 
287 #if CONFIG_MODEM_STATS
288 	init_stats(backend);
289 #endif
290 }
291