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