diff --git a/drivers/gnss/CMakeLists.txt b/drivers/gnss/CMakeLists.txt index cfa5c6ed21..32346282c0 100644 --- a/drivers/gnss/CMakeLists.txt +++ b/drivers/gnss/CMakeLists.txt @@ -9,3 +9,5 @@ zephyr_library_sources_ifdef(CONFIG_GNSS_NMEA0183 gnss_nmea0183.c) zephyr_library_sources_ifdef(CONFIG_GNSS_NMEA0183_MATCH gnss_nmea0183_match.c) zephyr_library_sources_ifdef(CONFIG_GNSS_NMEA_GENERIC gnss_nmea_generic.c) zephyr_library_sources_ifdef(CONFIG_GNSS_QUECTEL_LCX6G gnss_quectel_lcx6g.c) +zephyr_library_sources_ifdef(CONFIG_GNSS_U_BLOX_M10 gnss_u_blox_m10.c) +zephyr_library_sources_ifdef(CONFIG_GNSS_U_BLOX_PROTOCOL gnss_u_blox_protocol/gnss_u_blox_protocol.c) diff --git a/drivers/gnss/Kconfig b/drivers/gnss/Kconfig index 2ff552940a..db9762bd87 100644 --- a/drivers/gnss/Kconfig +++ b/drivers/gnss/Kconfig @@ -60,11 +60,18 @@ config GNSS_INIT_PRIORITY help Driver initialization priority for GNSS drivers. +config GNSS_U_BLOX_PROTOCOL + bool "GNSS U-BLOX protocol" + select MODEM_UBX + help + Enable gnss u-blox protocol. + module = GNSS module-str = gnss source "subsys/logging/Kconfig.template.log_config" rsource "Kconfig.generic" rsource "Kconfig.quectel_lcx6g" +rsource "Kconfig.u_blox_m10" endif diff --git a/drivers/gnss/Kconfig.u_blox_m10 b/drivers/gnss/Kconfig.u_blox_m10 new file mode 100644 index 0000000000..b651dd69ee --- /dev/null +++ b/drivers/gnss/Kconfig.u_blox_m10 @@ -0,0 +1,29 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config GNSS_U_BLOX_M10 + bool "U-BLOX M10 GNSS Module" + default y + depends on GNSS + depends on DT_HAS_U_BLOX_M10_ENABLED + select MODEM_MODULES + select MODEM_BACKEND_UART + select MODEM_CHAT + select MODEM_UBX + select GNSS_PARSE + select GNSS_NMEA0183 + select GNSS_NMEA0183_MATCH + select GNSS_U_BLOX_PROTOCOL + select UART_USE_RUNTIME_CONFIGURE + help + Enable U-BLOX M10 GNSS modem driver. + +config GNSS_U_BLOX_M10_SATELLITES_COUNT + int "Maximum satellite count" + depends on GNSS_SATELLITES + default 24 + help + Maximum number of satellite that the driver that can be decoded from + the GNSS device. This does not affect the number of devices that the + device is actually tracking, just how many of those can be reported + in the satellites callback. diff --git a/drivers/gnss/gnss_u_blox_m10.c b/drivers/gnss/gnss_u_blox_m10.c new file mode 100644 index 0000000000..a5bb8d1e07 --- /dev/null +++ b/drivers/gnss/gnss_u_blox_m10.c @@ -0,0 +1,1045 @@ +/* + * Copyright 2024 NXP + * + * 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 "gnss_u_blox_protocol/gnss_u_blox_protocol.h" + +#include +LOG_MODULE_REGISTER(ubx_m10, CONFIG_GNSS_LOG_LEVEL); + +#define DT_DRV_COMPAT u_blox_m10 + +#define UART_RECV_BUF_SZ 128 +#define UART_TRNF_BUF_SZ 128 + +#define CHAT_RECV_BUF_SZ 256 +#define CHAT_ARGV_SZ 32 + +#define UBX_RECV_BUF_SZ UBX_FRM_SZ_MAX +#define UBX_TRNS_BUF_SZ UBX_FRM_SZ_MAX +#define UBX_WORK_BUF_SZ UBX_FRM_SZ_MAX + +#define UBX_FRM_BUF_SZ UBX_FRM_SZ_MAX + +#define MODEM_UBX_SCRIPT_TIMEOUT_MS 1000 +#define UBX_M10_SCRIPT_RETRY_DEFAULT 10 + +#define UBX_M10_GNSS_SYS_CNT 8 +#define UBX_M10_GNSS_SUPP_SYS_CNT 6 +/* The datasheet of the device doesn't specify boot time. But 1 sec helped significantly. */ +#define UBX_M10_BOOT_TIME_MS 1000 + +struct ubx_m10_config { + const struct device *uart; + const uint32_t uart_baudrate; +}; + +struct ubx_m10_data { + struct gnss_nmea0183_match_data match_data; +#if CONFIG_GNSS_SATELLITES + struct gnss_satellite satellites[CONFIG_GNSS_U_BLOX_M10_SATELLITES_COUNT]; +#endif + + /* UART backend */ + struct modem_pipe *uart_pipe; + struct modem_backend_uart uart_backend; + uint8_t uart_backend_receive_buf[UART_RECV_BUF_SZ]; + uint8_t uart_backend_transmit_buf[UART_TRNF_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 ubx */ + struct modem_ubx ubx; + uint8_t ubx_receive_buf[UBX_RECV_BUF_SZ]; + uint8_t ubx_work_buf[UBX_WORK_BUF_SZ]; + + /* Modem ubx script */ + struct modem_ubx_script script; + uint8_t request_buf[UBX_FRM_BUF_SZ]; + uint8_t response_buf[UBX_FRM_BUF_SZ]; + uint8_t match_buf[UBX_FRM_BUF_SZ]; + + struct k_spinlock lock; +}; + +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 ubx_m10_resume(const struct device *dev) +{ + struct ubx_m10_data *data = dev->data; + int ret; + + ret = modem_pipe_open(data->uart_pipe); + if (ret < 0) { + return ret; + } + + ret = modem_chat_attach(&data->chat, data->uart_pipe); + if (ret < 0) { + (void)modem_pipe_close(data->uart_pipe); + return ret; + } + + return ret; +} + +static int ubx_m10_turn_off(const struct device *dev) +{ + struct ubx_m10_data *data = dev->data; + + return modem_pipe_close(data->uart_pipe); +} + +static int ubx_m10_init_nmea0183_match(const struct device *dev) +{ + struct ubx_m10_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 ubx_m10_init_pipe(const struct device *dev) +{ + const struct ubx_m10_config *cfg = dev->config; + struct ubx_m10_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 = ARRAY_SIZE(data->uart_backend_transmit_buf), + }; + + data->uart_pipe = modem_backend_uart_init(&data->uart_backend, &uart_backend_config); +} + +static uint8_t ubx_m10_char_delimiter[] = {'\r', '\n'}; + +static int ubx_m10_init_chat(const struct device *dev) +{ + struct ubx_m10_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 = ubx_m10_char_delimiter, + .delimiter_size = ARRAY_SIZE(ubx_m10_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 ubx_m10_init_ubx(const struct device *dev) +{ + struct ubx_m10_data *data = dev->data; + + const struct modem_ubx_config ubx_config = { + .user_data = data, + .receive_buf = data->ubx_receive_buf, + .receive_buf_size = sizeof(data->ubx_receive_buf), + .work_buf = data->ubx_work_buf, + .work_buf_size = sizeof(data->ubx_work_buf), + }; + + return modem_ubx_init(&data->ubx, &ubx_config); +} + +/** + * @brief Changes modem module (chat or ubx) attached to the uart pipe. + * @param dev Dev instance + * @param change_from_to 0 for changing from "chat" to "ubx", 1 for changing from "ubx" to "chat" + * @returns 0 if successful + * @returns negative errno code if failure + */ +static int ubx_m10_modem_module_change(const struct device *dev, bool change_from_to) +{ + struct ubx_m10_data *data = dev->data; + int ret; + + if (change_from_to == 0) { + modem_chat_release(&data->chat); + ret = modem_ubx_attach(&data->ubx, data->uart_pipe); + } else { /* change_from_to == 1 */ + modem_ubx_release(&data->ubx); + ret = modem_chat_attach(&data->chat, data->uart_pipe); + } + + if (ret < 0) { + (void)modem_pipe_close(data->uart_pipe); + } + + return ret; +} + +static int ubx_m10_modem_ubx_run_script(const struct device *dev, + struct modem_ubx_script *modem_ubx_script_tx) +{ + struct ubx_m10_data *data = dev->data; + int ret; + + ret = ubx_m10_modem_module_change(dev, 0); + if (ret < 0) { + goto reset_modem_module; + } + + ret = modem_ubx_run_script(&data->ubx, modem_ubx_script_tx); + if (ret < 0) { + goto reset_modem_module; + } + +reset_modem_module: + ret |= ubx_m10_modem_module_change(dev, 1); + + return ret; +} + +static void ubx_m10_modem_ubx_script_fill(const struct device *dev) +{ + struct ubx_m10_data *data = dev->data; + + data->script.request = (struct ubx_frame *)data->request_buf; + data->script.response = (struct ubx_frame *)data->response_buf; + data->script.match = (struct ubx_frame *)data->match_buf; + data->script.retry_count = UBX_M10_SCRIPT_RETRY_DEFAULT; + data->script.timeout = K_MSEC(MODEM_UBX_SCRIPT_TIMEOUT_MS); +} + +static int ubx_m10_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) +{ + int ret; + struct ubx_m10_data *data = dev->data; + struct ubx_cfg_ack_payload match_payload = { + .message_class = msg_cls, + .message_id = msg_id, + }; + + ubx_m10_modem_ubx_script_fill(dev); + + ret = ubx_create_and_validate_frame(data->match_buf, sizeof(data->match_buf), UBX_CLASS_ACK, + UBX_ACK_ACK, &match_payload, UBX_CFG_ACK_PAYLOAD_SZ); + if (ret < 0) { + return ret; + } + + ret = ubx_create_and_validate_frame(data->request_buf, sizeof(data->request_buf), msg_cls, + msg_id, payload, payld_sz); + if (ret < 0) { + return ret; + } + + return ret; +} + +static int ubx_m10_ubx_cfg_rate(const struct device *dev) +{ + int ret; + k_spinlock_key_t key; + struct ubx_m10_data *data = dev->data; + struct ubx_cfg_rate_payload payload; + + key = k_spin_lock(&data->lock); + + ubx_cfg_rate_payload_default(&payload); + + ret = ubx_m10_modem_ubx_script_init(dev, &payload, UBX_CFG_RATE_PAYLOAD_SZ, UBX_CLASS_CFG, + UBX_CFG_RATE); + if (ret < 0) { + goto unlock; + } + + ret = ubx_m10_modem_ubx_run_script(dev, &(data->script)); + if (ret < 0) { + goto unlock; + } + +unlock: + k_spin_unlock(&data->lock, key); + + return ret; +} + +static int ubx_m10_ubx_cfg_prt_set(const struct device *dev, uint32_t target_baudrate, + uint8_t retry) +{ + int ret; + k_spinlock_key_t key; + struct ubx_m10_data *data = dev->data; + struct ubx_cfg_prt_set_payload payload; + + key = k_spin_lock(&data->lock); + + ubx_cfg_prt_set_payload_default(&payload); + payload.baudrate = target_baudrate; + + ret = ubx_m10_modem_ubx_script_init(dev, &payload, UBX_CFG_PRT_SET_PAYLOAD_SZ, + UBX_CLASS_CFG, UBX_CFG_PRT); + if (ret < 0) { + goto unlock; + } + + data->script.retry_count = retry; + /* Returns failure if "target_baudrate" is different than device's currently set baudrate, + * because the device will change its baudrate and respond with UBX-ACK with new baudrate, + * which we will miss. Hence, we need to change uart's baudrate after sending the frame + * (in order to receive response as well), which we are not doing right now. + */ + ret = ubx_m10_modem_ubx_run_script(dev, &(data->script)); + if (ret < 0) { + goto unlock; + } + +unlock: + k_spin_unlock(&data->lock, key); + + return ret; +} + +static int ubx_m10_ubx_cfg_rst(const struct device *dev, uint8_t reset_mode) +{ + int ret; + k_spinlock_key_t key; + struct ubx_m10_data *data = dev->data; + struct ubx_cfg_rst_payload payload; + + key = k_spin_lock(&data->lock); + + ubx_cfg_rst_payload_default(&payload); + + payload.nav_bbr_mask = UBX_CFG_RST_NAV_BBR_MASK_HOT_START; + payload.reset_mode = reset_mode; + + ret = ubx_m10_modem_ubx_script_init(dev, &payload, UBX_CFG_RST_PAYLOAD_SZ, UBX_CLASS_CFG, + UBX_CFG_RST); + if (ret < 0) { + goto unlock; + } + + data->script.match = NULL; + ret = ubx_m10_modem_ubx_run_script(dev, &(data->script)); + if (ret < 0) { + goto unlock; + } + + if (reset_mode == UBX_CFG_RST_RESET_MODE_CONTROLLED_GNSS_STOP) { + k_sleep(K_MSEC(UBX_CFG_RST_WAIT_MS)); + } + +unlock: + k_spin_unlock(&data->lock, key); + + return ret; +} + +static int ubx_m10_set_uart_baudrate(const struct device *dev, uint32_t baudrate) +{ + int ret; + k_spinlock_key_t key; + struct ubx_m10_data *data = dev->data; + const struct ubx_m10_config *config = dev->config; + struct uart_config uart_cfg; + + key = k_spin_lock(&data->lock); + + ret = ubx_m10_turn_off(dev); + if (ret < 0) { + goto reset_and_unlock; + } + + ret = uart_config_get(config->uart, &uart_cfg); + if (ret < 0) { + goto reset_and_unlock; + } + uart_cfg.baudrate = baudrate; + + ret = uart_configure(config->uart, &uart_cfg); + if (ret < 0) { + goto reset_and_unlock; + } + +reset_and_unlock: + ubx_m10_init_pipe(dev); + ret |= ubx_m10_resume(dev); + if (ret < 0) { + goto unlock; + } + +unlock: + k_spin_unlock(&data->lock, key); + + return ret; +} + +static bool ubx_m10_validate_baudrate(const struct device *dev, uint32_t baudrate) +{ + for (int i = 0; i < UBX_BAUDRATE_COUNT; ++i) { + if (baudrate == ubx_baudrate[i]) { + return true; + } + } + + return false; +} + +/* This function will return failure if "target_baudrate" != device's current baudrate. + * Refer the function description of ubx_m10_ubx_cfg_prt_set for a detailed explanation. + */ +static int ubx_m10_configure_gnss_device_baudrate_prerequisite(const struct device *dev) +{ + /* Retry = 1 should be enough, but setting 2 just to be safe. */ + int ret, retry = 2; + const struct ubx_m10_config *config = dev->config; + uint32_t target_baudrate = config->uart_baudrate; + + ret = ubx_m10_validate_baudrate(dev, target_baudrate); + if (ret < 0) { + return ret; + } + + /* Try communication with device with all possible baudrates, because initially we don't + * know the currently set baudrate of the device. We will match the baudrate in one of the + * following attempts and the device will thus change its baudrate to "target_baudrate". + */ + for (int i = 0; i < UBX_BAUDRATE_COUNT; ++i) { + /* Set baudrate of UART pipe as ubx_baudrate[i]. */ + ret = ubx_m10_set_uart_baudrate(dev, ubx_baudrate[i]); + if (ret < 0) { + return ret; + } + + /* Try setting baudrate of device as target_baudrate. */ + ret = ubx_m10_ubx_cfg_prt_set(dev, target_baudrate, retry); + if (ret == 0) { + break; + } + } + + /* Reset baudrate of UART pipe as target_baudrate. */ + ret = ubx_m10_set_uart_baudrate(dev, target_baudrate); + if (ret < 0) { + return ret; + } + + return 0; +} + +static int ubx_m10_configure_gnss_device_baudrate(const struct device *dev) +{ + int ret; + const struct ubx_m10_config *config = dev->config; + uint32_t target_baudrate = config->uart_baudrate; + + ret = ubx_m10_validate_baudrate(dev, target_baudrate); + if (ret < 0) { + return ret; + } + + ret = ubx_m10_ubx_cfg_prt_set(dev, target_baudrate, UBX_M10_SCRIPT_RETRY_DEFAULT); + if (ret < 0) { + return ret; + } + + return 0; +} + +static int ubx_m10_configure_messages(const struct device *dev) +{ + int ret; + k_spinlock_key_t key; + struct ubx_m10_data *data = dev->data; + struct ubx_cfg_msg_payload payload; + + key = k_spin_lock(&data->lock); + + ubx_cfg_msg_payload_default(&payload); + + /* Enabling GGA, RMC and GSV messages. */ + payload.rate = 1; + uint8_t message_enable[] = {UBX_NMEA_GGA, UBX_NMEA_RMC, UBX_NMEA_GSV}; + + for (int i = 0; i < sizeof(message_enable); ++i) { + payload.message_id = message_enable[i]; + ret = ubx_m10_modem_ubx_script_init(dev, &payload, UBX_CFG_MSG_PAYLOAD_SZ, + UBX_CLASS_CFG, UBX_CFG_MSG); + if (ret < 0) { + goto unlock; + } + + ret = ubx_m10_modem_ubx_run_script(dev, &(data->script)); + if (ret < 0) { + goto unlock; + } + } + + /* Disabling DTM, GBS, GLL, GNS, GRS, GSA, GST, VLW, VTG and ZDA messages. */ + payload.rate = 0; + uint8_t message_disable[] = {UBX_NMEA_DTM, UBX_NMEA_GBS, UBX_NMEA_GLL, UBX_NMEA_GNS, + UBX_NMEA_GRS, UBX_NMEA_GSA, UBX_NMEA_GST, UBX_NMEA_VLW, + UBX_NMEA_VTG, UBX_NMEA_ZDA}; + + for (int i = 0; i < sizeof(message_disable); ++i) { + payload.message_id = message_disable[i]; + ret = ubx_m10_modem_ubx_script_init(dev, &payload, UBX_CFG_MSG_PAYLOAD_SZ, + UBX_CLASS_CFG, UBX_CFG_MSG); + if (ret < 0) { + goto unlock; + } + + ret = ubx_m10_modem_ubx_run_script(dev, &(data->script)); + if (ret < 0) { + goto unlock; + } + } + +unlock: + k_spin_unlock(&data->lock, key); + + return ret; +} + +static int ubx_m10_navigation_mode_to_ubx_dynamic_model(const struct device *dev, + enum gnss_navigation_mode mode) +{ + switch (mode) { + case GNSS_NAVIGATION_MODE_ZERO_DYNAMICS: + return UBX_DYN_MODEL_STATIONARY; + case GNSS_NAVIGATION_MODE_LOW_DYNAMICS: + return UBX_DYN_MODEL_PORTABLE; + case GNSS_NAVIGATION_MODE_BALANCED_DYNAMICS: + return UBX_DYN_MODEL_AIRBONE1G; + case GNSS_NAVIGATION_MODE_HIGH_DYNAMICS: + return UBX_DYN_MODEL_AIRBONE4G; + default: + return -EINVAL; + } +} + +static int ubx_m10_ubx_dynamic_model_to_navigation_mode(const struct device *dev, + enum ubx_dynamic_model dynamic_model) +{ + switch (dynamic_model) { + case UBX_DYN_MODEL_PORTABLE: + return GNSS_NAVIGATION_MODE_LOW_DYNAMICS; + case UBX_DYN_MODEL_STATIONARY: + return GNSS_NAVIGATION_MODE_ZERO_DYNAMICS; + case UBX_DYN_MODEL_PEDESTRIAN: + return GNSS_NAVIGATION_MODE_LOW_DYNAMICS; + case UBX_DYN_MODEL_AUTOMOTIV: + return GNSS_NAVIGATION_MODE_LOW_DYNAMICS; + case UBX_DYN_MODEL_SEA: + return GNSS_NAVIGATION_MODE_BALANCED_DYNAMICS; + case UBX_DYN_MODEL_AIRBONE1G: + return GNSS_NAVIGATION_MODE_BALANCED_DYNAMICS; + case UBX_DYN_MODEL_AIRBONE2G: + return GNSS_NAVIGATION_MODE_BALANCED_DYNAMICS; + case UBX_DYN_MODEL_AIRBONE4G: + return GNSS_NAVIGATION_MODE_HIGH_DYNAMICS; + case UBX_DYN_MODEL_WIRST: + return GNSS_NAVIGATION_MODE_BALANCED_DYNAMICS; + case UBX_DYN_MODEL_BIKE: + return GNSS_NAVIGATION_MODE_HIGH_DYNAMICS; + default: + return -EINVAL; + } +} + +static int ubx_m10_set_navigation_mode(const struct device *dev, enum gnss_navigation_mode mode) +{ + int ret; + k_spinlock_key_t key; + struct ubx_m10_data *data = dev->data; + struct ubx_cfg_nav5_payload payload; + + key = k_spin_lock(&data->lock); + + ubx_cfg_nav5_payload_default(&payload); + + ret = ubx_m10_navigation_mode_to_ubx_dynamic_model(dev, mode); + if (ret < 0) { + goto unlock; + } + + payload.dyn_model = ret; + + ret = ubx_m10_modem_ubx_script_init(dev, &payload, UBX_CFG_NAV5_PAYLOAD_SZ, UBX_CLASS_CFG, + UBX_CFG_NAV5); + if (ret < 0) { + goto unlock; + } + + ret = ubx_m10_modem_ubx_run_script(dev, &(data->script)); + if (ret < 0) { + goto unlock; + } + + k_sleep(K_MSEC(UBX_CFG_NAV5_WAIT_MS)); + +unlock: + k_spin_unlock(&data->lock, key); + + return ret; +} + +static int ubx_m10_get_navigation_mode(const struct device *dev, enum gnss_navigation_mode *mode) +{ + int ret; + k_spinlock_key_t key; + struct ubx_m10_data *data = dev->data; + enum ubx_dynamic_model dynamic_model; + + key = k_spin_lock(&data->lock); + + ret = ubx_m10_modem_ubx_script_init(dev, NULL, UBX_FRM_GET_PAYLOAD_SZ, UBX_CLASS_CFG, + UBX_CFG_NAV5); + if (ret < 0) { + goto unlock; + } + + ret = ubx_m10_modem_ubx_run_script(dev, &(data->script)); + if (ret < 0) { + goto unlock; + } + + struct ubx_frame *response = data->script.response; + + dynamic_model = ((struct ubx_cfg_nav5_payload *)response->payload_and_checksum)->dyn_model; + ret = ubx_m10_ubx_dynamic_model_to_navigation_mode(dev, dynamic_model); + if (ret < 0) { + goto unlock; + } + + *mode = ret; + +unlock: + k_spin_unlock(&data->lock, key); + + return ret; +} + +static int ubx_m10_get_supported_systems(const struct device *dev, gnss_systems_t *systems) +{ + *systems = (GNSS_SYSTEM_GPS | GNSS_SYSTEM_GLONASS | GNSS_SYSTEM_GALILEO | + GNSS_SYSTEM_BEIDOU | GNSS_SYSTEM_SBAS | GNSS_SYSTEM_QZSS); + + return 0; +} + +static int ubx_m10_ubx_gnss_id_to_gnss_system(const struct device *dev, enum ubx_gnss_id gnss_id) +{ + switch (gnss_id) { + case UBX_GNSS_ID_GPS: + return GNSS_SYSTEM_GPS; + case UBX_GNSS_ID_SBAS: + return GNSS_SYSTEM_SBAS; + case UBX_GNSS_ID_GALILEO: + return GNSS_SYSTEM_GALILEO; + case UBX_GNSS_ID_BEIDOU: + return GNSS_SYSTEM_BEIDOU; + case UBX_GNSS_ID_QZSS: + return GNSS_SYSTEM_QZSS; + case UBX_GNSS_ID_GLONASS: + return GNSS_SYSTEM_GLONASS; + default: + return -EINVAL; + }; +} + +static int ubx_m10_config_block_fill(const struct device *dev, gnss_systems_t gnss_system, + struct ubx_cfg_gnss_payload *payload, uint8_t index, + uint32_t enable) +{ + uint32_t signal_config; + + switch (gnss_system) { + case GNSS_SYSTEM_GPS: + payload->config_blocks[index].gnss_id = UBX_GNSS_ID_GPS; + signal_config = UBX_CFG_GNSS_FLAG_SGN_CNF_GPS_L1C_A; + break; + case GNSS_SYSTEM_GLONASS: + payload->config_blocks[index].gnss_id = UBX_GNSS_ID_GLONASS; + signal_config = UBX_CFG_GNSS_FLAG_SGN_CNF_GLONASS_L1; + break; + case GNSS_SYSTEM_GALILEO: + payload->config_blocks[index].gnss_id = UBX_GNSS_ID_GALILEO; + signal_config = UBX_CFG_GNSS_FLAG_SGN_CNF_GALILEO_E1; + break; + case GNSS_SYSTEM_BEIDOU: + payload->config_blocks[index].gnss_id = UBX_GNSS_ID_BEIDOU; + signal_config = UBX_CFG_GNSS_FLAG_SGN_CNF_BEIDOU_B1I; + break; + case GNSS_SYSTEM_QZSS: + payload->config_blocks[index].gnss_id = UBX_GNSS_ID_QZSS; + signal_config = UBX_CFG_GNSS_FLAG_SGN_CNF_QZSS_L1C_A; + break; + case GNSS_SYSTEM_SBAS: + payload->config_blocks[index].gnss_id = UBX_GNSS_ID_SBAS; + signal_config = UBX_CFG_GNSS_FLAG_SGN_CNF_SBAS_L1C_A; + break; + default: + return -EINVAL; + }; + + payload->config_blocks[index].flags = enable | signal_config; + + return 0; +} + +static int ubx_m10_set_enabled_systems(const struct device *dev, gnss_systems_t systems) +{ + int ret; + k_spinlock_key_t key; + struct ubx_m10_data *data = dev->data; + + key = k_spin_lock(&data->lock); + + struct ubx_cfg_gnss_payload *payload; + + /* Get number of tracking channels for each supported gnss system by sending CFG-GNSS. */ + ret = ubx_m10_modem_ubx_script_init(dev, NULL, UBX_FRM_GET_PAYLOAD_SZ, UBX_CLASS_CFG, + UBX_CFG_GNSS); + if (ret < 0) { + goto unlock; + } + + ret = ubx_m10_modem_ubx_run_script(dev, &(data->script)); + if (ret < 0) { + goto unlock; + } + + struct ubx_frame *response = data->script.response; + uint16_t res_trk_ch_sum = 0, max_trk_ch_sum = 0; + + /* Calculate sum of reserved and maximum tracking channels for each supported gnss system, + * and assert that the sum is not greater than the number of tracking channels in use. + */ + payload = (struct ubx_cfg_gnss_payload *) response->payload_and_checksum; + for (int i = 0; i < payload->num_config_blocks; ++i) { + ret = ubx_m10_ubx_gnss_id_to_gnss_system(dev, payload->config_blocks[i].gnss_id); + if (ret < 0) { + ret = -EINVAL; + goto unlock; + } + + if (ret & systems) { + res_trk_ch_sum += payload->config_blocks[i].num_res_trk_ch; + max_trk_ch_sum += payload->config_blocks[i].max_num_trk_ch; + } + + if (res_trk_ch_sum > payload->num_trk_ch_use || + max_trk_ch_sum > payload->num_trk_ch_use) { + ret = -EINVAL; + goto unlock; + } + } + + /* Prepare payload (payload) for sending CFG-GNSS for enabling the gnss systems. */ + payload = malloc(sizeof(*payload) + + sizeof(struct ubx_cfg_gnss_payload_config_block) * UBX_M10_GNSS_SUPP_SYS_CNT); + payload->num_config_blocks = UBX_M10_GNSS_SUPP_SYS_CNT; + + ubx_cfg_gnss_payload_default(payload); + + uint8_t filled_blocks = 0; + gnss_systems_t supported_systems; + + ret = ubx_m10_get_supported_systems(dev, &supported_systems); + if (ret < 0) { + goto free_and_unlock; + } + + for (int i = 0; i < UBX_M10_GNSS_SYS_CNT; ++i) { + gnss_systems_t gnss_system = 1 << i; + + if (gnss_system & supported_systems) { + uint32_t enable = (systems & gnss_system) ? + UBX_CFG_GNSS_FLAG_ENABLE : UBX_CFG_GNSS_FLAG_DISABLE; + + ret = ubx_m10_config_block_fill(dev, gnss_system, payload, filled_blocks, + enable); + if (ret < 0) { + goto free_and_unlock; + } + + ++filled_blocks; + } + } + + ret = ubx_m10_modem_ubx_script_init(dev, payload, + UBX_CFG_GNSS_PAYLOAD_SZ(UBX_M10_GNSS_SUPP_SYS_CNT), + UBX_CLASS_CFG, UBX_CFG_GNSS); + if (ret < 0) { + goto free_and_unlock; + } + + ret = ubx_m10_modem_ubx_run_script(dev, &(data->script)); + if (ret < 0) { + goto free_and_unlock; + } + + k_sleep(K_MSEC(UBX_CFG_GNSS_WAIT_MS)); + +free_and_unlock: + free(payload); + +unlock: + k_spin_unlock(&data->lock, key); + + return ret; +} + +static int ubx_m10_get_enabled_systems(const struct device *dev, gnss_systems_t *systems) +{ + int ret; + k_spinlock_key_t key; + struct ubx_m10_data *data = dev->data; + + key = k_spin_lock(&data->lock); + + ret = ubx_m10_modem_ubx_script_init(dev, NULL, UBX_FRM_GET_PAYLOAD_SZ, UBX_CLASS_CFG, + UBX_CFG_GNSS); + if (ret < 0) { + goto unlock; + } + + ret = ubx_m10_modem_ubx_run_script(dev, &(data->script)); + if (ret < 0) { + goto unlock; + } + + struct ubx_frame *response = data->script.response; + struct ubx_cfg_gnss_payload *payload = + (struct ubx_cfg_gnss_payload *) response->payload_and_checksum; + + *systems = 0; + for (int i = 0; i < payload->num_config_blocks; ++i) { + if (payload->config_blocks[i].flags & UBX_CFG_GNSS_FLAG_ENABLE) { + enum ubx_gnss_id gnss_id = payload->config_blocks[i].gnss_id; + + ret = ubx_m10_ubx_gnss_id_to_gnss_system(dev, gnss_id); + if (ret < 0) { + goto unlock; + } + + *systems |= ret; + } + } + +unlock: + k_spin_unlock(&data->lock, key); + + return ret; +} + +static int ubx_m10_set_fix_rate(const struct device *dev, uint32_t fix_interval_ms) +{ + int ret; + k_spinlock_key_t key; + struct ubx_m10_data *data = dev->data; + struct ubx_cfg_rate_payload payload; + + if (fix_interval_ms < 50) { + return -1; + } + + key = k_spin_lock(&data->lock); + + ubx_cfg_rate_payload_default(&payload); + payload.meas_rate_ms = fix_interval_ms; + + ret = ubx_m10_modem_ubx_script_init(dev, &payload, UBX_CFG_RATE_PAYLOAD_SZ, UBX_CLASS_CFG, + UBX_CFG_RATE); + if (ret < 0) { + goto unlock; + } + + ret = ubx_m10_modem_ubx_run_script(dev, &(data->script)); + if (ret < 0) { + goto unlock; + } + +unlock: + k_spin_unlock(&data->lock, key); + + return ret; +} + +static int ubx_m10_get_fix_rate(const struct device *dev, uint32_t *fix_interval_ms) +{ + int ret; + k_spinlock_key_t key; + struct ubx_m10_data *data = dev->data; + struct ubx_cfg_rate_payload *payload; + + key = k_spin_lock(&data->lock); + + ret = ubx_m10_modem_ubx_script_init(dev, NULL, UBX_FRM_GET_PAYLOAD_SZ, UBX_CLASS_CFG, + UBX_CFG_RATE); + if (ret < 0) { + goto unlock; + } + + ret = ubx_m10_modem_ubx_run_script(dev, &(data->script)); + if (ret < 0) { + goto unlock; + } + + struct ubx_frame *response = data->script.response; + + payload = (struct ubx_cfg_rate_payload *) response->payload_and_checksum; + *fix_interval_ms = payload->meas_rate_ms; + +unlock: + k_spin_unlock(&data->lock, key); + + return ret; +} + +static struct gnss_driver_api gnss_api = { + .set_fix_rate = ubx_m10_set_fix_rate, + .get_fix_rate = ubx_m10_get_fix_rate, + .set_navigation_mode = ubx_m10_set_navigation_mode, + .get_navigation_mode = ubx_m10_get_navigation_mode, + .set_enabled_systems = ubx_m10_set_enabled_systems, + .get_enabled_systems = ubx_m10_get_enabled_systems, + .get_supported_systems = ubx_m10_get_supported_systems, +}; + +static int ubx_m10_configure(const struct device *dev) +{ + int ret; + + /* The return value could be ignored. See function description for more details. */ + (void)ubx_m10_configure_gnss_device_baudrate_prerequisite(dev); + + /* Stopping GNSS messages for clearer communication while configuring the device. */ + ret = ubx_m10_ubx_cfg_rst(dev, UBX_CFG_RST_RESET_MODE_CONTROLLED_GNSS_STOP); + if (ret < 0) { + goto reset; + } + + ret = ubx_m10_ubx_cfg_rate(dev); + if (ret < 0) { + LOG_ERR("Configuring rate failed. Returned %d.", ret); + goto reset; + } + + ret = ubx_m10_configure_gnss_device_baudrate(dev); + if (ret < 0) { + LOG_ERR("Configuring baudrate failed. Returned %d.", ret); + goto reset; + } + + ret = ubx_m10_configure_messages(dev); + if (ret < 0) { + LOG_ERR("Configuring messages failed. Returned %d.", ret); + goto reset; + } + +reset: + ret = ubx_m10_ubx_cfg_rst(dev, UBX_CFG_RST_RESET_MODE_CONTROLLED_GNSS_START); + if (ret < 0) { + goto reset; + } + + return ret; +} + +static int ubx_m10_init(const struct device *dev) +{ + int ret; + + k_sleep(K_MSEC(UBX_M10_BOOT_TIME_MS)); + + ret = ubx_m10_init_nmea0183_match(dev); + if (ret < 0) { + return ret; + } + + ubx_m10_init_pipe(dev); + + ret = ubx_m10_init_chat(dev); + if (ret < 0) { + return ret; + } + + ret = ubx_m10_init_ubx(dev); + if (ret < 0) { + return ret; + } + + ret = ubx_m10_resume(dev); + if (ret < 0) { + return ret; + } + + ret = ubx_m10_configure(dev); + if (ret < 0) { + return ret; + } + + return 0; +} + +#define UBX_M10(inst) \ + static struct ubx_m10_config ubx_m10_cfg_##inst = { \ + .uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \ + .uart_baudrate = DT_PROP(DT_DRV_INST(inst), uart_baudrate), \ + }; \ + \ + static struct ubx_m10_data ubx_m10_data_##inst = { \ + .script.request = (struct ubx_frame *)ubx_m10_data_##inst.request_buf, \ + .script.response = (struct ubx_frame *)ubx_m10_data_##inst.response_buf, \ + .script.match = (struct ubx_frame *)ubx_m10_data_##inst.match_buf, \ + .script.retry_count = UBX_M10_SCRIPT_RETRY_DEFAULT, \ + .script.timeout = K_MSEC(MODEM_UBX_SCRIPT_TIMEOUT_MS), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, \ + ubx_m10_init, \ + NULL, \ + &ubx_m10_data_##inst, \ + &ubx_m10_cfg_##inst, \ + POST_KERNEL, \ + CONFIG_GNSS_INIT_PRIORITY, \ + &gnss_api); + +DT_INST_FOREACH_STATUS_OKAY(UBX_M10) diff --git a/drivers/gnss/gnss_u_blox_protocol/gnss_u_blox_protocol.c b/drivers/gnss/gnss_u_blox_protocol/gnss_u_blox_protocol.c new file mode 100644 index 0000000000..d9a42baa1b --- /dev/null +++ b/drivers/gnss/gnss_u_blox_protocol/gnss_u_blox_protocol.c @@ -0,0 +1,180 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "gnss_u_blox_protocol.h" + +const uint32_t ubx_baudrate[UBX_BAUDRATE_COUNT] = { + 4800, + 9600, + 19200, + 38400, + 57600, + 115200, + 230400, + 460800, + 921600, +}; + +static inline int ubx_validate_payload_size_ack(uint8_t msg_id, uint16_t payload_size) +{ + switch (msg_id) { + case UBX_ACK_ACK: + return payload_size == UBX_CFG_ACK_PAYLOAD_SZ ? 0 : -1; + case UBX_ACK_NAK: + return payload_size == UBX_CFG_NAK_PAYLOAD_SZ ? 0 : -1; + default: + return -1; + } +} + +static inline int ubx_validate_payload_size_cfg(uint8_t msg_id, uint16_t payload_size) +{ + switch (msg_id) { + case UBX_CFG_RATE: + return payload_size == UBX_CFG_RATE_PAYLOAD_SZ ? 0 : -1; + case UBX_CFG_PRT: + return (payload_size == UBX_CFG_PRT_POLL_PAYLOAD_SZ || + payload_size == UBX_CFG_PRT_SET_PAYLOAD_SZ) ? 0 : -1; + case UBX_CFG_RST: + return payload_size == UBX_CFG_RST_PAYLOAD_SZ ? 0 : -1; + case UBX_CFG_NAV5: + return payload_size == UBX_CFG_NAV5_PAYLOAD_SZ ? 0 : -1; + case UBX_CFG_GNSS: + return ((payload_size - UBX_CFG_GNSS_PAYLOAD_INIT_SZ) % + UBX_CFG_GNSS_PAYLOAD_CFG_BLK_SZ == 0) ? 0 : -1; + case UBX_CFG_MSG: + return payload_size == UBX_CFG_MSG_PAYLOAD_SZ ? 0 : -1; + default: + return -1; + } +} + +static inline int ubx_validate_payload_size(uint8_t msg_cls, uint8_t msg_id, uint16_t payload_size) +{ + if (payload_size == 0) { + return 0; + } + + if (payload_size > UBX_PAYLOAD_SZ_MAX) { + return -1; + } + + switch (msg_cls) { + case UBX_CLASS_ACK: + return ubx_validate_payload_size_ack(msg_id, payload_size); + case UBX_CLASS_CFG: + return ubx_validate_payload_size_cfg(msg_id, payload_size); + default: + return -1; + } +} + +int ubx_create_and_validate_frame(uint8_t *ubx_frame, uint16_t ubx_frame_size, uint8_t msg_cls, + uint8_t msg_id, const void *payload, uint16_t payload_size) +{ + if (ubx_validate_payload_size(msg_cls, msg_id, payload_size)) { + return -1; + } + + return modem_ubx_create_frame(ubx_frame, ubx_frame_size, msg_cls, msg_id, payload, + payload_size); +} + +void ubx_cfg_ack_payload_default(struct ubx_cfg_ack_payload *payload) +{ + payload->message_class = UBX_CLASS_CFG; + payload->message_id = UBX_CFG_PRT; +} + +void ubx_cfg_rate_payload_default(struct ubx_cfg_rate_payload *payload) +{ + payload->meas_rate_ms = 1000; + payload->nav_rate = 1; + payload->time_ref = UBX_CFG_RATE_TIME_REF_UTC; +} + +void ubx_cfg_prt_poll_payload_default(struct ubx_cfg_prt_poll_payload *payload) +{ + payload->port_id = UBX_PORT_NUMBER_UART; +} + +void ubx_cfg_prt_set_payload_default(struct ubx_cfg_prt_set_payload *payload) +{ + payload->port_id = UBX_PORT_NUMBER_UART; + payload->reserved0 = UBX_CFG_PRT_RESERVED0; + payload->tx_ready_pin_conf = UBX_CFG_PRT_TX_READY_PIN_CONF_POL_HIGH; + payload->port_mode = UBX_CFG_PRT_PORT_MODE_CHAR_LEN_8 | UBX_CFG_PRT_PORT_MODE_PARITY_NONE | + UBX_CFG_PRT_PORT_MODE_STOP_BITS_1; + payload->baudrate = ubx_baudrate[3]; + payload->in_proto_mask = UBX_CFG_PRT_IN_PROTO_UBX | UBX_CFG_PRT_IN_PROTO_NMEA | + UBX_CFG_PRT_IN_PROTO_RTCM; + payload->out_proto_mask = UBX_CFG_PRT_OUT_PROTO_UBX | UBX_CFG_PRT_OUT_PROTO_NMEA | + UBX_CFG_PRT_OUT_PROTO_RTCM3; + payload->flags = UBX_CFG_PRT_FLAGS_DEFAULT; + payload->reserved1 = UBX_CFG_PRT_RESERVED1; +} + +void ubx_cfg_rst_payload_default(struct ubx_cfg_rst_payload *payload) +{ + payload->nav_bbr_mask = UBX_CFG_RST_NAV_BBR_MASK_HOT_START; + payload->reset_mode = UBX_CFG_RST_RESET_MODE_CONTROLLED_SOFT_RESET; + payload->reserved0 = UBX_CFG_RST_RESERVED0; +} + +void ubx_cfg_nav5_payload_default(struct ubx_cfg_nav5_payload *payload) +{ + payload->mask = UBX_CFG_NAV5_MASK_ALL; + payload->dyn_model = UBX_DYN_MODEL_PORTABLE; + + payload->fix_mode = UBX_FIX_AUTO_FIX; + + payload->fixed_alt = UBX_CFG_NAV5_FIXED_ALT_DEFAULT; + payload->fixed_alt_var = UBX_CFG_NAV5_FIXED_ALT_VAR_DEFAULT; + + payload->min_elev = UBX_CFG_NAV5_MIN_ELEV_DEFAULT; + payload->dr_limit = UBX_CFG_NAV5_DR_LIMIT_DEFAULT; + + payload->p_dop = UBX_CFG_NAV5_P_DOP_DEFAULT; + payload->t_dop = UBX_CFG_NAV5_T_DOP_DEFAULT; + payload->p_acc = UBX_CFG_NAV5_P_ACC_DEFAULT; + payload->t_acc = UBX_CFG_NAV5_T_ACC_DEFAULT; + + payload->static_hold_threshold = UBX_CFG_NAV5_STATIC_HOLD_THRESHOLD_DEFAULT; + payload->dgnss_timeout = UBX_CFG_NAV5_DGNSS_TIMEOUT_DEFAULT; + payload->cno_threshold_num_svs = UBX_CFG_NAV5_CNO_THRESHOLD_NUM_SVS_DEFAULT; + payload->cno_threshold = UBX_CFG_NAV5_CNO_THRESHOLD_DEFAULT; + + payload->reserved0 = UBX_CFG_NAV5_RESERVED0; + + payload->static_hold_dist_threshold = UBX_CFG_NAV5_STATIC_HOLD_DIST_THRESHOLD; + payload->utc_standard = UBX_CFG_NAV5_UTC_STANDARD_DEFAULT; +} + +static struct ubx_cfg_gnss_payload_config_block ubx_cfg_gnss_payload_config_block_default = { + .gnss_id = UBX_GNSS_ID_GPS, + .num_res_trk_ch = 0x00, + .max_num_trk_ch = 0x00, + .reserved0 = UBX_CFG_GNSS_RESERVED0, + .flags = UBX_CFG_GNSS_FLAG_ENABLE | UBX_CFG_GNSS_FLAG_SGN_CNF_GPS_L1C_A, +}; + +void ubx_cfg_gnss_payload_default(struct ubx_cfg_gnss_payload *payload) +{ + payload->msg_ver = UBX_CFG_GNSS_MSG_VER; + payload->num_trk_ch_hw = UBX_CFG_GNSS_NUM_TRK_CH_HW_DEFAULT; + payload->num_trk_ch_use = UBX_CFG_GNSS_NUM_TRK_CH_USE_DEFAULT; + + for (int i = 0; i < payload->num_config_blocks; ++i) { + payload->config_blocks[i] = ubx_cfg_gnss_payload_config_block_default; + } +} + +void ubx_cfg_msg_payload_default(struct ubx_cfg_msg_payload *payload) +{ + payload->message_class = UBX_CLASS_NMEA; + payload->message_id = UBX_NMEA_GGA; + payload->rate = UBX_CFG_MSG_RATE_DEFAULT; +} diff --git a/drivers/gnss/gnss_u_blox_protocol/gnss_u_blox_protocol.h b/drivers/gnss/gnss_u_blox_protocol/gnss_u_blox_protocol.h new file mode 100644 index 0000000000..6842a708de --- /dev/null +++ b/drivers/gnss/gnss_u_blox_protocol/gnss_u_blox_protocol.h @@ -0,0 +1,251 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "gnss_u_blox_protocol_defines.h" + +#ifndef ZEPHYR_U_BLOX_PROTOCOL_ +#define ZEPHYR_U_BLOX_PROTOCOL_ + +#define UBX_BAUDRATE_COUNT 9 + +/* When a configuration frame is sent, the device requires some delay to reflect the changes. */ +/* TODO: check what is the precise waiting time for each message. */ +#define UBX_CFG_RST_WAIT_MS 6000 +#define UBX_CFG_GNSS_WAIT_MS 6000 +#define UBX_CFG_NAV5_WAIT_MS 6000 + +extern const uint32_t ubx_baudrate[UBX_BAUDRATE_COUNT]; + +#define UBX_FRM_GET_PAYLOAD_SZ 0 +#define UBX_CFG_ACK_PAYLOAD_SZ 2 +#define UBX_CFG_NAK_PAYLOAD_SZ 2 +#define UBX_CFG_RATE_PAYLOAD_SZ 6 +#define UBX_CFG_PRT_POLL_PAYLOAD_SZ 1 +#define UBX_CFG_PRT_POLL_FRM_SZ (UBX_FRM_SZ_WO_PAYLOAD + UBX_CFG_PRT_POLL_PAYLOAD_SZ) +#define UBX_CFG_PRT_SET_PAYLOAD_SZ 20 +#define UBX_CFG_PRT_SET_FRM_SZ (UBX_FRM_SZ_WO_PAYLOAD + UBX_CFG_PRT_SET_PAYLOAD_SZ) +#define UBX_CFG_RST_PAYLOAD_SZ 4 +#define UBX_CFG_RST_FRM_SZ (UBX_FRM_SZ_WO_PAYLOAD + UBX_CFG_RST_PAYLOAD_SZ) +#define UBX_CFG_NAV5_PAYLOAD_SZ 36 +#define UBX_CFG_NAV5_FRM_SZ (UBX_FRM_SZ_WO_PAYLOAD + UBX_CFG_NAV5_PAYLOAD_SZ) +#define UBX_CFG_MSG_PAYLOAD_SZ 3 +#define UBX_CFG_MSG_FRM_SZ (UBX_FRM_SZ_WO_PAYLOAD + UBX_CFG_MSG_PAYLOAD_SZ) +#define UBX_CFG_GNSS_PAYLOAD_INIT_SZ 4 +#define UBX_CFG_GNSS_PAYLOAD_CFG_BLK_SZ 8 +#define UBX_CFG_GNSS_PAYLOAD_SZ(n) \ + (UBX_CFG_GNSS_PAYLOAD_INIT_SZ + UBX_CFG_GNSS_PAYLOAD_CFG_BLK_SZ * n) +#define UBX_CFG_GNSS_FRM_SZ(n) (UBX_FRM_SZ_WO_PAYLOAD + UBX_CFG_GNSS_PAYLOAD_SZ(n)) + + +int ubx_create_and_validate_frame(uint8_t *ubx_frame, uint16_t ubx_frame_size, uint8_t msg_cls, + uint8_t msg_id, const void *payload, uint16_t payload_size); + +struct ubx_cfg_ack_payload { + uint8_t message_class; + uint8_t message_id; +}; + +void ubx_cfg_ack_payload_default(struct ubx_cfg_ack_payload *payload); + +#define UBX_CFG_RATE_TIME_REF_UTC 0 /* Align measurements to UTC time. */ +#define UBX_CFG_RATE_TIME_REF_GPS 1 /* Align measurements to GPS time. */ +#define UBX_CFG_RATE_TIME_REF_GLO 2 /* Align measurements to GLONASS time. */ +#define UBX_CFG_RATE_TIME_REF_BDS 3 /* Align measurements to BeiDou time. */ +#define UBX_CFG_RATE_TIME_REF_GAL 4 /* Align measurements to Galileo time. */ + +struct ubx_cfg_rate_payload { + uint16_t meas_rate_ms; + uint16_t nav_rate; + uint16_t time_ref; +}; + +void ubx_cfg_rate_payload_default(struct ubx_cfg_rate_payload *payload); + +struct ubx_cfg_prt_poll_payload { + uint8_t port_id; +}; + +void ubx_cfg_prt_poll_payload_default(struct ubx_cfg_prt_poll_payload *payload); + +#define UBX_CFG_PRT_IN_PROTO_UBX BIT(0) +#define UBX_CFG_PRT_IN_PROTO_NMEA BIT(1) +#define UBX_CFG_PRT_IN_PROTO_RTCM BIT(2) +#define UBX_CFG_PRT_IN_PROTO_RTCM3 BIT(5) +#define UBX_CFG_PRT_OUT_PROTO_UBX BIT(0) +#define UBX_CFG_PRT_OUT_PROTO_NMEA BIT(1) +#define UBX_CFG_PRT_OUT_PROTO_RTCM3 BIT(5) + +#define UBX_CFG_PRT_PORT_MODE_CHAR_LEN_5 0U +#define UBX_CFG_PRT_PORT_MODE_CHAR_LEN_6 BIT(6) +#define UBX_CFG_PRT_PORT_MODE_CHAR_LEN_7 BIT(7) +#define UBX_CFG_PRT_PORT_MODE_CHAR_LEN_8 (BIT(6) | BIT(7)) + +#define UBX_CFG_PRT_PORT_MODE_PARITY_EVEN 0U +#define UBX_CFG_PRT_PORT_MODE_PARITY_ODD BIT(9) +#define UBX_CFG_PRT_PORT_MODE_PARITY_NONE BIT(11) + +#define UBX_CFG_PRT_PORT_MODE_STOP_BITS_1 0U +#define UBX_CFG_PRT_PORT_MODE_STOP_BITS_1_HALF BIT(12) +#define UBX_CFG_PRT_PORT_MODE_STOP_BITS_2 BIT(13) +#define UBX_CFG_PRT_PORT_MODE_STOP_BITS_HALF (BIT(12) | BIT(13)) + +#define UBX_CFG_PRT_RESERVED0 0x00 +#define UBX_CFG_PRT_TX_READY_PIN_CONF_DEFAULT 0x0000 +#define UBX_CFG_PRT_TX_READY_PIN_CONF_EN BIT(0) +#define UBX_CFG_PRT_TX_READY_PIN_CONF_POL_LOW BIT(1) +#define UBX_CFG_PRT_TX_READY_PIN_CONF_POL_HIGH 0U +#define UBX_CFG_PRT_RESERVED1 0x00 +#define UBX_CFG_PRT_FLAGS_DEFAULT 0x0000 +#define UBX_CFG_PRT_FLAGS_EXTENDED_TX_TIMEOUT BIT(0) + +struct ubx_cfg_prt_set_payload { + uint8_t port_id; + uint8_t reserved0; + uint16_t tx_ready_pin_conf; + uint32_t port_mode; + uint32_t baudrate; + uint16_t in_proto_mask; + uint16_t out_proto_mask; + uint16_t flags; + uint8_t reserved1; +}; + +void ubx_cfg_prt_set_payload_default(struct ubx_cfg_prt_set_payload *payload); + +#define UBX_CFG_RST_NAV_BBR_MASK_HOT_START 0x0000 +#define UBX_CFG_RST_NAV_BBR_MASK_WARM_START 0x0001 +#define UBX_CFG_RST_NAV_BBR_MASK_COLD_START 0xFFFF + +#define UBX_CFG_RST_RESET_MODE_HARD_RESET 0x00 +#define UBX_CFG_RST_RESET_MODE_CONTROLLED_SOFT_RESET 0x01 +#define UBX_CFG_RST_RESET_MODE_CONTROLLED_SOFT_RESET_GNSS_ONLY 0x02 +#define UBX_CFG_RST_RESET_MODE_HARD_RESET_AFTER_SHUTDOWN 0x04 +#define UBX_CFG_RST_RESET_MODE_CONTROLLED_GNSS_STOP 0x08 +#define UBX_CFG_RST_RESET_MODE_CONTROLLED_GNSS_START 0x09 + +#define UBX_CFG_RST_RESERVED0 0x00 + +struct ubx_cfg_rst_payload { + uint16_t nav_bbr_mask; + uint8_t reset_mode; + uint8_t reserved0; +}; + +void ubx_cfg_rst_payload_default(struct ubx_cfg_rst_payload *payload); + +#define UBX_CFG_NAV5_MASK_ALL 0x05FF +#define UBX_CFG_NAV5_FIX_MODE_DEFAULT UBX_FIX_AUTO_FIX +#define UBX_CFG_NAV5_FIXED_ALT_DEFAULT 0 +#define UBX_CFG_NAV5_FIXED_ALT_VAR_DEFAULT 1U +#define UBX_CFG_NAV5_MIN_ELEV_DEFAULT 5 +#define UBX_CFG_NAV5_DR_LIMIT_DEFAULT 3U +#define UBX_CFG_NAV5_P_DOP_DEFAULT 100U +#define UBX_CFG_NAV5_T_DOP_DEFAULT 100U +#define UBX_CFG_NAV5_P_ACC_DEFAULT 100U +#define UBX_CFG_NAV5_T_ACC_DEFAULT 350U +#define UBX_CFG_NAV5_STATIC_HOLD_THRESHOLD_DEFAULT 0U +#define UBX_CFG_NAV5_DGNSS_TIMEOUT_DEFAULT 60U +#define UBX_CFG_NAV5_CNO_THRESHOLD_NUM_SVS_DEFAULT 0U +#define UBX_CFG_NAV5_CNO_THRESHOLD_DEFAULT 0U +#define UBX_CFG_NAV5_RESERVED0 0U +#define UBX_CFG_NAV5_STATIC_HOLD_DIST_THRESHOLD 0U +#define UBX_CFG_NAV5_UTC_STANDARD_DEFAULT UBX_UTC_AUTOUTC + +struct ubx_cfg_nav5_payload { + uint16_t mask; + uint8_t dyn_model; + + uint8_t fix_mode; + + int32_t fixed_alt; + uint32_t fixed_alt_var; + + int8_t min_elev; + uint8_t dr_limit; + + uint16_t p_dop; + uint16_t t_dop; + uint16_t p_acc; + uint16_t t_acc; + + uint8_t static_hold_threshold; + uint8_t dgnss_timeout; + uint8_t cno_threshold_num_svs; + uint8_t cno_threshold; + + uint16_t reserved0; + + uint16_t static_hold_dist_threshold; + uint8_t utc_standard; +}; + +void ubx_cfg_nav5_payload_default(struct ubx_cfg_nav5_payload *payload); + +#define UBX_CFG_GNSS_MSG_VER 0x00 +#define UBX_CFG_GNSS_NUM_TRK_CH_HW_DEFAULT 0x31 +#define UBX_CFG_GNSS_NUM_TRK_CH_USE_DEFAULT 0x31 + +#define UBX_CFG_GNSS_RESERVED0 0x00 +#define UBX_CFG_GNSS_FLAG_ENABLE BIT(0) +#define UBX_CFG_GNSS_FLAG_DISABLE 0U +#define UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT 16 +/* When gnss_id is 0 (GPS) */ +#define UBX_CFG_GNSS_FLAG_SGN_CNF_GPS_L1C_A (0x01 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +#define UBX_CFG_GNSS_FLAG_SGN_CNF_GPS_L2C (0x10 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +#define UBX_CFG_GNSS_FLAG_SGN_CNF_GPS_L5 (0x20 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +/* When gnss_id is 1 (SBAS) */ +#define UBX_CFG_GNSS_FLAG_SGN_CNF_SBAS_L1C_A (0x01 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +/* When gnss_id is 2 (Galileo) */ +#define UBX_CFG_GNSS_FLAG_SGN_CNF_GALILEO_E1 (0x01 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +#define UBX_CFG_GNSS_FLAG_SGN_CNF_GALILEO_E5A (0x10 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +#define UBX_CFG_GNSS_FLAG_SGN_CNF_GALILEO_E5B (0x20 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +/* When gnss_id is 3 (BeiDou) */ +#define UBX_CFG_GNSS_FLAG_SGN_CNF_BEIDOU_B1I (0x01 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +#define UBX_CFG_GNSS_FLAG_SGN_CNF_BEIDOU_B2I (0x10 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +#define UBX_CFG_GNSS_FLAG_SGN_CNF_BEIDOU_B2A (0x80 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +/* When gnss_id is 4 (IMES) */ +#define UBX_CFG_GNSS_FLAG_SGN_CNF_IMES_L1 (0x01 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +/* When gnss_id is 5 (QZSS) */ +#define UBX_CFG_GNSS_FLAG_SGN_CNF_QZSS_L1C_A (0x01 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +#define UBX_CFG_GNSS_FLAG_SGN_CNF_QZSS_L1S (0x04 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +#define UBX_CFG_GNSS_FLAG_SGN_CNF_QZSS_L2C (0x10 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +#define UBX_CFG_GNSS_FLAG_SGN_CNF_QZSS_L5 (0x20 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +/* When gnss_id is 6 (GLONASS) */ +#define UBX_CFG_GNSS_FLAG_SGN_CNF_GLONASS_L1 (0x01 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) +#define UBX_CFG_GNSS_FLAG_SGN_CNF_GLONASS_L2 (0x10 << UBX_CFG_GNSS_FLAG_SGN_CNF_SHIFT) + +struct ubx_cfg_gnss_payload_config_block { + uint8_t gnss_id; + uint8_t num_res_trk_ch; + uint8_t max_num_trk_ch; + uint8_t reserved0; + uint32_t flags; +}; + +struct ubx_cfg_gnss_payload { + uint8_t msg_ver; + uint8_t num_trk_ch_hw; + uint8_t num_trk_ch_use; + uint8_t num_config_blocks; + struct ubx_cfg_gnss_payload_config_block config_blocks[]; +}; + +void ubx_cfg_gnss_payload_default(struct ubx_cfg_gnss_payload *payload); + +#define UBX_CFG_MSG_RATE_DEFAULT 1 + +struct ubx_cfg_msg_payload { + uint8_t message_class; + uint8_t message_id; + uint8_t rate; +}; + +void ubx_cfg_msg_payload_default(struct ubx_cfg_msg_payload *payload); + +#endif /* ZEPHYR_U_BLOX_PROTOCOL_ */ diff --git a/drivers/gnss/gnss_u_blox_protocol/gnss_u_blox_protocol_defines.h b/drivers/gnss/gnss_u_blox_protocol/gnss_u_blox_protocol_defines.h new file mode 100644 index 0000000000..870f3f4880 --- /dev/null +++ b/drivers/gnss/gnss_u_blox_protocol/gnss_u_blox_protocol_defines.h @@ -0,0 +1,258 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Referred some enum definitions from file "include/zephyr/drivers/gnss/ublox_neo_m8_defines.h" + * from the pull request #46447 (link - https://github.com/zephyrproject-rtos/zephyr/pull/46447). + */ + +#ifndef ZEPHYR_U_BLOX_PROTOCOL_DEFINES_ +#define ZEPHYR_U_BLOX_PROTOCOL_DEFINES_ + +enum ubx_gnss_id { + UBX_GNSS_ID_GPS = 0, + UBX_GNSS_ID_SBAS = 1, + UBX_GNSS_ID_GALILEO = 2, + UBX_GNSS_ID_BEIDOU = 3, + UBX_GNSS_ID_IMES = 4, + UBX_GNSS_ID_QZSS = 5, + UBX_GNSS_ID_GLONASS = 6, +}; + +enum ubx_port_number { + UBX_PORT_NUMBER_DDC = 0, + UBX_PORT_NUMBER_UART, + UBX_PORT_NUMBER_USB, + UBX_PORT_NUMBER_SPI, +}; + +enum ubx_dynamic_model { + UBX_DYN_MODEL_PORTABLE = 0, + UBX_DYN_MODEL_STATIONARY = 2, + UBX_DYN_MODEL_PEDESTRIAN = 3, + UBX_DYN_MODEL_AUTOMOTIV = 4, + UBX_DYN_MODEL_SEA = 5, + UBX_DYN_MODEL_AIRBONE1G = 6, + UBX_DYN_MODEL_AIRBONE2G = 7, + UBX_DYN_MODEL_AIRBONE4G = 8, + UBX_DYN_MODEL_WIRST = 9, + UBX_DYN_MODEL_BIKE = 10, +}; + +enum ubx_fix_mode { + UBX_FIX_P_2D = 1, + UBX_FIX_P_3D, + UBX_FIX_AUTO_FIX, +}; + +enum ubx_utc_standard { + UBX_UTC_AUTOUTC = 0, + UBX_UTC_GPS = 3, + UBX_UTC_GALILEO = 5, + UBX_UTC_GLONASS, + UBX_UTC_BEIDOU, + UBX_UTC_NAVIC, +}; + +enum ubx_msg_class { + UBX_CLASS_NAV = 0x01, + UBX_CLASS_RXM = 0x02, + UBX_CLASS_INF = 0x04, + UBX_CLASS_ACK = 0x05, + UBX_CLASS_CFG = 0x06, + UBX_CLASS_UPD = 0x09, + UBX_CLASS_MON = 0x0A, + UBX_CLASS_AID = 0x0B, + UBX_CLASS_TIM = 0x0D, + UBX_CLASS_ESF = 0x10, + UBX_CLASS_MGA = 0x13, + UBX_CLASS_LOG = 0x21, + UBX_CLASS_SEC = 0x27, + UBX_CLASS_HNR = 0x28, + UBX_CLASS_NMEA = 0xF0, +}; + +enum ubx_ack_message { + UBX_ACK_ACK = 0x01, + UBX_ACK_NAK = 0x00, +}; + +enum ubx_config_message { + UBX_CFG_ANT = 0x13, + UBX_CFG_BATCH = 0x93, + UBX_CFG_CFG = 0x09, + UBX_CFG_DAT = 0x06, + UBX_CFG_DGNSS = 0x70, + UBX_CFG_DOSC = 0x61, + UBX_CFG_ESFALG = 0x56, + UBX_CFG_ESFAE = 0x4C, + UBX_CFG_ESFGE = 0x4D, + UBX_CFG_ESFWTE = 0x82, + UBX_CFG_ESRCE = 0x60, + UBX_CFG_GEOFENCE = 0x69, + UBX_CFG_GNSS = 0x3E, + UBX_CFG_HNR = 0x5C, + UBX_CFG_INF = 0x02, + UBX_CFG_ITFM = 0x39, + UBX_CFG_LOGFILTER = 0x47, + UBX_CFG_MSG = 0x01, + UBX_CFG_NAV5 = 0x24, + UBX_CFG_NAVX5 = 0x23, + UBX_CFG_NMEA = 0x17, + UBX_CFG_ODO = 0x1E, + UBX_CFG_PM2 = 0x3B, + UBX_CFG_PMS = 0x86, + UBX_CFG_PRT = 0x00, + UBX_CFG_PWR = 0x57, + UBX_CFG_RATE = 0x08, + UBX_CFG_RINV = 0x34, + UBX_CFG_RST = 0x04, + UBX_CFG_RXM = 0x11, + UBX_CFG_SBAS = 0x16, + UBX_CFG_SENIF = 0x88, + UBX_CFG_SLAS = 0x8D, + UBX_CFG_SMGR = 0x62, + UBX_CFG_SPT = 0x64, + UBX_CFG_TMODE2 = 0x3D, + UBX_CFG_TMODE3 = 0x71, + UBX_CFG_TP5 = 0x31, + UBX_CFG_TXSLOT = 0x53, + UBX_CFG_USB = 0x1B, +}; + +enum ubx_information_message { + UBX_INF_DEBUG = 0x04, + UBX_INF_ERROR = 0x00, + UBX_INF_NOTICE = 0x02, + UBX_INF_TEST = 0x03, + UBX_INF_WARNING = 0x01, +}; + +enum ubx_logging_message { + UBX_LOG_BATCH = 0x11, + UBX_LOG_CREATE = 0x07, + UBX_LOG_ERASE = 0x03, + UBX_LOG_FINDTIME = 0x0E, + UBX_LOG_INFO = 0x08, + UBX_LOG_RETRIEVEBATCH = 0x10, + UBX_LOG_RETRIEVEPOSEXTRA = 0x0f, + UBX_LOG_RETRIEVEPOS = 0x0b, + UBX_LOG_RETRIEVESTRING = 0x0d, + UBX_LOG_RETRIEVE = 0x09, + UBX_LOG_STRING = 0x04, +}; + +enum ubx_multiple_gnss_assistance_message { + UBX_MGA_ACK = 0x60, + UBX_MGA_ANO = 0x20, + UBX_MGA_BDS = 0x03, + UBX_MGA_DBD = 0x80, + UBX_MGA_FLASH = 0x21, + UBX_MGA_GAL = 0x02, + UBX_MGA_GLO = 0x06, + UBX_MGA_GPS = 0x00, + UBX_MGA_INI = 0x40, + UBX_MGA_QZSS = 0x05, +}; + +enum ubx_monitoring_message { + UBX_MON_BATCH = 0x32, + UBX_MON_GNSS = 0x28, + UBX_MON_HW2 = 0x0B, + UBX_MON_HW = 0x09, + UBX_MON_IO = 0x02, + UBX_MON_MSGPP = 0x06, + UBX_MON_PATCH = 0x27, + UBX_MON_RXBUF = 0x07, + UBX_MON_RXR = 0x21, + UBX_MON_SMGR = 0x2E, + UBX_MON_SPT = 0x2F, + UBX_MON_TXBUF = 0x08, + UBX_MON_VER = 0x04, +}; + +enum ubx_nagivation_results_message { + UBX_NAV_AOPSTATUS = 0x60, + UBX_NAV_ATT = 0x05, + UBX_NAV_CLOCK = 0x22, + UBX_NAV_COV = 0x36, + UBX_NAV_DGPS = 0x31, + UBX_NAV_DOP = 0x04, + UBX_NAV_EELL = 0x3d, + UBX_NAV_EOE = 0x61, + UBX_NAV_GEOFENCE = 0x39, + UBX_NAV_HPPOSECEF = 0x13, + UBX_NAV_HPPOSLLH = 0x14, + UBX_NAV_NMI = 0x28, + UBX_NAV_ODO = 0x09, + UBX_NAV_ORB = 0x34, + UBX_NAV_POSECEF = 0x01, + UBX_NAV_POSLLH = 0x02, + UBX_NAV_PVT = 0x07, + UBX_NAV_RELPOSNED = 0x3C, + UBX_NAV_RESETODO = 0x10, + UBX_NAV_SAT = 0x35, + UBX_NAV_SBAS = 0x32, + UBX_NAV_SLAS = 0x42, + UBX_NAV_SOL = 0x06, + UBX_NAV_STATUS = 0x03, + UBX_NAV_SVINFO = 0x30, + UBX_NAV_SVIN = 0x3B, + UBX_NAV_TIMEBDS = 0x24, + UBX_NAV_TIMEGAL = 0x25, + UBX_NAV_TIMEGLO = 0x23, + UBX_NAV_TIMEGPS = 0x20, + UBX_NAV_TIMELS = 0x26, + UBX_NAV_TIMEUTC = 0x21, + UBX_NAV_VELECEF = 0x11, + UBX_NAV_VELNED = 0x12, +}; + +enum ubx_receiver_manager_message { + UBX_RXM_IMES = 0x61, + UBX_RXM_MEASX = 0x14, + UBX_RXM_PMREQ = 0x41, + UBX_RXM_RAWX = 0x15, + UBX_RXM_RLM = 0x59, + UBX_RXM_RTCM = 0x32, + UBX_RXM_SFRBX = 0x13, +}; + +enum ubx_timing_message { + UBX_TIM_DOSC = 0x11, + UBX_TIM_FCHG = 0x16, + UBX_TIM_HOC = 0x17, + UBX_TIM_SMEAS = 0x13, + UBX_TIM_SVIN = 0x04, + UBX_TIM_TM2 = 0x03, + UBX_TIM_TOS = 0x12, + UBX_TIM_TP = 0x01, + UBX_TIM_VCOCAL = 0x15, + UBX_TIM_VRFY = 0x06, +}; + +enum ubx_nmea_message_id { + UBX_NMEA_DTM = 0x0A, + UBX_NMEA_GBQ = 0x44, + UBX_NMEA_GBS = 0x09, + UBX_NMEA_GGA = 0x00, + UBX_NMEA_GLL = 0x01, + UBX_NMEA_GLQ = 0x43, + UBX_NMEA_GNQ = 0x42, + UBX_NMEA_GNS = 0x0D, + UBX_NMEA_GPQ = 0x40, + UBX_NMEA_GRS = 0x06, + UBX_NMEA_GSA = 0x02, + UBX_NMEA_GST = 0x07, + UBX_NMEA_GSV = 0x03, + UBX_NMEA_RMC = 0x04, + UBX_NMEA_THS = 0x0E, + UBX_NMEA_TXT = 0x41, + UBX_NMEA_VLW = 0x0F, + UBX_NMEA_VTG = 0x05, + UBX_NMEA_ZDA = 0x08, +}; + +#endif /* ZEPHYR_U_BLOX_PROTOCOL_DEFINES_ */ diff --git a/dts/bindings/gnss/u-blox,m10.yaml b/dts/bindings/gnss/u-blox,m10.yaml new file mode 100644 index 0000000000..556213d8c4 --- /dev/null +++ b/dts/bindings/gnss/u-blox,m10.yaml @@ -0,0 +1,17 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: U-BLOX M10 GNSS Module + +compatible: "u-blox,m10" + +include: + - uart-device.yaml + +properties: + uart-baudrate: + type: int + description: | + Baudrate for communication on the UART port. + default: 115200 + enum: [4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600] diff --git a/include/zephyr/dt-bindings/gnss/u_blox_m10.h b/include/zephyr/dt-bindings/gnss/u_blox_m10.h new file mode 100644 index 0000000000..b017f42dca --- /dev/null +++ b/include/zephyr/dt-bindings/gnss/u_blox_m10.h @@ -0,0 +1,23 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_U_BLOX_M10_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_U_BLOX_M10_H_ + +#include + +/* UART Baudrate. */ +#define UBX_M10_UART_BAUDRATE_4800 0x00 +#define UBX_M10_UART_BAUDRATE_9600 0x01 +#define UBX_M10_UART_BAUDRATE_19200 0x02 +#define UBX_M10_UART_BAUDRATE_38400 0x03 +#define UBX_M10_UART_BAUDRATE_57600 0x04 +#define UBX_M10_UART_BAUDRATE_115200 0x05 +#define UBX_M10_UART_BAUDRATE_230400 0x06 +#define UBX_M10_UART_BAUDRATE_460800 0x07 +#define UBX_M10_UART_BAUDRATE_921600 0x08 + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_U_BLOX_M10_H_ */ diff --git a/include/zephyr/modem/ubx.h b/include/zephyr/modem/ubx.h new file mode 100644 index 0000000000..e51cb5cd40 --- /dev/null +++ b/include/zephyr/modem/ubx.h @@ -0,0 +1,175 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include + +#ifndef ZEPHYR_MODEM_UBX_ +#define ZEPHYR_MODEM_UBX_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Modem Ubx + * @defgroup modem_ubx Modem Ubx + * @ingroup modem + * @{ + */ + +#define UBX_FRM_HEADER_SZ 6 +#define UBX_FRM_FOOTER_SZ 2 +#define UBX_FRM_SZ_WITHOUT_PAYLOAD (UBX_FRM_HEADER_SZ + UBX_FRM_FOOTER_SZ) +#define UBX_FRM_SZ(payload_size) (payload_size + UBX_FRM_SZ_WITHOUT_PAYLOAD) + +#define UBX_PREAMBLE_SYNC_CHAR_1 0xB5 +#define UBX_PREAMBLE_SYNC_CHAR_2 0x62 + +#define UBX_FRM_PREAMBLE_SYNC_CHAR_1_IDX 0 +#define UBX_FRM_PREAMBLE_SYNC_CHAR_2_IDX 1 +#define UBX_FRM_MSG_CLASS_IDX 2 +#define UBX_FRM_MSG_ID_IDX 3 +#define UBX_FRM_PAYLOAD_SZ_L_IDX 4 +#define UBX_FRM_PAYLOAD_SZ_H_IDX 5 +#define UBX_FRM_PAYLOAD_IDX 6 +#define UBX_FRM_CHECKSUM_START_IDX 2 +#define UBX_FRM_CHECKSUM_STOP_IDX(frame_len) (frame_len - 2) + +#define UBX_PAYLOAD_SZ_MAX 256 +#define UBX_FRM_SZ_MAX UBX_FRM_SZ(UBX_PAYLOAD_SZ_MAX) + +struct ubx_frame { + uint8_t preamble_sync_char_1; + uint8_t preamble_sync_char_2; + uint8_t message_class; + uint8_t message_id; + uint8_t payload_size_low; + uint8_t payload_size_high; + uint8_t payload_and_checksum[]; +}; + +struct modem_ubx_script { + struct ubx_frame *request; + struct ubx_frame *response; + struct ubx_frame *match; + + uint16_t retry_count; + k_timeout_t timeout; +}; + +struct modem_ubx { + void *user_data; + + atomic_t state; + + uint8_t *receive_buf; + uint16_t receive_buf_size; + + uint8_t *work_buf; + uint16_t work_buf_size; + uint16_t work_buf_len; + bool ubx_preamble_sync_chars_received; + + const struct modem_ubx_script *script; + + struct modem_pipe *pipe; + + struct k_work send_work; + struct k_work process_work; + struct k_sem script_stopped_sem; + struct k_sem script_running_sem; +}; + +struct modem_ubx_config { + void *user_data; + + uint8_t *receive_buf; + uint16_t receive_buf_size; + uint8_t *work_buf; + uint16_t work_buf_size; +}; + +/** + * @brief Attach pipe to Modem Ubx + * + * @param ubx Modem Ubx instance + * @param pipe Pipe instance to attach Modem Ubx instance to + * @returns 0 if successful + * @returns negative errno code if failure + * @note Modem Ubx instance is enabled if successful + */ +int modem_ubx_attach(struct modem_ubx *ubx, struct modem_pipe *pipe); + +/** + * @brief Release pipe from Modem Ubx instance + * + * @param ubx Modem Ubx instance + */ +void modem_ubx_release(struct modem_ubx *ubx); + +/** + * @brief Initialize Modem Ubx instance + * @param ubx Modem Ubx instance + * @param config Configuration which shall be applied to the Modem Ubx instance + * @note Modem Ubx instance must be attached to a pipe instance + */ +int modem_ubx_init(struct modem_ubx *ubx, const struct modem_ubx_config *config); + +/** + * @brief Writes the ubx frame in script.request and reads back its response (if available) + * @details For each ubx frame sent, the device responds in 0, 1 or both of the following ways: + * 1. The device sends back a UBX-ACK frame to denote 'acknowledge' and 'not-acknowledge'. + * Note: the message id of UBX-ACK frame determines whether the device acknowledged. + * Ex: when we send a UBX-CFG frame, the device responds with a UBX-ACK frame. + * 2. The device sends back the same frame that we sent to it, with it's payload populated. + * It's used to get the current configuration corresponding to the frame that we sent. + * Ex: frame types such as "get" or "poll" ubx frames respond this way. + * This response (if received) is written to script.response. + * + * This function writes the ubx frame in script.request then reads back it's response. + * If script.match is not NULL, then every ubx frame received from the device is compared with + * script.match to check if a match occurred. This could be used to match UBX-ACK frame sent + * from the device by populating script.match with UBX-ACK that the script expects to receive. + * + * The script terminates when either of the following happens: + * 1. script.match is successfully received and matched. + * 2. timeout (denoted by script.timeout) occurs. + * @param ubx Modem Ubx instance + * @param script Script to be executed + * @note The length of ubx frame in the script.request should not exceed UBX_FRM_SZ_MAX + * @note Modem Ubx instance must be attached to a pipe instance + * @returns 0 if device acknowledged via UBX-ACK and no "get" response was received + * @returns positive integer denoting the length of "get" response that was received + * @returns negative errno code if failure + */ +int modem_ubx_run_script(struct modem_ubx *ubx, const struct modem_ubx_script *script); + +/** + * @brief Initialize ubx frame + * @param ubx_frame Ubx frame buffer + * @param ubx_frame_size Ubx frame buffer size + * @param msg_cls Message class + * @param msg_id Message id + * @param payload Payload buffer + * @param payload_size Payload buffer size + * @returns positive integer denoting the length of the ubx frame created + * @returns negative errno code if failure + */ +int modem_ubx_create_frame(uint8_t *ubx_frame, uint16_t ubx_frame_size, uint8_t msg_cls, + uint8_t msg_id, const void *payload, uint16_t payload_size); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_MODEM_UBX_ */ diff --git a/samples/drivers/gnss/boards/mimxrt1062_fmurt6.conf b/samples/drivers/gnss/boards/mimxrt1062_fmurt6.conf new file mode 100644 index 0000000000..a8b2dae2a7 --- /dev/null +++ b/samples/drivers/gnss/boards/mimxrt1062_fmurt6.conf @@ -0,0 +1,4 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_UART_INTERRUPT_DRIVEN=y diff --git a/samples/drivers/gnss/boards/mimxrt1062_fmurt6.overlay b/samples/drivers/gnss/boards/mimxrt1062_fmurt6.overlay new file mode 100644 index 0000000000..37942aed9b --- /dev/null +++ b/samples/drivers/gnss/boards/mimxrt1062_fmurt6.overlay @@ -0,0 +1,25 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + gnss = &lpuart2; + }; +}; + +&lpuart2 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&pinmux_lpuart2>; + pinctrl-1 = <&pinmux_lpuart2_sleep>; + pinctrl-names = "default", "sleep"; + + u_blox_m10: u-blox,m10 { + status = "okay"; + compatible = "u-blox,m10"; + uart-baudrate = <115200>; + }; +}; diff --git a/samples/drivers/gnss/boards/vmu_rt1170.overlay b/samples/drivers/gnss/boards/vmu_rt1170.overlay new file mode 100644 index 0000000000..baf0b47923 --- /dev/null +++ b/samples/drivers/gnss/boards/vmu_rt1170.overlay @@ -0,0 +1,22 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + gnss = &lpuart3; + }; +}; + +&lpuart3 { + status = "okay"; + current-speed = <115200>; + + u_blox_m10: u-blox,m10 { + status = "okay"; + compatible = "u-blox,m10"; + uart-baudrate = <115200>; + }; +}; diff --git a/subsys/modem/CMakeLists.txt b/subsys/modem/CMakeLists.txt index d491e190eb..a19916a0dd 100644 --- a/subsys/modem/CMakeLists.txt +++ b/subsys/modem/CMakeLists.txt @@ -9,6 +9,7 @@ zephyr_library_sources_ifdef(CONFIG_MODEM_CHAT modem_chat.c) zephyr_library_sources_ifdef(CONFIG_MODEM_CMUX modem_cmux.c) zephyr_library_sources_ifdef(CONFIG_MODEM_PIPE modem_pipe.c) zephyr_library_sources_ifdef(CONFIG_MODEM_PPP modem_ppp.c) +zephyr_library_sources_ifdef(CONFIG_MODEM_UBX modem_ubx.c) add_subdirectory(backends) diff --git a/subsys/modem/Kconfig b/subsys/modem/Kconfig index 69c102a07a..86168ad22c 100644 --- a/subsys/modem/Kconfig +++ b/subsys/modem/Kconfig @@ -54,6 +54,21 @@ config MODEM_PPP_NET_BUF_FRAG_SIZE endif +config MODEM_UBX + bool "Modem U-BLOX module" + select RING_BUFFER + select MODEM_PIPE + help + Enable Modem U-BLOX module. + +if MODEM_UBX + +config MODEM_UBX_LOG_BUFFER + int "Modem U-BLOX log buffer size" + default 128 + +endif + module = MODEM_MODULES module-str = modem_modules source "subsys/logging/Kconfig.template.log_config" diff --git a/subsys/modem/modem_ubx.c b/subsys/modem/modem_ubx.c new file mode 100644 index 0000000000..22ce98f23e --- /dev/null +++ b/subsys/modem/modem_ubx.c @@ -0,0 +1,338 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +LOG_MODULE_REGISTER(modem_ubx, CONFIG_MODEM_MODULES_LOG_LEVEL); + +#define MODEM_UBX_STATE_ATTACHED_BIT 0 + +static int modem_ubx_validate_frame_size(uint16_t ubx_frame_size, uint8_t msg_cls, uint8_t msg_id, + uint16_t payload_size) +{ + if (ubx_frame_size > UBX_FRM_SZ_MAX || + ubx_frame_size < UBX_FRM_SZ_WITHOUT_PAYLOAD || + ubx_frame_size < UBX_FRM_SZ_WITHOUT_PAYLOAD + payload_size) { + return -1; + } + + return 0; +} + +int modem_ubx_create_frame(uint8_t *ubx_frame, uint16_t ubx_frame_size, uint8_t msg_cls, + uint8_t msg_id, const void *payload, uint16_t payload_size) +{ + if (modem_ubx_validate_frame_size(ubx_frame_size, msg_cls, msg_id, payload_size)) { + return -1; + } + + struct ubx_frame *frame = (struct ubx_frame *) ubx_frame; + + frame->preamble_sync_char_1 = UBX_PREAMBLE_SYNC_CHAR_1; + frame->preamble_sync_char_2 = UBX_PREAMBLE_SYNC_CHAR_2; + frame->message_class = msg_cls; + frame->message_id = msg_id; + frame->payload_size_low = payload_size; + frame->payload_size_high = payload_size >> 8; + + memcpy(frame->payload_and_checksum, payload, payload_size); + + uint16_t ubx_frame_len = payload_size + UBX_FRM_SZ_WITHOUT_PAYLOAD; + + uint8_t ckA = 0, ckB = 0; + + for (unsigned int i = UBX_FRM_CHECKSUM_START_IDX; + i < (UBX_FRM_CHECKSUM_STOP_IDX(ubx_frame_len)); i++) { + ckA += ubx_frame[i]; + ckB += ckA; + } + + frame->payload_and_checksum[payload_size] = ckA; + frame->payload_and_checksum[payload_size + 1] = ckB; + + return ubx_frame_len; +} + +static void modem_ubx_reset_received_ubx_preamble_sync_chars(struct modem_ubx *ubx) +{ + ubx->ubx_preamble_sync_chars_received = false; +} + +static void modem_ubx_reset_parser(struct modem_ubx *ubx) +{ + modem_ubx_reset_received_ubx_preamble_sync_chars(ubx); +} + +static int modem_ubx_get_payload_length(struct ubx_frame *frame) +{ + uint16_t payload_len = frame->payload_size_high; + + payload_len = payload_len << 8; + + return payload_len | frame->payload_size_low; +} + +static int modem_ubx_get_frame_length(struct ubx_frame *frame) +{ + return modem_ubx_get_payload_length(frame) + UBX_FRM_SZ_WITHOUT_PAYLOAD; +} + +static bool modem_ubx_match_frame_type(struct ubx_frame *frame_1, struct ubx_frame *frame_2) +{ + if (frame_1->message_class == frame_2->message_class + && frame_1->message_id == frame_2->message_id) { + return true; + } else { + return false; + } +} + +static bool modem_ubx_match_frame_full(struct ubx_frame *frame_1, struct ubx_frame *frame_2) +{ + if (modem_ubx_get_frame_length(frame_1) != modem_ubx_get_frame_length(frame_2)) { + return false; + } + + if (memcmp(frame_1, frame_2, modem_ubx_get_frame_length(frame_1)) == 0) { + return true; + } else { + return false; + } +} + +static void modem_ubx_script_init(struct modem_ubx *ubx, const struct modem_ubx_script *script) +{ + ubx->script = script; +} + +static int modem_ubx_run_script_helper(struct modem_ubx *ubx, const struct modem_ubx_script *script) +{ + int ret; + + if (ubx->pipe == NULL) { + return -EPERM; + } + + k_sem_reset(&ubx->script_stopped_sem); + + modem_ubx_reset_parser(ubx); + + k_work_submit(&ubx->send_work); + + if (ubx->script->match == NULL) { + return 0; + } + + ret = k_sem_take(&ubx->script_stopped_sem, script->timeout); + if (ret < 0) { + return ret; + } + + return 0; +} + +int modem_ubx_run_script(struct modem_ubx *ubx, const struct modem_ubx_script *script) +{ + int ret, attempt; + + if (modem_ubx_get_frame_length(script->request) > UBX_FRM_SZ_MAX) { + return -EFBIG; + } + + if (atomic_test_bit(&ubx->state, MODEM_UBX_STATE_ATTACHED_BIT) == false) { + return -EPERM; + } + + ret = k_sem_take(&ubx->script_running_sem, K_FOREVER); + if (ret < 0) { + return ret; + } + + modem_ubx_script_init(ubx, script); + + for (attempt = 0; attempt < script->retry_count; ++attempt) { + ret = modem_ubx_run_script_helper(ubx, script); + if (ret > -1) { + LOG_INF("Successfully executed script on attempt: %d.", attempt); + break; + } else if (ret == -EPERM) { + break; + } + } + + if (ret < 0) { + LOG_ERR("Failed to execute script successfully. Attempts: %d.", attempt); + goto unlock; + } + +unlock: + k_sem_give(&ubx->script_running_sem); + + return ret; +} + +static void modem_ubx_pipe_callback(struct modem_pipe *pipe, enum modem_pipe_event event, + void *user_data) +{ + struct modem_ubx *ubx = (struct modem_ubx *)user_data; + + if (event == MODEM_PIPE_EVENT_RECEIVE_READY) { + k_work_submit(&ubx->process_work); + } +} + +static void modem_ubx_send_handler(struct k_work *item) +{ + struct modem_ubx *ubx = CONTAINER_OF(item, struct modem_ubx, send_work); + int ret, tx_frame_len; + + tx_frame_len = modem_ubx_get_frame_length(ubx->script->request); + ret = modem_pipe_transmit(ubx->pipe, (const uint8_t *) ubx->script->request, tx_frame_len); + if (ret < tx_frame_len) { + LOG_ERR("Ubx frame transmission failed. Returned %d.", ret); + return; + } +} + +static int modem_ubx_process_received_ubx_frame(struct modem_ubx *ubx) +{ + int ret; + struct ubx_frame *received = (struct ubx_frame *) ubx->work_buf; + + if (modem_ubx_match_frame_full(received, ubx->script->match) == true) { + /* Frame matched successfully. Terminate the script. */ + k_sem_give(&ubx->script_stopped_sem); + ret = 0; + } else if (modem_ubx_match_frame_type(received, ubx->script->request) == true) { + /* Response received successfully. Script not ended. */ + memcpy(ubx->script->response, ubx->work_buf, ubx->work_buf_len); + ret = -1; + } else { + /* Ignore the received frame. The device may automatically send periodic frames. + * These frames are not relevant for our script's execution and must be ignored. + */ + ret = -1; + } + + modem_ubx_reset_parser(ubx); + + return ret; +} + +static int modem_ubx_process_received_byte(struct modem_ubx *ubx, uint8_t byte) +{ + static uint8_t prev_byte; + static uint16_t rx_ubx_frame_len; + + if (ubx->ubx_preamble_sync_chars_received == false) { + if (prev_byte == UBX_PREAMBLE_SYNC_CHAR_1 && byte == UBX_PREAMBLE_SYNC_CHAR_2) { + ubx->ubx_preamble_sync_chars_received = true; + ubx->work_buf[0] = UBX_PREAMBLE_SYNC_CHAR_1; + ubx->work_buf[1] = UBX_PREAMBLE_SYNC_CHAR_2; + ubx->work_buf_len = 2; + } + } else { + ubx->work_buf[ubx->work_buf_len] = byte; + ++ubx->work_buf_len; + + if (ubx->work_buf_len == UBX_FRM_HEADER_SZ) { + uint16_t rx_ubx_payload_len = ubx->work_buf[UBX_FRM_PAYLOAD_SZ_H_IDX]; + + rx_ubx_payload_len = ubx->work_buf[UBX_FRM_PAYLOAD_SZ_H_IDX] << 8; + rx_ubx_payload_len |= ubx->work_buf[UBX_FRM_PAYLOAD_SZ_L_IDX]; + + rx_ubx_frame_len = rx_ubx_payload_len + UBX_FRM_SZ_WITHOUT_PAYLOAD; + } + + if (ubx->work_buf_len == rx_ubx_frame_len) { + return modem_ubx_process_received_ubx_frame(ubx); + } + } + + prev_byte = byte; + + return -1; +} + +static void modem_ubx_process_handler(struct k_work *item) +{ + struct modem_ubx *ubx = CONTAINER_OF(item, struct modem_ubx, process_work); + int ret; + + ret = modem_pipe_receive(ubx->pipe, ubx->receive_buf, ubx->receive_buf_size); + if (ret < 1) { + return; + } + + for (int i = 0; i < ret; i++) { + ret = modem_ubx_process_received_byte(ubx, ubx->receive_buf[i]); + if (ret == 0) { /* Frame matched successfully. Terminate the script. */ + break; + } + } + + k_work_submit(&ubx->process_work); +} + +int modem_ubx_attach(struct modem_ubx *ubx, struct modem_pipe *pipe) +{ + if (atomic_test_and_set_bit(&ubx->state, MODEM_UBX_STATE_ATTACHED_BIT) == true) { + return 0; + } + + ubx->pipe = pipe; + modem_pipe_attach(ubx->pipe, modem_ubx_pipe_callback, ubx); + k_sem_give(&ubx->script_running_sem); + + return 0; +} + +void modem_ubx_release(struct modem_ubx *ubx) +{ + struct k_work_sync sync; + + if (atomic_test_and_clear_bit(&ubx->state, MODEM_UBX_STATE_ATTACHED_BIT) == false) { + return; + } + + modem_pipe_release(ubx->pipe); + k_work_cancel_sync(&ubx->send_work, &sync); + k_work_cancel_sync(&ubx->process_work, &sync); + k_sem_reset(&ubx->script_stopped_sem); + k_sem_reset(&ubx->script_running_sem); + ubx->work_buf_len = 0; + modem_ubx_reset_parser(ubx); + ubx->pipe = NULL; +} + +int modem_ubx_init(struct modem_ubx *ubx, const struct modem_ubx_config *config) +{ + __ASSERT_NO_MSG(ubx != NULL); + __ASSERT_NO_MSG(config != NULL); + __ASSERT_NO_MSG(config->receive_buf != NULL); + __ASSERT_NO_MSG(config->receive_buf_size > 0); + __ASSERT_NO_MSG(config->work_buf != NULL); + __ASSERT_NO_MSG(config->work_buf_size > 0); + + memset(ubx, 0x00, sizeof(*ubx)); + ubx->user_data = config->user_data; + + ubx->receive_buf = config->receive_buf; + ubx->receive_buf_size = config->receive_buf_size; + ubx->work_buf = config->work_buf; + ubx->work_buf_size = config->work_buf_size; + + ubx->pipe = NULL; + + k_work_init(&ubx->send_work, modem_ubx_send_handler); + k_work_init(&ubx->process_work, modem_ubx_process_handler); + k_sem_init(&ubx->script_stopped_sem, 0, 1); + k_sem_init(&ubx->script_running_sem, 1, 1); + + return 0; +}