blob: 0d2cb627c067b7cf0c1dba874c791aa7c3ed75af [file] [log] [blame]
Michael Scott9182d2e2018-08-01 12:01:00 -08001/** @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
Gerard Marull-Paretasfb60aab2022-05-06 10:25:46 +020014#include <zephyr/kernel.h>
15#include <zephyr/init.h>
16#include <zephyr/drivers/uart.h>
17#include <zephyr/pm/device.h>
Michael Scott9182d2e2018-08-01 12:01:00 -080018
Gerard Marull-Paretasfb60aab2022-05-06 10:25:46 +020019#include <zephyr/logging/log.h>
Kumar Galab0e7d312019-02-12 00:00:29 -060020
21LOG_MODULE_REGISTER(mdm_receiver, CONFIG_MODEM_LOG_LEVEL);
Anas Nashif5c761432018-10-11 10:20:56 -040022
Anas Nashif158d9212019-06-21 13:30:27 -040023#include "modem_receiver.h"
Michael Scott9182d2e2018-08-01 12:01:00 -080024
25#define MAX_MDM_CTX CONFIG_MODEM_RECEIVER_MAX_CONTEXTS
26#define MAX_READ_SIZE 128
27
28static struct mdm_receiver_context *contexts[MAX_MDM_CTX];
29
Georgij Cernysiovde8b39f2019-02-13 22:59:17 +010030/**
31 * @brief Finds receiver context which manages provided device.
32 *
Benjamin Cabédb53c3f2023-07-13 14:18:49 +020033 * @param dev: device used by the receiver context.
Georgij Cernysiovde8b39f2019-02-13 22:59:17 +010034 *
35 * @retval Receiver context or NULL.
36 */
Tomasz Bursztykae18fcbb2020-04-30 20:33:38 +020037static struct mdm_receiver_context *context_from_dev(const struct device *dev)
Michael Scott9182d2e2018-08-01 12:01:00 -080038{
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
Georgij Cernysiovde8b39f2019-02-13 22:59:17 +010050/**
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 *
Benjamin Cabédb53c3f2023-07-13 14:18:49 +020056 * @param ctx: receiver context to persist.
Georgij Cernysiovde8b39f2019-02-13 22:59:17 +010057 *
58 * @retval 0 if ok, < 0 if error.
59 */
Michael Scott9182d2e2018-08-01 12:01:00 -080060static int mdm_receiver_get(struct mdm_receiver_context *ctx)
61{
62 int i;
63
Michael Scott9182d2e2018-08-01 12:01:00 -080064 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
Georgij Cernysiovde8b39f2019-02-13 22:59:17 +010074/**
75 * @brief Drains UART.
76 *
77 * @note Discards remaining data.
78 *
Benjamin Cabédb53c3f2023-07-13 14:18:49 +020079 * @param ctx: receiver context.
Georgij Cernysiovde8b39f2019-02-13 22:59:17 +010080 *
81 * @retval None.
82 */
Michael Scott9182d2e2018-08-01 12:01:00 -080083static void mdm_receiver_flush(struct mdm_receiver_context *ctx)
84{
Kumar Galaa1b77fd2020-05-27 11:26:57 -050085 uint8_t c;
Michael Scott9182d2e2018-08-01 12:01:00 -080086
Georgij Cernysiov886ab982019-02-13 23:06:40 +010087 __ASSERT(ctx, "invalid ctx");
88 __ASSERT(ctx->uart_dev, "invalid ctx device");
Michael Scott9182d2e2018-08-01 12:01:00 -080089
Michael Scott9182d2e2018-08-01 12:01:00 -080090 while (uart_fifo_read(ctx->uart_dev, &c, 1) > 0) {
91 continue;
92 }
Michael Scott9182d2e2018-08-01 12:01:00 -080093}
94
Georgij Cernysiovde8b39f2019-02-13 22:59:17 +010095/**
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 *
Benjamin Cabédb53c3f2023-07-13 14:18:49 +0200101 * @param uart_dev: uart device.
Georgij Cernysiovde8b39f2019-02-13 22:59:17 +0100102 *
103 * @retval None.
104 */
Tomasz Bursztykae18fcbb2020-04-30 20:33:38 +0200105static void mdm_receiver_isr(const struct device *uart_dev, void *user_data)
Michael Scott9182d2e2018-08-01 12:01:00 -0800106{
107 struct mdm_receiver_context *ctx;
108 int rx, ret;
Kumar Galaa1b77fd2020-05-27 11:26:57 -0500109 static uint8_t read_buf[MAX_READ_SIZE];
Michael Scott9182d2e2018-08-01 12:01:00 -0800110
Tomasz Bursztyka7d1af022020-06-24 19:06:44 +0200111 ARG_UNUSED(user_data);
112
Michael Scott9182d2e2018-08-01 12:01:00 -0800113 /* 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) {
Georgij Cernysiov94ee0dd2019-02-13 22:48:46 +0100124 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);
Michael Scott9182d2e2018-08-01 12:01:00 -0800129 mdm_receiver_flush(ctx);
Georgij Cernysiov94ee0dd2019-02-13 22:48:46 +0100130 k_sem_give(&ctx->rx_sem);
131 break;
Michael Scott9182d2e2018-08-01 12:01:00 -0800132 }
Michael Scott9182d2e2018-08-01 12:01:00 -0800133 k_sem_give(&ctx->rx_sem);
134 }
135 }
136}
137
Georgij Cernysiovde8b39f2019-02-13 22:59:17 +0100138/**
139 * @brief Configures receiver context and assigned device.
140 *
Benjamin Cabédb53c3f2023-07-13 14:18:49 +0200141 * @param ctx: receiver context.
Georgij Cernysiovde8b39f2019-02-13 22:59:17 +0100142 *
143 * @retval None.
144 */
145static void mdm_receiver_setup(struct mdm_receiver_context *ctx)
146{
Georgij Cernysiov886ab982019-02-13 23:06:40 +0100147 __ASSERT(ctx, "invalid ctx");
Georgij Cernysiovde8b39f2019-02-13 22:59:17 +0100148
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
156struct 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
Michael Scott9182d2e2018-08-01 12:01:00 -0800165int mdm_receiver_recv(struct mdm_receiver_context *ctx,
Kumar Galaa1b77fd2020-05-27 11:26:57 -0500166 uint8_t *buf, size_t size, size_t *bytes_read)
Michael Scott9182d2e2018-08-01 12:01:00 -0800167{
168 if (!ctx) {
169 return -EINVAL;
170 }
171
Georgij Cernysiov3fe917e2019-02-13 23:36:39 +0100172 if (size == 0) {
173 *bytes_read = 0;
174 return 0;
175 }
Georgij Cernysiov94ee0dd2019-02-13 22:48:46 +0100176
Georgij Cernysiov3fe917e2019-02-13 23:36:39 +0100177 *bytes_read = ring_buf_get(&ctx->rx_rb, buf, size);
Georgij Cernysiov94ee0dd2019-02-13 22:48:46 +0100178 return 0;
Michael Scott9182d2e2018-08-01 12:01:00 -0800179}
180
181int mdm_receiver_send(struct mdm_receiver_context *ctx,
Kumar Galaa1b77fd2020-05-27 11:26:57 -0500182 const uint8_t *buf, size_t size)
Michael Scott9182d2e2018-08-01 12:01:00 -0800183{
184 if (!ctx) {
185 return -EINVAL;
186 }
187
Georgij Cernysiov3fe917e2019-02-13 23:36:39 +0100188 if (size == 0) {
189 return 0;
190 }
191
Georgij Cernysiov27618402019-02-13 23:20:21 +0100192 do {
193 uart_poll_out(ctx->uart_dev, *buf++);
Michael Scottbc5a3f22019-03-02 09:51:05 -0800194 } while (--size);
Michael Scott9182d2e2018-08-01 12:01:00 -0800195
196 return 0;
197}
198
Christoph Schrammce4cc462019-06-07 17:51:30 +0200199int mdm_receiver_sleep(struct mdm_receiver_context *ctx)
200{
201 uart_irq_rx_disable(ctx->uart_dev);
Gerard Marull-Paretas56a35e52021-07-30 11:34:09 +0200202#ifdef CONFIG_PM_DEVICE
Flavio Ceolin64516262021-11-23 09:21:49 -0800203 pm_device_action_run(ctx->uart_dev, PM_DEVICE_ACTION_SUSPEND);
Christoph Schrammce4cc462019-06-07 17:51:30 +0200204#endif
205 return 0;
206}
207
208int mdm_receiver_wake(struct mdm_receiver_context *ctx)
209{
Gerard Marull-Paretas56a35e52021-07-30 11:34:09 +0200210#ifdef CONFIG_PM_DEVICE
Nick Ward941e4f02023-06-01 20:49:10 +1000211 pm_device_action_run(ctx->uart_dev, PM_DEVICE_ACTION_RESUME);
Christoph Schrammce4cc462019-06-07 17:51:30 +0200212#endif
213 uart_irq_rx_enable(ctx->uart_dev);
214
215 return 0;
216}
217
Michael Scott9182d2e2018-08-01 12:01:00 -0800218int mdm_receiver_register(struct mdm_receiver_context *ctx,
Marcin Niestroj26bd4fb2021-06-29 15:11:22 +0200219 const struct device *uart_dev,
Kumar Galaa1b77fd2020-05-27 11:26:57 -0500220 uint8_t *buf, size_t size)
Michael Scott9182d2e2018-08-01 12:01:00 -0800221{
222 int ret;
223
Georgij Cernysiov3fe917e2019-02-13 23:36:39 +0100224 if ((!ctx) || (size == 0)) {
Michael Scott9182d2e2018-08-01 12:01:00 -0800225 return -EINVAL;
226 }
227
Marcin Niestroj26bd4fb2021-06-29 15:11:22 +0200228 if (!device_is_ready(uart_dev)) {
229 LOG_ERR("Device is not ready: %s",
230 uart_dev ? uart_dev->name : "<null>");
Georgij Cernysiov23b64b02019-02-13 22:33:24 +0100231 return -ENODEV;
Michael Scott9182d2e2018-08-01 12:01:00 -0800232 }
233
Marcin Niestroj26bd4fb2021-06-29 15:11:22 +0200234 ctx->uart_dev = uart_dev;
Georgij Cernysiov94ee0dd2019-02-13 22:48:46 +0100235 ring_buf_init(&ctx->rx_rb, size, buf);
Michael Scott9182d2e2018-08-01 12:01:00 -0800236 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}