diff options
Diffstat (limited to 'firmware/os/drivers/ams_tmd2772/ams_tmd2772.c')
-rw-r--r-- | firmware/os/drivers/ams_tmd2772/ams_tmd2772.c | 676 |
1 files changed, 676 insertions, 0 deletions
diff --git a/firmware/os/drivers/ams_tmd2772/ams_tmd2772.c b/firmware/os/drivers/ams_tmd2772/ams_tmd2772.c new file mode 100644 index 00000000..f37ff594 --- /dev/null +++ b/firmware/os/drivers/ams_tmd2772/ams_tmd2772.c @@ -0,0 +1,676 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed 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. + */ + +#include <stdlib.h> +#include <string.h> +#include <float.h> + +#include <seos.h> +#include <i2c.h> +#include <timer.h> +#include <sensors.h> +#include <heap.h> +#include <hostIntf.h> +#include <nanohubPacket.h> +#include <eventnums.h> + +#define TMD2772_APP_VERSION 1 + +#define DRIVER_NAME "AMS: " + +#define I2C_BUS_ID 0 +#define I2C_SPEED 400000 +#define I2C_ADDR 0x39 + +#define AMS_TMD2772_ID 0x39 + +#define AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT 0xa0 + +#define AMS_TMD2772_REG_ENABLE (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x00) +#define AMS_TMD2772_REG_ATIME (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x01) +#define AMS_TMD2772_REG_PTIME (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x02) +#define AMS_TMD2772_REG_WTIME (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x03) +#define AMS_TMD2772_REG_AILTL (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x04) +#define AMS_TMD2772_REG_AILTH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x05) +#define AMS_TMD2772_REG_AIHTL (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x06) +#define AMS_TMD2772_REG_AIHTH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x07) +#define AMS_TMD2772_REG_PILTL (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x08) +#define AMS_TMD2772_REG_PILTH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x09) +#define AMS_TMD2772_REG_PIHTL (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x0a) +#define AMS_TMD2772_REG_PIHTH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x0b) +#define AMS_TMD2772_REG_PERS (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x0c) +#define AMS_TMD2772_REG_CONFIG (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x0d) +#define AMS_TMD2772_REG_PPULSE (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x0e) +#define AMS_TMD2772_REG_CONTROL (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x0f) +#define AMS_TMD2772_REG_ID (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x12) +#define AMS_TMD2772_REG_STATUS (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x13) +#define AMS_TMD2772_REG_C0DATA (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x14) +#define AMS_TMD2772_REG_C0DATAH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x15) +#define AMS_TMD2772_REG_C1DATA (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x16) +#define AMS_TMD2772_REG_C1DATAH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x17) +#define AMS_TMD2772_REG_PDATAL (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x18) +#define AMS_TMD2772_REG_PDATAH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x19) +#define AMS_TMD2772_REG_POFFSET (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x1E) + +#define AMS_TMD2772_ATIME_SETTING 0xdb +#define AMS_TMD2772_ATIME_MS ((256 - AMS_TMD2772_ATIME_SETTING) * 2.73) // in milliseconds +#define AMS_TMD2772_PTIME_SETTING 0xff +#define AMS_TMD2772_PTIME_MS ((256 - AMS_TMD2772_PTIME_SETTING) * 2.73) // in milliseconds +#define AMS_TMD2772_WTIME_SETTING_ALS_ON 0xdd // (256 - 221) * 2.73 ms = 95.55 ms +#define AMS_TMD2772_WTIME_SETTING_ALS_OFF 0xb8 // (256 - 184) * 2.73 ms = 196.56 ms +#define AMS_TMD2772_PPULSE_SETTING 8 + +#define AMS_TMD2772_CAL_DEFAULT_OFFSET 0 +#define AMS_TMD2772_CAL_MAX_OFFSET 500 + +/* AMS_TMD2772_REG_ENABLE */ +#define POWER_ON_BIT (1 << 0) +#define ALS_ENABLE_BIT (1 << 1) +#define PROX_ENABLE_BIT (1 << 2) +#define WAIT_ENABLE_BIT (1 << 3) + +/* AMS_TMD2772_REG_STATUS */ +#define PROX_INT_BIT (1 << 5) +#define ALS_INT_BIT (1 << 4) +#define PROX_VALID_BIT (1 << 1) +#define ALS_VALID_BIT (1 << 0) + +#define AMS_TMD2772_REPORT_NEAR_VALUE 0.0f // centimeters +#define AMS_TMD2772_REPORT_FAR_VALUE 5.0f // centimeters + +#define AMS_TMD2772_THRESHOLD_ASSERT_NEAR 213 // in PS units +#define AMS_TMD2772_THRESHOLD_DEASSERT_NEAR 96 // in PS units + +#define AMS_TMD2772_ALS_MAX_CHANNEL_COUNT 37888 // in raw data +#define AMS_TMD2772_ALS_MAX_REPORT_VALUE 10000 // in lux + +#define AMS_TMD2772_ALS_INVALID UINT32_MAX + +/* Used when SENSOR_RATE_ONCHANGE is requested */ +#define AMS_TMD2772_DEFAULT_RATE SENSOR_HZ(5) + +/* Private driver events */ +enum SensorEvents +{ + EVT_SENSOR_I2C = EVT_APP_START + 1, + EVT_SENSOR_ALS_TIMER, + EVT_SENSOR_PROX_TIMER, +}; + +/* I2C state machine */ +enum SensorState +{ + SENSOR_STATE_VERIFY_ID, + SENSOR_STATE_INIT, + + SENSOR_STATE_CALIBRATE_RESET, + SENSOR_STATE_CALIBRATE_START, + SENSOR_STATE_CALIBRATE_ENABLING, + SENSOR_STATE_CALIBRATE_POLLING_STATUS, + SENSOR_STATE_CALIBRATE_AWAITING_SAMPLE, + SENSOR_STATE_CALIBRATE_DISABLING, + + SENSOR_STATE_ENABLING_ALS, + SENSOR_STATE_ENABLING_PROX, + SENSOR_STATE_DISABLING_ALS, + SENSOR_STATE_DISABLING_PROX, + + SENSOR_STATE_IDLE, + SENSOR_STATE_SAMPLING, +}; + +enum ProxState +{ + PROX_STATE_INIT, + PROX_STATE_NEAR, + PROX_STATE_FAR, +}; + +struct SensorData +{ + union { + uint8_t bytes[16]; + struct { + uint8_t status; + uint16_t als[2]; + uint16_t prox; + } __attribute__((packed)) sample; + struct { + uint16_t prox; + } calibration; + } txrxBuf; + + uint32_t tid; + + uint32_t alsHandle; + uint32_t proxHandle; + uint32_t alsTimerHandle; + uint32_t proxTimerHandle; + uint32_t calibrationSampleTotal; + + union EmbeddedDataPoint lastAlsSample; + + uint8_t calibrationSampleCount; + uint8_t proxState; // enum ProxState + + bool alsOn; + bool alsReading; + bool proxOn; + bool proxReading; +}; + +static struct SensorData mData; + +/* TODO: check rates are supported */ +static const uint32_t supportedRates[] = +{ + SENSOR_HZ(0.1), + SENSOR_HZ(1), + SENSOR_HZ(4), + SENSOR_HZ(5), + SENSOR_RATE_ONCHANGE, + 0 +}; + +static const uint64_t rateTimerVals[] = //should match "supported rates in length" and be the timer length for that rate in nanosecs +{ + 10 * 1000000000ULL, + 1 * 1000000000ULL, + 1000000000ULL / 4, + 1000000000ULL / 5, +}; + +/* + * Helper functions + */ + +static void i2cCallback(void *cookie, size_t tx, size_t rx, int err) +{ + if (err == 0) + osEnqueuePrivateEvt(EVT_SENSOR_I2C, cookie, NULL, mData.tid); + else + osLog(LOG_INFO, DRIVER_NAME "i2c error (%d)\n", err); +} + +static void alsTimerCallback(uint32_t timerId, void *cookie) +{ + osEnqueuePrivateEvt(EVT_SENSOR_ALS_TIMER, cookie, NULL, mData.tid); +} + +static void proxTimerCallback(uint32_t timerId, void *cookie) +{ + osEnqueuePrivateEvt(EVT_SENSOR_PROX_TIMER, cookie, NULL, mData.tid); +} + +static inline float getLuxFromAlsData(uint16_t als0, uint16_t als1) +{ + float cpl = 1.0f / AMS_TMD2772_ATIME_MS; + float GA; + + if ((als0 * 10) < (als1 * 21)) { + // A light + GA = 0.274f; + } else if (((als0 * 10) >= (als1 * 21)) && ((als0 * 10) <= (als1 * 43)) && (als0 > 300)) { + // D65 + GA = 0.592f; + } else { + // cool white + GA = 1.97f; + } + + float lux1 = GA * 207 * (als0 - (1.799 * als1)) * cpl; + float lux2 = GA * 207 * ((0.188f * als0) - (0.303 * als1)) * cpl; + + if ((als0 >= AMS_TMD2772_ALS_MAX_CHANNEL_COUNT) || + (als1 >= AMS_TMD2772_ALS_MAX_CHANNEL_COUNT)) { + return AMS_TMD2772_ALS_MAX_REPORT_VALUE; + } else if ((lux1 > lux2) && (lux1 > 0.0f)) { + return lux1 > AMS_TMD2772_ALS_MAX_REPORT_VALUE ? AMS_TMD2772_ALS_MAX_REPORT_VALUE : lux1; + } else if (lux2 > 0.0f) { + return lux2 > AMS_TMD2772_ALS_MAX_REPORT_VALUE ? AMS_TMD2772_ALS_MAX_REPORT_VALUE : lux2; + } else { + return 0.0f; + } +} + +static void setMode(bool alsOn, bool proxOn, void *cookie) +{ + mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_ENABLE; + mData.txrxBuf.bytes[1] = POWER_ON_BIT | WAIT_ENABLE_BIT | + (alsOn ? ALS_ENABLE_BIT : 0) | (proxOn ? PROX_ENABLE_BIT : 0); + mData.txrxBuf.bytes[2] = AMS_TMD2772_ATIME_SETTING; + mData.txrxBuf.bytes[3] = AMS_TMD2772_PTIME_SETTING; + mData.txrxBuf.bytes[4] = alsOn ? AMS_TMD2772_WTIME_SETTING_ALS_ON : AMS_TMD2772_WTIME_SETTING_ALS_OFF; + i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 5, + &i2cCallback, cookie); +} + +static bool sensorPowerAls(bool on, void *cookie) +{ + osLog(LOG_INFO, DRIVER_NAME "sensorPowerAls: %d\n", on); + + if (mData.alsTimerHandle) { + timTimerCancel(mData.alsTimerHandle); + mData.alsTimerHandle = 0; + mData.alsReading = false; + } + + mData.lastAlsSample.idata = AMS_TMD2772_ALS_INVALID; + mData.alsOn = on; + setMode(on, mData.proxOn, (void *)(on ? SENSOR_STATE_ENABLING_ALS : SENSOR_STATE_DISABLING_ALS)); + + return true; +} + +static bool sensorFirmwareAls(void *cookie) +{ + sensorSignalInternalEvt(mData.alsHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0); + return true; +} + +static bool sensorRateAls(uint32_t rate, uint64_t latency, void *cookie) +{ + if (rate == SENSOR_RATE_ONCHANGE) { + rate = AMS_TMD2772_DEFAULT_RATE; + } + osLog(LOG_INFO, DRIVER_NAME "sensorRateAls: %ld/%lld\n", rate, latency); + + if (mData.alsTimerHandle) + timTimerCancel(mData.alsTimerHandle); + mData.alsTimerHandle = timTimerSet(sensorTimerLookupCommon(supportedRates, rateTimerVals, rate), 0, 50, alsTimerCallback, NULL, false); + osEnqueuePrivateEvt(EVT_SENSOR_ALS_TIMER, NULL, NULL, mData.tid); + sensorSignalInternalEvt(mData.alsHandle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency); + + return true; +} + +static bool sensorFlushAls(void *cookie) +{ + return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), SENSOR_DATA_EVENT_FLUSH, NULL); +} + +static bool sendLastSampleAls(void *cookie, uint32_t tid) { + bool result = true; + + // If we don't end up doing anything here, the expectation is that we are powering up/haven't got the + // first sample yet, so a broadcast event will go out soon with the first sample + if (mData.lastAlsSample.idata != AMS_TMD2772_ALS_INVALID) { + result = osEnqueuePrivateEvt(sensorGetMyEventType(SENS_TYPE_ALS), mData.lastAlsSample.vptr, NULL, tid); + } + return result; +} + +static bool sensorPowerProx(bool on, void *cookie) +{ + osLog(LOG_INFO, DRIVER_NAME "sensorPowerProx: %d\n", on); + + if (mData.proxTimerHandle) { + timTimerCancel(mData.proxTimerHandle); + mData.proxTimerHandle = 0; + mData.proxReading = false; + } + + mData.proxState = PROX_STATE_INIT; + mData.proxOn = on; + setMode(mData.alsOn, on, (void *)(on ? SENSOR_STATE_ENABLING_PROX : SENSOR_STATE_DISABLING_PROX)); + + return true; +} + +static bool sensorFirmwareProx(void *cookie) +{ + sensorSignalInternalEvt(mData.proxHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0); + return true; +} + +static bool sensorRateProx(uint32_t rate, uint64_t latency, void *cookie) +{ + if (rate == SENSOR_RATE_ONCHANGE) { + rate = AMS_TMD2772_DEFAULT_RATE; + } + osLog(LOG_INFO, DRIVER_NAME "sensorRateProx: %ld/%lld\n", rate, latency); + + if (mData.proxTimerHandle) + timTimerCancel(mData.proxTimerHandle); + mData.proxTimerHandle = timTimerSet(sensorTimerLookupCommon(supportedRates, rateTimerVals, rate), 0, 50, proxTimerCallback, NULL, false); + osEnqueuePrivateEvt(EVT_SENSOR_PROX_TIMER, NULL, NULL, mData.tid); + sensorSignalInternalEvt(mData.proxHandle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency); + + return true; +} + +static bool sensorFlushProx(void *cookie) +{ + return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), SENSOR_DATA_EVENT_FLUSH, NULL); +} + +static bool sendLastSampleProx(void *cookie, uint32_t tid) { + union EmbeddedDataPoint sample; + bool result = true; + + // See note in sendLastSampleAls + if (mData.proxState != PROX_STATE_INIT) { + sample.fdata = (mData.proxState == PROX_STATE_NEAR) ? + AMS_TMD2772_REPORT_NEAR_VALUE : AMS_TMD2772_REPORT_FAR_VALUE; + result = osEnqueuePrivateEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL, tid); + } + return result; +} + +static const struct SensorInfo sensorInfoAls = +{ + .sensorName = "ALS", + .supportedRates = supportedRates, + .sensorType = SENS_TYPE_ALS, + .numAxis = NUM_AXIS_EMBEDDED, + .interrupt = NANOHUB_INT_NONWAKEUP, + .minSamples = 20 +}; + +static const struct SensorOps sensorOpsAls = +{ + .sensorPower = sensorPowerAls, + .sensorFirmwareUpload = sensorFirmwareAls, + .sensorSetRate = sensorRateAls, + .sensorFlush = sensorFlushAls, + .sensorTriggerOndemand = NULL, + .sensorCalibrate = NULL, + .sensorSendOneDirectEvt = sendLastSampleAls +}; + +static const struct SensorInfo sensorInfoProx = +{ + .sensorName = "Proximity", + .supportedRates = supportedRates, + .sensorType = SENS_TYPE_PROX, + .numAxis = NUM_AXIS_EMBEDDED, + .interrupt = NANOHUB_INT_WAKEUP, + .minSamples = 300 +}; + +static const struct SensorOps sensorOpsProx = +{ + .sensorPower = sensorPowerProx, + .sensorFirmwareUpload = sensorFirmwareProx, + .sensorSetRate = sensorRateProx, + .sensorFlush = sensorFlushProx, + .sensorTriggerOndemand = NULL, + .sensorCalibrate = NULL, + .sensorSendOneDirectEvt = sendLastSampleProx +}; + +/* + * Sensor i2c state machine + */ + +static void handle_calibration_event(int state) { + switch (state) { + case SENSOR_STATE_CALIBRATE_RESET: + mData.calibrationSampleCount = 0; + mData.calibrationSampleTotal = 0; + /* Intentional fall-through */ + + case SENSOR_STATE_CALIBRATE_START: + mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_ENABLE; + mData.txrxBuf.bytes[1] = POWER_ON_BIT | PROX_ENABLE_BIT; + i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 2, + &i2cCallback, (void *)SENSOR_STATE_CALIBRATE_ENABLING); + break; + + case SENSOR_STATE_CALIBRATE_ENABLING: + mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_STATUS; + i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 1, + mData.txrxBuf.bytes, 1, &i2cCallback, + (void *)SENSOR_STATE_CALIBRATE_POLLING_STATUS); + break; + + case SENSOR_STATE_CALIBRATE_POLLING_STATUS: + if (mData.txrxBuf.bytes[0] & PROX_INT_BIT) { + /* Done */ + mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_PDATAL; + i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 1, + mData.txrxBuf.bytes, 2, &i2cCallback, + (void *)SENSOR_STATE_CALIBRATE_AWAITING_SAMPLE); + } else { + /* Poll again; go back to previous state */ + handle_calibration_event(SENSOR_STATE_CALIBRATE_ENABLING); + } + break; + + case SENSOR_STATE_CALIBRATE_AWAITING_SAMPLE: + mData.calibrationSampleCount++; + mData.calibrationSampleTotal += mData.txrxBuf.calibration.prox; + + mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_ENABLE; + mData.txrxBuf.bytes[1] = 0x00; + i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 2, + &i2cCallback, (void *)SENSOR_STATE_CALIBRATE_DISABLING); + break; + + case SENSOR_STATE_CALIBRATE_DISABLING: + if (mData.calibrationSampleCount >= 20) { + /* Done, calculate calibration */ + uint16_t average = mData.calibrationSampleTotal / mData.calibrationSampleCount; + uint16_t crosstalk = (average > 0x7f) ? 0x7f : average; + + mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_POFFSET; + mData.txrxBuf.bytes[1] = crosstalk; + i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 2, + &i2cCallback, (void *)SENSOR_STATE_IDLE); + } else { + /* Get another sample; go back to earlier state */ + handle_calibration_event(SENSOR_STATE_CALIBRATE_START); + } + break; + + default: + break; + } +} + +static void handle_i2c_event(int state) +{ + union EmbeddedDataPoint sample; + bool sendData; + + switch (state) { + case SENSOR_STATE_VERIFY_ID: + /* Check the sensor ID */ + if (mData.txrxBuf.bytes[0] != AMS_TMD2772_ID) { + osLog(LOG_INFO, DRIVER_NAME "not detected\n"); + sensorUnregister(mData.alsHandle); + sensorUnregister(mData.proxHandle); + break; + } + + /* Start address */ + mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_ENABLE; + /* ENABLE */ + mData.txrxBuf.bytes[1] = 0x00; + /* ATIME */ + mData.txrxBuf.bytes[2] = AMS_TMD2772_ATIME_SETTING; + /* PTIME */ + mData.txrxBuf.bytes[3] = AMS_TMD2772_PTIME_SETTING; + /* WTIME */ + mData.txrxBuf.bytes[4] = 0xFF; + i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 5, + &i2cCallback, (void *)SENSOR_STATE_INIT); + break; + + case SENSOR_STATE_INIT: + /* Start address */ + mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_PERS; + /* PERS */ + mData.txrxBuf.bytes[1] = 0x00; + /* CONFIG */ + mData.txrxBuf.bytes[2] = 0x00; + /* PPULSE */ + mData.txrxBuf.bytes[3] = AMS_TMD2772_PPULSE_SETTING; + /* CONTROL */ + mData.txrxBuf.bytes[4] = 0x20; + i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 5, + &i2cCallback, (void *)SENSOR_STATE_IDLE); + break; + + case SENSOR_STATE_IDLE: + sensorRegisterInitComplete(mData.alsHandle); + sensorRegisterInitComplete(mData.proxHandle); + break; + + case SENSOR_STATE_ENABLING_ALS: + sensorSignalInternalEvt(mData.alsHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0); + break; + + case SENSOR_STATE_ENABLING_PROX: + sensorSignalInternalEvt(mData.proxHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0); + break; + + case SENSOR_STATE_DISABLING_ALS: + sensorSignalInternalEvt(mData.alsHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, false, 0); + break; + + case SENSOR_STATE_DISABLING_PROX: + sensorSignalInternalEvt(mData.proxHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, false, 0); + break; + + case SENSOR_STATE_SAMPLING: + /* TEST: log collected data + osLog(LOG_INFO, DRIVER_NAME "sample ready: status=%02x prox=%u als0=%u als1=%u\n", + mData.txrxBuf.sample.status, mData.txrxBuf.sample.prox, + mData.txrxBuf.sample.als[0], mData.txrxBuf.sample.als[1]); + */ + + if (mData.alsOn && mData.alsReading && + (mData.txrxBuf.sample.status & ALS_VALID_BIT)) { + /* Create event */ + sample.fdata = getLuxFromAlsData(mData.txrxBuf.sample.als[0], + mData.txrxBuf.sample.als[1]); + if (mData.lastAlsSample.idata != sample.idata) { + osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), sample.vptr, NULL); + mData.lastAlsSample.fdata = sample.fdata; + } + } + + if (mData.proxOn && mData.proxReading && + (mData.txrxBuf.sample.status & PROX_VALID_BIT)) { + /* Create event */ + sendData = true; + if (mData.proxState == PROX_STATE_INIT) { + if (mData.txrxBuf.sample.prox > AMS_TMD2772_THRESHOLD_ASSERT_NEAR) { + sample.fdata = AMS_TMD2772_REPORT_NEAR_VALUE; + mData.proxState = PROX_STATE_NEAR; + } else { + sample.fdata = AMS_TMD2772_REPORT_FAR_VALUE; + mData.proxState = PROX_STATE_FAR; + } + } else { + if (mData.proxState == PROX_STATE_NEAR && + mData.txrxBuf.sample.prox < AMS_TMD2772_THRESHOLD_DEASSERT_NEAR) { + sample.fdata = AMS_TMD2772_REPORT_FAR_VALUE; + mData.proxState = PROX_STATE_FAR; + } else if (mData.proxState == PROX_STATE_FAR && + mData.txrxBuf.sample.prox > AMS_TMD2772_THRESHOLD_ASSERT_NEAR) { + sample.fdata = AMS_TMD2772_REPORT_NEAR_VALUE; + mData.proxState = PROX_STATE_NEAR; + } else { + sendData = false; + } + } + + if (sendData) + osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL); + } + + mData.alsReading = false; + mData.proxReading = false; + break; + + default: + handle_calibration_event(state); + break; + } +} + +/* + * Main driver entry points + */ + +static bool init_app(uint32_t myTid) +{ + /* Set up driver private data */ + mData.tid = myTid; + mData.alsOn = false; + mData.alsReading = false; + mData.proxOn = false; + mData.proxReading = false; + mData.lastAlsSample.idata = AMS_TMD2772_ALS_INVALID; + mData.proxState = PROX_STATE_INIT; + + /* Register sensors */ + mData.alsHandle = sensorRegister(&sensorInfoAls, &sensorOpsAls, NULL, false); + mData.proxHandle = sensorRegister(&sensorInfoProx, &sensorOpsProx, NULL, false); + + osEventSubscribe(myTid, EVT_APP_START); + + return true; +} + +static void end_app(void) +{ + sensorUnregister(mData.alsHandle); + sensorUnregister(mData.proxHandle); + + i2cMasterRelease(I2C_BUS_ID); +} + +static void handle_event(uint32_t evtType, const void* evtData) +{ + switch (evtType) { + case EVT_APP_START: + osEventUnsubscribe(mData.tid, EVT_APP_START); + i2cMasterRequest(I2C_BUS_ID, I2C_SPEED); + + /* TODO: reset chip first */ + + mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_ID; + i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 1, + mData.txrxBuf.bytes, 1, &i2cCallback, + (void *)SENSOR_STATE_VERIFY_ID); + break; + + case EVT_SENSOR_I2C: + handle_i2c_event((int)evtData); + break; + + case EVT_SENSOR_ALS_TIMER: + case EVT_SENSOR_PROX_TIMER: + /* Start sampling for a value */ + if (!mData.alsReading && !mData.proxReading) { + mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_STATUS; + i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 1, + mData.txrxBuf.bytes, 7, &i2cCallback, + (void *)SENSOR_STATE_SAMPLING); + } + + if (evtType == EVT_SENSOR_ALS_TIMER) + mData.alsReading = true; + else + mData.proxReading = true; + break; + } +} + +INTERNAL_APP_INIT(APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, 9), TMD2772_APP_VERSION, init_app, end_app, handle_event); |