/**************************************************************************** * 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 #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 = (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 */