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