1528b8dcca
Signed-off-by: Petro Karashchenko <petro.karashchenko@gmail.com>
2540 lines
63 KiB
C
2540 lines
63 KiB
C
/****************************************************************************
|
|
* 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/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 */
|
|
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_write(FAR struct apds9922_dev_s *priv,
|
|
uint8_t const regaddr,
|
|
uint8_t const *data, int len);
|
|
|
|
/* 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, uint8_t 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,
|
|
uint8_t 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_read(priv, APDS9922_MAIN_STATUS, &status, 1);
|
|
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;
|
|
const uint8_t reset = APDS9922_SW_RESET;
|
|
|
|
ret = apds9922_i2c_write(priv, APDS9922_MAIN_CTRL, &reset, 1);
|
|
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_read(priv, APDS9922_ID, &id, 1);
|
|
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_read(priv, APDS9922_ALS_MEAS_RATE, ®val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval &= ~ALS_RESOLUTION_MASK;
|
|
regval |= ALS_SET_RESOLUTION(res);
|
|
ret = apds9922_i2c_write(priv, APDS9922_ALS_MEAS_RATE, ®val, 1);
|
|
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_read(priv, APDS9922_INT_CFG, ®val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval &= ~ALS_INT_SRC_MASK;
|
|
regval |= ALS_INT_SET_SRC(channel);
|
|
|
|
ret = apds9922_i2c_write(priv, APDS9922_INT_CFG, ®val, 1);
|
|
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_read(priv, APDS9922_INT_CFG, ®val, 1);
|
|
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_write(priv, APDS9922_INT_CFG, ®val, 1);
|
|
|
|
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,
|
|
uint8_t variance)
|
|
{
|
|
int ret;
|
|
|
|
if (variance > ALS_VAR1024)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = apds9922_i2c_write(priv, APDS9922_ALS_THRESH_VAR, &variance, 1);
|
|
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_read(priv, APDS9922_INT_PERSIST, ®val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval &= ~ALS_PERSISTANCE_MASK;
|
|
regval |= ALS_SET_PERSISTANCE(persistance);
|
|
ret = apds9922_i2c_write(priv, APDS9922_INT_PERSIST, ®val, 1);
|
|
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_read(priv, APDS9922_ALS_MEAS_RATE, ®val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval &= ~ALS_MEASURERATE_MASK;
|
|
regval |= ALS_SET_MEASURERATE(rate);
|
|
ret = apds9922_i2c_write(priv, APDS9922_ALS_MEAS_RATE, ®val, 1);
|
|
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, uint8_t gain)
|
|
{
|
|
int ret;
|
|
|
|
if (gain > ALS_GAINX18)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = apds9922_i2c_write(priv, APDS9922_ALS_GAIN, &gain, 1);
|
|
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_read(priv, APDS9922_PS_MEAS_RATE, ®val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval &= ~PS_RESOLUTION_MASK;
|
|
regval |= PS_SET_RESOLUTION(res);
|
|
ret = apds9922_i2c_write(priv, APDS9922_PS_MEAS_RATE, ®val, 1);
|
|
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_read(priv, APDS9922_PS_MEAS_RATE, ®val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval &= ~PS_MEASURERATE_MASK;
|
|
regval |= PS_SET_MEASURERATE(rate);
|
|
ret = apds9922_i2c_write(priv, APDS9922_PS_MEAS_RATE, ®val, 1);
|
|
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_read(priv, APDS9922_PS_LED, ®val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval &= ~PS_LED_FREQ_MASK;
|
|
regval |= PS_SET_LED_FREQ(freq);
|
|
ret = apds9922_i2c_write(priv, APDS9922_PS_LED, ®val, 1);
|
|
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_read(priv, APDS9922_PS_LED, ®val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval &= ~PS_LED_CURRENT_MASK;
|
|
regval |= PS_SET_LED_CURRENT(current);
|
|
ret = apds9922_i2c_write(priv, APDS9922_PS_LED, ®val, 1);
|
|
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_read(priv, APDS9922_PS_LED, ®val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if (enable)
|
|
{
|
|
regval |= PS_LED_PEAKING_ON;
|
|
}
|
|
else
|
|
{
|
|
regval &= ~PS_LED_PEAKING_ON;
|
|
}
|
|
|
|
ret = apds9922_i2c_write(priv, APDS9922_PS_LED, ®val, 1);
|
|
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_write(priv, APDS9922_PS_PULSES, &num_p, 1);
|
|
|
|
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_read(priv, APDS9922_INT_CFG, ®val, 1);
|
|
|
|
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_write(priv, APDS9922_INT_CFG, ®val, 1);
|
|
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_read(priv, APDS9922_INT_PERSIST, ®val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval &= ~PS_PERSISTANCE_MASK;
|
|
regval |= PS_SET_PERSISTANCE(persistance);
|
|
ret = apds9922_i2c_write(priv, APDS9922_INT_PERSIST, ®val, 1);
|
|
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;
|
|
|
|
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 */
|
|
|
|
ret = i2c_writeread(priv->config->i2c, &config, ®addr, 1, regval, len);
|
|
if (ret < 0)
|
|
{
|
|
snerr ("i2c_writeread failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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;
|
|
uint8_t *buffer;
|
|
|
|
buffer = 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 */
|
|
|
|
ret = i2c_write(priv->config->i2c, &config, buffer, len + 1);
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: i2c_write failed: %d\n", ret);
|
|
}
|
|
|
|
kmm_free(buffer);
|
|
|
|
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;
|
|
|
|
inode = filep->f_inode;
|
|
priv = inode->i_private;
|
|
|
|
DEBUGASSERT(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 = (FAR 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(fds);
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
priv = 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(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(fds);
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
priv = 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_write(priv, APDS9922_MAIN_CTRL, ®val, 1);
|
|
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 */
|