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