Skip to content

Commit

Permalink
drivers: gnss: add a generic NMEA GNSS driver
Browse files Browse the repository at this point in the history
wip

Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>
  • Loading branch information
fabiobaltieri committed Nov 17, 2023
1 parent bc08e80 commit 2ae7a75
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/gnss/CMakeLists.txt
Expand Up @@ -8,3 +8,4 @@ zephyr_library_sources_ifdef(CONFIG_GNSS_PARSE gnss_parse.c)
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_QUECTEL_LCX6G gnss_quectel_lcx6g.c)
zephyr_library_sources_ifdef(CONFIG_GNSS_NMEA_GENERIC gnss_nmea_generic.c)
1 change: 1 addition & 0 deletions drivers/gnss/Kconfig
Expand Up @@ -65,5 +65,6 @@ module-str = gnss
source "subsys/logging/Kconfig.template.log_config"

rsource "Kconfig.quectel_lcx6g"
rsource "Kconfig.generic"

endif
17 changes: 17 additions & 0 deletions drivers/gnss/Kconfig.generic
@@ -0,0 +1,17 @@
# Copyright 2023 Google LLC
# SPDX-License-Identifier: Apache-2.0

config GNSS_NMEA_GENERIC
bool "Generic GNSS NMEA device"
default y
depends on GNSS
depends on DT_HAS_GNSS_NMEA_GENERIC_ENABLED
select UART_INTERRUPT_DRIVEN
select MODEM_MODULES
select MODEM_BACKEND_UART
select MODEM_CHAT
select GNSS_PARSE
select GNSS_NMEA0183
select GNSS_NMEA0183_MATCH
help
TODO
265 changes: 265 additions & 0 deletions drivers/gnss/gnss_nmea_generic.c
@@ -0,0 +1,265 @@
/*
* Copyright 2023 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/drivers/gnss.h>
#include <zephyr/modem/chat.h>
#include <zephyr/modem/backend/uart.h>
#include <zephyr/kernel.h>
#include <zephyr/pm/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/pm/device_runtime.h>
#include <string.h>

#include "gnss_publish.h"
#include "gnss_nmea0183.h"
#include "gnss_nmea0183_match.h"
#include "gnss_parse.h"

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(gnss_nmea_generic, CONFIG_GNSS_LOG_LEVEL);

#define DT_DRV_COMPAT gnss_nmea_generic

struct gnss_nmea_generic_config {
const struct device *uart;
};

struct gnss_nmea_generic_data {
struct gnss_nmea0183_match_data match_data;
#if CONFIG_GNSS_SATELLITES
struct gnss_satellite satellites[24];
#endif

/* UART backend */
struct modem_pipe *uart_pipe;
struct modem_backend_uart uart_backend;
uint8_t uart_backend_receive_buf[128];

/* Modem chat */
struct modem_chat chat;
uint8_t chat_receive_buf[256];
uint8_t chat_delimiter[2];
uint8_t *chat_argv[32];

/* Dynamic chat script */
uint8_t dynamic_match_buf[32];
uint8_t dynamic_separators_buf[2];
uint8_t dynamic_request_buf[32];
struct modem_chat_match dynamic_match;
struct modem_chat_script_chat dynamic_script_chat;
struct modem_chat_script dynamic_script;

struct k_spinlock lock;
};

#define MODEM_CHAT_SCRIPT_NO_ABORT_DEFINE(_sym, _script_chats, _callback, _timeout) \
static struct modem_chat_script _sym = { \
.name = #_sym, \
.script_chats = _script_chats, \
.script_chats_size = ARRAY_SIZE(_script_chats), \
.abort_matches = NULL, \
.abort_matches_size = 0, \
.callback = _callback, \
.timeout = _timeout, \
}

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)
{
struct gnss_nmea_generic_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) {
modem_pipe_close(data->uart_pipe);
return ret;
}

return ret;
}

#ifdef CONFIG_PM_DEVICE
static int gnss_nmea_generic_suspend(const struct device *dev)
{
return 0;
}

static int gnss_nmea_generic_turn_off(const struct device *dev)
{
struct gnss_nmea_generic_data *data = dev->data;

return modem_pipe_close(data->uart_pipe);
}

static int gnss_nmea_generic_pm_action(const struct device *dev, enum pm_device_action action)
{
struct gnss_nmea_generic_data *data = dev->data;
k_spinlock_key_t key;
int ret = -ENOTSUP;

key = k_spin_lock(&data->lock);

switch (action) {
case PM_DEVICE_ACTION_SUSPEND:
ret = gnss_nmea_generic_suspend(dev);
break;

case PM_DEVICE_ACTION_RESUME:
ret = gnss_nmea_generic_resume(dev);
break;

case PM_DEVICE_ACTION_TURN_ON:
ret = 0;
break;

case PM_DEVICE_ACTION_TURN_OFF:
ret = gnss_nmea_generic_turn_off(dev);
break;

default:
break;
}

k_spin_unlock(&data->lock, key);
return ret;
}
#endif /* CONFIG_PM_DEVICE */

static 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 config = {
.gnss = dev,
#if CONFIG_GNSS_SATELLITES
.satellites = data->satellites,
.satellites_size = ARRAY_SIZE(data->satellites),
#endif
.timeout_ms = 200,
};

return gnss_nmea0183_match_init(&data->match_data, &config);
}

static void gnss_nmea_generic_init_pipe(const struct device *dev)
{
const struct gnss_nmea_generic_config *config = dev->config;
struct gnss_nmea_generic_data *data = dev->data;

const struct modem_backend_uart_config uart_backend_config = {
.uart = config->uart,
.receive_buf = data->uart_backend_receive_buf,
.receive_buf_size = ARRAY_SIZE(data->uart_backend_receive_buf),
};

data->uart_pipe = modem_backend_uart_init(&data->uart_backend, &uart_backend_config);
}

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 = ARRAY_SIZE(data->chat_receive_buf),
.delimiter = data->chat_delimiter,
.delimiter_size = ARRAY_SIZE(data->chat_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),
.process_timeout = K_MSEC(2),
};

return modem_chat_init(&data->chat, &chat_config);
}

static void gnss_nmea_generic_init_dynamic_script(const struct device *dev)
{
struct gnss_nmea_generic_data *data = dev->data;

data->dynamic_match.match = data->dynamic_match_buf;
data->dynamic_match.separators = data->dynamic_separators_buf;
data->dynamic_match.separators_size = sizeof(data->dynamic_separators_buf);
data->dynamic_match.wildcards = false;
data->dynamic_match.partial = false;

data->dynamic_script_chat.request = data->dynamic_request_buf;
data->dynamic_script_chat.response_matches = &data->dynamic_match;
data->dynamic_script_chat.response_matches_size = 1;
data->dynamic_script_chat.timeout = 0;

data->dynamic_script.name = "pair";
data->dynamic_script.script_chats = &data->dynamic_script_chat;
data->dynamic_script.script_chats_size = 1;
data->dynamic_script.abort_matches = NULL;
data->dynamic_script.abort_matches_size = 0;
data->dynamic_script.callback = NULL;
data->dynamic_script.timeout = 10;
}

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;
}

gnss_nmea_generic_init_dynamic_script(dev);

#ifdef CONFIG_PM_DEVICE
pm_device_init_suspended(dev);
#else
ret = gnss_nmea_generic_resume(dev);
#endif
return ret;
}

#define GNSS_NMEA_GENERIC(inst) \
static struct gnss_nmea_generic_config gnss_nmea_generic_cfg_##inst = { \
.uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \
}; \
\
static struct gnss_nmea_generic_data gnss_nmea_generic_data_##inst = { \
.chat_delimiter = {'\r', '\n'}, \
.dynamic_separators_buf = {',', '*'}, \
}; \
\
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, 99, &gnss_api);

DT_INST_FOREACH_STATUS_OKAY(GNSS_NMEA_GENERIC)
9 changes: 9 additions & 0 deletions dts/bindings/gnss/gnss-nmea-generic.yaml
@@ -0,0 +1,9 @@
# Copyright 2023 Google LLC
# SPDX-License-Identifier: Apache-2.0

description: Generic GNSS NMEA receiver

compatible: "gnss-nmea-generic"

include:
- uart-device.yaml

0 comments on commit 2ae7a75

Please sign in to comment.