1 /** @file
2 * @brief interface for modem context
3 *
4 * UART-based modem interface implementation for modem context driver.
5 */
6
7 /*
8 * Copyright (c) 2019 Foundries.io
9 *
10 * SPDX-License-Identifier: Apache-2.0
11 */
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(modem_iface_uart, CONFIG_MODEM_LOG_LEVEL);
15
16 #include <zephyr/kernel.h>
17 #include <zephyr/drivers/uart.h>
18
19 #include "modem_context.h"
20 #include "modem_iface_uart.h"
21
22 /**
23 * @brief Drains UART.
24 *
25 * @note Discards remaining data.
26 *
27 * @param iface: modem interface.
28 *
29 * @retval None.
30 */
modem_iface_uart_flush(struct modem_iface * iface)31 static void modem_iface_uart_flush(struct modem_iface *iface)
32 {
33 uint8_t c;
34
35 while (uart_fifo_read(iface->dev, &c, 1) > 0) {
36 continue;
37 }
38 }
39
40 /**
41 * @brief Modem interface interrupt handler.
42 *
43 * @note Fills interfaces ring buffer with received data.
44 * When ring buffer is full the data is discarded.
45 *
46 * @param uart_dev: uart device.
47 *
48 * @retval None.
49 */
modem_iface_uart_isr(const struct device * uart_dev,void * user_data)50 static void modem_iface_uart_isr(const struct device *uart_dev,
51 void *user_data)
52 {
53 struct modem_context *ctx;
54 struct modem_iface_uart_data *data;
55 int rx = 0, ret;
56 uint8_t *dst;
57 uint32_t partial_size = 0;
58 uint32_t total_size = 0;
59
60 ARG_UNUSED(user_data);
61
62 /* lookup the modem context */
63 ctx = modem_context_from_iface_dev(uart_dev);
64 if (!ctx || !ctx->iface.iface_data) {
65 return;
66 }
67
68 data = (struct modem_iface_uart_data *)(ctx->iface.iface_data);
69 /* get all of the data off UART as fast as we can */
70 while (uart_irq_update(ctx->iface.dev) &&
71 uart_irq_rx_ready(ctx->iface.dev)) {
72 if (!partial_size) {
73 partial_size = ring_buf_put_claim(&data->rx_rb, &dst,
74 UINT32_MAX);
75 }
76 if (!partial_size) {
77 if (data->hw_flow_control) {
78 uart_irq_rx_disable(ctx->iface.dev);
79 } else {
80 LOG_ERR("Rx buffer doesn't have enough space");
81 modem_iface_uart_flush(&ctx->iface);
82 }
83 break;
84 }
85
86 rx = uart_fifo_read(ctx->iface.dev, dst, partial_size);
87 if (rx <= 0) {
88 continue;
89 }
90
91 dst += rx;
92 total_size += rx;
93 partial_size -= rx;
94 }
95
96 ret = ring_buf_put_finish(&data->rx_rb, total_size);
97 __ASSERT_NO_MSG(ret == 0);
98
99 if (total_size > 0) {
100 k_sem_give(&data->rx_sem);
101 }
102 }
103
modem_iface_uart_read(struct modem_iface * iface,uint8_t * buf,size_t size,size_t * bytes_read)104 static int modem_iface_uart_read(struct modem_iface *iface,
105 uint8_t *buf, size_t size, size_t *bytes_read)
106 {
107 struct modem_iface_uart_data *data;
108
109 if (!iface || !iface->iface_data) {
110 return -EINVAL;
111 }
112
113 if (size == 0) {
114 *bytes_read = 0;
115 return 0;
116 }
117
118 data = (struct modem_iface_uart_data *)(iface->iface_data);
119 *bytes_read = ring_buf_get(&data->rx_rb, buf, size);
120
121 if (data->hw_flow_control && *bytes_read == 0) {
122 uart_irq_rx_enable(iface->dev);
123 }
124
125 return 0;
126 }
127
modem_iface_uart_write(struct modem_iface * iface,const uint8_t * buf,size_t size)128 static int modem_iface_uart_write(struct modem_iface *iface,
129 const uint8_t *buf, size_t size)
130 {
131 if (!iface || !iface->iface_data) {
132 return -EINVAL;
133 }
134
135 if (size == 0) {
136 return 0;
137 }
138
139 do {
140 uart_poll_out(iface->dev, *buf++);
141 } while (--size);
142
143 return 0;
144 }
145
modem_iface_uart_init_dev(struct modem_iface * iface,const struct device * dev)146 int modem_iface_uart_init_dev(struct modem_iface *iface,
147 const struct device *dev)
148 {
149 /* get UART device */
150 const struct device *prev = iface->dev;
151
152 if (!device_is_ready(dev)) {
153 return -ENODEV;
154 }
155
156 /* Check if there's already a device inited to this iface. If so,
157 * interrupts needs to be disabled on that too before switching to avoid
158 * race conditions with modem_iface_uart_isr.
159 */
160 if (prev) {
161 uart_irq_tx_disable(prev);
162 uart_irq_rx_disable(prev);
163 }
164
165 uart_irq_rx_disable(dev);
166 uart_irq_tx_disable(dev);
167 iface->dev = dev;
168
169 modem_iface_uart_flush(iface);
170 uart_irq_callback_set(iface->dev, modem_iface_uart_isr);
171 uart_irq_rx_enable(iface->dev);
172
173 if (prev) {
174 uart_irq_rx_enable(prev);
175 }
176
177 return 0;
178 }
179
modem_iface_uart_init(struct modem_iface * iface,struct modem_iface_uart_data * data,const struct modem_iface_uart_config * config)180 int modem_iface_uart_init(struct modem_iface *iface, struct modem_iface_uart_data *data,
181 const struct modem_iface_uart_config *config)
182 {
183 int ret;
184
185 if (iface == NULL || data == NULL || config == NULL) {
186 return -EINVAL;
187 }
188
189 iface->iface_data = data;
190 iface->read = modem_iface_uart_read;
191 iface->write = modem_iface_uart_write;
192
193 ring_buf_init(&data->rx_rb, config->rx_rb_buf_len, config->rx_rb_buf);
194 k_sem_init(&data->rx_sem, 0, 1);
195
196 /* Configure hardware flow control */
197 data->hw_flow_control = config->hw_flow_control;
198
199 /* Get UART device */
200 ret = modem_iface_uart_init_dev(iface, config->dev);
201 if (ret < 0) {
202 iface->iface_data = NULL;
203 iface->read = NULL;
204 iface->write = NULL;
205
206 return ret;
207 }
208
209 return 0;
210 }
211