1 /*
2 * Copyright (c) 2023 Trackunit Corporation
3 * Copyright (c) 2023 Bjarki Arge Andreasen
4 * Copyright 2023 Google LLC
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/drivers/gnss.h>
10 #include <zephyr/drivers/gnss/gnss_publish.h>
11 #include <zephyr/modem/chat.h>
12 #include <zephyr/modem/backend/uart.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/drivers/gpio.h>
15 #include <zephyr/pm/device.h>
16 #include <string.h>
17
18 #include "gnss_nmea0183.h"
19 #include "gnss_nmea0183_match.h"
20 #include "gnss_parse.h"
21
22 #include <zephyr/logging/log.h>
23 LOG_MODULE_REGISTER(gnss_nmea_generic, CONFIG_GNSS_LOG_LEVEL);
24
25 #define UART_RX_BUF_SZ (256 + IS_ENABLED(CONFIG_GNSS_SATELLITES) * 512)
26 #define UART_TX_BUF_SZ 64
27 #define CHAT_RECV_BUF_SZ 256
28 #define CHAT_ARGV_SZ 32
29
30 struct gnss_nmea_generic_config {
31 const struct device *uart;
32 const struct modem_chat_script *const init_chat_script;
33 };
34
35 struct gnss_nmea_generic_data {
36 struct gnss_nmea0183_match_data match_data;
37 #if CONFIG_GNSS_SATELLITES
38 struct gnss_satellite satellites[CONFIG_GNSS_NMEA_GENERIC_SATELLITES_COUNT];
39 #endif
40
41 /* UART backend */
42 struct modem_pipe *uart_pipe;
43 struct modem_backend_uart uart_backend;
44 uint8_t uart_backend_receive_buf[UART_RX_BUF_SZ];
45 uint8_t uart_backend_transmit_buf[UART_TX_BUF_SZ];
46
47 /* Modem chat */
48 struct modem_chat chat;
49 uint8_t chat_receive_buf[CHAT_RECV_BUF_SZ];
50 uint8_t *chat_argv[CHAT_ARGV_SZ];
51 };
52
53 MODEM_CHAT_MATCHES_DEFINE(unsol_matches,
54 MODEM_CHAT_MATCH_WILDCARD("$??GGA,", ",*", gnss_nmea0183_match_gga_callback),
55 MODEM_CHAT_MATCH_WILDCARD("$??RMC,", ",*", gnss_nmea0183_match_rmc_callback),
56 #if CONFIG_GNSS_SATELLITES
57 MODEM_CHAT_MATCH_WILDCARD("$??GSV,", ",*", gnss_nmea0183_match_gsv_callback),
58 #endif
59 );
60
gnss_nmea_generic_resume(const struct device * dev)61 static int gnss_nmea_generic_resume(const struct device *dev)
62 {
63 const struct gnss_nmea_generic_config *cfg = dev->config;
64 struct gnss_nmea_generic_data *data = dev->data;
65 int ret;
66
67 ret = modem_pipe_open(data->uart_pipe, K_SECONDS(10));
68 if (ret < 0) {
69 return ret;
70 }
71
72 ret = modem_chat_attach(&data->chat, data->uart_pipe);
73
74 if (ret == 0) {
75 ret = modem_chat_run_script(&data->chat, cfg->init_chat_script);
76 }
77
78 if (ret < 0) {
79 modem_pipe_close(data->uart_pipe, K_SECONDS(10));
80 }
81 return ret;
82 }
83
84 static DEVICE_API(gnss, gnss_api) = {
85 };
86
gnss_nmea_generic_init_nmea0183_match(const struct device * dev)87 static int gnss_nmea_generic_init_nmea0183_match(const struct device *dev)
88 {
89 struct gnss_nmea_generic_data *data = dev->data;
90
91 const struct gnss_nmea0183_match_config match_config = {
92 .gnss = dev,
93 #if CONFIG_GNSS_SATELLITES
94 .satellites = data->satellites,
95 .satellites_size = ARRAY_SIZE(data->satellites),
96 #endif
97 };
98
99 return gnss_nmea0183_match_init(&data->match_data, &match_config);
100 }
101
gnss_nmea_generic_init_pipe(const struct device * dev)102 static void gnss_nmea_generic_init_pipe(const struct device *dev)
103 {
104 const struct gnss_nmea_generic_config *cfg = dev->config;
105 struct gnss_nmea_generic_data *data = dev->data;
106
107 const struct modem_backend_uart_config uart_backend_config = {
108 .uart = cfg->uart,
109 .receive_buf = data->uart_backend_receive_buf,
110 .receive_buf_size = sizeof(data->uart_backend_receive_buf),
111 .transmit_buf = data->uart_backend_transmit_buf,
112 .transmit_buf_size = sizeof(data->uart_backend_transmit_buf),
113 };
114
115 data->uart_pipe = modem_backend_uart_init(&data->uart_backend, &uart_backend_config);
116 }
117
118 static uint8_t gnss_nmea_generic_char_delimiter[] = {'\r', '\n'};
119
gnss_nmea_generic_init_chat(const struct device * dev)120 static int gnss_nmea_generic_init_chat(const struct device *dev)
121 {
122 struct gnss_nmea_generic_data *data = dev->data;
123
124 const struct modem_chat_config chat_config = {
125 .user_data = data,
126 .receive_buf = data->chat_receive_buf,
127 .receive_buf_size = sizeof(data->chat_receive_buf),
128 .delimiter = gnss_nmea_generic_char_delimiter,
129 .delimiter_size = ARRAY_SIZE(gnss_nmea_generic_char_delimiter),
130 .filter = NULL,
131 .filter_size = 0,
132 .argv = data->chat_argv,
133 .argv_size = ARRAY_SIZE(data->chat_argv),
134 .unsol_matches = unsol_matches,
135 .unsol_matches_size = ARRAY_SIZE(unsol_matches),
136 };
137
138 return modem_chat_init(&data->chat, &chat_config);
139 }
140
gnss_nmea_generic_init(const struct device * dev)141 static int gnss_nmea_generic_init(const struct device *dev)
142 {
143 int ret;
144
145 ret = gnss_nmea_generic_init_nmea0183_match(dev);
146 if (ret < 0) {
147 return ret;
148 }
149
150 gnss_nmea_generic_init_pipe(dev);
151
152 ret = gnss_nmea_generic_init_chat(dev);
153 if (ret < 0) {
154 return ret;
155 }
156
157 #if CONFIG_PM_DEVICE
158 pm_device_init_suspended(dev);
159 #else
160 ret = gnss_nmea_generic_resume(dev);
161 if (ret < 0) {
162 return ret;
163 }
164 #endif
165
166 return 0;
167 }
168
169 #if CONFIG_PM_DEVICE
gnss_nmea_generic_pm_action(const struct device * dev,enum pm_device_action action)170 static int gnss_nmea_generic_pm_action(const struct device *dev, enum pm_device_action action)
171 {
172 switch (action) {
173 case PM_DEVICE_ACTION_RESUME:
174 return gnss_nmea_generic_resume(dev);
175 default:
176 return -ENOTSUP;
177 }
178 }
179 #endif
180
181 #if DT_HAS_COMPAT_STATUS_OKAY(gnss_nmea_generic)
182 MODEM_CHAT_SCRIPT_EMPTY_DEFINE(gnss_nmea_generic_init_chat_script);
183 #endif
184
185 #define GNSS_NMEA_GENERIC(inst) \
186 static const struct gnss_nmea_generic_config gnss_nmea_generic_cfg_##inst = { \
187 .uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \
188 .init_chat_script = &_CONCAT(DT_DRV_COMPAT, _init_chat_script), \
189 }; \
190 \
191 static struct gnss_nmea_generic_data gnss_nmea_generic_data_##inst; \
192 \
193 PM_DEVICE_DT_INST_DEFINE(inst, gnss_nmea_generic_pm_action); \
194 \
195 DEVICE_DT_INST_DEFINE(inst, gnss_nmea_generic_init, PM_DEVICE_DT_INST_GET(inst),\
196 &gnss_nmea_generic_data_##inst, \
197 &gnss_nmea_generic_cfg_##inst, \
198 POST_KERNEL, CONFIG_GNSS_INIT_PRIORITY, &gnss_api);
199
200 #define DT_DRV_COMPAT gnss_nmea_generic
201 DT_INST_FOREACH_STATUS_OKAY(GNSS_NMEA_GENERIC)
202 #undef DT_DRV_COMPAT
203