Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers: gnss: add a generic NMEA GNSS driver #65422

Merged
merged 2 commits into from Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions MAINTAINERS.yml
Expand Up @@ -1101,6 +1101,7 @@ Release Notes:
- doc/hardware/peripherals/gnss.rst
- drivers/gnss/
- include/zephyr/drivers/gnss.h
- tests/drivers/build_all/gnss/
- tests/drivers/gnss/
labels:
- "area: GNSS"
Expand Down
1 change: 1 addition & 0 deletions drivers/gnss/CMakeLists.txt
Expand Up @@ -7,4 +7,5 @@ zephyr_library_sources_ifdef(CONFIG_GNSS_DUMP gnss_dump.c)
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_NMEA_GENERIC gnss_nmea_generic.c)
zephyr_library_sources_ifdef(CONFIG_GNSS_QUECTEL_LCX6G gnss_quectel_lcx6g.c)
1 change: 1 addition & 0 deletions drivers/gnss/Kconfig
Expand Up @@ -64,6 +64,7 @@ module = GNSS
module-str = gnss
source "subsys/logging/Kconfig.template.log_config"

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

endif
26 changes: 26 additions & 0 deletions drivers/gnss/Kconfig.generic
@@ -0,0 +1,26 @@
# 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 MODEM_MODULES
select MODEM_BACKEND_UART
select MODEM_CHAT
select GNSS_PARSE
select GNSS_NMEA0183
select GNSS_NMEA0183_MATCH
help
Generic NMEA based GNSS device.

config GNSS_NMEA_GENERIC_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.
176 changes: 176 additions & 0 deletions drivers/gnss/gnss_nmea_generic.c
@@ -0,0 +1,176 @@
/*
* Copyright (c) 2023 Trackunit Corporation
* Copyright (c) 2023 Bjarki Arge Andreasen
* Copyright 2023 Google LLC
fabiobaltieri marked this conversation as resolved.
Show resolved Hide resolved
*
* 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/drivers/gpio.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

#define UART_RECV_BUF_SZ 128
#define CHAT_RECV_BUF_SZ 256
#define CHAT_ARGV_SZ 32
Comment on lines +26 to +28
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could always convert to Kconfig, but not blocking


struct gnss_nmea_generic_config {
const struct device *uart;
uint16_t nmea_timeout_ms;
};

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_RECV_BUF_SZ];

/* Modem chat */
struct modem_chat chat;
uint8_t chat_receive_buf[CHAT_RECV_BUF_SZ];
uint8_t *chat_argv[CHAT_ARGV_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 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;
}

static struct gnss_driver_api gnss_api = {
};

static int gnss_nmea_generic_init_nmea0183_match(const struct device *dev)
{
const struct gnss_nmea_generic_config *cfg = dev->config;
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
.timeout_ms = cfg->nmea_timeout_ms,
};

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

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),
.process_timeout = K_MSEC(2),
};

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

ret = gnss_nmea_generic_resume(dev);
if (ret < 0) {
return ret;
}

return 0;
}

#define GNSS_NMEA_GENERIC(inst) \
static struct gnss_nmea_generic_config gnss_nmea_generic_cfg_##inst = { \
.uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \
.nmea_timeout_ms = DT_INST_PROP(inst, nmea_timeout_ms), \
}; \
\
static struct gnss_nmea_generic_data gnss_nmea_generic_data_##inst; \
\
DEVICE_DT_INST_DEFINE(inst, gnss_nmea_generic_init, NULL, \
&gnss_nmea_generic_data_##inst, \
&gnss_nmea_generic_cfg_##inst, \
POST_KERNEL, CONFIG_GNSS_INIT_PRIORITY, &gnss_api);

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

description: |
Generic GNSS NMEA receiver

Implement a generic NMEA based GNSS device.

Example configuration:

&uart0 {
current-speed = <9600>;
...
gnss: gnss-nmea-generic {
compatible = "gnss-nmea-generic";
};
};

compatible: "gnss-nmea-generic"

include:
- uart-device.yaml

properties:
nmea-timeout-ms:
type: int
default: 500
description: |
Synchronization timeout for NMEA sentences. The NMEA parser is expecting
to receive a GGA and RMC sentences within this time frame to publish a
location data. Set accordingly to the UART datarate and location
reporting frequency. Defaults to 500ms if unspecified.
8 changes: 8 additions & 0 deletions tests/drivers/build_all/gnss/CMakeLists.txt
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(build_all)

FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
22 changes: 22 additions & 0 deletions tests/drivers/build_all/gnss/app.overlay
@@ -0,0 +1,22 @@
/*
* Copyright 2023 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

/ {
test {
#address-cells = <1>;
#size-cells = <1>;

test_uart: uart@0 {
compatible = "vnd,serial";
reg = <0x0 0x1000>;
status = "okay";

gnss: gnss-nmea-generic {
compatible = "gnss-nmea-generic";
};
};
};
};
3 changes: 3 additions & 0 deletions tests/drivers/build_all/gnss/prj.conf
@@ -0,0 +1,3 @@
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_GNSS=y
10 changes: 10 additions & 0 deletions tests/drivers/build_all/gnss/src/main.c
@@ -0,0 +1,10 @@
/*
* Copyright 2023 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

int main(void)
{
return 0;
}
8 changes: 8 additions & 0 deletions tests/drivers/build_all/gnss/testcase.yaml
@@ -0,0 +1,8 @@
common:
tags:
- drivers
- gnss
build_only: true
platform_allow: native_sim
tests:
drivers.gnss.default: {}