/* * Copyright (c) 2023 Trackunit Corporation * Copyright (c) 2023 Bjarki Arge Andreasen * Copyright 2023 Google LLC * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include "gnss_nmea0183.h" #include "gnss_nmea0183_match.h" #include "gnss_parse.h" #include LOG_MODULE_REGISTER(gnss_nmea_generic, CONFIG_GNSS_LOG_LEVEL); #define UART_RX_BUF_SZ (256 + IS_ENABLED(CONFIG_GNSS_SATELLITES) * 512) #define UART_TX_BUF_SZ 64 #define CHAT_RECV_BUF_SZ 256 #define CHAT_ARGV_SZ 32 struct gnss_nmea_generic_config { const struct device *uart; const struct modem_chat_script *const init_chat_script; }; struct gnss_nmea_generic_data { struct gnss_nmea0183_match_data match_data; #if CONFIG_GNSS_SATELLITES struct gnss_satellite satellites[CONFIG_GNSS_NMEA_GENERIC_SATELLITES_COUNT]; #endif /* UART backend */ struct modem_pipe *uart_pipe; struct modem_backend_uart uart_backend; uint8_t uart_backend_receive_buf[UART_RX_BUF_SZ]; uint8_t uart_backend_transmit_buf[UART_TX_BUF_SZ]; /* Modem chat */ struct modem_chat chat; uint8_t chat_receive_buf[CHAT_RECV_BUF_SZ]; uint8_t *chat_argv[CHAT_ARGV_SZ]; }; MODEM_CHAT_MATCHES_DEFINE(unsol_matches, MODEM_CHAT_MATCH_WILDCARD("$??GGA,", ",*", gnss_nmea0183_match_gga_callback), MODEM_CHAT_MATCH_WILDCARD("$??RMC,", ",*", gnss_nmea0183_match_rmc_callback), #if CONFIG_GNSS_SATELLITES MODEM_CHAT_MATCH_WILDCARD("$??GSV,", ",*", gnss_nmea0183_match_gsv_callback), #endif ); static int gnss_nmea_generic_resume(const struct device *dev) { const struct gnss_nmea_generic_config *cfg = dev->config; struct gnss_nmea_generic_data *data = dev->data; int ret; ret = modem_pipe_open(data->uart_pipe, K_SECONDS(10)); if (ret < 0) { return ret; } ret = modem_chat_attach(&data->chat, data->uart_pipe); if (ret == 0) { ret = modem_chat_run_script(&data->chat, cfg->init_chat_script); } if (ret < 0) { modem_pipe_close(data->uart_pipe, K_SECONDS(10)); } return ret; } static const struct gnss_driver_api gnss_api = { }; static int gnss_nmea_generic_init_nmea0183_match(const struct device *dev) { struct gnss_nmea_generic_data *data = dev->data; const struct gnss_nmea0183_match_config match_config = { .gnss = dev, #if CONFIG_GNSS_SATELLITES .satellites = data->satellites, .satellites_size = ARRAY_SIZE(data->satellites), #endif }; return gnss_nmea0183_match_init(&data->match_data, &match_config); } static void gnss_nmea_generic_init_pipe(const struct device *dev) { const struct gnss_nmea_generic_config *cfg = dev->config; struct gnss_nmea_generic_data *data = dev->data; const struct modem_backend_uart_config uart_backend_config = { .uart = cfg->uart, .receive_buf = data->uart_backend_receive_buf, .receive_buf_size = sizeof(data->uart_backend_receive_buf), .transmit_buf = data->uart_backend_transmit_buf, .transmit_buf_size = sizeof(data->uart_backend_transmit_buf), }; data->uart_pipe = modem_backend_uart_init(&data->uart_backend, &uart_backend_config); } static uint8_t gnss_nmea_generic_char_delimiter[] = {'\r', '\n'}; static int gnss_nmea_generic_init_chat(const struct device *dev) { struct gnss_nmea_generic_data *data = dev->data; const struct modem_chat_config chat_config = { .user_data = data, .receive_buf = data->chat_receive_buf, .receive_buf_size = sizeof(data->chat_receive_buf), .delimiter = gnss_nmea_generic_char_delimiter, .delimiter_size = ARRAY_SIZE(gnss_nmea_generic_char_delimiter), .filter = NULL, .filter_size = 0, .argv = data->chat_argv, .argv_size = ARRAY_SIZE(data->chat_argv), .unsol_matches = unsol_matches, .unsol_matches_size = ARRAY_SIZE(unsol_matches), }; return modem_chat_init(&data->chat, &chat_config); } static int gnss_nmea_generic_init(const struct device *dev) { int ret; ret = gnss_nmea_generic_init_nmea0183_match(dev); if (ret < 0) { return ret; } gnss_nmea_generic_init_pipe(dev); ret = gnss_nmea_generic_init_chat(dev); if (ret < 0) { return ret; } #if CONFIG_PM_DEVICE pm_device_init_suspended(dev); #else ret = gnss_nmea_generic_resume(dev); if (ret < 0) { return ret; } #endif return 0; } #if CONFIG_PM_DEVICE static int gnss_nmea_generic_pm_action(const struct device *dev, enum pm_device_action action) { switch (action) { case PM_DEVICE_ACTION_RESUME: return gnss_nmea_generic_resume(dev); default: return -ENOTSUP; } } #endif #if DT_HAS_COMPAT_STATUS_OKAY(gnss_nmea_generic) MODEM_CHAT_SCRIPT_EMPTY_DEFINE(gnss_nmea_generic_init_chat_script); #endif #define GNSS_NMEA_GENERIC(inst) \ static const struct gnss_nmea_generic_config gnss_nmea_generic_cfg_##inst = { \ .uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \ .init_chat_script = &_CONCAT(DT_DRV_COMPAT, _init_chat_script), \ }; \ \ static struct gnss_nmea_generic_data gnss_nmea_generic_data_##inst; \ \ PM_DEVICE_DT_INST_DEFINE(inst, gnss_nmea_generic_pm_action); \ \ DEVICE_DT_INST_DEFINE(inst, gnss_nmea_generic_init, PM_DEVICE_DT_INST_GET(inst),\ &gnss_nmea_generic_data_##inst, \ &gnss_nmea_generic_cfg_##inst, \ POST_KERNEL, CONFIG_GNSS_INIT_PRIORITY, &gnss_api); #define DT_DRV_COMPAT gnss_nmea_generic DT_INST_FOREACH_STATUS_OKAY(GNSS_NMEA_GENERIC) #undef DT_DRV_COMPAT