1 /** @file
2  * @brief Modem receiver driver
3  *
4  * A modem receiver driver allowing application to handle all
5  * aspects of received protocol data.
6  */
7 
8 /*
9  * Copyright (c) 2018 Foundries.io
10  *
11  * SPDX-License-Identifier: Apache-2.0
12  */
13 
14 #include <zephyr/kernel.h>
15 #include <zephyr/init.h>
16 #include <zephyr/drivers/uart.h>
17 #include <zephyr/pm/device.h>
18 
19 #include <zephyr/logging/log.h>
20 
21 LOG_MODULE_REGISTER(mdm_receiver, CONFIG_MODEM_LOG_LEVEL);
22 
23 #include "modem_receiver.h"
24 
25 #define MAX_MDM_CTX	CONFIG_MODEM_RECEIVER_MAX_CONTEXTS
26 #define MAX_READ_SIZE	128
27 
28 static struct mdm_receiver_context *contexts[MAX_MDM_CTX];
29 
30 /**
31  * @brief  Finds receiver context which manages provided device.
32  *
33  * @param  dev: device used by the receiver context.
34  *
35  * @retval Receiver context or NULL.
36  */
context_from_dev(const struct device * dev)37 static struct mdm_receiver_context *context_from_dev(const struct device *dev)
38 {
39 	int i;
40 
41 	for (i = 0; i < MAX_MDM_CTX; i++) {
42 		if (contexts[i] && contexts[i]->uart_dev == dev) {
43 			return contexts[i];
44 		}
45 	}
46 
47 	return NULL;
48 }
49 
50 /**
51  * @brief  Persists receiver context if there is a free place.
52  *
53  * @note   Amount of stored receiver contexts is determined by
54  *         MAX_MDM_CTX.
55  *
56  * @param  ctx: receiver context to persist.
57  *
58  * @retval 0 if ok, < 0 if error.
59  */
mdm_receiver_get(struct mdm_receiver_context * ctx)60 static int mdm_receiver_get(struct mdm_receiver_context *ctx)
61 {
62 	int i;
63 
64 	for (i = 0; i < MAX_MDM_CTX; i++) {
65 		if (!contexts[i]) {
66 			contexts[i] = ctx;
67 			return 0;
68 		}
69 	}
70 
71 	return -ENOMEM;
72 }
73 
74 /**
75  * @brief  Drains UART.
76  *
77  * @note   Discards remaining data.
78  *
79  * @param  ctx: receiver context.
80  *
81  * @retval None.
82  */
mdm_receiver_flush(struct mdm_receiver_context * ctx)83 static void mdm_receiver_flush(struct mdm_receiver_context *ctx)
84 {
85 	uint8_t c;
86 
87 	__ASSERT(ctx, "invalid ctx");
88 	__ASSERT(ctx->uart_dev, "invalid ctx device");
89 
90 	while (uart_fifo_read(ctx->uart_dev, &c, 1) > 0) {
91 		continue;
92 	}
93 }
94 
95 /**
96  * @brief  Receiver UART interrupt handler.
97  *
98  * @note   Fills contexts ring buffer with received data.
99  *         When ring buffer is full the data is discarded.
100  *
101  * @param  uart_dev: uart device.
102  *
103  * @retval None.
104  */
mdm_receiver_isr(const struct device * uart_dev,void * user_data)105 static void mdm_receiver_isr(const struct device *uart_dev, void *user_data)
106 {
107 	struct mdm_receiver_context *ctx;
108 	int rx, ret;
109 	static uint8_t read_buf[MAX_READ_SIZE];
110 
111 	ARG_UNUSED(user_data);
112 
113 	/* lookup the device */
114 	ctx = context_from_dev(uart_dev);
115 	if (!ctx) {
116 		return;
117 	}
118 
119 	/* get all of the data off UART as fast as we can */
120 	while (uart_irq_update(ctx->uart_dev) &&
121 	       uart_irq_rx_ready(ctx->uart_dev)) {
122 		rx = uart_fifo_read(ctx->uart_dev, read_buf, sizeof(read_buf));
123 		if (rx > 0) {
124 			ret = ring_buf_put(&ctx->rx_rb, read_buf, rx);
125 			if (ret != rx) {
126 				LOG_ERR("Rx buffer doesn't have enough space. "
127 						"Bytes pending: %d, written: %d",
128 						rx, ret);
129 				mdm_receiver_flush(ctx);
130 				k_sem_give(&ctx->rx_sem);
131 				break;
132 			}
133 			k_sem_give(&ctx->rx_sem);
134 		}
135 	}
136 }
137 
138 /**
139  * @brief  Configures receiver context and assigned device.
140  *
141  * @param  ctx: receiver context.
142  *
143  * @retval None.
144  */
mdm_receiver_setup(struct mdm_receiver_context * ctx)145 static void mdm_receiver_setup(struct mdm_receiver_context *ctx)
146 {
147 	__ASSERT(ctx, "invalid ctx");
148 
149 	uart_irq_rx_disable(ctx->uart_dev);
150 	uart_irq_tx_disable(ctx->uart_dev);
151 	mdm_receiver_flush(ctx);
152 	uart_irq_callback_set(ctx->uart_dev, mdm_receiver_isr);
153 	uart_irq_rx_enable(ctx->uart_dev);
154 }
155 
mdm_receiver_context_from_id(int id)156 struct mdm_receiver_context *mdm_receiver_context_from_id(int id)
157 {
158 	if (id >= 0 && id < MAX_MDM_CTX) {
159 		return contexts[id];
160 	} else {
161 		return NULL;
162 	}
163 }
164 
mdm_receiver_recv(struct mdm_receiver_context * ctx,uint8_t * buf,size_t size,size_t * bytes_read)165 int mdm_receiver_recv(struct mdm_receiver_context *ctx,
166 		      uint8_t *buf, size_t size, size_t *bytes_read)
167 {
168 	if (!ctx) {
169 		return -EINVAL;
170 	}
171 
172 	if (size == 0) {
173 		*bytes_read = 0;
174 		return 0;
175 	}
176 
177 	*bytes_read = ring_buf_get(&ctx->rx_rb, buf, size);
178 	return 0;
179 }
180 
mdm_receiver_send(struct mdm_receiver_context * ctx,const uint8_t * buf,size_t size)181 int mdm_receiver_send(struct mdm_receiver_context *ctx,
182 		      const uint8_t *buf, size_t size)
183 {
184 	if (!ctx) {
185 		return -EINVAL;
186 	}
187 
188 	if (size == 0) {
189 		return 0;
190 	}
191 
192 	do {
193 		uart_poll_out(ctx->uart_dev, *buf++);
194 	} while (--size);
195 
196 	return 0;
197 }
198 
mdm_receiver_sleep(struct mdm_receiver_context * ctx)199 int mdm_receiver_sleep(struct mdm_receiver_context *ctx)
200 {
201 	uart_irq_rx_disable(ctx->uart_dev);
202 #ifdef CONFIG_PM_DEVICE
203 	pm_device_action_run(ctx->uart_dev, PM_DEVICE_ACTION_SUSPEND);
204 #endif
205 	return 0;
206 }
207 
mdm_receiver_wake(struct mdm_receiver_context * ctx)208 int mdm_receiver_wake(struct mdm_receiver_context *ctx)
209 {
210 #ifdef CONFIG_PM_DEVICE
211 	pm_device_action_run(ctx->uart_dev, PM_DEVICE_ACTION_RESUME);
212 #endif
213 	uart_irq_rx_enable(ctx->uart_dev);
214 
215 	return 0;
216 }
217 
mdm_receiver_register(struct mdm_receiver_context * ctx,const struct device * uart_dev,uint8_t * buf,size_t size)218 int mdm_receiver_register(struct mdm_receiver_context *ctx,
219 			  const struct device *uart_dev,
220 			  uint8_t *buf, size_t size)
221 {
222 	int ret;
223 
224 	if ((!ctx) || (size == 0)) {
225 		return -EINVAL;
226 	}
227 
228 	if (!device_is_ready(uart_dev)) {
229 		LOG_ERR("Device is not ready: %s",
230 			uart_dev ? uart_dev->name : "<null>");
231 		return -ENODEV;
232 	}
233 
234 	ctx->uart_dev = uart_dev;
235 	ring_buf_init(&ctx->rx_rb, size, buf);
236 	k_sem_init(&ctx->rx_sem, 0, 1);
237 
238 	ret = mdm_receiver_get(ctx);
239 	if (ret < 0) {
240 		return ret;
241 	}
242 
243 	mdm_receiver_setup(ctx);
244 	return 0;
245 }
246