1 /*
2  * Copyright 2024 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/gnss.h>
8 #include <zephyr/drivers/gnss/gnss_publish.h>
9 #include <zephyr/modem/chat.h>
10 #include <zephyr/modem/ubx.h>
11 #include <zephyr/modem/backend/uart.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <string.h>
15 #include <stdlib.h>
16 
17 #include "gnss_nmea0183.h"
18 #include "gnss_nmea0183_match.h"
19 #include "gnss_parse.h"
20 
21 #include "gnss_u_blox_protocol/gnss_u_blox_protocol.h"
22 
23 #include <zephyr/logging/log.h>
24 LOG_MODULE_REGISTER(ubx_m8, CONFIG_GNSS_LOG_LEVEL);
25 
26 #define DT_DRV_COMPAT			u_blox_m8
27 
28 #define UART_RECV_BUF_SZ		128
29 #define UART_TRNF_BUF_SZ		128
30 
31 #define CHAT_RECV_BUF_SZ		256
32 #define CHAT_ARGV_SZ			32
33 
34 #define UBX_RECV_BUF_SZ			UBX_FRM_SZ_MAX
35 #define UBX_TRNS_BUF_SZ			UBX_FRM_SZ_MAX
36 #define UBX_WORK_BUF_SZ			UBX_FRM_SZ_MAX
37 
38 #define UBX_FRM_BUF_SZ			UBX_FRM_SZ_MAX
39 
40 #define MODEM_UBX_SCRIPT_TIMEOUT_MS	1000
41 #define UBX_M8_SCRIPT_RETRY_DEFAULT	10
42 
43 #define UBX_M8_GNSS_SYS_CNT		8
44 #define UBX_M8_GNSS_SUPP_SYS_CNT	6
45 
46 struct ubx_m8_config {
47 	const struct device *uart;
48 	const uint32_t uart_baudrate;
49 };
50 
51 struct ubx_m8_data {
52 	struct gnss_nmea0183_match_data match_data;
53 #if CONFIG_GNSS_SATELLITES
54 	struct gnss_satellite satellites[CONFIG_GNSS_U_BLOX_M8_SATELLITES_COUNT];
55 #endif
56 
57 	/* UART backend */
58 	struct modem_pipe *uart_pipe;
59 	struct modem_backend_uart uart_backend;
60 	uint8_t uart_backend_receive_buf[UART_RECV_BUF_SZ];
61 	uint8_t uart_backend_transmit_buf[UART_TRNF_BUF_SZ];
62 
63 	/* Modem chat */
64 	struct modem_chat chat;
65 	uint8_t chat_receive_buf[CHAT_RECV_BUF_SZ];
66 	uint8_t *chat_argv[CHAT_ARGV_SZ];
67 
68 	/* Modem ubx */
69 	struct modem_ubx ubx;
70 	uint8_t ubx_receive_buf[UBX_RECV_BUF_SZ];
71 	uint8_t ubx_work_buf[UBX_WORK_BUF_SZ];
72 
73 	/* Modem ubx script */
74 	struct modem_ubx_script script;
75 	uint8_t request_buf[UBX_FRM_BUF_SZ];
76 	uint8_t response_buf[UBX_FRM_BUF_SZ];
77 	uint8_t match_buf[UBX_FRM_BUF_SZ];
78 
79 	struct k_spinlock lock;
80 };
81 
82 MODEM_CHAT_MATCHES_DEFINE(unsol_matches,
83 	MODEM_CHAT_MATCH_WILDCARD("$??GGA,", ",*", gnss_nmea0183_match_gga_callback),
84 	MODEM_CHAT_MATCH_WILDCARD("$??RMC,", ",*", gnss_nmea0183_match_rmc_callback),
85 #if CONFIG_GNSS_SATELLITES
86 	MODEM_CHAT_MATCH_WILDCARD("$??GSV,", ",*", gnss_nmea0183_match_gsv_callback),
87 #endif
88 );
89 
ubx_m8_resume(const struct device * dev)90 static int ubx_m8_resume(const struct device *dev)
91 {
92 	struct ubx_m8_data *data = dev->data;
93 	int ret;
94 
95 	ret = modem_pipe_open(data->uart_pipe, K_SECONDS(10));
96 	if (ret < 0) {
97 		return ret;
98 	}
99 
100 	ret = modem_chat_attach(&data->chat, data->uart_pipe);
101 	if (ret < 0) {
102 		(void)modem_pipe_close(data->uart_pipe, K_SECONDS(10));
103 		return ret;
104 	}
105 
106 	return ret;
107 }
108 
ubx_m8_turn_off(const struct device * dev)109 static int ubx_m8_turn_off(const struct device *dev)
110 {
111 	struct ubx_m8_data *data = dev->data;
112 
113 	return modem_pipe_close(data->uart_pipe, K_SECONDS(10));
114 }
115 
ubx_m8_init_nmea0183_match(const struct device * dev)116 static int ubx_m8_init_nmea0183_match(const struct device *dev)
117 {
118 	struct ubx_m8_data *data = dev->data;
119 
120 	const struct gnss_nmea0183_match_config match_config = {
121 		.gnss = dev,
122 #if CONFIG_GNSS_SATELLITES
123 		.satellites = data->satellites,
124 		.satellites_size = ARRAY_SIZE(data->satellites),
125 #endif
126 	};
127 
128 	return gnss_nmea0183_match_init(&data->match_data, &match_config);
129 }
130 
ubx_m8_init_pipe(const struct device * dev)131 static void ubx_m8_init_pipe(const struct device *dev)
132 {
133 	const struct ubx_m8_config *cfg = dev->config;
134 	struct ubx_m8_data *data = dev->data;
135 
136 	const struct modem_backend_uart_config uart_backend_config = {
137 		.uart = cfg->uart,
138 		.receive_buf = data->uart_backend_receive_buf,
139 		.receive_buf_size = sizeof(data->uart_backend_receive_buf),
140 		.transmit_buf = data->uart_backend_transmit_buf,
141 		.transmit_buf_size = ARRAY_SIZE(data->uart_backend_transmit_buf),
142 	};
143 
144 	data->uart_pipe = modem_backend_uart_init(&data->uart_backend, &uart_backend_config);
145 }
146 
147 static uint8_t ubx_m8_char_delimiter[] = {'\r', '\n'};
148 
ubx_m8_init_chat(const struct device * dev)149 static int ubx_m8_init_chat(const struct device *dev)
150 {
151 	struct ubx_m8_data *data = dev->data;
152 
153 	const struct modem_chat_config chat_config = {
154 		.user_data = data,
155 		.receive_buf = data->chat_receive_buf,
156 		.receive_buf_size = sizeof(data->chat_receive_buf),
157 		.delimiter = ubx_m8_char_delimiter,
158 		.delimiter_size = ARRAY_SIZE(ubx_m8_char_delimiter),
159 		.filter = NULL,
160 		.filter_size = 0,
161 		.argv = data->chat_argv,
162 		.argv_size = ARRAY_SIZE(data->chat_argv),
163 		.unsol_matches = unsol_matches,
164 		.unsol_matches_size = ARRAY_SIZE(unsol_matches),
165 	};
166 
167 	return modem_chat_init(&data->chat, &chat_config);
168 }
169 
ubx_m8_init_ubx(const struct device * dev)170 static int ubx_m8_init_ubx(const struct device *dev)
171 {
172 	struct ubx_m8_data *data = dev->data;
173 
174 	const struct modem_ubx_config ubx_config = {
175 		.user_data = data,
176 		.receive_buf = data->ubx_receive_buf,
177 		.receive_buf_size = sizeof(data->ubx_receive_buf),
178 		.work_buf = data->ubx_work_buf,
179 		.work_buf_size = sizeof(data->ubx_work_buf),
180 	};
181 
182 	return modem_ubx_init(&data->ubx, &ubx_config);
183 }
184 
185 /**
186  * @brief Changes modem module (chat or ubx) attached to the uart pipe.
187  * @param dev Dev instance
188  * @param change_from_to 0 for changing from "chat" to "ubx", 1 for changing from "ubx" to "chat"
189  * @returns 0 if successful
190  * @returns negative errno code if failure
191  */
ubx_m8_modem_module_change(const struct device * dev,bool change_from_to)192 static int ubx_m8_modem_module_change(const struct device *dev, bool change_from_to)
193 {
194 	struct ubx_m8_data *data = dev->data;
195 	int ret;
196 
197 	if (change_from_to == 0) {
198 		modem_chat_release(&data->chat);
199 		ret = modem_ubx_attach(&data->ubx, data->uart_pipe);
200 	} else { /* change_from_to == 1 */
201 		modem_ubx_release(&data->ubx);
202 		ret = modem_chat_attach(&data->chat, data->uart_pipe);
203 	}
204 
205 	if (ret < 0) {
206 		(void)modem_pipe_close(data->uart_pipe, K_SECONDS(10));
207 	}
208 
209 	return ret;
210 }
211 
ubx_m8_modem_ubx_run_script(const struct device * dev,struct modem_ubx_script * modem_ubx_script_tx)212 static int ubx_m8_modem_ubx_run_script(const struct device *dev,
213 					struct modem_ubx_script *modem_ubx_script_tx)
214 {
215 	struct ubx_m8_data *data = dev->data;
216 	int ret;
217 
218 	ret = ubx_m8_modem_module_change(dev, 0);
219 	if (ret < 0) {
220 		goto reset_modem_module;
221 	}
222 
223 	ret = modem_ubx_run_script(&data->ubx, modem_ubx_script_tx);
224 
225 reset_modem_module:
226 	ret |= ubx_m8_modem_module_change(dev, 1);
227 
228 	return ret;
229 }
230 
ubx_m8_modem_ubx_script_fill(const struct device * dev)231 static void ubx_m8_modem_ubx_script_fill(const struct device *dev)
232 {
233 	struct ubx_m8_data *data = dev->data;
234 
235 	data->script.request = (struct ubx_frame *)data->request_buf;
236 	data->script.response = (struct ubx_frame *)data->response_buf;
237 	data->script.match = (struct ubx_frame *)data->match_buf;
238 	data->script.retry_count = UBX_M8_SCRIPT_RETRY_DEFAULT;
239 	data->script.timeout = K_MSEC(MODEM_UBX_SCRIPT_TIMEOUT_MS);
240 }
241 
ubx_m8_modem_ubx_script_init(const struct device * dev,void * payload,uint16_t payld_sz,enum ubx_msg_class msg_cls,enum ubx_config_message msg_id)242 static int ubx_m8_modem_ubx_script_init(const struct device *dev, void *payload, uint16_t payld_sz,
243 					 enum ubx_msg_class msg_cls, enum ubx_config_message msg_id)
244 {
245 	int ret;
246 	struct ubx_m8_data *data = dev->data;
247 	struct ubx_cfg_ack_payload match_payload = {
248 		.message_class = msg_cls,
249 		.message_id = msg_id,
250 	};
251 
252 	ubx_m8_modem_ubx_script_fill(dev);
253 
254 	ret = ubx_create_and_validate_frame(data->match_buf, sizeof(data->match_buf), UBX_CLASS_ACK,
255 					    UBX_ACK_ACK, &match_payload, UBX_CFG_ACK_PAYLOAD_SZ);
256 	if (ret < 0) {
257 		return ret;
258 	}
259 
260 	ret = ubx_create_and_validate_frame(data->request_buf, sizeof(data->request_buf), msg_cls,
261 					    msg_id, payload, payld_sz);
262 
263 	return ret;
264 }
265 
ubx_m8_ubx_cfg_rate(const struct device * dev)266 static int ubx_m8_ubx_cfg_rate(const struct device *dev)
267 {
268 	int ret;
269 	k_spinlock_key_t key;
270 	struct ubx_m8_data *data = dev->data;
271 	struct ubx_cfg_rate_payload payload;
272 
273 	key = k_spin_lock(&data->lock);
274 
275 	ubx_cfg_rate_payload_default(&payload);
276 
277 	ret = ubx_m8_modem_ubx_script_init(dev, &payload, UBX_CFG_RATE_PAYLOAD_SZ, UBX_CLASS_CFG,
278 					    UBX_CFG_RATE);
279 	if (ret < 0) {
280 		goto unlock;
281 	}
282 
283 	ret = ubx_m8_modem_ubx_run_script(dev, &(data->script));
284 
285 unlock:
286 	k_spin_unlock(&data->lock, key);
287 
288 	return ret;
289 }
290 
ubx_m8_ubx_cfg_prt_set(const struct device * dev,uint32_t target_baudrate,uint8_t retry)291 static int ubx_m8_ubx_cfg_prt_set(const struct device *dev, uint32_t target_baudrate,
292 				   uint8_t retry)
293 {
294 	int ret;
295 	k_spinlock_key_t key;
296 	struct ubx_m8_data *data = dev->data;
297 	struct ubx_cfg_prt_set_payload payload;
298 
299 	key = k_spin_lock(&data->lock);
300 
301 	ubx_cfg_prt_set_payload_default(&payload);
302 	payload.baudrate = target_baudrate;
303 
304 	ret = ubx_m8_modem_ubx_script_init(dev, &payload, UBX_CFG_PRT_SET_PAYLOAD_SZ,
305 					    UBX_CLASS_CFG, UBX_CFG_PRT);
306 	if (ret < 0) {
307 		goto unlock;
308 	}
309 
310 	data->script.retry_count = retry;
311 	/* Returns failure if "target_baudrate" is different than device's currently set baudrate,
312 	 * because the device will change its baudrate and respond with UBX-ACK with new baudrate,
313 	 * which we will miss. Hence, we need to change uart's baudrate after sending the frame
314 	 * (in order to receive response as well), which we are not doing right now.
315 	 */
316 	ret = ubx_m8_modem_ubx_run_script(dev, &(data->script));
317 
318 unlock:
319 	k_spin_unlock(&data->lock, key);
320 
321 	return ret;
322 }
323 
ubx_m8_ubx_cfg_rst(const struct device * dev,uint8_t reset_mode)324 static int ubx_m8_ubx_cfg_rst(const struct device *dev, uint8_t reset_mode)
325 {
326 	int ret;
327 	k_spinlock_key_t key;
328 	struct ubx_m8_data *data = dev->data;
329 	struct ubx_cfg_rst_payload payload;
330 
331 	key = k_spin_lock(&data->lock);
332 
333 	ubx_cfg_rst_payload_default(&payload);
334 
335 	payload.nav_bbr_mask = UBX_CFG_RST_NAV_BBR_MASK_HOT_START;
336 	payload.reset_mode = reset_mode;
337 
338 	ret = ubx_m8_modem_ubx_script_init(dev, &payload, UBX_CFG_RST_PAYLOAD_SZ, UBX_CLASS_CFG,
339 					    UBX_CFG_RST);
340 	if (ret < 0) {
341 		goto unlock;
342 	}
343 
344 	data->script.match = NULL;
345 	ret = ubx_m8_modem_ubx_run_script(dev, &(data->script));
346 	if (ret < 0) {
347 		goto unlock;
348 	}
349 
350 	if (reset_mode == UBX_CFG_RST_RESET_MODE_CONTROLLED_GNSS_STOP) {
351 		k_sleep(K_MSEC(UBX_CFG_RST_WAIT_MS));
352 	}
353 
354 unlock:
355 	k_spin_unlock(&data->lock, key);
356 
357 	return ret;
358 }
359 
ubx_m8_set_uart_baudrate(const struct device * dev,uint32_t baudrate)360 static int ubx_m8_set_uart_baudrate(const struct device *dev, uint32_t baudrate)
361 {
362 	int ret;
363 	k_spinlock_key_t key;
364 	struct ubx_m8_data *data = dev->data;
365 	const struct ubx_m8_config *config = dev->config;
366 	struct uart_config uart_cfg;
367 
368 	key = k_spin_lock(&data->lock);
369 
370 	ret = ubx_m8_turn_off(dev);
371 	if (ret < 0) {
372 		goto reset_and_unlock;
373 	}
374 
375 	ret = uart_config_get(config->uart, &uart_cfg);
376 	if (ret < 0) {
377 		goto reset_and_unlock;
378 	}
379 	uart_cfg.baudrate = baudrate;
380 
381 	ret = uart_configure(config->uart, &uart_cfg);
382 
383 reset_and_unlock:
384 	ret |= ubx_m8_resume(dev);
385 
386 	k_spin_unlock(&data->lock, key);
387 
388 	return ret;
389 }
390 
ubx_m8_validate_baudrate(const struct device * dev,uint32_t baudrate)391 static bool ubx_m8_validate_baudrate(const struct device *dev, uint32_t baudrate)
392 {
393 	for (int i = 0; i < UBX_BAUDRATE_COUNT; ++i) {
394 		if (baudrate == ubx_baudrate[i]) {
395 			return true;
396 		}
397 	}
398 
399 	return false;
400 }
401 
402 /* This function will return failure if "target_baudrate" != device's current baudrate.
403  * Refer the function description of ubx_m8_ubx_cfg_prt_set for a detailed explanation.
404  */
ubx_m8_configure_gnss_device_baudrate_prerequisite(const struct device * dev)405 static int ubx_m8_configure_gnss_device_baudrate_prerequisite(const struct device *dev)
406 {
407 	/* Retry = 1 should be enough, but setting 2 just to be safe. */
408 	int ret, retry = 2;
409 	const struct ubx_m8_config *config = dev->config;
410 	uint32_t target_baudrate = config->uart_baudrate;
411 
412 	ret = ubx_m8_validate_baudrate(dev, target_baudrate);
413 	if (ret < 0) {
414 		return ret;
415 	}
416 
417 	/* Try communication with device with all possible baudrates, because initially we don't
418 	 * know the currently set baudrate of the device. We will match the baudrate in one of the
419 	 * following attempts and the device will thus change its baudrate to "target_baudrate".
420 	 */
421 	for (int i = 0; i < UBX_BAUDRATE_COUNT; ++i) {
422 		/* Set baudrate of UART pipe as ubx_baudrate[i]. */
423 		ret = ubx_m8_set_uart_baudrate(dev, ubx_baudrate[i]);
424 		if (ret < 0) {
425 			return ret;
426 		}
427 
428 		/* Try setting baudrate of device as target_baudrate. */
429 		ret = ubx_m8_ubx_cfg_prt_set(dev, target_baudrate, retry);
430 		if (ret == 0) {
431 			break;
432 		}
433 	}
434 
435 	/* Reset baudrate of UART pipe as target_baudrate. */
436 	ret = ubx_m8_set_uart_baudrate(dev, target_baudrate);
437 	if (ret < 0) {
438 		return ret;
439 	}
440 
441 	return 0;
442 }
443 
ubx_m8_configure_gnss_device_baudrate(const struct device * dev)444 static int ubx_m8_configure_gnss_device_baudrate(const struct device *dev)
445 {
446 	int ret;
447 	const struct ubx_m8_config *config = dev->config;
448 	uint32_t target_baudrate = config->uart_baudrate;
449 
450 	ret = ubx_m8_validate_baudrate(dev, target_baudrate);
451 	if (ret < 0) {
452 		return ret;
453 	}
454 
455 	ret = ubx_m8_ubx_cfg_prt_set(dev, target_baudrate, UBX_M8_SCRIPT_RETRY_DEFAULT);
456 	if (ret < 0) {
457 		return ret;
458 	}
459 
460 	return 0;
461 }
462 
ubx_m8_configure_messages(const struct device * dev)463 static int ubx_m8_configure_messages(const struct device *dev)
464 {
465 	int ret = 0;
466 	k_spinlock_key_t key;
467 	struct ubx_m8_data *data = dev->data;
468 	struct ubx_cfg_msg_payload payload;
469 
470 	key = k_spin_lock(&data->lock);
471 
472 	ubx_cfg_msg_payload_default(&payload);
473 
474 	/* Enabling GGA, RMC and GSV messages. */
475 	payload.rate = 1;
476 	uint8_t message_enable[] = {UBX_NMEA_GGA, UBX_NMEA_RMC, UBX_NMEA_GSV};
477 
478 	for (int i = 0; i < sizeof(message_enable); ++i) {
479 		payload.message_id = message_enable[i];
480 		ret = ubx_m8_modem_ubx_script_init(dev, &payload,  UBX_CFG_MSG_PAYLOAD_SZ,
481 						    UBX_CLASS_CFG, UBX_CFG_MSG);
482 		if (ret < 0) {
483 			goto unlock;
484 		}
485 
486 		ret = ubx_m8_modem_ubx_run_script(dev, &(data->script));
487 		if (ret < 0) {
488 			goto unlock;
489 		}
490 	}
491 
492 	/* Disabling DTM, GBS, GLL, GNS, GRS, GSA, GST, VLW, VTG and ZDA messages. */
493 	payload.rate = 0;
494 	uint8_t message_disable[] = {UBX_NMEA_DTM, UBX_NMEA_GBS, UBX_NMEA_GLL, UBX_NMEA_GNS,
495 				     UBX_NMEA_GRS, UBX_NMEA_GSA, UBX_NMEA_GST, UBX_NMEA_VLW,
496 				     UBX_NMEA_VTG, UBX_NMEA_ZDA};
497 
498 	for (int i = 0; i < sizeof(message_disable); ++i) {
499 		payload.message_id = message_disable[i];
500 		ret = ubx_m8_modem_ubx_script_init(dev, &payload, UBX_CFG_MSG_PAYLOAD_SZ,
501 						    UBX_CLASS_CFG, UBX_CFG_MSG);
502 		if (ret < 0) {
503 			goto unlock;
504 		}
505 
506 		ret = ubx_m8_modem_ubx_run_script(dev, &(data->script));
507 		if (ret < 0) {
508 			goto unlock;
509 		}
510 	}
511 
512 unlock:
513 	k_spin_unlock(&data->lock, key);
514 
515 	return ret;
516 }
517 
ubx_m8_navigation_mode_to_ubx_dynamic_model(const struct device * dev,enum gnss_navigation_mode mode)518 static int ubx_m8_navigation_mode_to_ubx_dynamic_model(const struct device *dev,
519 							enum gnss_navigation_mode mode)
520 {
521 	switch (mode) {
522 	case GNSS_NAVIGATION_MODE_ZERO_DYNAMICS:
523 		return UBX_DYN_MODEL_STATIONARY;
524 	case GNSS_NAVIGATION_MODE_LOW_DYNAMICS:
525 		return UBX_DYN_MODEL_PORTABLE;
526 	case GNSS_NAVIGATION_MODE_BALANCED_DYNAMICS:
527 		return UBX_DYN_MODEL_AIRBORNE1G;
528 	case GNSS_NAVIGATION_MODE_HIGH_DYNAMICS:
529 		return UBX_DYN_MODEL_AIRBORNE4G;
530 	default:
531 		return -EINVAL;
532 	}
533 }
534 
ubx_m8_ubx_dynamic_model_to_navigation_mode(const struct device * dev,enum ubx_dynamic_model dynamic_model)535 static int ubx_m8_ubx_dynamic_model_to_navigation_mode(const struct device *dev,
536 							enum ubx_dynamic_model dynamic_model)
537 {
538 	switch (dynamic_model) {
539 	case UBX_DYN_MODEL_PORTABLE:
540 		return GNSS_NAVIGATION_MODE_LOW_DYNAMICS;
541 	case UBX_DYN_MODEL_STATIONARY:
542 		return GNSS_NAVIGATION_MODE_ZERO_DYNAMICS;
543 	case UBX_DYN_MODEL_PEDESTRIAN:
544 		return GNSS_NAVIGATION_MODE_LOW_DYNAMICS;
545 	case UBX_DYN_MODEL_AUTOMOTIVE:
546 		return GNSS_NAVIGATION_MODE_LOW_DYNAMICS;
547 	case UBX_DYN_MODEL_SEA:
548 		return GNSS_NAVIGATION_MODE_BALANCED_DYNAMICS;
549 	case UBX_DYN_MODEL_AIRBORNE1G:
550 		return GNSS_NAVIGATION_MODE_BALANCED_DYNAMICS;
551 	case UBX_DYN_MODEL_AIRBORNE2G:
552 		return GNSS_NAVIGATION_MODE_BALANCED_DYNAMICS;
553 	case UBX_DYN_MODEL_AIRBORNE4G:
554 		return GNSS_NAVIGATION_MODE_HIGH_DYNAMICS;
555 	case UBX_DYN_MODEL_WRIST:
556 		return GNSS_NAVIGATION_MODE_BALANCED_DYNAMICS;
557 	case UBX_DYN_MODEL_BIKE:
558 		return GNSS_NAVIGATION_MODE_HIGH_DYNAMICS;
559 	default:
560 		return -EINVAL;
561 	}
562 }
563 
ubx_m8_set_navigation_mode(const struct device * dev,enum gnss_navigation_mode mode)564 static int ubx_m8_set_navigation_mode(const struct device *dev, enum gnss_navigation_mode mode)
565 {
566 	int ret;
567 	k_spinlock_key_t key;
568 	struct ubx_m8_data *data = dev->data;
569 	struct ubx_cfg_nav5_payload payload;
570 
571 	key = k_spin_lock(&data->lock);
572 
573 	ubx_cfg_nav5_payload_default(&payload);
574 
575 	ret = ubx_m8_navigation_mode_to_ubx_dynamic_model(dev, mode);
576 	if (ret < 0) {
577 		goto unlock;
578 	}
579 
580 	payload.dyn_model = ret;
581 
582 	ret = ubx_m8_modem_ubx_script_init(dev, &payload, UBX_CFG_NAV5_PAYLOAD_SZ, UBX_CLASS_CFG,
583 					    UBX_CFG_NAV5);
584 	if (ret < 0) {
585 		goto unlock;
586 	}
587 
588 	ret = ubx_m8_modem_ubx_run_script(dev, &(data->script));
589 	if (ret < 0) {
590 		goto unlock;
591 	}
592 
593 	k_sleep(K_MSEC(UBX_CFG_NAV5_WAIT_MS));
594 
595 unlock:
596 	k_spin_unlock(&data->lock, key);
597 
598 	return ret;
599 }
600 
ubx_m8_get_navigation_mode(const struct device * dev,enum gnss_navigation_mode * mode)601 static int ubx_m8_get_navigation_mode(const struct device *dev, enum gnss_navigation_mode *mode)
602 {
603 	int ret;
604 	k_spinlock_key_t key;
605 	struct ubx_m8_data *data = dev->data;
606 	enum ubx_dynamic_model dynamic_model;
607 
608 	key = k_spin_lock(&data->lock);
609 
610 	ret = ubx_m8_modem_ubx_script_init(dev, NULL, UBX_FRM_GET_PAYLOAD_SZ, UBX_CLASS_CFG,
611 					    UBX_CFG_NAV5);
612 	if (ret < 0) {
613 		goto unlock;
614 	}
615 
616 	ret = ubx_m8_modem_ubx_run_script(dev, &(data->script));
617 	if (ret < 0) {
618 		goto unlock;
619 	}
620 
621 	struct ubx_frame *response = data->script.response;
622 
623 	dynamic_model = ((struct ubx_cfg_nav5_payload *)response->payload_and_checksum)->dyn_model;
624 	ret = ubx_m8_ubx_dynamic_model_to_navigation_mode(dev, dynamic_model);
625 	if (ret < 0) {
626 		goto unlock;
627 	}
628 
629 	*mode = ret;
630 
631 unlock:
632 	k_spin_unlock(&data->lock, key);
633 
634 	return ret;
635 }
636 
ubx_m8_get_supported_systems(const struct device * dev,gnss_systems_t * systems)637 static int ubx_m8_get_supported_systems(const struct device *dev, gnss_systems_t *systems)
638 {
639 	*systems = (GNSS_SYSTEM_GPS | GNSS_SYSTEM_GLONASS | GNSS_SYSTEM_GALILEO |
640 		    GNSS_SYSTEM_BEIDOU | GNSS_SYSTEM_SBAS | GNSS_SYSTEM_QZSS);
641 
642 	return 0;
643 }
644 
ubx_m8_ubx_gnss_id_to_gnss_system(const struct device * dev,enum ubx_gnss_id gnss_id)645 static int ubx_m8_ubx_gnss_id_to_gnss_system(const struct device *dev, enum ubx_gnss_id gnss_id)
646 {
647 	switch (gnss_id) {
648 	case UBX_GNSS_ID_GPS:
649 		return GNSS_SYSTEM_GPS;
650 	case UBX_GNSS_ID_SBAS:
651 		return GNSS_SYSTEM_SBAS;
652 	case UBX_GNSS_ID_GALILEO:
653 		return GNSS_SYSTEM_GALILEO;
654 	case UBX_GNSS_ID_BEIDOU:
655 		return GNSS_SYSTEM_BEIDOU;
656 	case UBX_GNSS_ID_QZSS:
657 		return GNSS_SYSTEM_QZSS;
658 	case UBX_GNSS_ID_GLONASS:
659 		return GNSS_SYSTEM_GLONASS;
660 	default:
661 		return -EINVAL;
662 	};
663 }
664 
ubx_m8_config_block_fill(const struct device * dev,gnss_systems_t gnss_system,struct ubx_cfg_gnss_payload * payload,uint8_t index,uint32_t enable)665 static int ubx_m8_config_block_fill(const struct device *dev, gnss_systems_t gnss_system,
666 				     struct ubx_cfg_gnss_payload *payload, uint8_t index,
667 				     uint32_t enable)
668 {
669 	uint32_t signal_config;
670 
671 	switch (gnss_system) {
672 	case GNSS_SYSTEM_GPS:
673 		payload->config_blocks[index].gnss_id = UBX_GNSS_ID_GPS;
674 		signal_config = UBX_CFG_GNSS_FLAG_SGN_CNF_GPS_L1C_A;
675 		break;
676 	case GNSS_SYSTEM_GLONASS:
677 		payload->config_blocks[index].gnss_id = UBX_GNSS_ID_GLONASS;
678 		signal_config = UBX_CFG_GNSS_FLAG_SGN_CNF_GLONASS_L1;
679 		break;
680 	case GNSS_SYSTEM_GALILEO:
681 		payload->config_blocks[index].gnss_id = UBX_GNSS_ID_GALILEO;
682 		signal_config = UBX_CFG_GNSS_FLAG_SGN_CNF_GALILEO_E1;
683 		break;
684 	case GNSS_SYSTEM_BEIDOU:
685 		payload->config_blocks[index].gnss_id = UBX_GNSS_ID_BEIDOU;
686 		signal_config = UBX_CFG_GNSS_FLAG_SGN_CNF_BEIDOU_B1I;
687 		break;
688 	case GNSS_SYSTEM_QZSS:
689 		payload->config_blocks[index].gnss_id = UBX_GNSS_ID_QZSS;
690 		signal_config = UBX_CFG_GNSS_FLAG_SGN_CNF_QZSS_L1C_A;
691 		break;
692 	case GNSS_SYSTEM_SBAS:
693 		payload->config_blocks[index].gnss_id = UBX_GNSS_ID_SBAS;
694 		signal_config = UBX_CFG_GNSS_FLAG_SGN_CNF_SBAS_L1C_A;
695 		break;
696 	default:
697 		return -EINVAL;
698 	};
699 
700 	payload->config_blocks[index].flags = enable | signal_config;
701 
702 	return 0;
703 }
704 
ubx_m8_set_enabled_systems(const struct device * dev,gnss_systems_t systems)705 static int ubx_m8_set_enabled_systems(const struct device *dev, gnss_systems_t systems)
706 {
707 	int ret;
708 	k_spinlock_key_t key;
709 	struct ubx_m8_data *data = dev->data;
710 
711 	key = k_spin_lock(&data->lock);
712 
713 	struct ubx_cfg_gnss_payload *payload;
714 
715 	/* Get number of tracking channels for each supported gnss system by sending CFG-GNSS. */
716 	ret = ubx_m8_modem_ubx_script_init(dev, NULL, UBX_FRM_GET_PAYLOAD_SZ, UBX_CLASS_CFG,
717 					    UBX_CFG_GNSS);
718 	if (ret < 0) {
719 		goto unlock;
720 	}
721 
722 	ret = ubx_m8_modem_ubx_run_script(dev, &(data->script));
723 	if (ret < 0) {
724 		goto unlock;
725 	}
726 
727 	struct ubx_frame *response = data->script.response;
728 	uint16_t res_trk_ch_sum = 0, max_trk_ch_sum = 0;
729 
730 	/* Calculate sum of reserved and maximum tracking channels for each supported gnss system,
731 	 * and assert that the sum is not greater than the number of tracking channels in use.
732 	 */
733 	payload = (struct ubx_cfg_gnss_payload *) response->payload_and_checksum;
734 	for (int i = 0; i < payload->num_config_blocks; ++i) {
735 		ret = ubx_m8_ubx_gnss_id_to_gnss_system(dev, payload->config_blocks[i].gnss_id);
736 		if (ret < 0) {
737 			goto unlock;
738 		}
739 
740 		if (ret & systems) {
741 			res_trk_ch_sum += payload->config_blocks[i].num_res_trk_ch;
742 			max_trk_ch_sum += payload->config_blocks[i].max_num_trk_ch;
743 		}
744 
745 		if (res_trk_ch_sum > payload->num_trk_ch_use ||
746 		    max_trk_ch_sum > payload->num_trk_ch_use) {
747 			ret = -EINVAL;
748 			goto unlock;
749 		}
750 	}
751 
752 	/* Prepare payload (payload) for sending CFG-GNSS for enabling the gnss systems. */
753 	payload = malloc(sizeof(*payload) +
754 		sizeof(struct ubx_cfg_gnss_payload_config_block) * UBX_M8_GNSS_SUPP_SYS_CNT);
755 	if (!payload) {
756 		ret = -ENOMEM;
757 		goto unlock;
758 	}
759 
760 	payload->num_config_blocks = UBX_M8_GNSS_SUPP_SYS_CNT;
761 
762 	ubx_cfg_gnss_payload_default(payload);
763 
764 	uint8_t filled_blocks = 0;
765 	gnss_systems_t supported_systems;
766 
767 	ret = ubx_m8_get_supported_systems(dev, &supported_systems);
768 	if (ret < 0) {
769 		goto free_and_unlock;
770 	}
771 
772 	for (int i = 0; i < UBX_M8_GNSS_SYS_CNT; ++i) {
773 		gnss_systems_t gnss_system = 1 << i;
774 
775 		if (gnss_system & supported_systems) {
776 			uint32_t enable = (systems & gnss_system) ?
777 					  UBX_CFG_GNSS_FLAG_ENABLE : UBX_CFG_GNSS_FLAG_DISABLE;
778 
779 			ret = ubx_m8_config_block_fill(dev, gnss_system, payload, filled_blocks,
780 							enable);
781 			if (ret < 0) {
782 				goto free_and_unlock;
783 			}
784 
785 			++filled_blocks;
786 		}
787 	}
788 
789 	ret = ubx_m8_modem_ubx_script_init(dev, payload,
790 					    UBX_CFG_GNSS_PAYLOAD_SZ(UBX_M8_GNSS_SUPP_SYS_CNT),
791 					    UBX_CLASS_CFG, UBX_CFG_GNSS);
792 	if (ret < 0) {
793 		goto free_and_unlock;
794 	}
795 
796 	ret = ubx_m8_modem_ubx_run_script(dev, &(data->script));
797 	if (ret < 0) {
798 		goto free_and_unlock;
799 	}
800 
801 	k_sleep(K_MSEC(UBX_CFG_GNSS_WAIT_MS));
802 
803 free_and_unlock:
804 	free(payload);
805 
806 unlock:
807 	k_spin_unlock(&data->lock, key);
808 
809 	return ret;
810 }
811 
ubx_m8_get_enabled_systems(const struct device * dev,gnss_systems_t * systems)812 static int ubx_m8_get_enabled_systems(const struct device *dev, gnss_systems_t *systems)
813 {
814 	int ret;
815 	k_spinlock_key_t key;
816 	struct ubx_m8_data *data = dev->data;
817 
818 	key = k_spin_lock(&data->lock);
819 
820 	ret = ubx_m8_modem_ubx_script_init(dev, NULL, UBX_FRM_GET_PAYLOAD_SZ, UBX_CLASS_CFG,
821 					    UBX_CFG_GNSS);
822 	if (ret < 0) {
823 		goto unlock;
824 	}
825 
826 	ret = ubx_m8_modem_ubx_run_script(dev, &(data->script));
827 	if (ret < 0) {
828 		goto unlock;
829 	}
830 
831 	struct ubx_frame *response = data->script.response;
832 	struct ubx_cfg_gnss_payload *payload =
833 		(struct ubx_cfg_gnss_payload *) response->payload_and_checksum;
834 
835 	*systems = 0;
836 	for (int i = 0; i < payload->num_config_blocks; ++i) {
837 		if (payload->config_blocks[i].flags & UBX_CFG_GNSS_FLAG_ENABLE) {
838 			enum ubx_gnss_id gnss_id = payload->config_blocks[i].gnss_id;
839 
840 			ret = ubx_m8_ubx_gnss_id_to_gnss_system(dev, gnss_id);
841 			if (ret < 0) {
842 				goto unlock;
843 			}
844 
845 			*systems |= ret;
846 		}
847 	}
848 
849 unlock:
850 	k_spin_unlock(&data->lock, key);
851 
852 	return ret;
853 }
854 
ubx_m8_set_fix_rate(const struct device * dev,uint32_t fix_interval_ms)855 static int ubx_m8_set_fix_rate(const struct device *dev, uint32_t fix_interval_ms)
856 {
857 	int ret;
858 	k_spinlock_key_t key;
859 	struct ubx_m8_data *data = dev->data;
860 	struct ubx_cfg_rate_payload payload;
861 
862 	if (fix_interval_ms < 50) {
863 		return -1;
864 	}
865 
866 	key = k_spin_lock(&data->lock);
867 
868 	ubx_cfg_rate_payload_default(&payload);
869 	payload.meas_rate_ms = fix_interval_ms;
870 
871 	ret = ubx_m8_modem_ubx_script_init(dev, &payload, UBX_CFG_RATE_PAYLOAD_SZ, UBX_CLASS_CFG,
872 					    UBX_CFG_RATE);
873 	if (ret < 0) {
874 		goto unlock;
875 	}
876 
877 	ret = ubx_m8_modem_ubx_run_script(dev, &(data->script));
878 
879 unlock:
880 	k_spin_unlock(&data->lock, key);
881 
882 	return ret;
883 }
884 
ubx_m8_get_fix_rate(const struct device * dev,uint32_t * fix_interval_ms)885 static int ubx_m8_get_fix_rate(const struct device *dev, uint32_t *fix_interval_ms)
886 {
887 	int ret;
888 	k_spinlock_key_t key;
889 	struct ubx_m8_data *data = dev->data;
890 	struct ubx_cfg_rate_payload *payload;
891 
892 	key = k_spin_lock(&data->lock);
893 
894 	ret = ubx_m8_modem_ubx_script_init(dev, NULL, UBX_FRM_GET_PAYLOAD_SZ, UBX_CLASS_CFG,
895 					    UBX_CFG_RATE);
896 	if (ret < 0) {
897 		goto unlock;
898 	}
899 
900 	ret = ubx_m8_modem_ubx_run_script(dev, &(data->script));
901 	if (ret < 0) {
902 		goto unlock;
903 	}
904 
905 	struct ubx_frame *response = data->script.response;
906 
907 	payload = (struct ubx_cfg_rate_payload *) response->payload_and_checksum;
908 	*fix_interval_ms = payload->meas_rate_ms;
909 
910 unlock:
911 	k_spin_unlock(&data->lock, key);
912 
913 	return ret;
914 }
915 
916 static DEVICE_API(gnss, gnss_api) = {
917 	.set_fix_rate = ubx_m8_set_fix_rate,
918 	.get_fix_rate = ubx_m8_get_fix_rate,
919 	.set_navigation_mode = ubx_m8_set_navigation_mode,
920 	.get_navigation_mode = ubx_m8_get_navigation_mode,
921 	.set_enabled_systems = ubx_m8_set_enabled_systems,
922 	.get_enabled_systems = ubx_m8_get_enabled_systems,
923 	.get_supported_systems = ubx_m8_get_supported_systems,
924 };
925 
ubx_m8_configure(const struct device * dev)926 static int ubx_m8_configure(const struct device *dev)
927 {
928 	int ret;
929 
930 	/* The return value could be ignored. See function description for more details. */
931 	(void)ubx_m8_configure_gnss_device_baudrate_prerequisite(dev);
932 
933 	/* Stopping GNSS messages for clearer communication while configuring the device. */
934 	ret = ubx_m8_ubx_cfg_rst(dev, UBX_CFG_RST_RESET_MODE_CONTROLLED_GNSS_STOP);
935 	if (ret < 0) {
936 		goto reset;
937 	}
938 
939 	ret = ubx_m8_ubx_cfg_rate(dev);
940 	if (ret < 0) {
941 		LOG_ERR("Configuring rate failed. Returned %d.", ret);
942 		goto reset;
943 	}
944 
945 	ret = ubx_m8_configure_gnss_device_baudrate(dev);
946 	if (ret < 0) {
947 		LOG_ERR("Configuring baudrate failed. Returned %d.", ret);
948 		goto reset;
949 	}
950 
951 	ret = ubx_m8_configure_messages(dev);
952 	if (ret < 0) {
953 		LOG_ERR("Configuring messages failed. Returned %d.", ret);
954 	}
955 
956 reset:
957 	ret = ubx_m8_ubx_cfg_rst(dev, UBX_CFG_RST_RESET_MODE_CONTROLLED_GNSS_START);
958 
959 	return ret;
960 }
961 
ubx_m8_init(const struct device * dev)962 static int ubx_m8_init(const struct device *dev)
963 {
964 	int ret;
965 
966 	ret = ubx_m8_init_nmea0183_match(dev);
967 	if (ret < 0) {
968 		return ret;
969 	}
970 
971 	ubx_m8_init_pipe(dev);
972 
973 	ret = ubx_m8_init_chat(dev);
974 	if (ret < 0) {
975 		return ret;
976 	}
977 
978 	ret = ubx_m8_init_ubx(dev);
979 	if (ret < 0) {
980 		return ret;
981 	}
982 
983 	ret = ubx_m8_resume(dev);
984 	if (ret < 0) {
985 		return ret;
986 	}
987 
988 	ret = ubx_m8_configure(dev);
989 	if (ret < 0) {
990 		return ret;
991 	}
992 
993 	return 0;
994 }
995 
996 #define UBX_M8(inst)										\
997 	static const struct ubx_m8_config ubx_m8_cfg_##inst = {				\
998 		.uart = DEVICE_DT_GET(DT_INST_BUS(inst)),					\
999 		.uart_baudrate = DT_PROP(DT_DRV_INST(inst), uart_baudrate),			\
1000 	};											\
1001 												\
1002 	static struct ubx_m8_data ubx_m8_data_##inst = {					\
1003 		.script.request = (struct ubx_frame *)ubx_m8_data_##inst.request_buf,		\
1004 		.script.response = (struct ubx_frame *)ubx_m8_data_##inst.response_buf,	\
1005 		.script.match = (struct ubx_frame *)ubx_m8_data_##inst.match_buf,		\
1006 		.script.retry_count = UBX_M8_SCRIPT_RETRY_DEFAULT,				\
1007 		.script.timeout = K_MSEC(MODEM_UBX_SCRIPT_TIMEOUT_MS),				\
1008 	};											\
1009 												\
1010 	DEVICE_DT_INST_DEFINE(inst,								\
1011 			      ubx_m8_init,							\
1012 			      NULL,								\
1013 			      &ubx_m8_data_##inst,						\
1014 			      &ubx_m8_cfg_##inst,						\
1015 			      POST_KERNEL,							\
1016 			      CONFIG_GNSS_INIT_PRIORITY,					\
1017 			      &gnss_api);
1018 
1019 DT_INST_FOREACH_STATUS_OKAY(UBX_M8)
1020