This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 4719e44b8b APDS9922
4719e44b8b is described below
commit 4719e44b8be6557ad78281683661c4f2ce1053b4
Author: TimJTi <[email protected]>
AuthorDate: Sat Feb 11 12:08:40 2023 +0000
APDS9922
WIP
WIP - ALS now OK with full IOCTL.
WIP
WIP
ALS now works as intended
WIP - getting to bottom of crash when ctrl-c the app
Seems to all work...
ioctl #define changes
Update ioctl.h
Maybe final changes
Maybe final changes - again
Delete .settings directory
Delete nuttx Default.launch
Maybe final changes - again
changes after initial feedback
changes after initial feedback
Add snerr error when incorrect device ID seen
Update apds9922.c
Revert "Merge branch 'apds9922' of https://github.com/TimJTi/nuttx into
apds9922"
This reverts commit 8fdf5cbfb783d25251d13bc338ece6adca1308bc, reversing
changes made to 0d58237ba27f3cf87cf711658f5388d974be502e.
Improve probe error messages
APDS9922
WIP
WIP - ALS now OK with full IOCTL.
WIP
WIP
ALS now works as intended
WIP - getting to bottom of crash when ctrl-c the app
Seems to all work...
ioctl #define changes
Update ioctl.h
Maybe final changes
Maybe final changes - again
Delete .settings directory
Delete nuttx Default.launch
Maybe final changes - again
changes after initial feedback
changes after initial feedback
Add snerr error when incorrect device ID seen
Update apds9922.c
Revert "Merge branch 'apds9922' of https://github.com/TimJTi/nuttx into
apds9922"
This reverts commit 8fdf5cbfb783d25251d13bc338ece6adca1308bc, reversing
changes made to 0d58237ba27f3cf87cf711658f5388d974be502e.
Improve probe error messages
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Update drivers/sensors/apds9922.c
Co-authored-by: Petro Karashchenko <[email protected]>
Delete .gitignore
Changes after PR review
Remove static arrays from header file
Update apds9922.c
improvements to enum usage
Restore .gitignore
Update Kconfig
Update .gitignore
Update .gitignore
---
drivers/sensors/Kconfig | 26 +
drivers/sensors/Make.defs | 4 +
drivers/sensors/apds9922.c | 2601 ++++++++++++++++++++++++++++++++++++++
include/nuttx/sensors/apds9922.h | 276 ++++
include/nuttx/sensors/ioctl.h | 19 +
5 files changed, 2926 insertions(+)
diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig
index 67ed6de1a6..f72061e8c0 100644
--- a/drivers/sensors/Kconfig
+++ b/drivers/sensors/Kconfig
@@ -60,6 +60,32 @@ config APDS9960_I2C_FREQUENCY
default 400000
depends on SENSORS_APDS9960
+config SENSORS_APDS9922
+ bool "Broadcom APDS-9922 Proximity and Ambient Light Sensor"
+ default n
+ select I2C
+ ---help---
+ Enable driver support for the Broadcom APDS-9922 proximity and
ambient light sensor.
+
+config APDS9922_I2C_FREQUENCY
+ int "APDS-9922 I2C frequency"
+ default 400000
+ depends on SENSORS_APDS9922
+
+config APDS9922_ALS_NPOLLWAITERS
+ int "APDS-9922 - number of ALS poll waiters"
+ default 2
+ depends on SENSORS_APDS9922
+ ---help---
+ Sets the number of poll waiters for the ambient light sensor
+
+config APDS9922_PS_NPOLLWAITERS
+ int "APDS-9922 - number of PS poll waiters"
+ default 2
+ depends on SENSORS_APDS9922
+ ---help---
+ Sets the number of poll waiters for the proximity sensor
+
config SENSORS_AK09912
bool "Asahi AK09911/AK09912 Compass Sensor"
default n
diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs
index 18e2545be2..046ba03872 100644
--- a/drivers/sensors/Make.defs
+++ b/drivers/sensors/Make.defs
@@ -60,6 +60,10 @@ ifeq ($(CONFIG_SENSORS_APDS9960),y)
CSRCS += apds9960.c
endif
+ifeq ($(CONFIG_SENSORS_APDS9922),y)
+ CSRCS += apds9922.c
+endif
+
ifeq ($(CONFIG_SENSORS_AK09912),y)
CSRCS += ak09912.c
endif
diff --git a/drivers/sensors/apds9922.c b/drivers/sensors/apds9922.c
new file mode 100644
index 0000000000..0ed66378f5
--- /dev/null
+++ b/drivers/sensors/apds9922.c
@@ -0,0 +1,2601 @@
+/****************************************************************************
+ * drivers/sensors/apds9922.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.
+ *
+ ****************************************************************************/
+
+/* Character driver for the APDS9922 Proximity and Ambient Light Sensor */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <debug.h>
+#include <stdlib.h>
+
+#include <nuttx/compiler.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/signal.h>
+
+#include <nuttx/sensors/apds9922.h>
+#include <sys/ioctl.h>
+
+#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_APDS9922)
+
+/****************************************************************************
+ * Pre-process Definitions
+ ****************************************************************************/
+
+#ifndef CONFIG_APDS9922_I2C_FREQUENCY
+# define CONFIG_APDS9922_I2C_FREQUENCY 400000
+#endif
+
+#ifndef CONFIG_APDS9922_ALS_NPOLLWAITERS
+# define CONFIG_APDS9922_ALS_NPOLLWAITERS 2
+#endif
+
+#ifndef CONFIG_APDS9922_PS_NPOLLWAITERS
+# define CONFIG_APDS9922_PS_NPOLLWAITERS 2
+#endif
+
+/* Helper macros */
+
+#ifdef CONFIG_ENDIAN_BIG
+# define APDS9922_PACK_TO_UINT32(a) \
+ (((a)[0] >> 24) | ((a)[1] >> 16) | ((a)[2] >> 8) | ((a)[3]))
+# define APDS9922_PACK_TO_UINT16(a) \
+ (((a)[0] >> 0) | ((a)[1]))
+# define APDS9922_UNPACK_FROM_UINT32(w, b) \
+ do \
+ { \
+ (b)[0] = ((w) >> 24) & 0xff; \
+ (b)[1] = ((w) >> 16) & 0xff; \
+ (b)[2] = ((w) >> 8) & 0xff; \
+ (b)[3] = (w) & 0xff; \
+ } \
+ while (0)
+# define APDS9922_UNPACK_FROM_UINT16(w, b) \
+ do \
+ { \
+ (b)[0] = ((w) >> 8) & 0xff; \
+ (b)[1] = (w) & 0xff; \
+ } \
+ while (0)
+#else
+# define APDS9922_PACK_TO_UINT32(a) \
+ (((a)[3] << 24) | ((a)[2] << 16) | ((a)[1] << 8) | ((a)[0]))
+# define APDS9922_PACK_TO_UINT16(a) \
+ (((a)[1] << 8) | ((a)[0]))
+# define APDS9922_UNPACK_FROM_UINT32(w, b) \
+ do \
+ { \
+ (b)[3] = ((w) >> 24) & 0xff; \
+ (b)[2] = ((w) >> 16) & 0xff; \
+ (b)[1] = ((w) >> 8) & 0xff; \
+ (b)[0] = (w) & 0xff; \
+ } \
+ while (0)
+# define APDS9922_UNPACK_FROM_UINT16(w, b) \
+ do \
+ { \
+ (b)[1] = ((w) >> 8) & 0xff; \
+ (b)[0] = (w) & 0xff; \
+ } \
+ while (0)
+#endif
+
+/* Register mappings */
+
+#define APDS9922_MAIN_CTRL (0x00) /* SW reset, ALS Enable, PS enable */
+#define APDS9922_PS_LED (0x01) /* PS LED setup */
+#define APDS9922_PS_PULSES (0x02) /* PS pulses setup */
+#define APDS9922_PS_MEAS_RATE (0x03) /* PS Measurement rate */
+#define APDS9922_ALS_MEAS_RATE (0x04) /* ALS Measurement rate */
+#define APDS9922_ALS_GAIN (0x05) /* ALS gain */
+#define APDS9922_ID (0x06) /* Part and Revision ID */
+#define APDS9922_MAIN_STATUS (0x07) /* Status register */
+#define APDS9922_PS_DATA0 (0x08) /* LSB of measured PS data */
+#define APDS9922_ALS_DATA0 (0x0d) /* LSB of measured ALS data */
+#define APDS9922_INT_CFG (0x19) /* Interrupt configuration */
+#define APDS9922_INT_PERSIST (0x1a) /* Interrupt persistance */
+#define APDS9922_PS_THRESHU (0x1b) /* PS threshold, upper limit */
+#define APDS9922_PS_THRESHL (0x1d) /* PS threshold, lower limit */
+#define APDS9922_CANCEL_LVLL (0x1f) /* Intelligent Cancellation level */
+#define APDS9922_CANCEL_LVLU (0x20) /* Intelligent Cancellation level */
+#define APDS9922_ALS_THRESHU (0x21) /* ALS threshold, upper limit */
+#define APDS9922_ALS_THRESHL (0x24) /* ALS threshold, lower limit */
+#define APDS9922_ALS_THRESH_VAR (0x27) /* ALS threshold variation */
+
+/* APDS9922_MAIN_CTRL Register 0x01 */
+
+#define PS_ACTIVE_SHIFT (0)
+#define PS_ACTIVE (1 << PS_ACTIVE_SHIFT)
+#define ALS_ACTIVE_SHIFT (1)
+#define ALS_ACTIVE (1 << ALS_ACTIVE_SHIFT)
+#define SW_RESET_SHIFT (4)
+#define APDS9922_SW_RESET (1 << SW_RESET_SHIFT)
+
+/* APDS922_PS_LED register 0x02 */
+
+#define PS_LED_FREQ_SHIFT (4)
+#define PS_LED_FREQ_MASK (7 << PS_LED_FREQ_SHIFT)
+#define PS_SET_LED_FREQ(f) ((f) << PS_LED_FREQ_SHIFT)
+#define PS_LED_PEAKING_ON (1 << 3)
+#define PS_LED_CURRENT_SHIFT (0)
+#define PS_LED_CURRENT_MASK (7 << PS_LED_CURRENT_SHIFT)
+#define PS_SET_LED_CURRENT(i) ((i) << PS_LED_CURRENT_SHIFT)
+
+/* APDS922_PS_PULSES register 0x03 */
+
+#define PS_LED_PULSES_MASK (0x0fff)
+#define PS_SET_LED_PULSES(p) ((p) & PS_LED_PULSES_MASK)
+
+/* APDS922_PS_MEAS_RATE 0x03 */
+
+#define PS_RESOLUTION_SHIFT (3)
+#define PS_RESOLUTION_MASK (3 << PS_RESOLUTION_SHIFT)
+#define PS_SET_RESOLUTION(r) (((r) << PS_RESOLUTION_SHIFT) | 0x20)
+#define PS_MEASURERATE_SHIFT (0)
+#define PS_MEASURERATE_MASK (7 << PS_MEASURERATE_SHIFT)
+#define PS_SET_MEASURERATE(r) ((r) << PS_MEASURERATE_SHIFT)
+
+/* APDS922_ALS_MEAS_RATE 0x04 */
+
+#define ALS_RESOLUTION_SHIFT (4)
+#define ALS_RESOLUTION_MASK (7 << ALS_RESOLUTION_SHIFT)
+#define ALS_SET_RESOLUTION(r) ((r) << ALS_RESOLUTION_SHIFT)
+#define ALS_MEASURERATE_SHIFT (0)
+#define ALS_MEASURERATE_MASK (7 << ALS_MEASURERATE_SHIFT)
+#define ALS_SET_MEASURERATE(r) ((r) << ALS_MEASURERATE_SHIFT)
+
+/* APDS922_ALS_GAIN 0x05 */
+
+#define ALS_GAIN_SHIFT (0)
+#define ALS_GAIN_MASK (7 << ALS_GAIN_SHIFT)
+#define ALS_SET_GAIN(g) ((g) << ALS_GAIN_SHIFT)
+
+/* APDS_ALS_MAIN_STATUS 0x07 */
+
+#define ALS_INT_STATUS (16)
+#define ALS_NEW_DATA (8)
+#define PS_LOGIC_STATUS (4)
+#define PS_INT_STATUS (2)
+#define PS_NEW_DATA (1)
+
+/* APDS9922_PS_DATA0 0x08 */
+
+#define PS_DATA_OVERFLOW_SHIFT (3)
+#define PS_DATA_OVERFLOW (1 << PS_DATA_OVERFLOW_SHIFT)
+
+/* APDS9922_INT_CFG 0x19 */
+
+#define PS_INT_EN_SHIFT (0)
+#define PS_INT_EN (1 << PS_INT_EN_SHIFT)
+#define PS_INT_MASK (1 << PS_INT_EN_SHIFT)
+#define PS_LOGIC_MODE_SHIFT (1)
+#define PS_LOGIC_MODE_NORMAL (0 << PS_LOGIC_MODE_SHIFT)
+#define PS_LOGIC_MODE_LOGIC (1 << PS_LOGIC_MODE_SHIFT)
+
+#define ALS_INT_EN_SHIFT (2)
+#define ALS_INT_EN (1 << ALS_INT_EN_SHIFT)
+#define ALS_INT_MASK (5 << ALS_INT_EN_SHIFT)
+#define ALS_INT_VAR_SHIFT (3)
+#define ALS_INT_VAR_MODE (1 << ALS_INT_VAR_SHIFT)
+#define ALS_INT_THRESH_MODE (0 << ALS_INT_VAR_SHIFT)
+#define ALS_INT_SRC_SHIFT (4)
+#define ALS_INT_SRC_MASK (3 << ALS_INT_SRC_SHIFT)
+#define ALS_INT_SET_SRC(s) ((s) << ALS_INT_SRC_SHIFT)
+
+/* APDS922_INT_PERSIST 0x1a */
+
+#define ALS_PERSISTANCE_SHIFT (4)
+#define ALS_PERSISTANCE_MASK (15 << ALS_PERSISTANCE_SHIFT)
+#define ALS_SET_PERSISTANCE(p) ((p) << ALS_PERSISTANCE_SHIFT)
+#define ALS_PERSISTANCE_MAX (255)
+#define PS_PERSISTANCE_SHIFT (0)
+#define PS_PERSISTANCE_MASK (15 << PS_PERSISTANCE_SHIFT)
+#define PS_SET_PERSISTANCE(p) ((p) << PS_PERSISTANCE_SHIFT)
+#define PS_PERSISTANCE_MAX (255)
+
+/* APDS922_ALS_THRESH_VAR 0x27 */
+
+#define ALS_THRESH_VAR_SHIFT (0)
+#define ALS_THRESH_VAR_MASK (7 << ALS_THRESH_VAR_SHIFT)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct als_data
+{
+ uint32_t gain; /* Gain multiplier */
+ uint32_t rate; /* Corresponding rate in ms */
+ uint32_t maxval; /* Maximum value the als value can attain for this rate */
+};
+
+static const struct als_data als_data[] =
+{
+ {1, 400, 1048575},
+ {3, 200, 524287},
+ {6, 100, 262143},
+ {9, 50, 131071},
+ {18, 25, 65535},
+};
+
+struct apds9922_dev_s
+{
+ FAR struct pollfd *fds_als[CONFIG_APDS9922_ALS_NPOLLWAITERS];
+ FAR struct pollfd *fds_ps[CONFIG_APDS9922_PS_NPOLLWAITERS];
+ struct work_s work; /* Handles interrupt */
+ mutex_t devlock; /* Manages exclusive access */
+ FAR struct apds9922_config_s *config; /* Platform specific config */
+ struct apds9922_als_setup_s als_setup; /* Device ALS config */
+ struct apds9922_ps_setup_s ps_setup; /* Device PS config */
+ int als; /* ALS data */
+ FAR struct apds9922_ps_data *ps_data; /* PS data */
+ uint8_t devid; /* Device ID read at startup */
+ int crefs; /* Number of opens, als or ps */
+ };
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Probe function to verify if sensor is present */
+
+static int apds9922_probe(FAR struct apds9922_dev_s *priv);
+
+/* Work queue */
+
+static void apds9922_worker(FAR void *arg);
+
+/* i2c read/write functions */
+
+static int apds9922_i2c_read(FAR struct apds9922_dev_s *priv,
+ uint8_t const regaddr, FAR uint8_t *regval,
+ int len);
+static int apds9922_i2c_read8(FAR struct apds9922_dev_s *priv,
+ uint8_t const regaddr,
+ FAR uint8_t *regval);
+static int apds9922_i2c_write(FAR struct apds9922_dev_s *priv,
+ uint8_t const regaddr,
+ uint8_t const *data, int len);
+static int apds9922_i2c_write8(FAR struct apds9922_dev_s *priv,
+ uint8_t const regaddr, uint8_t regval);
+
+/* local functions */
+
+static int apds9922_reset(FAR struct apds9922_dev_s *priv);
+
+/* Ambient light sensor functions */
+
+static int apds9922_als_config(FAR struct apds9922_dev_s *priv,
+ FAR struct apds9922_als_setup_s *config);
+static int apds9922_lux_calc(FAR struct apds9922_dev_s *priv);
+static int apds9922_als_gain(FAR struct apds9922_dev_s *priv, int gain);
+static int apds9922_autogain(FAR struct apds9922_dev_s *priv, bool enable);
+static int apds9922_als_resolution(FAR struct apds9922_dev_s *priv, int res);
+static int apds9922_als_rate(FAR struct apds9922_dev_s *priv, int rate);
+static int apds9922_als_persistance(FAR struct apds9922_dev_s *priv,
+ uint8_t persistance);
+static int apds9922_als_variance(FAR struct apds9922_dev_s *priv,
+ int variance);
+static int apds9922_als_thresh(FAR struct apds9922_dev_s *priv,
+ FAR struct adps9922_als_thresh thresholds);
+static int apds9922_als_int_mode(FAR struct apds9922_dev_s *priv, int mode);
+static int apds9922_als_channel(FAR struct apds9922_dev_s *priv,
+ int channel);
+static int apds9922_als_factor(FAR struct apds9922_dev_s *priv,
+ uint32_t factor);
+static int apds9922_als_limit(FAR struct apds9922_dev_s *priv,
+ uint32_t limit);
+
+/* Proximity sensor functions */
+
+static int apds9922_ps_config(FAR struct apds9922_dev_s *priv,
+ FAR struct apds9922_ps_setup_s *config);
+static int apds9922_ps_resolution(FAR struct apds9922_dev_s *priv, int res);
+static int apds9922_ps_rate(FAR struct apds9922_dev_s *priv, int rate);
+static int apds9922_ps_ledf(FAR struct apds9922_dev_s *priv, int freq);
+static int apds9922_ps_ledi(FAR struct apds9922_dev_s *priv, int current);
+static int apds9922_ps_ledpk(FAR struct apds9922_dev_s *priv, bool enable);
+static int apds9922_ps_pulses(FAR struct apds9922_dev_s *priv,
+ uint8_t num_p);
+static int apds9922_ps_thresh(FAR struct apds9922_dev_s *priv,
+ FAR struct adps9922_ps_thresh thresh);
+static int apds9922_ps_canc_lev(FAR struct apds9922_dev_s *priv,
+ uint16_t lev);
+static int apds9922_ps_int_mode(FAR struct apds9922_dev_s *priv, int mode);
+static int apds9922_ps_persistance(FAR struct apds9922_dev_s *priv,
+ uint8_t persistance);
+static int apds9922_ps_notify_mode(FAR struct apds9922_dev_s *priv,
+ int notify);
+
+/* Character driver methods */
+
+static int apds9922_open(FAR struct file *filep);
+static int apds9922_close(FAR struct file *filep);
+static ssize_t apds9922_als_read(FAR struct file *filep,
+ FAR char *, size_t buflen);
+static ssize_t apds9922_als_write(FAR struct file *filep,
+ FAR const char *buffer, size_t buflen);
+static int apds9922_als_ioctl(FAR struct file *filep, int cmd,
+ unsigned long arg);
+static int apds9922_als_poll(FAR struct file *filep,
+ FAR struct pollfd *fds, bool setup);
+static ssize_t apds9922_ps_read(FAR struct file *filep,
+ FAR char *, size_t buflen);
+static ssize_t apds9922_ps_write(FAR struct file *filep,
+ FAR const char *buffer, size_t buflen);
+static int apds9922_ps_ioctl(FAR struct file *filep, int cmd,
+ unsigned long arg);
+static int apds9922_ps_poll(FAR struct file *filep,
+ FAR struct pollfd *fds, bool setup);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_apds9922_alsfops =
+{
+ apds9922_open, /* open */
+ apds9922_close, /* close */
+ apds9922_als_read, /* read */
+ apds9922_als_write, /* write */
+ NULL, /* seek */
+ apds9922_als_ioctl, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* truncate */
+ apds9922_als_poll, /* poll */
+};
+
+static const struct file_operations g_apds9922_psfops =
+{
+ apds9922_open, /* open */
+ apds9922_close, /* close */
+ apds9922_ps_read, /* read */
+ apds9922_ps_write, /* write */
+ NULL, /* seek */
+ apds9922_ps_ioctl, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* truncate */
+ apds9922_ps_poll, /* poll */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: apds9922_worker
+ *
+ * Description:
+ * Worker task to deal with new device interrupt
+ *
+ * Input Parameters:
+ * arg - Pointer to device
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void apds9922_worker(FAR void *arg)
+{
+ FAR struct apds9922_dev_s *priv = (FAR struct apds9922_dev_s *)arg;
+ int ret;
+ uint8_t status;
+ uint8_t data[4];
+ bool notify_ps;
+
+ DEBUGASSERT(priv);
+
+ ret = apds9922_i2c_read8(priv, APDS9922_MAIN_STATUS, &status);
+ if (ret < 0)
+ {
+ snerr("Failed to read status: %d\n", ret);
+ goto err_out;
+ }
+
+ if (status & ALS_INT_STATUS)
+ {
+ ret = apds9922_i2c_read(priv, APDS9922_ALS_DATA0, data, 3);
+ if (ret < 0)
+ {
+ snerr("Failed to read als data: %d\n", ret);
+ goto err_out;
+ }
+
+ priv->als = APDS9922_PACK_TO_UINT32(data) & 0x0fffff;
+ poll_notify(priv->fds_als, CONFIG_APDS9922_ALS_NPOLLWAITERS, POLLIN);
+ }
+
+ if (status & PS_INT_STATUS)
+ {
+ notify_ps = false;
+ if (priv->ps_setup.notify != PS_FAR_OR_CLOSE_ONLY)
+ {
+ ret = apds9922_i2c_read(priv, APDS9922_PS_DATA0, data, 2);
+ if (ret < 0)
+ {
+ snerr("Failed to read ps data: %d\n", ret);
+ goto err_out;
+ }
+
+ priv->ps_data->ps = APDS9922_PACK_TO_UINT16(data) & 0x0fff;
+ notify_ps = true;
+ }
+
+ if ((priv->ps_setup.notify != PS_PROXIMITY_DATA_ONLY) &&
+ (priv->ps_data->close != (status & PS_LOGIC_STATUS)))
+ {
+ notify_ps = true;
+ priv->ps_data->close = (status & PS_LOGIC_STATUS) ? true : false;
+ }
+
+ sninfo("INFO: ps=0x%x\t close=%d\n",
+ priv->ps_data->ps, priv->ps_data->close);
+
+ if (notify_ps)
+ {
+ poll_notify(priv->fds_ps, CONFIG_APDS9922_PS_NPOLLWAITERS, POLLIN);
+ }
+ }
+
+ /* if there's been a fail, there's an issue with the device.
+ * Set proximity and lux to error value and notify.
+ */
+
+err_out:
+
+ if (ret < 0)
+ {
+ priv->als = ret;
+ priv->ps_data->ps = ret;
+ snerr("ERR: Error while dealing with worker \n");
+
+ poll_notify(priv->fds_als, CONFIG_APDS9922_ALS_NPOLLWAITERS, POLLIN);
+ poll_notify(priv->fds_ps, CONFIG_APDS9922_PS_NPOLLWAITERS, POLLIN);
+ }
+}
+
+/****************************************************************************
+ * Name: apds9922_int_handler
+ *
+ * Description:
+ * Interrupt handler (ISR) for APDS-9922 INT pin.
+ *
+ * Input Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ * arg - Argument passed to the interrupt callback
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_int_handler(int irq, FAR void *context, FAR void *arg)
+{
+ int ret;
+
+ FAR struct apds9922_dev_s *priv = (FAR struct apds9922_dev_s *)arg;
+
+ DEBUGASSERT(priv != NULL);
+
+ /* Transfer processing to the worker thread. Since APDS-9922 interrupts
+ * are disabled until the data is read, no special action should be
+ * required to protect the work queue.
+ */
+
+ DEBUGASSERT(priv->work.worker == NULL);
+
+ ret = work_queue(HPWORK, &priv->work, apds9922_worker, priv, 0);
+ if (ret < 0)
+ {
+ snerr("ERROR: Failed to queue work: %d\n", ret);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: apds9922_reset
+ *
+ * Description:
+ * Reset the chip
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_reset(FAR struct apds9922_dev_s *priv)
+{
+ int ret;
+
+ ret = apds9922_i2c_write8(priv, APDS9922_MAIN_CTRL, APDS9922_SW_RESET);
+ if (ret < 0)
+ {
+ snerr("ERROR: Failed to reset the APDS9922\n");
+ return ret;
+ }
+
+ /* initialise setup to match the reset defaults etc. */
+
+ priv->als_setup.rate = ALS_RATE100MS;
+ priv->als_setup.res = ALS_RES200MS;
+ priv->als_setup.thresh.upper = ALS_DEF_THRESHU;
+ priv->als_setup.thresh.lower = ALS_DEF_THRESHL;
+ priv->als_setup.thresh_var = ALS_DEF_VAR;
+ priv->als_setup.int_mode = ALS_INT_MODE_THRESHOLD;
+ priv->als_setup.persistance = ALS_DEF_PERSISTANCE;
+ priv->als_setup.als_factor = 1;
+ priv->als_setup.range_lim = 1;
+ priv->als_setup.autogain = false;
+ priv->als_setup.channel = ALS_VISIBLE;
+
+ priv->ps_setup.rate = PS_RATE100MS;
+ priv->ps_setup.res = PS_RES8;
+ priv->ps_setup.led_f = PS_LED_FREQ60K;
+ priv->ps_setup.led_pk_on = false;
+ priv->ps_setup.led_i = PS_LED_CURRENT100MA;
+ priv->ps_setup.pulses = PS_DEF_PULSES;
+ priv->ps_setup.thresh.upper = PS_DEF_THRESHU;
+ priv->ps_setup.thresh.lower = PS_DEF_THRESHL;
+ priv->ps_setup.cancel_lev = PS_DEF_CANCEL_LVL;
+ priv->ps_setup.persistance = PS_DEF_PERSISTANCE;
+ priv->ps_setup.notify = PS_ALL_INFO;
+ priv->ps_setup.int_mode = PS_INT_MODE_NORMAL;
+
+ /* Wait for device to power up properly after reset */
+
+ nxsig_usleep(50000);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_probe
+ *
+ * Description:
+ * Verify if sensor is present. Check if ID is 0xAB.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_probe(FAR struct apds9922_dev_s *priv)
+{
+ int ret;
+ uint8_t id = 0;
+
+ ret = apds9922_i2c_read8(priv, APDS9922_ID, &id);
+ if (ret < 0)
+ {
+ snerr("ERROR: Failed to probe the APDS9922\n");
+ return ret;
+ }
+
+ if (id != APDS9922_ID_VAL)
+ {
+ snerr("ERROR: APDS9922 device ID is incorrect\n");
+ return -ENODEV;
+ }
+
+ priv->devid = id;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds_als_config
+ *
+ * Description:
+ * Set the measurement resolution required.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * config - pointer to the apds9922_als_setup_s config struct
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+/* Ambient light sensor functions */
+
+static int apds9922_als_config(FAR struct apds9922_dev_s *priv,
+ FAR struct apds9922_als_setup_s *config)
+{
+ int ret;
+
+ ret = apds9922_als_factor(priv, config->als_factor);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_als_limit(priv, config->range_lim);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Do gain before autogain as autogain will change gain as well */
+
+ ret = apds9922_autogain(priv, config->autogain);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_als_gain(priv, config->gain);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_als_resolution(priv, config->res);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_als_rate(priv, config->rate);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_als_persistance(priv, config->persistance);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_als_variance(priv, config->thresh_var);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_als_thresh(priv, config->thresh);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_als_channel(priv, config->channel);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_als_int_mode(priv, config->int_mode);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: apds_als_resolution
+ *
+ * Description:
+ * Set the measurement resolution required.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * res - resolution to be used
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_als_resolution(FAR struct apds9922_dev_s *priv, int res)
+{
+ uint8_t regval;
+ int ret;
+
+ ret = apds9922_i2c_read8(priv, APDS9922_ALS_MEAS_RATE, ®val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ regval &= ~ALS_RESOLUTION_MASK;
+ regval |= ALS_SET_RESOLUTION(res);
+ ret = apds9922_i2c_write8(priv, APDS9922_ALS_MEAS_RATE, regval);
+ priv->als_setup.res = res;
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: apds9922_als_channel
+ *
+ * Description:
+ * Sets the ALS interrupt channel - visible or IR light.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * channel - interrupt channel source
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_als_channel(FAR struct apds9922_dev_s *priv, int channel)
+{
+ uint8_t regval;
+ int ret;
+
+ if (channel > ALS_VISIBLE)
+ {
+ return -EINVAL;
+ }
+
+ ret = apds9922_i2c_read8(priv, APDS9922_INT_CFG, ®val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ regval &= ~ALS_INT_SRC_MASK;
+ regval |= ALS_INT_SET_SRC(channel);
+
+ ret = apds9922_i2c_write8(priv, APDS9922_INT_CFG, regval);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->als_setup.channel = channel;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_als_factor
+ *
+ * Description:
+ * Sets the ALS correction factor, used for lux calculation
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * factor - als factor to use
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_als_factor(FAR struct apds9922_dev_s *priv,
+ uint32_t factor)
+{
+ if (factor < 1)
+ {
+ return -EINVAL;
+ }
+
+ priv->als_setup.als_factor = factor;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_als_limit
+ *
+ * Description:
+ * Sets the ALS auto range limit - "limit percent" of full scale value
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * limi - limit to use (1-100 %)
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_als_limit(FAR struct apds9922_dev_s *priv,
+ uint32_t limit)
+{
+ if ((limit < 1) || (limit > 100))
+ {
+ return -EINVAL;
+ }
+
+ priv->als_setup.range_lim = limit;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_als_int_mode
+ *
+ * Description:
+ * Sets the ALS interrupt mode - disabled, threshold or variance.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * channel - interrupt mode
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_als_int_mode(FAR struct apds9922_dev_s *priv, int mode)
+{
+ uint8_t regval;
+ int ret;
+
+ if (mode > ALS_INT_MODE_VARIANCE)
+ {
+ return -EINVAL;
+ }
+
+ ret = apds9922_i2c_read8(priv, APDS9922_INT_CFG, ®val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ regval &= ~ALS_INT_MASK;
+
+ switch (mode)
+ {
+ case ALS_INT_MODE_VARIANCE:
+ regval |= ALS_INT_VAR_MODE | ALS_INT_EN;
+ break;
+ case ALS_INT_MODE_THRESHOLD:
+ regval |= ALS_INT_EN;
+ break;
+ case ALS_INT_MODE_DISABLED:
+ default:
+ break;
+ }
+
+ ret = apds9922_i2c_write8(priv, APDS9922_INT_CFG, regval);
+
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->als_setup.int_mode = mode;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_als_thresh
+ *
+ * Description:
+ * Sets the ALS thresholds, upper and lower.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * thresholds - struct of thresholds to set
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_als_thresh(FAR struct apds9922_dev_s *priv,
+ FAR struct adps9922_als_thresh
+ thresholds)
+{
+ int res_index = priv->als_setup.res;
+ uint32_t threshmax = als_data[res_index].maxval;
+ int ret;
+ uint8_t data[8];
+
+ /* Make the values are within the current device resolution setting */
+
+ if (thresholds.upper > threshmax)
+ {
+ snerr(
+ "ALS upper threshold out of range: %" PRIu32 ", max: %" PRIu32 "\n",
+ thresholds.upper, threshmax);
+ return -EINVAL;
+ }
+
+ if (thresholds.lower > threshmax)
+ {
+ snerr(
+ "ALS lower threshold out of range: %" PRIu32 ", max: %" PRIu32 "\n",
+ thresholds.lower, threshmax);
+ return -EINVAL;
+ }
+
+ APDS9922_UNPACK_FROM_UINT32(thresholds.upper, data);
+ APDS9922_UNPACK_FROM_UINT32(thresholds.lower, data + 3);
+
+ ret = apds9922_i2c_write(priv, APDS9922_ALS_THRESHU, data, 6);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->als_setup.thresh = thresholds;
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: apds9922_als_variance
+ *
+ * Description:
+ * Sets the ALS threshold variance.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * variance - the value to set
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_als_variance(FAR struct apds9922_dev_s *priv,
+ int variance)
+{
+ int ret;
+
+ if (variance > ALS_VAR1024)
+ {
+ return -EINVAL;
+ }
+
+ ret = apds9922_i2c_write8(priv, APDS9922_ALS_THRESH_VAR, variance);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->als_setup.thresh_var = variance;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_als_persistance
+ *
+ * Description:
+ * Set the number of consecutive int events needed before int is asserted.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * persistance - number of values to be out of range before int asserted
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_als_persistance(FAR struct apds9922_dev_s *priv,
+ uint8_t persistance)
+{
+ uint8_t regval;
+ int ret;
+
+ if (persistance > ALS_PERSISTANCE_MAX)
+ {
+ return -EINVAL;
+ }
+
+ ret = apds9922_i2c_read8(priv, APDS9922_INT_PERSIST, ®val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ regval &= ~ALS_PERSISTANCE_MASK;
+ regval |= ALS_SET_PERSISTANCE(persistance);
+ ret = apds9922_i2c_write8(priv, APDS9922_INT_PERSIST, regval);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->als_setup.persistance = persistance;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds_als_measure_rate
+ *
+ * Description:
+ * Set the measurement rate required.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * rate - measurement rate required
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_als_rate(FAR struct apds9922_dev_s *priv, int rate)
+{
+ uint8_t regval;
+ int ret;
+
+ if (rate > ALS_RATE4000MS)
+ {
+ return -EINVAL;
+ }
+
+ ret = apds9922_i2c_read8(priv, APDS9922_ALS_MEAS_RATE, ®val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ regval &= ~ALS_MEASURERATE_MASK;
+ regval |= ALS_SET_MEASURERATE(rate);
+ ret = apds9922_i2c_write8(priv, APDS9922_ALS_MEAS_RATE, regval);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->als_setup.rate = rate;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_autogain
+ *
+ * Description:
+ * Enables/disables gain range adjustment.
+ * This keeps the ADC counts in optimum range and starts with max gain
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * enable - enable/disable autogain
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_autogain(FAR struct apds9922_dev_s *priv,
+ bool enable)
+{
+ int ret;
+
+ ret = apds9922_als_gain(priv, ALS_GAINX18);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->als_setup.autogain = enable;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_als_gain
+ *
+ * Description:
+ * Sets the ALS gain.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * gain - the gain to set
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_als_gain(FAR struct apds9922_dev_s *priv, int gain)
+{
+ int ret;
+
+ if (gain > ALS_GAINX18)
+ {
+ return -EINVAL;
+ }
+
+ ret = apds9922_i2c_write8(priv, APDS9922_ALS_GAIN, gain);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->als_setup.gain = gain;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_lux_calc
+ *
+ * Description:
+ * Calculate lux value from the current als value.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * als - the raw value of als from the sensor to work with
+ *
+ *
+ * Returned Value:
+ * Calculated lux or -errno if failed
+ *
+ ****************************************************************************/
+
+static int apds9922_lux_calc(FAR struct apds9922_dev_s *priv)
+{
+ uint32_t lux;
+ int thresh_h;
+ int thresh_l;
+ int ret;
+ uint32_t als = (uint32_t)priv->als;
+ int gain_idx = priv->als_setup.gain;
+ uint32_t gain = als_data[gain_idx].gain;
+ int res_idx = priv->als_setup.res;
+ uint32_t limit = priv->als_setup.range_lim;
+ uint32_t factor = priv->als_setup.als_factor;
+ uint32_t res = als_data[res_idx].rate;
+ uint32_t fs = als_data[res_idx].maxval;
+
+ lux = (als * factor) / (res * gain);
+
+ thresh_l = (fs * limit) / 100;
+ thresh_h = (fs * (100 - limit)) / 100;
+
+ if (priv->als_setup.autogain)
+ {
+ /* Ensure ALS gain is optimised to keep als value within "range_lim %"
+ * of the maximum range of the ADC, as determined by the resolution
+ * setting (and above 0 by the same amount).
+ */
+
+ gain_idx = priv->als_setup.gain;
+ if (als >= thresh_h)
+ {
+ if (gain_idx > ALS_GAINX1)
+ {
+ gain_idx--;
+ }
+ }
+ else if (als < thresh_l)
+ {
+ if (gain_idx < ALS_GAINX18)
+ {
+ gain_idx++;
+ }
+ }
+
+ if (gain_idx != priv->als_setup.gain)
+ {
+ ret = apds9922_als_gain(priv, gain);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->als_setup.gain = gain_idx;
+ sninfo("Auto gain changed ok: %" PRIu32 "\n", gain);
+ }
+ }
+
+ return (int)lux;
+}
+
+/* Proximity sensor functions */
+
+/****************************************************************************
+ * Name: apds_ps_config
+ *
+ * Description:
+ * Set the measurement resolution required.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * config - pointer to the apds9922_ps_setup_s config struct
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_config(FAR struct apds9922_dev_s *priv,
+ FAR struct apds9922_ps_setup_s *config)
+{
+ int ret;
+
+ ret = apds9922_ps_resolution(priv, config->res);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_ps_rate(priv, config->rate);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_ps_ledf(priv, config->led_f) ;
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_ps_ledi(priv, config->led_i) ;
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_ps_ledpk(priv, config->led_pk_on) ;
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_ps_pulses(priv, config->pulses) ;
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_ps_thresh(priv, config->thresh) ;
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_ps_canc_lev(priv, config->cancel_lev) ;
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_ps_persistance(priv, config->persistance) ;
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_ps_notify_mode(priv, config->notify) ;
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = apds9922_ps_int_mode(priv, config->int_mode) ;
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds_ps_resolution
+ *
+ * Description:
+ * Set the measurement resolution required.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * res - resolution to be used
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_resolution(FAR struct apds9922_dev_s *priv, int res)
+{
+ int ret;
+ uint8_t regval;
+
+ if (res > PS_RES11)
+ {
+ return -EINVAL;
+ }
+
+ ret = apds9922_i2c_read8(priv, APDS9922_PS_MEAS_RATE, ®val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ regval &= ~PS_RESOLUTION_MASK;
+ regval |= PS_SET_RESOLUTION(res);
+ ret = apds9922_i2c_write8(priv, APDS9922_PS_MEAS_RATE, regval);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->ps_setup.res = res;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds_ps_measure_rate
+ *
+ * Description:
+ * Set the measurement rate required.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * rate - measurement rate required
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_rate(FAR struct apds9922_dev_s *priv, int rate)
+{
+ uint8_t regval;
+ int ret;
+
+ if (rate > PS_RATE400MS)
+ {
+ return -EINVAL;
+ }
+
+ ret = apds9922_i2c_read8(priv, APDS9922_PS_MEAS_RATE, ®val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ regval &= ~PS_MEASURERATE_MASK;
+ regval |= PS_SET_MEASURERATE(rate);
+ ret = apds9922_i2c_write8(priv, APDS9922_PS_MEAS_RATE, regval);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->ps_setup.rate = rate;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_ledf
+ *
+ * Description:
+ * Set the LED pulse modulation rate required.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * freq - LED frequency
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_ledf(FAR struct apds9922_dev_s *priv, int freq)
+{
+ uint8_t regval;
+ int ret;
+
+ if ((freq > PS_LED_FREQ100K) || (freq < PS_LED_FREQ60K))
+ {
+ return -EINVAL;
+ }
+
+ ret = apds9922_i2c_read8(priv, APDS9922_PS_LED, ®val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ regval &= ~PS_LED_FREQ_MASK;
+ regval |= PS_SET_LED_FREQ(freq);
+ ret = apds9922_i2c_write8(priv, APDS9922_PS_LED, regval);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->ps_setup.led_f = freq;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_ledi
+ *
+ * Description:
+ * Set the LED current required.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * rate - LED current
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_ledi(FAR struct apds9922_dev_s *priv, int current)
+{
+ uint8_t regval;
+ int ret;
+
+ if (current > PS_LED_CURRENT125MA)
+ {
+ return -EINVAL;
+ }
+
+ ret = apds9922_i2c_read8(priv, APDS9922_PS_LED, ®val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ regval &= ~PS_LED_CURRENT_MASK;
+ regval |= PS_SET_LED_CURRENT(current);
+ ret = apds9922_i2c_write8(priv, APDS9922_PS_LED, regval);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->ps_setup.led_i = current;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_ledpk
+ *
+ * Description:
+ * Turn LED peaking on/off.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * enable - enable or disable peaking
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_ledpk(FAR struct apds9922_dev_s *priv, bool enable)
+{
+ uint8_t regval;
+ int ret;
+
+ ret = apds9922_i2c_read8(priv, APDS9922_PS_LED, ®val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (enable)
+ {
+ regval |= PS_LED_PEAKING_ON;
+ }
+ else
+ {
+ regval &= ~PS_LED_PEAKING_ON;
+ }
+
+ ret = apds9922_i2c_write8(priv, APDS9922_PS_LED, regval);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->ps_setup.led_pk_on = enable;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_pulses
+ *
+ * Description:
+ * Set the number of LED pulses.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * num_p - the number of pulses
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_pulses(FAR struct apds9922_dev_s *priv, uint8_t num_p)
+{
+ int ret;
+
+ ret = apds9922_i2c_write8(priv, APDS9922_PS_PULSES, num_p);
+
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->ps_setup.pulses = num_p;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_thresh
+ *
+ * Description:
+ * Sets the PS thresholds, upper and lower.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * thresholds - struct of thresholds to set
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_thresh(FAR struct apds9922_dev_s *priv,
+ FAR struct adps9922_ps_thresh thresholds)
+{
+ int res_index = priv->ps_setup.res;
+ uint32_t threshmax = 256 << res_index;
+ int ret;
+ uint8_t data[4];
+
+ /* Make the values are within the current device resolution setting */
+
+ if (thresholds.upper > threshmax)
+ {
+ snerr("ERROR: ps upper threshold out of range: %d, max: %" PRIu32 "\n",
+ thresholds.upper, threshmax);
+ return -EINVAL;
+ }
+
+ if (thresholds.lower > threshmax)
+ {
+ snerr("ERROR: ps lower threshold out of range: %d, max: %" PRIu32 "\n",
+ thresholds.lower, threshmax);
+ return -EINVAL;
+ }
+
+ APDS9922_UNPACK_FROM_UINT16(thresholds.upper, data);
+ APDS9922_UNPACK_FROM_UINT16(thresholds.lower, data + 2);
+
+ ret = apds9922_i2c_write(priv, APDS9922_PS_THRESHU, data, 4);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->ps_setup.thresh = thresholds;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_canc_lev
+ *
+ * Description:
+ * Sets the PS cancellation level to compensate for reading when nothing is
+ * near to the sensor, due to housing. overlays, etc.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * lev - struct of thresholds to set
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_canc_lev(FAR struct apds9922_dev_s *priv,
+ uint16_t lev)
+{
+ int ret;
+ int res_index = priv->ps_setup.res;
+ int levmax = 256 << res_index;
+ uint8_t data[2];
+
+ /* Make the values are within the current device resolution setting */
+
+ if (lev > levmax)
+ {
+ return -EINVAL;
+ }
+
+ APDS9922_UNPACK_FROM_UINT16(lev, data);
+ ret = apds9922_i2c_write(priv, APDS9922_CANCEL_LVLL, data, 2);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->ps_setup.cancel_lev = lev;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_int_mode
+ *
+ * Description:
+ * Sets the PS interrupt mode - disabled, logic or normal.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * channel - interrupt mode
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_int_mode(FAR struct apds9922_dev_s *priv, int mode)
+{
+ int ret;
+ uint8_t regval;
+
+ if (mode > PS_INT_MODE_NORMAL)
+ {
+ return -EINVAL;
+ }
+
+ ret = apds9922_i2c_read8(priv, APDS9922_INT_CFG, ®val);
+
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ regval &= ~PS_INT_MASK;
+
+ switch (mode)
+ {
+ case PS_INT_MODE_NORMAL:
+ regval |= PS_LOGIC_MODE_NORMAL | PS_INT_EN;
+ break;
+ case PS_INT_MODE_LOGIC:
+ regval |= PS_LOGIC_MODE_LOGIC | PS_INT_EN;
+ break;
+ case PS_INT_MODE_DISABLED:
+ default:
+ break;
+ }
+
+ ret = apds9922_i2c_write8(priv, APDS9922_INT_CFG, regval);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->ps_setup.int_mode = mode;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_persistance
+ *
+ * Description:
+ * Set the number of consecutive int events needed before int is asserted.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * persistance - number of values to be out of range before int asserted
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_persistance(FAR struct apds9922_dev_s *priv,
+ uint8_t persistance)
+{
+ uint8_t regval;
+ int ret;
+
+ if (persistance > PS_PERSISTANCE_MAX)
+ {
+ return -EINVAL;
+ }
+
+ ret = apds9922_i2c_read8(priv, APDS9922_INT_PERSIST, ®val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ regval &= ~PS_PERSISTANCE_MASK;
+ regval |= PS_SET_PERSISTANCE(persistance);
+ ret = apds9922_i2c_write8(priv, APDS9922_INT_PERSIST, regval);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ priv->ps_setup.persistance = persistance;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_notify_mode
+ *
+ * Description:
+ * Set the rules for poll notify: proxmity value, far/close, or both.
+ *
+ * Input Parameters:
+ * priv - pointer to device structure
+ * notify - number of values to be out of range before int asserted
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_notify_mode(FAR struct apds9922_dev_s *priv,
+ int notify)
+{
+ if (notify > PS_FAR_OR_CLOSE_ONLY)
+ {
+ return -EINVAL;
+ }
+
+ priv->ps_setup.notify = notify;
+
+ return OK;
+}
+
+/* i2c helper functions */
+
+/****************************************************************************
+ * Name: apds9922_i2c_read
+ *
+ * Description:
+ * Read an arbitrary number of bytes starting at regaddr
+ *
+ ****************************************************************************/
+
+static int apds9922_i2c_read(FAR struct apds9922_dev_s *priv,
+ uint8_t const regaddr,
+ FAR uint8_t *regval, int len)
+{
+ struct i2c_config_s config;
+ int ret;
+ irqstate_t flags;
+
+ DEBUGASSERT(priv);
+
+ /* Set up the I2C configuration */
+
+ config.frequency = CONFIG_APDS9922_I2C_FREQUENCY;
+ config.address = priv->config->i2c_addr;
+ config.addrlen = 7;
+
+ /* Write the register address to read from */
+
+ flags = spin_lock_irqsave(NULL);
+ ret = i2c_write(priv->config->i2c, &config, ®addr, 1);
+ spin_unlock_irqrestore(NULL, flags);
+ if (ret < 0)
+ {
+ snerr ("i2c_write failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Read "len" bytes from regaddr */
+
+ flags = spin_lock_irqsave(NULL);
+ ret = i2c_read(priv->config->i2c, &config, regval, len);
+ spin_unlock_irqrestore(NULL, flags);
+ if (ret < 0)
+ {
+ snerr ("i2c_read failed: %d\n", ret);
+ return ret;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_i2c_read8
+ *
+ * Description:
+ * Read 8-bit register
+ *
+ ****************************************************************************/
+
+static int apds9922_i2c_read8(FAR struct apds9922_dev_s *priv,
+ uint8_t const regaddr, FAR uint8_t *regval)
+{
+ int ret;
+
+ ret = apds9922_i2c_read(priv, regaddr, regval, 1);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: apds9922_i2c_write
+ *
+ * Description:
+ * Write an arbitrary number of bytes starting at regaddr.
+ *
+ ****************************************************************************/
+
+static int apds9922_i2c_write(FAR struct apds9922_dev_s *priv,
+ uint8_t const regaddr,
+ FAR uint8_t const *data, int len)
+{
+ struct i2c_config_s config;
+ int ret;
+ irqstate_t flags;
+ uint8_t *buffer;
+
+ buffer = (uint8_t *)kmm_malloc((len + 1) * sizeof(uint8_t));
+ if (!buffer)
+ {
+ snerr("ERROR: Failed to create i2c write buffer space\n");
+ return -ENOMEM;
+ }
+
+ /* Set up the I2C configuration */
+
+ config.frequency = CONFIG_APDS9922_I2C_FREQUENCY;
+ config.address = priv->config->i2c_addr;
+ config.addrlen = 7;
+
+ buffer[0] = regaddr;
+ memcpy(&buffer[1], data, len);
+
+ /* Write the data */
+
+ flags = spin_lock_irqsave(NULL);
+ ret = i2c_write(priv->config->i2c, &config, buffer, len + 1);
+ spin_unlock_irqrestore(NULL, flags);
+ if (ret < 0)
+ {
+ snerr("ERROR: i2c_write failed: %d\n", ret);
+ }
+
+ kmm_free(buffer);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: apds9922_i2c_write8
+ *
+ * Description:
+ * Write an single byte of date to regaddr.
+ *
+ ****************************************************************************/
+
+static int apds9922_i2c_write8(FAR struct apds9922_dev_s *priv,
+ uint8_t const regaddr, uint8_t regval)
+{
+ int ret;
+
+ ret = apds9922_i2c_write(priv, regaddr, ®val, 1);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: apds9922_open
+ *
+ * Description:
+ * Standard character driver close method.
+ *
+ * Input Parameters:
+ * filep - file structure pointer
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_open(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct apds9922_dev_s *priv = inode->i_private;
+ int ret;
+
+ ret = nxmutex_lock(&priv->devlock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (priv->crefs == 0)
+ {
+ priv->config->irq_attach(priv->config, apds9922_int_handler, priv);
+ priv->config->irq_enable(priv->config, true);
+ }
+
+ priv->crefs++;
+
+ nxmutex_unlock(&priv->devlock);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_close
+ *
+ * Description:
+ * Standard character driver close method.
+ *
+ * Input Parameters:
+ * filep - file structure pointer
+ *
+ * Returned Value:
+ * Success or failure
+ *
+ ****************************************************************************/
+
+static int apds9922_close(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct apds9922_dev_s *priv = inode->i_private;
+ int ret;
+
+ ret = nxmutex_lock(&priv->devlock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ DEBUGASSERT(priv->crefs > 0);
+
+ if (priv->crefs == 0)
+ {
+ apds9922_reset(priv);
+ apds9922_als_int_mode(priv, ALS_INT_MODE_DISABLED);
+ apds9922_ps_int_mode(priv, PS_INT_MODE_DISABLED);
+ priv->config->irq_detach(priv->config);
+ }
+
+ nxmutex_unlock(&priv->devlock);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: apds9922_als_read
+ *
+ * Description:
+ * Standard character driver read method.
+ *
+ * Input Parameters:
+ * filep - File structure pointer
+ * buffer - Buffer to write
+ * buflen - The write length of the buffer
+ *
+ * Returned Value:
+ * Size of buffer read
+ *
+ ****************************************************************************/
+
+static ssize_t apds9922_als_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen)
+{
+ FAR struct inode *inode;
+
+ FAR struct apds9922_dev_s *priv;
+ int *ptr;
+ int ret;
+
+ DEBUGASSERT(filep);
+
+ inode = filep->f_inode;
+ priv = inode->i_private;
+
+ DEBUGASSERT(inode && inode->i_private);
+
+ ret = nxmutex_lock(&priv->devlock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (buflen < 1)
+ {
+ snerr("ERROR: Buffer not large enough to read data\n");
+ return (ssize_t)-EINVAL;
+ }
+
+ ptr = (int *)buffer;
+
+ if (priv->als < 0)
+ {
+ *ptr = priv->als;
+ }
+ else
+ {
+ *ptr = apds9922_lux_calc(priv);
+ }
+
+ nxmutex_unlock(&priv->devlock);
+
+ return buflen;
+}
+
+/****************************************************************************
+ * Name: apds9922_als_write
+ *
+ * Description:
+ * Standard character driver write method.
+ *
+ * Input Parameters:
+ * filep - File structure pointer
+ * buffer - Buffer to write
+ * buflen - The write length of the buffer
+ *
+ * Returned Value:
+ * -ENOSYS - this driver does not support the write method
+ *
+ ****************************************************************************/
+
+static ssize_t apds9922_als_write(FAR struct file *filep,
+ FAR const char *buffer, size_t buflen)
+{
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: apds9922_als_ioctl
+ *
+ * Description:
+ * This routine is called when ioctl function call is performed for
+ * the ambient light sensor of the apds9922 device.
+ *
+ * Input Parameters:
+ * filep - file structure pointer.
+ * cmd - The IOCTL command.
+ * arg - The argument of the IOCTL command.
+ *
+ * Returned Value:
+ * Returns OK or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int apds9922_als_ioctl(FAR struct file *filep, int cmd,
+ unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct apds9922_dev_s *priv = inode->i_private;
+ int ret;
+ FAR uint8_t *ptr;
+
+ static struct apds9922_als_setup_s *als_setup;
+
+ ret = nxmutex_lock(&priv->devlock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ sninfo("cmd: 0x%" PRIx16 ", arg: %ld\n", cmd, arg);
+
+ switch (cmd)
+ {
+ case SNIOC_RESET:
+ ret = apds9922_reset(priv);
+ break;
+ case SNIOC_ALS_CONFIG:
+ als_setup = (struct apds9922_als_setup_s *)arg;
+ ret = apds9922_als_config(priv, als_setup);
+ break;
+ case SNIOC_GET_DEV_ID:
+ {
+ ptr = (FAR uint8_t *)arg;
+ DEBUGASSERT(ptr != NULL);
+ *ptr = priv->devid;
+ ret = OK;
+ }
+ break;
+ default:
+ {
+ snerr("ERROR: Unrecognized cmd: %x\n", cmd);
+ ret = -ENOTTY;
+ }
+ break;
+ }
+
+ nxmutex_unlock(&priv->devlock);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: apds9922_als_poll
+ *
+ * Description:
+ * Standard character driver poll method
+ *
+ * Input Parameters:
+ * filep - file structure pointer
+ * fds - Array of file descriptor
+ * setup - 1 if start poll, 0 if stop poll
+ *
+ * Returned Value:
+ * Returns OK or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int apds9922_als_poll(FAR struct file *filep,
+ FAR struct pollfd *fds, bool setup)
+{
+ FAR struct inode *inode;
+ FAR struct apds9922_dev_s *priv;
+ int ret;
+ int i;
+
+ DEBUGASSERT(filep && fds);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct apds9922_dev_s *)inode->i_private;
+
+ ret = nxmutex_lock(&priv->devlock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (setup)
+ {
+ /* Ignore waits that do not include POLLIN */
+
+ if ((fds->events & POLLIN) == 0)
+ {
+ ret = -EDEADLK;
+ goto out;
+ }
+
+ /* This is a request to set up the poll. Find an available
+ * slot for the poll structure reference.
+ */
+
+ for (i = 0; i < CONFIG_APDS9922_ALS_NPOLLWAITERS; i++)
+ {
+ /* Find an available slot */
+
+ if (!priv->fds_als[i])
+ {
+ /* Bind the poll structure and this slot */
+
+ priv->fds_als[i] = fds;
+ fds->priv = &priv->fds_als[i];
+ break;
+ }
+ }
+
+ if (i >= CONFIG_APDS9922_ALS_NPOLLWAITERS)
+ {
+ fds->priv = NULL;
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+ else if (fds->priv)
+ {
+ /* This is a request to tear down the poll. */
+
+ struct pollfd **slot = (struct pollfd **)fds->priv;
+ DEBUGASSERT(slot != NULL);
+
+ /* Remove all memory of the poll setup */
+
+ *slot = NULL;
+ fds->priv = NULL;
+ }
+
+out:
+ nxmutex_unlock(&priv->devlock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_read
+ *
+ * Description:
+ * Standard character driver read method.
+ *
+ * Input Parameters:
+ * filep - File structure pointer
+ * buffer - Buffer to write
+ * buflen - The write length of the buffer
+ *
+ * Returned Value:
+ * Size of buffer read
+ *
+ ****************************************************************************/
+
+static ssize_t apds9922_ps_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct apds9922_dev_s *priv = inode->i_private;
+ FAR struct apds9922_ps_data *ptr;
+ int ret;
+
+ DEBUGASSERT(filep);
+
+ DEBUGASSERT(inode && inode->i_private);
+
+ if (buflen < sizeof(struct apds9922_ps_data))
+ {
+ snerr("ERROR: Buffer not large enough to read data\n");
+ return (ssize_t)-EINVAL;
+ }
+
+ ptr = (struct apds9922_ps_data *)buffer;
+
+ ret = nxmutex_lock(&priv->devlock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ *ptr = *priv->ps_data;
+
+ nxmutex_unlock(&priv->devlock);
+
+ return buflen;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_write
+ *
+ * Description:
+ * Standard character driver write method.
+ *
+ * Input Parameters:
+ * filep - File structure pointer
+ * buffer - Buffer to write
+ * buflen - The write length of the buffer
+ *
+ * Returned Value:
+ * -ENOSYS - this driver does not support the write method
+ *
+ ****************************************************************************/
+
+static ssize_t apds9922_ps_write(FAR struct file *filep,
+ FAR const char *buffer, size_t buflen)
+{
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_ioctl
+ *
+ * Description:
+ * This routine is called when ioctl function call is performed for
+ * the proximity sensor of the apds9922 device.
+ *
+ * Input Parameters:
+ * filep - file structure pointer.
+ * cmd - The IOCTL command.
+ * arg - The argument of the IOCTL command.
+ *
+ * Returned Value:
+ * Returns OK or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_ioctl(FAR struct file *filep, int cmd,
+ unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct apds9922_dev_s *priv = inode->i_private;
+ int ret;
+ FAR uint8_t *ptr;
+ static struct apds9922_ps_setup_s *ps_setup;
+
+ ret = nxmutex_lock(&priv->devlock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ sninfo("cmd: 0x%02X, arg:%lu\n", cmd, arg);
+
+ switch (cmd)
+ {
+ case SNIOC_PS_CONFIG:
+ ps_setup = (struct apds9922_ps_setup_s *)arg;
+ ret = OK;
+ ret = apds9922_ps_config(priv, ps_setup);
+ break;
+ case SNIOC_GET_DEV_ID:
+ {
+ ptr = (FAR uint8_t *)arg;
+ DEBUGASSERT(ptr != NULL);
+ *ptr = priv->devid;
+ ret = OK;
+ }
+ break;
+ case SNIOC_PS_CANC_LVL:
+ {
+ ret = apds9922_ps_canc_lev(priv, (uint16_t)arg);
+ }
+ break;
+ default:
+ {
+ snerr("ERROR: Unrecognized cmd: %x\n", cmd);
+ ret = -ENOTTY;
+ }
+ break;
+ }
+
+ nxmutex_unlock(&priv->devlock);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: apds9922_ps_poll
+ *
+ * Description:
+ * Standard character driver poll method
+ *
+ * Input Parameters:
+ * filep - file structure pointer
+ * fds - Array of file descriptor
+ * setup - 1 if start poll, 0 if stop poll
+ *
+ * Returned Value:
+ * Returns OK or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int apds9922_ps_poll(FAR struct file *filep,
+ FAR struct pollfd *fds, bool setup)
+{
+ FAR struct inode *inode;
+ FAR struct apds9922_dev_s *priv;
+ int ret;
+ int i;
+
+ DEBUGASSERT(filep && fds);
+ inode = filep->f_inode;
+
+ DEBUGASSERT(inode && inode->i_private);
+ priv = (FAR struct apds9922_dev_s *)inode->i_private;
+
+ ret = nxmutex_lock(&priv->devlock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (setup)
+ {
+ /* Ignore waits that do not include POLLIN */
+
+ if ((fds->events & POLLIN) == 0)
+ {
+ ret = -EDEADLK;
+ goto out;
+ }
+
+ /* This is a request to set up the poll. Find an available
+ * slot for the poll structure reference.
+ */
+
+ for (i = 0; i < CONFIG_APDS9922_PS_NPOLLWAITERS; i++)
+ {
+ /* Find an available slot */
+
+ if (!priv->fds_ps[i])
+ {
+ /* Bind the poll structure and this slot */
+
+ priv->fds_ps[i] = fds;
+ fds->priv = &priv->fds_ps[i];
+ break;
+ }
+ }
+
+ if (i >= CONFIG_APDS9922_PS_NPOLLWAITERS)
+ {
+ fds->priv = NULL;
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+ else if (fds->priv)
+ {
+ /* This is a request to tear down the poll. */
+
+ struct pollfd **slot = (struct pollfd **)fds->priv;
+ DEBUGASSERT(slot != NULL);
+
+ /* Remove all memory of the poll setup */
+
+ *slot = NULL;
+ fds->priv = NULL;
+ }
+
+out:
+ nxmutex_unlock(&priv->devlock);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: apds9922_register
+ *
+ * Description:
+ * Register the APDS9922 character devices.
+ *
+ * Input Parameters:
+ * devpath_als - The full path to the driver to register for the als,
+ * e.g., "/dev/als0". If NULL the dreiver will not be registered.
+ *
+ * devpath_ps - The full path to the driver to register for the als,
+ * e.g., "/dev/ps0". If NULL the dreiver will not be registered.
+ *
+ * config - Pointer to the device configuration
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int apds9922_register(FAR const char *devpath_als,
+ FAR const char *devpath_ps,
+ FAR struct apds9922_config_s *config)
+{
+ int ret;
+ uint8_t regval;
+
+ /* Initialize the APDS9922 device structure */
+
+ FAR struct apds9922_dev_s *priv;
+
+ priv = kmm_zalloc(sizeof(*priv));
+
+ if (priv == NULL)
+ {
+ snerr("ERROR: Failed to allocate instance\n");
+ return -ENOMEM;
+ }
+
+ nxmutex_init(&priv->devlock);
+
+ priv->config = config;
+
+ /* Reset the device to make it sane */
+
+ apds9922_reset(priv);
+
+ /* Probe APDS9922 device */
+
+ ret = apds9922_probe(priv);
+ if (ret < 0)
+ {
+ goto err_out;
+ }
+
+ /* Enable ALS and/or PS */
+
+ regval = (devpath_ps != NULL) ? PS_ACTIVE : 0;
+ regval |= (devpath_als != NULL) ? ALS_ACTIVE : regval;
+ ret = apds9922_i2c_write8(priv, APDS9922_MAIN_CTRL, regval);
+ if (ret < 0)
+ {
+ snerr("ERROR: Failed to enable als and/or ps.\n");
+ goto err_out;
+ }
+
+ /* device interrupts are enabled by default. Disable them. */
+
+ ret = apds9922_als_int_mode(priv, ALS_INT_MODE_DISABLED);
+ if (ret < 0)
+ {
+ snerr("ERROR: Failed to disable ALS interrupts.\n");
+ goto err_out;
+ }
+
+ apds9922_ps_int_mode(priv, PS_INT_MODE_DISABLED);
+ if (ret < 0)
+ {
+ snerr("ERROR: Failed to disable PS interrupts.\n");
+ goto err_out;
+ }
+
+ /* Register the character driver */
+
+ if (devpath_als != NULL)
+ {
+ ret = register_driver(devpath_als, &g_apds9922_alsfops, 0666, priv);
+ if (ret < 0)
+ {
+ snerr("ERROR: Failed to register driver %s: %d\n",
+ devpath_als, ret);
+
+ goto err_out;
+ }
+ }
+
+ if (devpath_ps != NULL)
+ {
+ ret = register_driver(devpath_ps, &g_apds9922_psfops, 0666, priv);
+ if (ret < 0)
+ {
+ snerr("ERROR: Failed to register driver %s: %d\n",
+ devpath_ps, ret);
+ goto err_out;
+ }
+ }
+
+ priv->ps_data->close = false;
+ priv->ps_data->ps = 0;
+ priv->als = 0;
+ priv->crefs = 0;
+
+ return OK;
+
+err_out:
+ kmm_free(priv);
+ return ret;
+}
+
+#endif /* CONFIG_I2C && CONFIG_SENSORS_APDS9922 */
diff --git a/include/nuttx/sensors/apds9922.h b/include/nuttx/sensors/apds9922.h
new file mode 100644
index 0000000000..a5e2927c15
--- /dev/null
+++ b/include/nuttx/sensors/apds9922.h
@@ -0,0 +1,276 @@
+/****************************************************************************
+ * include/nuttx/sensors/apds9922.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_APDS9922_H
+#define __INCLUDE_NUTTX_SENSORS_APDS9922_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/irq.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/sensors/ioctl.h>
+
+#if defined(CONFIG_SENSORS_APDS9922)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+#define APDS9922_I2C_ADDR (0x53)
+#define APDS9922_ID_VAL (0xb3)
+
+#define PS_DEF_THRESHU (0x07ff)
+#define PS_DEF_THRESHL (0)
+#define PS_DEF_CANCEL_LVL (0)
+#define PS_DEF_PULSES (0x08)
+#define ALS_DEF_THRESHU (0x0fffff)
+#define ALS_DEF_THRESHL (0)
+#define PS_DEF_PERSISTANCE (0)
+#define ALS_DEF_PERSISTANCE (0)
+#define ALS_DEF_VAR (0)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+enum
+{
+ ALS_RATE25MS,
+ ALS_RATE50MS,
+ ALS_RATE100MS,
+ ALS_RATE200MS,
+ ALS_RATE500MS,
+ ALS_RATE1000MS,
+ ALS_RATE2000MS,
+ ALS_RATE4000MS,
+};
+
+enum
+{
+ ALS_RES400MS,
+ ALS_RES200MS,
+ ALS_RES100MS,
+ ALS_RES50MS,
+ ALS_RES25MS,
+};
+
+enum
+{
+ ALS_GAINX1,
+ ALS_GAINX3,
+ ALS_GAINX6,
+ ALS_GAINX9,
+ ALS_GAINX18,
+};
+
+enum
+{
+ ALS_VAR8,
+ ALS_VAR16,
+ ALS_VAR32,
+ ALS_VAR64,
+ ALS_VAR128,
+ ALS_VAR256,
+ ALS_VAR512,
+ ALS_VAR1024,
+};
+
+enum
+{
+ ALS_IR,
+ ALS_VISIBLE,
+};
+
+enum
+{
+ PS_RATE_RESERVED,
+ PS_RATE6MS25,
+ PS_RATE12MS5,
+ PS_RATE25MS,
+ PS_RATE50MS,
+ PS_RATE100MS,
+ PS_RATE200MS,
+ PS_RATE400MS,
+};
+
+enum
+{
+ PS_RES8,
+ PS_RES9,
+ PS_RES10,
+ PS_RES11,
+};
+
+enum
+{
+ PS_LED_FREQ60K = 3,
+ PS_LED_FREQ70K,
+ PS_LED_FREQ80K,
+ PS_LED_FREQ90K,
+ PS_LED_FREQ100K,
+};
+
+enum
+{
+ PS_LED_CURRENT2MA5,
+ PS_LED_CURRENT5MA,
+ PS_LED_CURRENT10MA,
+ PS_LED_CURRENT25MA,
+ PS_LED_CURRENT50MA,
+ PS_LED_CURRENT75MA,
+ PS_LED_CURRENT100MA,
+ PS_LED_CURRENT125MA,
+};
+
+enum
+{
+ ALS_INT_MODE_DISABLED,
+ ALS_INT_MODE_THRESHOLD,
+ ALS_INT_MODE_VARIANCE,
+};
+
+enum
+{
+ PS_INT_MODE_DISABLED,
+ PS_INT_MODE_LOGIC,
+ PS_INT_MODE_NORMAL,
+};
+
+enum
+{
+ PS_ALL_INFO,
+ PS_PROXIMITY_DATA_ONLY,
+ PS_FAR_OR_CLOSE_ONLY,
+};
+
+/* Interrupt configuration data structure */
+
+struct apds9922_config_s
+{
+ int (*irq_attach)(FAR struct apds9922_config_s *state, xcpt_t isr,
+ FAR void *arg);
+ void (*irq_enable)(FAR struct apds9922_config_s *state, bool enable);
+ FAR struct i2c_master_s *i2c;
+ uint8_t i2c_addr;
+};
+
+struct adps9922_als_thresh
+{
+ uint32_t upper; /* Upper threshold */
+ uint32_t lower; /* Lower threshold */
+};
+
+struct adps9922_ps_thresh
+{
+ uint16_t upper; /* Upper threshold */
+ uint16_t lower; /* Lower threshold */
+};
+
+/* ambient light data setup data */
+
+struct apds9922_als_setup_s
+{
+ int rate; /* als measurement rate */
+ int res; /* als resolution */
+ int gain; /* als gain */
+ struct adps9922_als_thresh
+ thresh; /* Upper and lower thresholds */
+ int thresh_var; /* threshold variation */
+ int int_mode; /* Interrupt mode */
+ uint8_t persistance; /* Num events before interrupt */
+ uint32_t als_factor; /* Lux correction factor applied */
+ uint32_t range_lim; /* % limit of ADC full range
+ * allowed in autogain mode.
+ */
+ bool autogain; /* Auto gain mode on/off */
+ int channel; /* Visible or IR light channel */
+};
+
+/* proximity sensor data setup data */
+
+struct apds9922_ps_setup_s
+{
+ int rate; /* Measurement rate */
+ int res; /* Resolution, bits */
+ int led_f; /* LED modulation frequency */
+ bool led_pk_on; /* LED current peaking on/off */
+ int led_i; /* LED pulsed current level */
+ uint8_t pulses; /* Number of LED pulses, 0-32 */
+ struct adps9922_ps_thresh
+ thresh; /* Upper and lower thresholds */
+ uint16_t cancel_lev; /* Intelligent cancellation lev. */
+ int int_mode; /* Interrupt mode */
+ uint8_t persistance; /* Num events before interrupt */
+ int notify; /* States that cause a notify */
+};
+
+/* data that can be read from proximity sensor */
+
+struct apds9922_ps_data
+{
+ uint16_t ps; /* Current prximity measure */
+ bool close; /* Object is far (false) or close */
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: apds9922_register
+ *
+ * Description:
+ * Register the APDS9922 character devices.
+ *
+ * Input Parameters:
+ * devpath_als - The full path to the driver to register for the als,
+ * e.g., "/dev/als0"
+ *
+ * devpath_ps - The full path to the driver to register for the als,
+ * e.g., "/dev/ps0"
+ *
+ * config - Pointer to the device configuration
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int apds9922_register(FAR const char *devpath_als,
+ FAR const char *devpath_ps,
+ FAR struct apds9922_config_s *config);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_SENSORS_APDS9922 */
+#endif /* __INCLUDE_NUTTX_SENSORS_APDS9922_H */
diff --git a/include/nuttx/sensors/ioctl.h b/include/nuttx/sensors/ioctl.h
index 6433313861..94bd1b911f 100644
--- a/include/nuttx/sensors/ioctl.h
+++ b/include/nuttx/sensors/ioctl.h
@@ -348,4 +348,23 @@
#define SNIOC_GET_USTATE _SNIOC(0x0092)
+/* IOCTL commands unique to the APDS9922 sensor.
+ * The sensor detects both ambient light and proximity.
+ */
+
+ /* Args: */
+
+/* SNIOC_RESET - as defined already, above None */
+
+/* SNIOC_GET_DEV_ID - as defined already, above uint8_t* pointer */
+
+#define SNIOC_ALS_CONFIG _SNIOC(0x0093) /* struct
+ * apds9922_als_setup_s* */
+#define SNIOC_PS_CONFIG _SNIOC(0x0094) /* struct
+ * apds9922_ps_setup_s* */
+
+/* Set proximity sensor cancellation level */
+
+#define SNIOC_PS_CANC_LVL _SNIOC(0x0095) /* uint16_t level */
+
#endif /* __INCLUDE_NUTTX_SENSORS_IOCTL_H */