You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by pr...@apache.org on 2020/10/22 13:17:46 UTC

[incubator-nuttx] 01/03: driver/sensor: add unified management for sensor

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 <do...@xiaomi.com>
AuthorDate: Mon Oct 19 21:24:13 2020 +0800

    driver/sensor: add unified management for sensor
    
    Signed-off-by: dongjiuzhu <do...@xiaomi.com>
---
 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 */