1 /*
2  * Copyright (c) 2022, Commonwealth Scientific and Industrial Research
3  * Organisation (CSIRO) ABN 41 687 119 230.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/logging/log.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/uart.h>
11 
12 #include "modem_context.h"
13 #include "modem_iface_uart.h"
14 
15 LOG_MODULE_REGISTER(modem_iface_uart_async, CONFIG_MODEM_LOG_LEVEL);
16 
17 #define RX_BUFFER_SIZE CONFIG_MODEM_IFACE_UART_ASYNC_RX_BUFFER_SIZE
18 #define RX_BUFFER_NUM CONFIG_MODEM_IFACE_UART_ASYNC_RX_NUM_BUFFERS
19 
20 K_MEM_SLAB_DEFINE(uart_modem_async_rx_slab, RX_BUFFER_SIZE, RX_BUFFER_NUM, 1);
21 
iface_uart_async_callback(const struct device * dev,struct uart_event * evt,void * user_data)22 static void iface_uart_async_callback(const struct device *dev,
23 				      struct uart_event *evt,
24 				      void *user_data)
25 {
26 	struct modem_iface *iface = user_data;
27 	struct modem_iface_uart_data *data = iface->iface_data;
28 	uint32_t written;
29 	void *buf;
30 	int rc;
31 
32 	switch (evt->type) {
33 	case UART_TX_DONE:
34 		k_sem_give(&data->tx_sem);
35 		break;
36 	case UART_RX_BUF_REQUEST:
37 		/* Allocate next RX buffer for UART driver */
38 		rc = k_mem_slab_alloc(&uart_modem_async_rx_slab, (void **)&buf, K_NO_WAIT);
39 		if (rc < 0) {
40 			/* Major problems, UART_RX_BUF_RELEASED event is not being generated, or
41 			 * CONFIG_MODEM_IFACE_UART_ASYNC_RX_NUM_BUFFERS is not large enough.
42 			 */
43 			LOG_ERR("RX buffer starvation");
44 			break;
45 		}
46 		/* Provide the buffer to the UART driver */
47 		uart_rx_buf_rsp(dev, buf, RX_BUFFER_SIZE);
48 		break;
49 	case UART_RX_BUF_RELEASED:
50 		/* UART driver is done with memory, free it */
51 		k_mem_slab_free(&uart_modem_async_rx_slab, (void *)evt->data.rx_buf.buf);
52 		break;
53 	case UART_RX_RDY:
54 		/* Place received data on the ring buffer */
55 		written = ring_buf_put(&data->rx_rb,
56 				       evt->data.rx.buf + evt->data.rx.offset,
57 				       evt->data.rx.len);
58 		if (written != evt->data.rx.len) {
59 			LOG_WRN("Received bytes dropped from ring buf");
60 		}
61 		/* Notify upper layer that new data has arrived */
62 		k_sem_give(&data->rx_sem);
63 		break;
64 	case UART_RX_STOPPED:
65 		break;
66 	case UART_RX_DISABLED:
67 		/* RX stopped (likely due to line error), re-enable it */
68 		rc = k_mem_slab_alloc(&uart_modem_async_rx_slab, (void **)&buf, K_FOREVER);
69 		if (rc < 0) {
70 			LOG_ERR("RX disabled and buffer starvation");
71 			break;
72 		}
73 		rc = uart_rx_enable(dev, buf, RX_BUFFER_SIZE,
74 				    CONFIG_MODEM_IFACE_UART_ASYNC_RX_TIMEOUT_US);
75 		if (rc < 0) {
76 			LOG_ERR("Failed to re-enable UART");
77 		}
78 		break;
79 	default:
80 		break;
81 	}
82 }
83 
modem_iface_uart_async_read(struct modem_iface * iface,uint8_t * buf,size_t size,size_t * bytes_read)84 static int modem_iface_uart_async_read(struct modem_iface *iface,
85 				       uint8_t *buf, size_t size, size_t *bytes_read)
86 {
87 	struct modem_iface_uart_data *data;
88 
89 	if (!iface || !iface->iface_data) {
90 		return -EINVAL;
91 	}
92 
93 	if (size == 0) {
94 		*bytes_read = 0;
95 		return 0;
96 	}
97 
98 	/* Pull data off the ring buffer */
99 	data = iface->iface_data;
100 	*bytes_read = ring_buf_get(&data->rx_rb, buf, size);
101 	return 0;
102 }
103 
modem_iface_uart_async_write(struct modem_iface * iface,const uint8_t * buf,size_t size)104 static int modem_iface_uart_async_write(struct modem_iface *iface,
105 					const uint8_t *buf, size_t size)
106 {
107 	struct modem_iface_uart_data *data;
108 	int rc;
109 
110 	if (!iface || !iface->iface_data) {
111 		return -EINVAL;
112 	}
113 
114 	if (size == 0) {
115 		return 0;
116 	}
117 
118 	/* Start the transmission */
119 	rc = uart_tx(iface->dev, buf, size, SYS_FOREVER_MS);
120 	if (rc >= 0) {
121 		/* Wait until the transmission completes */
122 		data = iface->iface_data;
123 		k_sem_take(&data->tx_sem, K_FOREVER);
124 	}
125 	return rc;
126 }
127 
modem_iface_uart_init_dev(struct modem_iface * iface,const struct device * dev)128 int modem_iface_uart_init_dev(struct modem_iface *iface,
129 			      const struct device *dev)
130 {
131 	struct modem_iface_uart_data *data;
132 	void *buf;
133 	int rc;
134 
135 	if (!device_is_ready(dev)) {
136 		return -ENODEV;
137 	}
138 
139 	/* Check if there's already a device inited to this iface. If so,
140 	 * interrupts needs to be disabled on that too before switching to avoid
141 	 * race conditions with modem_iface_uart_isr.
142 	 */
143 	if (iface->dev) {
144 		LOG_WRN("Device %s already inited", iface->dev->name);
145 		uart_rx_disable(iface->dev);
146 	}
147 
148 	iface->dev = dev;
149 	data = iface->iface_data;
150 
151 	/* Configure async UART callback */
152 	rc = uart_callback_set(dev, iface_uart_async_callback, iface);
153 	if (rc < 0) {
154 		LOG_ERR("Failed to set UART callback");
155 		return rc;
156 	}
157 	/* Enable reception permanently on the interface */
158 	k_mem_slab_alloc(&uart_modem_async_rx_slab, (void **)&buf, K_FOREVER);
159 	rc = uart_rx_enable(dev, buf, RX_BUFFER_SIZE, CONFIG_MODEM_IFACE_UART_ASYNC_RX_TIMEOUT_US);
160 	if (rc < 0) {
161 		LOG_ERR("Failed to enable UART RX");
162 	}
163 	return rc;
164 }
165 
modem_iface_uart_init(struct modem_iface * iface,struct modem_iface_uart_data * data,const struct modem_iface_uart_config * config)166 int modem_iface_uart_init(struct modem_iface *iface, struct modem_iface_uart_data *data,
167 			  const struct modem_iface_uart_config *config)
168 {
169 	int ret;
170 
171 	if (iface == NULL || data == NULL || config == NULL) {
172 		return -EINVAL;
173 	}
174 
175 	iface->iface_data = data;
176 	iface->read = modem_iface_uart_async_read;
177 	iface->write = modem_iface_uart_async_write;
178 
179 	ring_buf_init(&data->rx_rb, config->rx_rb_buf_len, config->rx_rb_buf);
180 	k_sem_init(&data->rx_sem, 0, 1);
181 	k_sem_init(&data->tx_sem, 0, 1);
182 
183 	/* Configure hardware flow control */
184 	data->hw_flow_control = config->hw_flow_control;
185 
186 	/* Get UART device */
187 	ret = modem_iface_uart_init_dev(iface, config->dev);
188 	if (ret < 0) {
189 		iface->iface_data = NULL;
190 		iface->read = NULL;
191 		iface->write = NULL;
192 
193 		return ret;
194 	}
195 
196 	return 0;
197 }
198