This is an automated email from the ASF dual-hosted git repository. protobits pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit fa09c6a8bc98be155611a4e3047c46a88e56a79f Author: dongjiuzhu <[email protected]> AuthorDate: Mon Oct 19 21:24:13 2020 +0800 driver/sensor: add unified management for sensor Signed-off-by: dongjiuzhu <[email protected]> --- drivers/sensors/Make.defs | 2 + drivers/sensors/sensor.c | 756 +++++++++++++++++++++++++++++++++++++++++ include/nuttx/sensors/ioctl.h | 46 +++ include/nuttx/sensors/sensor.h | 615 +++++++++++++++++++++++++++++++++ 4 files changed, 1419 insertions(+) diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index 53d2132..bf87397 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -37,6 +37,8 @@ ifeq ($(CONFIG_SENSORS),y) +CSRCS += sensor.c + ifeq ($(CONFIG_SENSORS_HCSR04),y) CSRCS += hc_sr04.c endif diff --git a/drivers/sensors/sensor.c b/drivers/sensors/sensor.c new file mode 100644 index 0000000..99ab9e7 --- /dev/null +++ b/drivers/sensors/sensor.c @@ -0,0 +1,756 @@ +/**************************************************************************** + * drivers/sensors/sensor.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 <nuttx/config.h> + +#include <sys/types.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <debug.h> + +#include <poll.h> +#include <fcntl.h> +#include <nuttx/kmalloc.h> +#include <nuttx/sensors/sensor.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Device naming ************************************************************/ + +#define ROUNDUP(x, esize) ((x + (esize - 1)) / (esize)) * (esize) +#define DEVNAME_FMT "/dev/sensor/%s%s%d" +#define DEVNAME_MAX 64 +#define DEVNAME_UNCAL "_uncal" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes sensor info */ + +struct sensor_info +{ + uint8_t idx; + const uint8_t esize; + FAR const char *name; +}; + +/* This structure describes sensor circular buffer */ + +struct sensor_buffer_s +{ + uint32_t head; + uint32_t tail; + uint32_t size; + FAR void *data; +}; + +/* This structure describes the state of the upper half driver */ + +struct sensor_upperhalf_s +{ + FAR struct sensor_lowerhalf_s *lower; /* the handle of lower half driver */ + FAR struct sensor_buffer_s *buffer; /* The circualr buffer of sensor device */ + FAR struct pollfd *fds; /* poll structures of threads waiting for driver events. */ + uint8_t idx; /* The index number of node path */ + uint8_t crefs; /* Number of times the device has been opened */ + sem_t exclsem; /* Manages exclusive access to file operations */ + sem_t buffersem; /* Wakeup user waiting for data in circular buffer */ + bool enabled; /* The status of sensor enable or disable */ + unsigned int interval; /* The sample interval for sensor, in us */ + unsigned int latency; /* The batch latency for sensor, in us */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void sensor_pollnotify(FAR struct sensor_upperhalf_s *upper, + pollevent_t eventset); +static int sensor_open(FAR struct file *filep); +static int sensor_close(FAR struct file *filep); +static ssize_t sensor_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static int sensor_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); +static int sensor_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct sensor_info g_sensor_info[] = +{ + {0, sizeof(struct sensor_event_accel), "accel"}, + {0, sizeof(struct sensor_event_mag), "mag"}, + {0, sizeof(struct sensor_event_gyro), "gyro"}, + {0, sizeof(struct sensor_event_light), "light"}, + {0, sizeof(struct sensor_event_baro), "baro"}, + {0, sizeof(struct sensor_event_prox), "prox"}, + {0, sizeof(struct sensor_event_humi), "humi"}, + {0, sizeof(struct sensor_event_temp), "temp"}, + {0, sizeof(struct sensor_event_rgb), "rgb"}, + {0, sizeof(struct sensor_event_hall), "hall"}, + {0, sizeof(struct sensor_event_ir), "ir"}, + {0, sizeof(struct sensor_event_gps), "gps"}, + {0, sizeof(struct sensor_event_uv), "uv"}, + {0, sizeof(struct sensor_event_noise), "noise"}, + {0, sizeof(struct sensor_event_pm25), "pm25"}, + {0, sizeof(struct sensor_event_pm1p0), "pm1p0"}, + {0, sizeof(struct sensor_event_pm10), "pm10"}, + {0, sizeof(struct sensor_event_co2), "co2"}, + {0, sizeof(struct sensor_event_hcho), "hcho"}, + {0, sizeof(struct sensor_event_tvoc), "tvoc"}, + {0, sizeof(struct sensor_event_ph), "ph"}, + {0, sizeof(struct sensor_event_dust), "dust"}, + {0, sizeof(struct sensor_event_hrate), "hrate"}, + {0, sizeof(struct sensor_event_hbeat), "hbeat"}, +}; + +static const struct file_operations g_sensor_fops = +{ + sensor_open, /* open */ + sensor_close, /* close */ + sensor_read, /* read */ + NULL, /* write */ + NULL, /* seek */ + sensor_ioctl, /* ioctl */ + sensor_poll /* poll */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static bool sensor_buffer_is_empty(FAR struct sensor_buffer_s *buffer) +{ + return buffer->head == buffer->tail; +} + +static uint32_t sensor_buffer_len(FAR struct sensor_buffer_s *buffer) +{ + return buffer->head - buffer->tail; +} + +static uint32_t sensor_buffer_unused(FAR struct sensor_buffer_s *buffer) +{ + return buffer->size - sensor_buffer_len(buffer); +} + +static void sensor_buffer_reset(FAR struct sensor_buffer_s *buffer) +{ + buffer->head = buffer->tail = 0; +} + +static void sensor_buffer_push(FAR struct sensor_buffer_s *buffer, + FAR const void *data, uint32_t bytes) +{ + uint32_t space = sensor_buffer_unused(buffer); + uint32_t off = buffer->head % buffer->size; + uint32_t overwrite = 0; + + /* If buffer is full or there is not enough space, overwriting of old + * data will occur, we should move tail point after pushing data + * completely. + */ + + if (bytes > buffer->size) + { + data += bytes - buffer->size; + bytes = buffer->size; + } + + if (bytes > space) + { + overwrite = bytes - space; + } + + space = buffer->size - off; + if (bytes < space) + { + space = bytes; + } + + memcpy(buffer->data + off, data, space); + memcpy(buffer->data, data + space, bytes - space); + buffer->head += bytes; + buffer->tail += overwrite; +} + +static uint32_t sensor_buffer_pop(FAR struct sensor_buffer_s *buffer, + FAR void *data, uint32_t bytes) +{ + uint32_t len = sensor_buffer_len(buffer); + uint32_t off; + + if (bytes > len) + { + bytes = len; + } + + if (!data) + { + goto skip; + } + + off = buffer->tail % buffer->size; + len = buffer->size - off; + if (bytes < len) + { + len = bytes; + } + + memcpy(data, buffer->data + off, len); + memcpy(data + len, buffer->data, bytes - len); + +skip: + buffer->tail += bytes; + + return bytes; +} + +static int sensor_buffer_resize(FAR struct sensor_buffer_s **buffer, + int type, uint32_t bytes) +{ + FAR struct sensor_buffer_s *tmp; + int len = sensor_buffer_len(*buffer); + int skipped; + + bytes = ROUNDUP(bytes, g_sensor_info[type].esize); + tmp = kmm_malloc(sizeof(*tmp) + bytes); + if (!tmp) + { + snerr("Faild to alloc memory for circular buffer\n"); + return -ENOMEM; + } + + tmp->data = tmp + 1; + + skipped = (bytes > len) ? 0 : len - bytes; + len -= skipped; + sensor_buffer_pop(*buffer, NULL, skipped); + sensor_buffer_pop(*buffer, tmp->data, len); + + tmp->size = bytes; + tmp->head = len; + tmp->tail = 0; + + kmm_free(*buffer); + *buffer = tmp; + + return 0; +} + +static int sensor_buffer_create(FAR struct sensor_buffer_s **buffer, + int type, uint32_t bytes) +{ + FAR struct sensor_buffer_s *tmp; + + bytes = ROUNDUP(bytes, g_sensor_info[type].esize); + + tmp = kmm_malloc(sizeof(*tmp) + bytes); + if (!tmp) + { + snerr("Faild to malloc memory for circular buffer\n"); + return -ENOMEM; + } + + tmp->size = bytes; + tmp->data = tmp + 1; + tmp->head = 0; + tmp->tail = 0; + + *buffer = tmp; + + return 0; +} + +static void sensor_buffer_release(FAR struct sensor_buffer_s *buffer) +{ + kmm_free(buffer); +} + +static void sensor_pollnotify(FAR struct sensor_upperhalf_s *upper, + pollevent_t eventset) +{ + int semcount; + + if (upper->fds) + { + upper->fds->revents |= (upper->fds->events & eventset); + + if (upper->fds->revents != 0) + { + sninfo("Report events: %02x\n", upper->fds->revents); + + nxsem_get_value(upper->fds->sem, &semcount); + if (semcount < 1) + { + nxsem_post(upper->fds->sem); + } + } + } +} + +static int sensor_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct sensor_upperhalf_s *upper = inode->i_private; + int ret; + + ret = nxsem_wait(&upper->exclsem); + if (ret < 0) + { + return ret; + } + + if (upper->crefs) + { + ret = -EBUSY; + } + else + { + upper->crefs++; + upper->fds = NULL; + sensor_buffer_reset(upper->buffer); + } + + nxsem_post(&upper->exclsem); + return ret; +} + +static int sensor_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct sensor_upperhalf_s *upper = inode->i_private; + FAR struct sensor_lowerhalf_s *lower = upper->lower; + int ret; + + ret = nxsem_wait(&upper->exclsem); + if (ret < 0) + { + return ret; + } + + if (--upper->crefs <= 0 && upper->enabled) + { + ret = lower->ops->activate ? + lower->ops->activate(lower, false) : -ENOTSUP; + if (ret >= 0) + { + upper->enabled = false; + } + } + + nxsem_post(&upper->exclsem); + return ret; +} + +static ssize_t sensor_read(FAR struct file *filep, FAR char *buffer, + size_t len) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct sensor_upperhalf_s *upper = inode->i_private; + FAR struct sensor_lowerhalf_s *lower = upper->lower; + ssize_t ret; + + if (!buffer || !len) + { + return -EINVAL; + } + + ret = nxsem_wait(&upper->exclsem); + if (ret < 0) + { + return ret; + } + + /* We must make sure that when the semaphore is equal to 1, there must + * be events avaliable in the buffer, so we use a while statement to + * synchronize this case that other read operations consume events + * that have just entered the buffer. + */ + + while (sensor_buffer_is_empty(upper->buffer)) + { + if (filep->f_oflags & O_NONBLOCK) + { + ret = -EAGAIN; + goto again; + } + else + { + nxsem_post(&upper->exclsem); + ret = nxsem_wait_uninterruptible(&upper->buffersem); + if (ret) + { + return ret; + } + + ret = nxsem_wait(&upper->exclsem); + if (ret < 0) + { + return ret; + } + } + } + + ret = sensor_buffer_pop(upper->buffer, buffer, len); + + /* Release some buffer space when current mode isn't batch mode and last + * mode is batch mode, and the number of bytes avaliable in buffer is + * less than the number of bytes origin. + */ + + if (upper->latency == 0 && + upper->buffer->size > lower->buffer_bytes && + sensor_buffer_len(upper->buffer) <= lower->buffer_bytes) + { + sensor_buffer_resize(&upper->buffer, lower->type, lower->buffer_bytes); + } + +again: + nxsem_post(&upper->exclsem); + return ret; +} + +static int sensor_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct sensor_upperhalf_s *upper = inode->i_private; + FAR struct sensor_lowerhalf_s *lower = upper->lower; + FAR unsigned int *val = (unsigned int *)(uintptr_t)arg; + int ret; + + sninfo("cmd=%x arg=%08x\n", cmd, arg); + + ret = nxsem_wait(&upper->exclsem); + if (ret < 0) + { + return ret; + } + + switch (cmd) + { + case SNIOC_ACTIVATE: + { + if (upper->enabled == !!arg) + { + break; + } + + ret = lower->ops->activate ? + lower->ops->activate(lower, !!arg) : -ENOTSUP; + if (ret >= 0) + { + upper->enabled = !!arg; + } + } + break; + + case SNIOC_SET_INTERVAL: + { + if (upper->interval == *val) + { + break; + } + + ret = lower->ops->set_interval ? + lower->ops->set_interval(lower, val) : -ENOTSUP; + if (ret >= 0) + { + upper->interval = *val; + } + } + break; + + case SNIOC_BATCH: + { + if (upper->interval == 0) + { + ret = -EINVAL; + break; + } + + if (upper->latency == *val) + { + break; + } + + ret = lower->ops->batch ? + lower->ops->batch(lower, val) : -ENOTSUP; + if (ret >= 0) + { + upper->latency = *val; + if (*val != 0) + { + /* Adjust length of buffer in batch mode */ + + sensor_buffer_resize(&upper->buffer, lower->type, + lower->buffer_bytes + + ROUNDUP(*val, upper->interval) / + upper->interval * + g_sensor_info[lower->type].esize); + } + } + } + break; + + case SNIOC_GET_NEVENTBUF: + { + *val = lower->buffer_bytes / g_sensor_info[lower->type].esize; + } + break; + + default: + ret = -ENOTTY; + break; + } + + nxsem_post(&upper->exclsem); + return ret; +} + +static int sensor_poll(FAR struct file *filep, + struct pollfd *fds, bool setup) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct sensor_upperhalf_s *upper = inode->i_private; + pollevent_t eventset = 0; + int ret; + + ret = nxsem_wait(&upper->exclsem); + if (ret < 0) + { + return ret; + } + + if (setup) + { + if (upper->fds) + { + ret = -EBUSY; + goto errout; + } + + upper->fds = fds; + fds->priv = &upper->fds; + + if (!sensor_buffer_is_empty(upper->buffer)) + { + eventset |= (fds->events & POLLIN); + } + + if (eventset) + { + sensor_pollnotify(upper, eventset); + } + } + else if (fds->priv != NULL) + { + FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv; + + if (!slot) + { + ret = -EIO; + goto errout; + } + + *slot = NULL; + fds->priv = NULL; + } + +errout: + nxsem_post(&upper->exclsem); + return ret; +} + +static void sensor_push_event(FAR void *priv, FAR const void *data, + uint32_t bytes) +{ + FAR struct sensor_upperhalf_s *upper = priv; + int semcount; + + if (!bytes || nxsem_wait(&upper->exclsem) < 0) + { + return; + } + + sensor_buffer_push(upper->buffer, data, bytes); + sensor_pollnotify(upper, POLLIN); + nxsem_get_value(&upper->buffersem, &semcount); + if (semcount < 1) + { + nxsem_post(&upper->buffersem); + } + + nxsem_post(&upper->exclsem); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sensor_register + * + * Description: + * This function binds an instance of a "lower half" Sensor driver with the + * "upper half" Sensor device and registers that device so that can be used + * by application code. + * + * We will register the chararter device by node name format based on the + * type of sensor. Multiple types of the same type are distinguished by + * numbers. eg: accel0, accel1 + * + * Input Parameters: + * dev - A pointer to an instance of lower half sensor driver. This + * instance is bound to the sensor driver and must persists as long + * as the driver persists. + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int sensor_register(FAR struct sensor_lowerhalf_s *lower) +{ + FAR struct sensor_upperhalf_s *upper; + char path[DEVNAME_MAX]; + int ret = -EINVAL; + + DEBUGASSERT(lower != NULL); + + if (lower->type >= SENSOR_TYPE_COUNT) + { + snerr("ERROR: Type is invalid\n"); + return ret; + } + + /* Allocate the upper-half data structure */ + + upper = kmm_zalloc(sizeof(struct sensor_upperhalf_s)); + if (!upper) + { + snerr("ERROR: Allocation failed\n"); + return -ENOMEM; + } + + /* Initialize the upper-half data structure */ + + upper->lower = lower; + + nxsem_init(&upper->exclsem, 0, 1); + nxsem_init(&upper->buffersem, 0, 0); + + nxsem_set_protocol(&upper->buffersem, SEM_PRIO_NONE); + + /* Bind the lower half data structure member */ + + lower->priv = upper; + lower->push_event = sensor_push_event; + + if (!lower->buffer_bytes) + { + lower->buffer_bytes = g_sensor_info[lower->type].esize; + } + + /* Initialize sensor buffer */ + + ret = sensor_buffer_create(&upper->buffer, + lower->type, lower->buffer_bytes); + if (ret) + { + goto buf_err; + } + + upper->idx = g_sensor_info[lower->type].idx++; + snprintf(path, DEVNAME_MAX, DEVNAME_FMT, + g_sensor_info[lower->type].name, + lower->uncalibrated ? DEVNAME_UNCAL : "", + upper->idx); + sninfo("Registering %s\n", path); + + ret = register_driver(path, &g_sensor_fops, 0666, upper); + if (ret) + { + goto drv_err; + } + + return ret; + +drv_err: + sensor_buffer_release(upper->buffer); + g_sensor_info[lower->type].idx--; +buf_err: + nxsem_destroy(&upper->exclsem); + nxsem_destroy(&upper->buffersem); + + kmm_free(upper); + + return ret; +} + +/**************************************************************************** + * Name: sensor_unregister + * + * Description: + * This function unregister character node and release all resource about + * upper half driver. + * + * Input Parameters: + * dev - A pointer to an instance of lower half sensor driver. This + * instance is bound to the sensor driver and must persists as long + * as the driver persists. + ****************************************************************************/ + +void sensor_unregister(FAR struct sensor_lowerhalf_s *lower) +{ + FAR struct sensor_upperhalf_s *upper; + char path[DEVNAME_MAX]; + + DEBUGASSERT(lower != NULL); + DEBUGASSERT(lower->priv != NULL); + + upper = lower->priv; + + snprintf(path, DEVNAME_MAX, DEVNAME_FMT, + g_sensor_info[lower->type].name, + lower->uncalibrated ? DEVNAME_UNCAL : "", + upper->idx); + sninfo("UnRegistering %s\n", path); + unregister_driver(path); + + nxsem_destroy(&upper->exclsem); + nxsem_destroy(&upper->buffersem); + + sensor_buffer_release(upper->buffer); + kmm_free(upper); +} diff --git a/include/nuttx/sensors/ioctl.h b/include/nuttx/sensors/ioctl.h index 889b2e2..39f09fc 100644 --- a/include/nuttx/sensors/ioctl.h +++ b/include/nuttx/sensors/ioctl.h @@ -181,8 +181,11 @@ /* IOCTL commands unique to the SCD30 */ /* SNIOC_RESET */ /* Arg: None */ + /* SNIOC_START */ /* Arg: None */ + /* SNIOC_STOP */ /* Arg: None */ + /* SNIOC_READ_CONVERT_DATA */ /* Arg: struct scd30_conv_data_s* */ #define SNIOC_SET_INTERVAL _SNIOC(0x0054) /* Arg: uint16_t value (seconds) */ #define SNIOC_SET_TEMP_OFFSET _SNIOC(0x0055) /* Arg: uint16_t value (0.01 Kelvin) */ @@ -194,9 +197,13 @@ /* IOCTL commands unique to the SGP30 */ /* SNIOC_RESET */ /* Arg: None */ + /* SNIOC_START_SELFTEST */ /* Arg: None */ + /* SNIOC_READ_CONVERT_DATA */ /* Arg: struct sgp30_conv_data_s* */ + /* SNIOC_READ_RAW_DATA */ /* Arg: struct sgp30_raw_data_s* */ + #define SNIOC_GET_BASELINE _SNIOC(0x005a) /* Arg: struct sgp30_baseline_s* */ #define SNIOC_SET_BASELINE _SNIOC(0x005b) /* Arg: const struct sgp30_baseline_s* */ #define SNIOC_SET_HUMIDITY _SNIOC(0x005c) /* Arg: uint32_t value (mg/m³) */ @@ -204,9 +211,13 @@ /* IOCTL commands unique to the SPS30 */ /* SNIOC_RESET */ /* Arg: None */ + /* SNIOC_START */ /* Arg: None */ + /* SNIOC_STOP */ /* Arg: None */ + /* SNIOC_READ_CONVERT_DATA */ /* Arg: struct sps30_conv_data_s* */ + #define SNIOC_SET_CLEAN_INTERVAL _SNIOC(0x005d) /* Arg: uint32_t value (seconds) */ #define SNIOC_START_FAN_CLEANING _SNIOC(0x005e) /* Arg: None */ @@ -227,4 +238,39 @@ #define SNIOC_SET_RESOLUTION _SNIOC(0x0065) /* Arg: uint8_t value */ #define SNIOC_SET_RANGE _SNIOC(0x0066) /* Arg: uint8_t value */ +/* Command: SNIOC_ACTIVATE + * Description: Enable or disable sensor + * Argument: true or false. + */ + +#define SNIOC_ACTIVATE _SNIOC(0x0067) + +/* Command: SNIOC_SET_INTERVAL + * Description: Set interval between samples + * Argument: This is the interval pointer, in microseconds + */ + +#define SNIOC_SET_INTERVAL _SNIOC(0x0068) + +/* Command: SNIOC_BATCH + * Description: Set batch latency between batch data. + * Argument: This is the latency pointer, in microseconds + */ + +#define SNIOC_BATCH _SNIOC(0x0069) + +/* Command: SNIOC_GET_NEVENTBUF + * Description: the number of sensor events that sensor buffer of upper half holds. + * Argument: This is the number of events pointer, is output parameter. + * Note: We need to tell the application layer number of sensor events in + * sensor buffer. This buffer is used to solve the problem that the + * application layer can't read the sensor event in time. We recommend + * the number of sensor events in application layer's buffer is same as + * result by call this function. + * This is number of sensor events rather than the length of buffer. + * See sensor.h(struct sensor_lower_half_s buffer_bytes). + */ + +#define SNIOC_GET_NEVENTBUF _SNIOC(0x0070) + #endif /* __INCLUDE_NUTTX_SENSORS_IOCTL_H */ diff --git a/include/nuttx/sensors/sensor.h b/include/nuttx/sensors/sensor.h new file mode 100644 index 0000000..6d108b4 --- /dev/null +++ b/include/nuttx/sensors/sensor.h @@ -0,0 +1,615 @@ +/**************************************************************************** + * include/nuttx/sensors/sensors.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_SENSOR_H +#define __INCLUDE_NUTTX_SENSORS_SENSOR_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> +#include <time.h> + +#include <nuttx/sensors/ioctl.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* sensor type definition */ + +/* Accelerometer + * All values are in SI units (m/s^2), and measure the acceleration of the + * device minus the acceleration dut to gravity. + */ + +#define SENSOR_TYPE_ACCELEROMETER 0 + +/* Magneric Field + * All values are in micro-Tesla (uT) and measure the geomagnetic field + * in X, Y and Z axis. + */ + +#define SENSOR_TYPE_MAGNETIC_FIELD 1 + +/* Gyroscope + * All values are in radians/second and measure the rate of rotation around + * the X, Y and Z axis. + */ + +#define SENSOR_TYPE_GYROSCOPE 2 + +/* Ambient Light + * The ambient light sensor value is returned in SI units lux. + */ + +#define SENSOR_TYPE_LIGHT 3 + +/* Barometer + * All values are in hectopascal (hPa) and measure the athmospheric pressure + * we can calculate altitude by perssure. + */ + +#define SENSOR_TYPE_BAROMETER 4 + +/* Proximity + * The values correspond to the distance to the nearest + * object in centimeters. + */ + +#define SENSOR_TYPE_PROXIMITY 5 + +/* Relative Humidity + * A relative humidity sensor measure relative ambient air humidity and + * return a value in percent. + */ + +#define SENSOR_TYPE_RELATIVE_HUMIDITY 6 + +/* Ambient Temperature + * The ambient (room) temperature in degree Celsius + */ + +#define SENSOR_TYPE_AMBIENT_TEMPERATURE 7 + +/* RGB + * We use these values of RGB to weighted to obtain the color of LED. + * These values is in unit percent. + */ + +#define SENSOR_TYPE_RGB 8 + +/* Hall + * All values are in bool type (0 or 1) and it often is used to as switch. + * A values of 1 indicates that switch on. + */ + +#define SENSOR_TYPE_HALL 9 + +/* IR (Infrared Ray) + * This sensor can detect a human approach and outputs a signal from + * interrupt pins. This sensor value is in lux. + */ + +#define SENSOR_TYPE_IR 10 + +/* GPS + * A sensor of this type returns gps data. Include year, month, day, + * hour, minutes, seconds, altitude, longitude, latitude. + */ + +#define SENSOR_TYPE_GPS 11 + +/* Ultraviolet light sensor + * This sensor can identify the UV index in ambient light help people + * to effectively protect themselves from sunburns, cancer or eye damage. + * This value range is 0 - 15. + */ +#define SENSOR_TYPE_ULTRAVIOLET 12 + +/* Noise Loudness + * A sensor of this type returns the loudness of noise in SI units (db) + */ + +#define SENSOR_TYPE_NOISE 13 + +/* PM25 + * A sensor of this type returns the content of pm2.5 in the air + * This value is in SI units (ug/m^3) + */ + +#define SENSOR_TYPE_PM25 14 + +/* PM1P0 + * A sensor of this type returns the content of pm1.0 in the air + * This value is in SI units (ug/m^3) + */ + +#define SENSOR_TYPE_PM1P0 15 + +/* PM10 + * A sensor of this type returns the content of pm10 in the air + * This value is in SI units (ug/m^3) + */ + +#define SENSOR_TYPE_PM10 16 + +/* CO2 + * A sensor of this type returns the content of CO2 in the air + * This vaule is in units (ppm-part per million). + */ + +#define SENSOR_TYPE_CO2 17 + +/* HCHO + * The HCHO pollution is an important indicator of household air + * pollution. This value is in units (ppm-part per million). + */ + +#define SENSOR_TYPE_HCHO 18 + +/* TVOC (total volatile organic compounds) + * The indoor TVOC is cause indoor air pollution is one of the + * main reasons why. This value is in units (ppb-part per billion). + */ + +#define SENSOR_TYPE_TVOC 19 + +/* PH + * The acid-base degree describes the strength of the aqueous + * solution, expressed by pH. In the thermodynamic standard + * condition, the aqueous solution with pH=7 is neutral, + * pH<7 is acidic, and pH>7 is alkaline. + */ + +#define SENSOR_TYPE_PH 20 + +/* Dust + * A sensor of this type returns the content of dust in the air + * values is in ug/m^3. + */ + +#define SENSOR_TYPE_DUST 21 + +/* Heart Rate + * A sensor of this type returns the current heart rate. + * Current heart rate is in beats per minute (BPM). + */ + +#define SENSOR_TYPE_HEART_RATE 22 + +/* Heart Beat + * A sensor of this type returns an event evetytime + * a hear beat peek is detected. Peak here ideally corresponds + * to the positive peak in the QRS complex of and ECG signal. + */ + +#define SENSOR_TYPE_HEART_BEAT 23 + +/* The total number of sensor */ + +#define SENSOR_TYPE_COUNT 24 + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +static inline uint64_t sensor_get_timestamp(void) +{ + struct timespec ts; + +#ifdef CONFIG_CLOCK_MONOTONIC + clock_gettime(CLOCK_MONOTONIC, &ts); +#else + clock_gettime(CLOCK_REALTIME, &ts); +#endif + + return 1000000ull * ts.tv_sec + ts.tv_nsec / 1000; +} + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* These structures prefixed with sensor_event are sensor data, and member + * that are not used must be written as NAN or INT_MIN/INT_MAX, than + * reported. + */ + +struct sensor_event_accel /* Type: Accerometer */ +{ + uint64_t timestamp; /* Units is microseconds */ + float x; /* Axis X in m/s^2 */ + float y; /* Axis Y in m/s^2 */ + float z; /* Axis Z in m/s^2 */ + float temperature; /* Temperature in degrees celsius */ +}; + +struct sensor_event_gyro /* Type: Gyroscope */ +{ + uint64_t timestamp; /* Units is microseconds */ + float x; /* Axis X in rad/s */ + float y; /* Axis Y in rad/s */ + float z; /* Axis Z in rad/s */ + float temperature; /* Temperature in degrees celsius */ +}; + +struct sensor_event_mag /* Type: Magnetic Field */ +{ + uint64_t timestamp; /* Units is microseconds */ + float x; /* Axis X in Gauss or micro Tesla (uT) */ + float y; /* Axis Y in Gauss or micro Tesla (uT) */ + float z; /* Axis Z in Gauss or micro Tesla (uT) */ + float temperature; /* Temperature in degrees celsius */ +}; + +struct sensor_event_baro /* Type: Barometer */ +{ + uint64_t timestamp; /* Units is microseconds */ + float pressure; /* pressure measurement in millibar or hpa */ + float temperature; /* Temperature in degrees celsius */ +}; + +struct sensor_event_prox /* Type: proximity */ +{ + uint64_t timestamp; /* Units is microseconds */ + float proximity; /* distance to the nearest object in centimeters */ +}; + +struct sensor_event_light /* Type: Light */ +{ + uint64_t timestamp; /* Units is microseconds */ + float light; /* in SI lux units */ +}; + +struct sensor_event_humi /* Type: Relative Humidity */ +{ + uint64_t timestamp; /* Units is microseconds */ + float humidity; /* in percent */ +}; + +struct sensor_event_temp /* Type: Ambient Temperature */ +{ + uint64_t timestamp; /* Units is microseconds */ + float temperature; /* Temperature in degrees celsius */ +}; + +struct sensor_event_rgb /* Type: RGB */ +{ + uint64_t timestamp; /* Units is microseconds */ + float r; /* Units is percent */ + float g; /* Units is percent */ + float b; /* Units is percent */ +}; + +struct sensor_event_hall /* Type: HALL */ +{ + uint64_t timestamp; /* Units is microseconds */ + bool hall; /* Boolean type */ +}; + +struct sensor_event_ir /* Type: Infrared Ray */ +{ + uint64_t timestamp; /* Units is microseconds */ + float ir; /* in SI units lux */ +}; + +struct sensor_event_gps /* Type: Gps */ +{ + int year; /* Time */ + int month; + int day; + int hour; + int min; + int sec; + int msec; + + float yaw; /* Unit is Si degrees */ + float height; /* Unit is SI m */ + float speed; /* Unit is m/s */ + float latitude; /* Unit is degrees */ + float longitude; /* Unit is degrees */ +}; + +struct sensor_event_uv /* Type: Ultraviolet Light */ +{ + uint64_t timestamp; /* Units is microseconds */ + float uvi; /* the vaule range is 0 - 15 */ +}; + +struct sensor_event_noise /* Type: Noise Loudness */ +{ + uint64_t timestamp; /* Units is microseconds */ + float db; /* in SI units db */ +}; + +struct sensor_event_pm25 /* Type: PM25 */ +{ + uint64_t timestamp; /* Units is microseconds */ + float pm25; /* in SI units ug/m^3 */ +}; + +struct sensor_event_pm10 /* Type: PM10 */ +{ + uint64_t timestamp; /* Units is microseconds */ + float pm10; /* in SI units ug/m^3 */ +}; + +struct sensor_event_pm1p0 /* Type: PM1P0 */ +{ + uint64_t timestamp; /* Units is microseconds */ + float pm1p0; /* in SI units ug/m^3 */ +}; + +struct sensor_event_co2 /* Type: CO2 */ +{ + uint64_t timestamp; /* Units is microseconds */ + float co2; /* in SI units ppm */ +}; + +struct sensor_event_hcho /* Type: HCHO */ +{ + uint64_t timestamp; /* Units is microseconds */ + float hcho; /* in SI units ppm */ +}; + +struct sensor_event_tvoc /* Type: TVOC */ +{ + uint64_t timestamp; /* Units is microseconds */ + float tvoc; /* in SI units ppm */ +}; + +struct sensor_event_ph /* Type: PH */ +{ + uint64_t timestamp; /* Units is microseconds */ + float ph; /* PH = 7.0 neutral, PH < 7.0 acidic, PH > 7.0 alkaline */ +}; + +struct sensor_event_dust /* Type: DUST */ +{ + uint64_t timestamp; /* Units is microseconds */ + float dust; /* is SI units ug/m^3 */ +}; + +struct sensor_event_hrate /* Type: Heart Rate */ +{ + uint64_t timestamp; /* Units is microseconds */ + float bpm; /* is SI units BPM */ +}; + +struct sensor_event_hbeat /* Type: Heart Beat */ +{ + uint64_t timestamp; /* Units is microseconds */ + float beat; /* Units is times/minutes */ +}; + +/* The sensor lower half driver interface */ + +struct sensor_lowerhalf_s; +struct sensor_ops_s +{ + /************************************************************************** + * Name: activate + * + * Description: + * Enable or disable sensor device. when enable sensor, sensor will + * work in current mode(if not set, use default mode). when disable + * sensor, it will disable sense path and stop convert. + * + * Input Parameters: + * lower - The instance of lower half sensor driver + * 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 sensor_lowerhalf_s *lower, bool enable); + + /************************************************************************** + * Name: set_interval + * + * Description: + * Set the sensor output data period in microseconds for a given sensor. + * If *period_us > max_delay it will be truncated to max_dealy and if + * *period_us < min_delay it will be replaced by min_delay. + * + * Before changing the interval, we need to push the prepared data to + * ensure that they are not lost. + * + * Input Parameters: + * lower - The instance of lower half sensor driver. + * 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 sensor_lowerhalf_s *lower, + FAR unsigned int *period_us); + + /************************************************************************** + * Name: batch + * + * Description: + * Set sensor's maximum report latency in microseconds. + * + * This function can be called while the sensor is activated, + * in which case it must not cause any sensor measurements to be lost. + * So, it is necessary to flush fifo or read ready data from data + * register to prevent data lost before we using batch mode. + * + * This sensor default mode isn't batch mode, so we need call this + * function and *latency_us != 0. + * If *latency_us > max_report_latency it will be truncated to + * max_report_latency and return *latency_us to user + * And we must flush fifo data to prevent data lost, then adjust latency. + * + * We can exit batch mode by call this function with *latency_us = 0. + * And we must flush fifo data to prevent data lost, then stop batch. + * + * If sensor doesn't support batching (FIFO size zero), set batch to + * NULL. + * + * We must set interval by calling set_interval before calling batch(), + * othrewise, -EINVAL is returned. + * + * The reason why we don't have flush operation is that we need to push + * the prepared data out before adjusting the latency to ensure that the + * data will not be lost. + * + * Input Parameters: + * lower - The instance of lower half sensor driver. + * latency_us - the time between batch data, in us. It may by overwrite + * by lower half driver. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*batch)(FAR struct sensor_lowerhalf_s *lower, + FAR unsigned int *latency_us); +}; + +/* This structure is the generic form of state structure used by lower half + * Sensor driver. + */ + +struct sensor_lowerhalf_s +{ + /* The type of sensor device */ + + int type; + + /* The bytes length of the circular buffer used. + * This sensor circular buffer is used to slove issue that application + * can't read sensor event in time. If this length of buffer is too large, + * the latency of sensor event will be too larage. If the length of buffer + * is too small, the event will be overwrite before application read them. + * So, it's recommended to set according to sensor odr. If odr is low, you + * can set to a length of sensor event. If odr is high, you can set to two + * or three length of sensor event. + */ + + uint32_t buffer_bytes; + + /* The uncalibrated use to describe whether the sensor event is + * uncalibrated. True is uncalibrated data, false is calibrated data, + * default false. + */ + + bool uncalibrated; + + /* The lower half sensor driver operations */ + + FAR const struct sensor_ops_s *ops; + + /************************************************************************** + * Name: push_event + * + * Description: + * Lower half driver push sensor event by calling this function. + * It is provided by upper half driver to lower half driver. + * + * Input Parameters: + * priv - Upper half driver handle + * data - The buffer of event, it can be all type of sensor events. + * bytes - The number of bytes of sensor event + **************************************************************************/ + + CODE void (*push_event)(FAR void *priv, FAR const void *data, + uint32_t bytes); + + /* The private opaque pointer to be passed to upper-layer during callback */ + + FAR void *priv; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * "Upper Half" Sensor Driver Interfaces + ****************************************************************************/ + +/**************************************************************************** + * Name: sensor_register + * + * Description: + * This function binds an instance of a "lower half" Sensor driver with the + * "upper half" Sensor device and registers that device so that can be used + * by application code. + * + * We will register the chararter device by node name format based on the + * type of sensor. Multiple types of the same type are distinguished by + * numbers. eg: accel0, accel1 + * + * Input Parameters: + * dev - A pointer to an instance of lower half sensor driver. This + * instance is bound to the sensor driver and must persists as long + * as the driver persists. + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int sensor_register(FAR struct sensor_lowerhalf_s *dev); + +/**************************************************************************** + * Name: sensor_unregister + * + * Description: + * This function unregister character node and release all resource about + * upper half driver. + * + * Input Parameters: + * dev - A pointer to an instance of lower half sensor driver. This + * instance is bound to the sensor driver and must persists as long + * as the driver persists. + ****************************************************************************/ + +void sensor_unregister(FAR struct sensor_lowerhalf_s *dev); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif +#endif /* __INCLUDE_NUTTX_SENSORS_SENSOR_H */
