From 2945252b710c01a66131f792edac9f331f19f1a1 Mon Sep 17 00:00:00 2001 From: dongjiuzhu1 Date: Fri, 19 May 2023 23:11:04 +0800 Subject: [PATCH] sensor/gps: support gps driver model Signed-off-by: dongjiuzhu1 --- drivers/sensors/Kconfig | 7 + drivers/sensors/Make.defs | 4 + drivers/sensors/gps.c | 747 ++++++++++++++++++++++++++++++++++++ include/nuttx/sensors/gps.h | 233 +++++++++++ 4 files changed, 991 insertions(+) create mode 100644 drivers/sensors/gps.c create mode 100644 include/nuttx/sensors/gps.h diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index 57e0c6f8d2..047d71703d 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -24,6 +24,13 @@ config SENSORS_RPMSG ---help--- Allow application to read or control remote sensor device by rpmsg. +config SENSORS_GPS + bool "GPS Support" + default n + depends on GPSUTILS_MINMEA_LIB + ---help--- + Allow application to use gps by /dev/uorb/gps or /dev/ttyGPS + config SENSORS_WTGAHRS2 bool "Wtgahrs2 Sensor Support" default n diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index 2f94ff7ec6..048e93d4e6 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -32,6 +32,10 @@ ifeq ($(CONFIG_SENSORS_RPMSG),y) CSRCS += sensor_rpmsg.c endif +ifeq ($(CONFIG_SENSORS_GPS),y) + CSRCS += gps.c +endif + ifeq ($(CONFIG_SENSORS_WTGAHRS2),y) CSRCS += wtgahrs2.c endif diff --git a/drivers/sensors/gps.c b/drivers/sensors/gps.c new file mode 100644 index 0000000000..90c2375e72 --- /dev/null +++ b/drivers/sensors/gps.c @@ -0,0 +1,747 @@ +/**************************************************************************** + * drivers/sensors/gps.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define GPS_PATH_FMT "/dev/ttyGPS%d" + +#define GPS_IDX 0 +#define GPS_SATELLITE_IDX 1 +#define GPS_MAX_IDX 2 + +#define GPS_RECV_BUFFERSIZE 2048 +#define GPS_PARSE_BUFFERSIZE 256 + +#define GPS_KNOT_TO_KMH 1.852f +#define GPS_KMH_TO_MPS 3.6f + +#define GPS_FLAG_GGA (1 << 0) +#define GPS_FLAG_RMC (1 << 1) +#define GPS_FLAG_MARK (GPS_FLAG_GGA | GPS_FLAG_RMC) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes gps sensor info */ + +struct gps_sensor_s +{ + struct sensor_lowerhalf_s lower; + FAR void *upper; +}; + +/* This structure describes user info of gps */ + +struct gps_user_s +{ + FAR struct pollfd *fds; + size_t pos; +}; + +/* This structure describes the state of the upper half driver */ + +struct gps_upperhalf_s +{ + struct gps_sensor_s dev[GPS_MAX_IDX]; + FAR struct gps_lowerhalf_s *lower; + uint8_t crefs; + uint8_t flags; + mutex_t lock; + sem_t buffersem; + size_t parsenext; + char parsebuffer[GPS_PARSE_BUFFERSIZE]; + struct circbuf_s buffer; + struct sensor_gps gps; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int gps_activate(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, bool enable); +static int gps_set_interval(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR unsigned long *interval); +static int gps_control(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, int cmd, unsigned long arg); + +static int gps_open(FAR struct file *filep); +static int gps_close(FAR struct file *filep); +static ssize_t gps_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t gps_write(FAR struct file *filep, + FAR const char *buffer, size_t buflen); +static int gps_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); +static int gps_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct sensor_ops_s g_gps_sensor_ops = +{ + .activate = gps_activate, + .set_interval = gps_set_interval, + .control = gps_control, +}; + +static const struct file_operations g_gps_fops = +{ + gps_open, /* open */ + gps_close, /* close */ + gps_read, /* read */ + gps_write, /* write */ + NULL, /* seek */ + gps_ioctl, /* ioctl */ + NULL, /* mmap */ + NULL, /* truncate */ + gps_poll /* poll */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int gps_activate(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, bool enable) +{ + FAR struct gps_sensor_s *dev = (FAR struct gps_sensor_s *)lower; + FAR struct gps_upperhalf_s *upper = dev->upper; + int ret = OK; + + nxmutex_lock(&upper->lock); + if ((upper->crefs == 0 && enable) || (upper->crefs == 1 && !enable)) + { + ret = upper->lower->ops->activate(upper->lower, filep, enable); + if (ret >= 0) + { + upper->crefs += enable ? 1 : -1; + } + } + + nxmutex_unlock(&upper->lock); + return ret; +} + +static int gps_set_interval(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR unsigned long *interval) +{ + FAR struct gps_sensor_s *dev = (FAR struct gps_sensor_s *)lower; + FAR struct gps_upperhalf_s *upper = dev->upper; + + if (upper->lower->ops->set_interval == NULL) + { + return -ENOTTY; + } + + return upper->lower->ops->set_interval(upper->lower, filep, interval); +} + +static int gps_control(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct gps_sensor_s *dev = (FAR struct gps_sensor_s *)lower; + FAR struct gps_upperhalf_s *upper = dev->upper; + + if (upper->lower->ops->control == NULL) + { + return -ENOTTY; + } + + return upper->lower->ops->control(upper->lower, filep, cmd, arg); +} + +static int gps_open(FAR struct file *filep) +{ + FAR struct gps_upperhalf_s *upper; + FAR struct gps_user_s *user; + int ret = OK; + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + upper = filep->f_inode->i_private; + + user = kmm_zalloc(sizeof(struct gps_user_s)); + if (user == NULL) + { + return -ENOMEM; + } + + nxmutex_lock(&upper->lock); + if (upper->crefs >= 255) + { + ret = -EMFILE; + kmm_free(user); + goto out; + } + else + { + if (upper->crefs == 0) + { + ret = upper->lower->ops->activate(upper->lower, filep, true); + if (ret < 0) + { + kmm_free(user); + goto out; + } + } + + upper->crefs++; + } + + filep->f_priv = user; + user->pos = upper->buffer.head; + +out: + nxmutex_unlock(&upper->lock); + return ret; +} + +static int gps_close(FAR struct file *filep) +{ + FAR struct gps_upperhalf_s *upper; + FAR struct gps_user_s *user; + int ret = OK; + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL && filep->f_priv); + upper = filep->f_inode->i_private; + user = filep->f_priv; + + nxmutex_lock(&upper->lock); + if (upper->crefs > 0) + { + if (upper->crefs == 1) + { + ret = upper->lower->ops->activate(upper->lower, filep, false); + if (ret < 0) + { + goto out; + } + } + + upper->crefs--; + } + + kmm_free(user); + +out: + nxmutex_unlock(&upper->lock); + return ret; +} + +static ssize_t gps_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct gps_upperhalf_s *upper; + FAR struct gps_user_s *user; + ssize_t ret; + + if (buffer == NULL || buflen == 0) + { + return 0; + } + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + upper = filep->f_inode->i_private; + user = filep->f_priv; + + nxmutex_lock(&upper->lock); + if (user->pos < upper->buffer.tail) + { + user->pos = upper->buffer.tail; + } + +check: + if (upper->buffer.head - user->pos == 0) + { + if (filep->f_oflags & O_NONBLOCK) + { + ret = -EAGAIN; + goto out; + } + else + { + nxmutex_unlock(&upper->lock); + ret = nxsem_wait_uninterruptible(&upper->buffersem); + if (ret < 0) + { + return ret; + } + + nxmutex_lock(&upper->lock); + goto check; + } + } + + ret = circbuf_peekat(&upper->buffer, user->pos, + buffer, buflen); + user->pos += ret; + +out: + nxmutex_unlock(&upper->lock); + return ret; +} + +static ssize_t gps_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + FAR struct gps_upperhalf_s *upper; + int ret = -ENOTSUP; + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + upper = filep->f_inode->i_private; + + nxmutex_lock(&upper->lock); + if (upper->lower->ops->inject_data != NULL) + { + ret = upper->lower->ops->inject_data(upper->lower, filep, + buffer, buflen); + } + + nxmutex_unlock(&upper->lock); + return ret; +} + +static int gps_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct gps_upperhalf_s *upper; + int ret = -ENOTTY; + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + upper = filep->f_inode->i_private; + + nxmutex_lock(&upper->lock); + if (cmd == SNIOC_SET_INTERVAL) + { + if (upper->lower->ops->set_interval != NULL) + { + ret = upper->lower->ops->set_interval(upper->lower, filep, + (FAR unsigned long *)(uintptr_t)arg); + } + } + else if (upper->lower->ops->control != NULL) + { + ret = upper->lower->ops->control(upper->lower, filep, cmd, arg); + } + + nxmutex_unlock(&upper->lock); + return ret; +} + +static int gps_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct gps_upperhalf_s *upper; + FAR struct gps_user_s *user; + ssize_t ret = OK; + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + upper = filep->f_inode->i_private; + user = filep->f_priv; + + nxmutex_lock(&upper->lock); + if (setup) + { + if (user->fds) + { + ret = -ENOSPC; + goto out; + } + + user->fds = fds; + fds->priv = filep; + if (upper->buffer.head > user->pos) + { + poll_notify(&user->fds, 1, POLLIN); + } + } + else if (user->fds) + { + user->fds = NULL; + fds->priv = NULL; + } + +out: + nxmutex_unlock(&upper->lock); + return ret; +} + +static void gps_init_data(FAR struct sensor_gps *gps) +{ + gps->timestamp = ULONG_MAX; + gps->time_utc = ULONG_MAX; + gps->latitude = NAN; + gps->longitude = NAN; + gps->altitude = NAN; + gps->altitude_ellipsoid = NAN; + gps->eph = NAN; + gps->epv = NAN; + gps->hdop = NAN; + gps->pdop = NAN; + gps->vdop = NAN; + gps->ground_speed = NAN; + gps->course = NAN; + gps->satellites_used = UINT_MAX; +} + +static void gps_parse_nmea(FAR struct gps_upperhalf_s *upper, + FAR const char *nmea) +{ + FAR struct sensor_lowerhalf_s *lower; + + switch (minmea_sentence_id(nmea, false)) + { + case MINMEA_SENTENCE_GGA: + { + struct minmea_sentence_gga frame; + + if (minmea_parse_gga(&frame, nmea)) + { + upper->gps.altitude = minmea_tofloat(&frame.altitude); + upper->gps.altitude_ellipsoid = + minmea_tofloat(&frame.height); + upper->gps.hdop = minmea_tofloat(&frame.hdop); + upper->gps.satellites_used = frame.satellites_tracked; + upper->flags |= GPS_FLAG_GGA; + } + + break; + } + + case MINMEA_SENTENCE_RMC: + { + struct minmea_sentence_rmc frame; + struct tm t; + + if (minmea_parse_rmc(&frame, nmea)) + { + upper->gps.timestamp = sensor_get_timestamp(); + memset(&t, 0, sizeof(t)); + t.tm_sec = frame.time.seconds; + t.tm_min = frame.time.minutes; + t.tm_hour = frame.time.hours; + t.tm_mday = frame.date.day; + t.tm_mon = frame.date.month - 1; + t.tm_year = frame.date.year + 2000 - 1900; + t.tm_isdst = 0; + upper->gps.time_utc = mktime(&t); + upper->gps.latitude = minmea_tocoord(&frame.latitude); + upper->gps.longitude = minmea_tocoord(&frame.longitude); + upper->gps.ground_speed = minmea_tofloat(&frame.speed) * + GPS_KNOT_TO_KMH / GPS_KMH_TO_MPS; + upper->gps.course = minmea_tofloat(&frame.course); + if (frame.valid) + { + upper->flags |= GPS_FLAG_RMC; + } + } + + break; + } + + case MINMEA_SENTENCE_GST: + { + struct minmea_sentence_gst frame; + float lat_err; + float lon_err; + + if (minmea_parse_gst(&frame, nmea)) + { + lat_err = minmea_tofloat( + &frame.latitude_error_deviation); + lat_err *= lat_err; + lon_err = minmea_tofloat( + &frame.longitude_error_deviation); + lon_err *= lon_err; + upper->gps.eph = sqrtf(lat_err + lon_err); + upper->gps.epv = minmea_tofloat( + &frame.altitude_error_deviation); + } + + break; + } + + case MINMEA_SENTENCE_GSV: + { + struct minmea_sentence_gsv frame; + struct sensor_gps_satellite satellite; + + memset(&satellite, 0, sizeof(satellite)); + if (minmea_parse_gsv(&frame, nmea)) + { + satellite.timestamp = sensor_get_timestamp(); + satellite.count = frame.total_msgs; + satellite.satellites = frame.total_sats; + memcpy(satellite.info, frame.sats, + sizeof(satellite.info[0]) * 4); + lower = &upper->dev[GPS_SATELLITE_IDX].lower; + lower->push_event(lower->priv, &satellite, + sizeof(satellite)); + } + + break; + } + + default: + break; + } + + if (GPS_FLAG_MARK == upper->flags) + { + upper->flags &= ~GPS_FLAG_MARK; + lower = &upper->dev[GPS_IDX].lower; + lower->push_event(lower->priv, &upper->gps, sizeof(upper->gps)); + gps_init_data(&upper->gps); + } +} + +static void gps_parse(FAR struct gps_upperhalf_s *upper, + FAR const char *buffer, size_t bytes) +{ + bool newline = upper->parsenext != 0; + + for (; bytes > 0; buffer++, bytes--) + { + if (*buffer == '$') + { + newline = true; + } + + if (newline) + { + if (*buffer != '\r' && *buffer != '\n') + { + upper->parsebuffer[upper->parsenext++] = *buffer; + continue; + } + + upper->parsebuffer[upper->parsenext] = '\0'; + gps_parse_nmea(upper, upper->parsebuffer); + upper->parsenext = 0; + newline = false; + } + } +} + +static void gps_push_data(FAR void *priv, FAR const void *data, + size_t bytes, bool is_nmea) +{ + FAR struct gps_upperhalf_s *upper = priv; + int semcount; + + if (data == NULL || bytes == 0) + { + return; + } + + nxmutex_lock(&upper->lock); + if (is_nmea) + { + gps_parse(upper, data, bytes); + } + + circbuf_overwrite(&upper->buffer, data, bytes); + nxmutex_unlock(&upper->lock); + + nxsem_get_value(&upper->buffersem, &semcount); + while (semcount++ <= 0) + { + nxsem_post(&upper->buffersem); + } +} + +static void gps_push_event(FAR void *priv, FAR const void *data, + size_t bytes, int type) +{ + FAR struct gps_upperhalf_s *upper = priv; + FAR struct sensor_lowerhalf_s *lower; + + if (data == NULL || bytes == 0) + { + return; + } + + nxmutex_lock(&upper->lock); + if (type == SENSOR_TYPE_GPS) + { + lower = &upper->dev[GPS_IDX].lower; + lower->push_event(lower->priv, data, bytes); + } + else if (type == SENSOR_TYPE_GPS_SATELLITE) + { + lower = &upper->dev[GPS_SATELLITE_IDX].lower; + lower->push_event(lower->priv, data, bytes); + } + + nxmutex_unlock(&upper->lock); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: gps_register + * + * Description: + * This function binds an instance of a "lower half" gps driver with the + * "upper half" gps device and registers that device so that can be used + * by application code. + * + * Input Parameters: + * dev - A pointer to an instance of lower half gps driver. This + * instance is bound to the gps driver and must persist as long + * as the driver persists. + * devno - The user specifies which device of this type, from 0. If the + * devno alerady exists, -EEXIST will be returned. + * nbuffer - The number of events that the circular buffer can hold. + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int gps_register(FAR struct gps_lowerhalf_s *lower, int devno, + uint32_t nbuffer) +{ + FAR struct gps_upperhalf_s *upper; + FAR struct gps_sensor_s *dev; + char path[PATH_MAX]; + int ret; + + upper = kmm_zalloc(sizeof(struct gps_upperhalf_s)); + if (upper == NULL) + { + return -ENOMEM; + } + + lower->push_data = gps_push_data; + lower->push_event = gps_push_event; + lower->priv = upper; + upper->lower = lower; + + nxmutex_init(&upper->lock); + nxsem_init(&upper->buffersem, 0, 0); + gps_init_data(&upper->gps); + + /* GPS register */ + + dev = &upper->dev[GPS_IDX]; + dev->lower.ops = &g_gps_sensor_ops; + dev->lower.type = SENSOR_TYPE_GPS; + dev->lower.nbuffer = nbuffer; + dev->upper = upper; + ret = sensor_register(&dev->lower, devno); + if (ret < 0) + { + goto gps_err; + } + + /* Satellite register */ + + dev = &upper->dev[GPS_SATELLITE_IDX]; + dev->lower.ops = &g_gps_sensor_ops; + dev->lower.type = SENSOR_TYPE_GPS_SATELLITE; + dev->lower.nbuffer = nbuffer; + dev->upper = upper; + ret = sensor_register(&dev->lower, devno); + if (ret < 0) + { + goto satellite_err; + } + + ret = circbuf_init(&upper->buffer, NULL, GPS_RECV_BUFFERSIZE); + if (ret < 0) + { + goto circ_err; + } + + snprintf(path, PATH_MAX, GPS_PATH_FMT, devno); + ret = register_driver(path, &g_gps_fops, 0666, upper); + if (ret < 0) + { + goto driver_err; + } + + return ret; + +driver_err: + circbuf_uninit(&upper->buffer); +circ_err: + sensor_unregister(&upper->dev[GPS_SATELLITE_IDX].lower, devno); +satellite_err: + sensor_unregister(&upper->dev[GPS_IDX].lower, devno); +gps_err: + nxmutex_destroy(&upper->lock); + nxsem_destroy(&upper->buffersem); + kmm_free(upper); + return ret; +} + +/**************************************************************************** + * Name: gps_unregister + * + * Description: + * This function unregisters character node and releases all resource from + * upper half driver. This API corresponds to the gps_register. + * + * Input Parameters: + * dev - A pointer to an instance of lower half gps driver. This + * instance is bound to the gps driver and must persists as long + * as the driver persists. + * devno - The user specifies which device of this type, from 0. + ****************************************************************************/ + +void gps_unregister(FAR struct gps_lowerhalf_s *lower, int devno) +{ + FAR struct gps_upperhalf_s *upper = lower->priv; + char path[PATH_MAX]; + + sensor_unregister(&upper->dev[GPS_IDX].lower, devno); + sensor_unregister(&upper->dev[GPS_SATELLITE_IDX].lower, devno); + snprintf(path, PATH_MAX, GPS_PATH_FMT, devno); + unregister_driver(path); + nxsem_destroy(&upper->buffersem); + circbuf_uninit(&upper->buffer); + kmm_free(upper); +} diff --git a/include/nuttx/sensors/gps.h b/include/nuttx/sensors/gps.h new file mode 100644 index 0000000000..46df44098e --- /dev/null +++ b/include/nuttx/sensors/gps.h @@ -0,0 +1,233 @@ +/**************************************************************************** + * include/nuttx/sensors/gps.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_SENSORS_GPS_H +#define __INCLUDE_NUTTX_SENSORS_GPS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* The gps lower half driver interface */ + +struct gps_lowerhalf_s; +struct gps_ops_s +{ + /************************************************************************** + * Name: activate + * + * Description: + * Enable or disable gps device. when enable gps, gps will + * work in current mode(if not set, use default mode). when disable + * gps, it will disable sense path and stop work. + * + * Input Parameters: + * lower - The instance of lower half gps driver + * filep - The pointer of file, represents each user using the gps. + * enable - true(enable) and false(disable) + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*activate)(FAR struct gps_lowerhalf_s *lower, + FAR struct file *filep, bool enable); + + /************************************************************************** + * Name: set_interval + * + * Description: + * Set the gps output data period in microseconds for a given gps. + * If *period_us > max_delay it will be truncated to max_delay and if + * *period_us < min_delay it will be replaced by min_delay. + * + * The lower-half can update update *period_us to reflect the actual + * period in case the value is rounded up to nearest supported value. + * + * Before changing the interval, you need to push the prepared data to + * ensure that they are not lost. + * + * Input Parameters: + * lower - The instance of lower half gps driver. + * filep - The pointer of file, represents each user using gps. + * period_us - the time between samples, in us, it may be overwrite by + * lower half driver. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*set_interval)(FAR struct gps_lowerhalf_s *lower, + FAR struct file *filep, + FAR unsigned long *period_us); + + /************************************************************************** + * Name: control + * + * With this method, the user can set some special config for the gps, + * such as changing the custom mode, setting the custom resolution, reset, + * etc, which are all parsed and implemented by lower half driver. + * + * Input Parameters: + * lower - The instance of lower half gps driver. + * filep - The pointer of file, represents each user using gps. + * cmd - The special cmd for gps. + * arg - The parameters associated with cmd. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * -ENOTTY - The cmd don't support. + * + **************************************************************************/ + + CODE int (*control)(FAR struct gps_lowerhalf_s *lower, + FAR struct file *filep, + int cmd, unsigned long arg); + + /************************************************************************** + * Name: inject_data + * + * With this method, the user can inject some data to gps driver, + * such as inject lto data, utc data, etc. + * + * Input Parameters: + * lower - The instance of lower half gps driver. + * filep - The pointer of file, represents each user using gps. + * buffer - The buffer of inject data. + * buflen - The length of buffer. + * + * Returned Value: + * The written bytes returned on success; + * A negated errno value on failure. + * + **************************************************************************/ + + CODE ssize_t (*inject_data)(FAR struct gps_lowerhalf_s *lower, + FAR struct file *filep, + FAR const void *buffer, size_t buflen); +}; + +/* This structure is the generic form of state structure used by lower half + * gps driver. + */ + +typedef CODE void (*gps_push_data_t)(FAR void *priv, FAR const void *data, + size_t bytes, bool is_nmea); + +typedef CODE void (*gps_push_event_t)(FAR void *priv, FAR const void *data, + size_t bytes, int type); + +struct gps_lowerhalf_s +{ + /* The lower half gps driver operations */ + + FAR const struct gps_ops_s *ops; + + /* Lower half driver pushes raw data by calling this function. + * It is provided by upper half driver to lower half driver, + * if paramenter is_nmea is true, the data includes nmea message. + */ + + gps_push_data_t push_data; + + /* Lower half driver pushes gps event by calling this function. + * It is provided by upper half driver to lower half driver. + * lower half can use type to description the data type, eg: + * SENSOR_TYPE_GPS, SENSOR_TYPE_GPS_SATELLITE + */ + + gps_push_event_t push_event; + + /* The private opaque pointer to be passed to upper-layer during callback */ + + FAR void *priv; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: gps_register + * + * Description: + * This function binds an instance of a "lower half" gps driver with the + * "upper half" gps device and registers that device so that can be used + * by application code. + * + * Input Parameters: + * dev - A pointer to an instance of lower half gps driver. This + * instance is bound to the gps driver and must persist as long + * as the driver persists. + * devno - The user specifies which device of this type, from 0. If the + * devno alerady exists, -EEXIST will be returned. + * nbuffer - The number of events that the circular buffer can hold. + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int gps_register(FAR struct gps_lowerhalf_s *dev, int devno, + uint32_t nbuffer); + +/**************************************************************************** + * Name: gps_unregister + * + * Description: + * This function unregisters character node and releases all resource from + * upper half driver. This API corresponds to the gps_register. + * + * Input Parameters: + * dev - A pointer to an instance of lower half gps driver. This + * instance is bound to the gps driver and must persists as long + * as the driver persists. + * devno - The user specifies which device of this type, from 0. + ****************************************************************************/ + +void gps_unregister(FAR struct gps_lowerhalf_s *dev, int devno); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_SENSORS_GPS_H */