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_DECLARE(modem_backend_uart);
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 		LOG_WRN("Receive buffer overrun");
34 		ring_buf_put_finish(receive_rb, 0);
35 		ring_buf_reset(receive_rb);
36 		size = ring_buf_put_claim(receive_rb, &buffer, UINT32_MAX);
37 	}
38 
39 	ret = uart_fifo_read(backend->uart, buffer, size);
40 	if (ret < 0) {
41 		ring_buf_put_finish(receive_rb, 0);
42 	} else {
43 		ring_buf_put_finish(receive_rb, (uint32_t)ret);
44 	}
45 
46 	if (ret > 0) {
47 		k_work_submit(&backend->receive_ready_work);
48 	}
49 }
50 
modem_backend_uart_isr_irq_handler_transmit_ready(struct modem_backend_uart * backend)51 static void modem_backend_uart_isr_irq_handler_transmit_ready(struct modem_backend_uart *backend)
52 {
53 	uint32_t size;
54 	uint8_t *buffer;
55 	int ret;
56 
57 	if (ring_buf_is_empty(&backend->isr.transmit_rb) == true) {
58 		uart_irq_tx_disable(backend->uart);
59 		k_work_submit(&backend->transmit_idle_work);
60 		return;
61 	}
62 
63 	size = ring_buf_get_claim(&backend->isr.transmit_rb, &buffer, UINT32_MAX);
64 	ret = uart_fifo_fill(backend->uart, buffer, size);
65 	if (ret < 0) {
66 		ring_buf_get_finish(&backend->isr.transmit_rb, 0);
67 	} else {
68 		ring_buf_get_finish(&backend->isr.transmit_rb, (uint32_t)ret);
69 
70 		/* Update transmit buf capacity tracker */
71 		atomic_sub(&backend->isr.transmit_buf_len, (uint32_t)ret);
72 	}
73 }
74 
modem_backend_uart_isr_irq_handler(const struct device * uart,void * user_data)75 static void modem_backend_uart_isr_irq_handler(const struct device *uart, void *user_data)
76 {
77 	struct modem_backend_uart *backend = (struct modem_backend_uart *)user_data;
78 
79 	if (uart_irq_update(uart) < 1) {
80 		return;
81 	}
82 
83 	if (uart_irq_rx_ready(uart)) {
84 		modem_backend_uart_isr_irq_handler_receive_ready(backend);
85 	}
86 
87 	if (uart_irq_tx_ready(uart)) {
88 		modem_backend_uart_isr_irq_handler_transmit_ready(backend);
89 	}
90 }
91 
modem_backend_uart_isr_open(void * data)92 static int modem_backend_uart_isr_open(void *data)
93 {
94 	struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
95 
96 	ring_buf_reset(&backend->isr.receive_rdb[0]);
97 	ring_buf_reset(&backend->isr.receive_rdb[1]);
98 	ring_buf_reset(&backend->isr.transmit_rb);
99 	atomic_set(&backend->isr.transmit_buf_len, 0);
100 	modem_backend_uart_isr_flush(backend);
101 	uart_irq_rx_enable(backend->uart);
102 	uart_irq_tx_enable(backend->uart);
103 	modem_pipe_notify_opened(&backend->pipe);
104 	return 0;
105 }
106 
modem_backend_uart_isr_transmit_buf_above_limit(struct modem_backend_uart * backend)107 static bool modem_backend_uart_isr_transmit_buf_above_limit(struct modem_backend_uart *backend)
108 {
109 	return backend->isr.transmit_buf_put_limit < atomic_get(&backend->isr.transmit_buf_len);
110 }
111 
modem_backend_uart_isr_transmit(void * data,const uint8_t * buf,size_t size)112 static int modem_backend_uart_isr_transmit(void *data, const uint8_t *buf, size_t size)
113 {
114 	struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
115 	int written;
116 
117 	if (modem_backend_uart_isr_transmit_buf_above_limit(backend) == true) {
118 		return 0;
119 	}
120 
121 	uart_irq_tx_disable(backend->uart);
122 	written = ring_buf_put(&backend->isr.transmit_rb, buf, size);
123 	uart_irq_tx_enable(backend->uart);
124 
125 	/* Update transmit buf capacity tracker */
126 	atomic_add(&backend->isr.transmit_buf_len, written);
127 	return written;
128 }
129 
modem_backend_uart_isr_receive(void * data,uint8_t * buf,size_t size)130 static int modem_backend_uart_isr_receive(void *data, uint8_t *buf, size_t size)
131 {
132 	struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
133 
134 	uint32_t read_bytes;
135 	uint8_t receive_rdb_unused;
136 
137 	read_bytes = 0;
138 	receive_rdb_unused = (backend->isr.receive_rdb_used == 1) ? 0 : 1;
139 
140 	/* Read data from unused ring double buffer first */
141 	read_bytes += ring_buf_get(&backend->isr.receive_rdb[receive_rdb_unused], buf, size);
142 
143 	if (ring_buf_is_empty(&backend->isr.receive_rdb[receive_rdb_unused]) == false) {
144 		return (int)read_bytes;
145 	}
146 
147 	/* Swap receive ring double buffer */
148 	uart_irq_rx_disable(backend->uart);
149 	backend->isr.receive_rdb_used = receive_rdb_unused;
150 	uart_irq_rx_enable(backend->uart);
151 
152 	/* Read data from previously used buffer */
153 	receive_rdb_unused = (backend->isr.receive_rdb_used == 1) ? 0 : 1;
154 
155 	read_bytes += ring_buf_get(&backend->isr.receive_rdb[receive_rdb_unused],
156 				   &buf[read_bytes], (size - read_bytes));
157 
158 	return (int)read_bytes;
159 }
160 
modem_backend_uart_isr_close(void * data)161 static int modem_backend_uart_isr_close(void *data)
162 {
163 	struct modem_backend_uart *backend = (struct modem_backend_uart *)data;
164 
165 	uart_irq_rx_disable(backend->uart);
166 	uart_irq_tx_disable(backend->uart);
167 	modem_pipe_notify_closed(&backend->pipe);
168 	return 0;
169 }
170 
171 struct modem_pipe_api modem_backend_uart_isr_api = {
172 	.open = modem_backend_uart_isr_open,
173 	.transmit = modem_backend_uart_isr_transmit,
174 	.receive = modem_backend_uart_isr_receive,
175 	.close = modem_backend_uart_isr_close,
176 };
177 
modem_backend_uart_isr_init(struct modem_backend_uart * backend,const struct modem_backend_uart_config * config)178 void modem_backend_uart_isr_init(struct modem_backend_uart *backend,
179 				 const struct modem_backend_uart_config *config)
180 {
181 	uint32_t receive_double_buf_size;
182 
183 	backend->isr.transmit_buf_put_limit =
184 		config->transmit_buf_size - (config->transmit_buf_size / 4);
185 
186 	receive_double_buf_size = config->receive_buf_size / 2;
187 
188 	ring_buf_init(&backend->isr.receive_rdb[0], receive_double_buf_size,
189 		      &config->receive_buf[0]);
190 
191 	ring_buf_init(&backend->isr.receive_rdb[1], receive_double_buf_size,
192 		      &config->receive_buf[receive_double_buf_size]);
193 
194 	ring_buf_init(&backend->isr.transmit_rb, config->transmit_buf_size,
195 		      config->transmit_buf);
196 
197 	atomic_set(&backend->isr.transmit_buf_len, 0);
198 	uart_irq_rx_disable(backend->uart);
199 	uart_irq_tx_disable(backend->uart);
200 	uart_irq_callback_user_data_set(backend->uart, modem_backend_uart_isr_irq_handler,
201 					backend);
202 
203 	modem_pipe_init(&backend->pipe, backend, &modem_backend_uart_isr_api);
204 }
205