summaryrefslogtreecommitdiff
path: root/firmware/os/drivers/ams_tmd2772/ams_tmd2772.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/os/drivers/ams_tmd2772/ams_tmd2772.c')
-rw-r--r--firmware/os/drivers/ams_tmd2772/ams_tmd2772.c676
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);