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