From 4719e44b8be6557ad78281683661c4f2ce1053b4 Mon Sep 17 00:00:00 2001 From: TimJTi <56726697+TimJTi@users.noreply.github.com> Date: Sat, 11 Feb 2023 12:08:40 +0000 Subject: [PATCH] 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 Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko Update drivers/sensors/apds9922.c Co-authored-by: Petro Karashchenko 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(+) create mode 100644 drivers/sensors/apds9922.c create mode 100644 include/nuttx/sensors/apds9922.h 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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 +#include +#include +#include + +#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 */