blob: 95df33eebb05bdc25f955de1c8b2706b3c5d5d70 [file] [log] [blame]
/*
* Copyright (C) 2017 STMicroelectronics
* Copyright (C) 2017 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>
#include <util.h>
#include <variant/variant.h>
#ifdef DEBUG
#undef ISL29034_DBG_ENABLED
#define ISL29034_DBG_ENABLED 1
#endif
#ifndef ISL29034_DBG_ENABLED
#define ISL29034_DBG_ENABLED 0
#endif /* ISL29034_DBG_ENABLED */
#define ISL29034_APP_VERSION 1
#define ISL29034_REG_CMD_1 0x00
#define ISL29034_REG_CMD_2 0x01
#define ISL29034_REG_DATA_L 0x02
#define ISL29034_REG_DATA_H 0x03
#define ISL29034_REG_ID 0x0f
#define ISL29034_CMD1_POWERDOWN (0 << 5)
#define ISL29034_CMD1_MEASURE (5 << 5)
#define ISL29034_CMD2_RANGE_1KLUX 0
#define ISL29034_CMD2_RANGE_4KLUX 1
#define ISL29034_CMD2_RANGE_16KLUX 2
#define ISL29034_CMD2_RANGE_64KLUX 3
#define ISL29034_CMD2_RES_16BIT (0 << 2)
#define ISL29034_CMD2_RES_12BIT (1 << 2)
#define ISL29034_CMD2_RES_8BIT (2 << 2)
#define ISL29034_CMD2_RES_4BIT (3 << 2)
#define ISL29034_ID (5 << 3)
#define ISL29034_ID_MASK (7 << 3)
#define ISL29034_ID_BOUT (1 << 7)
#define ISL29034_ALS_INVALID UINT32_MAX
#define ISL29034_MAX_PENDING_I2C_REQUESTS 4
#define ISL29034_MAX_I2C_TRANSFER_SIZE 3
/* Used when SENSOR_RATE_ONCHANGE is requested */
#define ISL29034_DEFAULT_RATE SENSOR_HZ(10)
#ifndef ISL29034_I2C_BUS_ID
#error "ISL29034_I2C_BUS_ID is not defined; please define in variant.h"
#endif
#ifndef ISL29034_I2C_SPEED
#error "ISL29034_I2C_SPEED is not defined; please define in variant.h"
#endif
#ifndef ISL29034_I2C_ADDR
#define ISL29034_I2C_ADDR 0x44
#endif
#define INFO_PRINT(fmt, ...) \
do { \
osLog(LOG_INFO, "%s " fmt, "[ISL29034]", ##__VA_ARGS__); \
} while (0);
#define DEBUG_PRINT(fmt, ...) \
do { \
if (ISL29034_DBG_ENABLED) { \
osLog(LOG_DEBUG, "%s " fmt, "[ISL29034]", ##__VA_ARGS__); \
} \
} while (0);
#define ERROR_PRINT(fmt, ...) \
do { \
osLog(LOG_ERROR, "%s " fmt, "[ISL29034]", ##__VA_ARGS__); \
} while (0);
/* Private driver events */
enum SensorEvents
{
EVT_SENSOR_I2C = EVT_APP_START + 1,
EVT_SENSOR_ALS_TIMER,
};
/* I2C state machine */
enum SensorState
{
SENSOR_STATE_VERIFY_ID,
SENSOR_STATE_CLEAR_BOUT,
SENSOR_STATE_ENABLING_ALS,
SENSOR_STATE_DISABLING_ALS,
SENSOR_STATE_IDLE,
SENSOR_STATE_SAMPLING,
};
struct I2cTransfer
{
size_t tx;
size_t rx;
int err;
uint8_t txrxBuf[ISL29034_MAX_I2C_TRANSFER_SIZE];
uint8_t state;
bool inUse;
};
struct SensorData
{
uint32_t tid;
uint32_t alsHandle;
uint32_t alsTimerHandle;
struct I2cTransfer transfers[ISL29034_MAX_PENDING_I2C_REQUESTS];
union EmbeddedDataPoint lastAlsSample;
bool alsOn;
bool alsReading;
};
static struct SensorData mData;
static const uint32_t supportedRates[] =
{
SENSOR_HZ(10),
SENSOR_RATE_ONCHANGE,
0
};
// should match "supported rates in length" and be the timer length for that rate in nanosecs
static const uint64_t rateTimerVals[] =
{
1000000000ULL / 10,
};
/*
* Helper functions
*/
static void i2cCallback(void *cookie, size_t tx, size_t rx, int err)
{
struct I2cTransfer *xfer = cookie;
xfer->tx = tx;
xfer->rx = rx;
xfer->err = err;
osEnqueuePrivateEvt(EVT_SENSOR_I2C, cookie, NULL, mData.tid);
if (err != 0)
INFO_PRINT("i2c error (tx: %d, rx: %d, err: %d)\n", tx, rx, err);
}
// Allocate a buffer and mark it as in use with the given state, or return NULL
// if no buffers available. Must *not* be called from interrupt context.
static struct I2cTransfer *allocXfer(uint8_t state)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(mData.transfers); i++) {
if (!mData.transfers[i].inUse) {
mData.transfers[i].inUse = true;
mData.transfers[i].state = state;
return &mData.transfers[i];
}
}
ERROR_PRINT("Ran out of i2c buffers!");
return NULL;
}
// Helper function to write a one byte register. Returns true if we got a
// successful return value from i2cMasterTx().
static bool writeRegister(uint8_t reg, uint8_t value, uint8_t state)
{
struct I2cTransfer *xfer = allocXfer(state);
int ret = -1;
if (xfer != NULL) {
xfer->txrxBuf[0] = reg;
xfer->txrxBuf[1] = value;
ret = i2cMasterTx(ISL29034_I2C_BUS_ID, ISL29034_I2C_ADDR, xfer->txrxBuf, 2, i2cCallback, xfer);
}
return (ret == 0);
}
static void alsTimerCallback(uint32_t timerId, void *cookie)
{
osEnqueuePrivateEvt(EVT_SENSOR_ALS_TIMER, cookie, NULL, mData.tid);
}
static inline float getLuxFromAlsData(uint16_t als)
{
float range = 64000.0;
float resolution = 65536.0;
return range * als / resolution;
}
static bool sensorPowerAls(bool on, void *cookie)
{
INFO_PRINT("sensorPowerAls: %d\n", on);
if (mData.alsTimerHandle) {
timTimerCancel(mData.alsTimerHandle);
mData.alsTimerHandle = 0;
mData.alsReading = false;
}
mData.lastAlsSample.idata = ISL29034_ALS_INVALID;
mData.alsOn = on;
writeRegister(ISL29034_REG_CMD_1,
on ? ISL29034_CMD1_MEASURE : ISL29034_CMD1_POWERDOWN,
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 = ISL29034_DEFAULT_RATE;
INFO_PRINT("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 != ISL29034_ALS_INVALID)
result = osEnqueuePrivateEvt(sensorGetMyEventType(SENS_TYPE_ALS), mData.lastAlsSample.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
};
/*
* Sensor i2c state machine
*/
static void handle_i2c_event(struct I2cTransfer *xfer)
{
union EmbeddedDataPoint sample;
struct I2cTransfer *nextXfer;
switch (xfer->state) {
case SENSOR_STATE_VERIFY_ID:
/* Check the sensor ID */
if (xfer->err != 0 ||
(xfer->txrxBuf[0] & ISL29034_ID_MASK) != ISL29034_ID) {
INFO_PRINT("not detected\n");
sensorUnregister(mData.alsHandle);
break;
}
INFO_PRINT("detected\n")
if (xfer->txrxBuf[0] & ISL29034_ID_BOUT) {
INFO_PRINT("Brownout Condition\n");
writeRegister(ISL29034_REG_ID,
xfer->txrxBuf[0] ^ ISL29034_ID_BOUT,
SENSOR_STATE_CLEAR_BOUT);
break;
}
/* fallthrough */
case SENSOR_STATE_CLEAR_BOUT:
nextXfer = allocXfer(SENSOR_STATE_IDLE);
if (nextXfer != NULL) {
nextXfer->txrxBuf[0] = ISL29034_REG_CMD_1;
nextXfer->txrxBuf[1] = ISL29034_CMD1_POWERDOWN;
nextXfer->txrxBuf[2] = ISL29034_CMD2_RANGE_64KLUX | ISL29034_CMD2_RES_16BIT;
i2cMasterTx(ISL29034_I2C_BUS_ID, ISL29034_I2C_ADDR, nextXfer->txrxBuf, 3, i2cCallback, nextXfer);
}
break;
case SENSOR_STATE_IDLE:
sensorRegisterInitComplete(mData.alsHandle);
break;
case SENSOR_STATE_ENABLING_ALS:
sensorSignalInternalEvt(mData.alsHandle, 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_SAMPLING:
DEBUG_PRINT("sample ready: als0=%u als1=%u\n", xfer->txrxBuf[0], xfer->txrxBuf[1]);
if (mData.alsOn && mData.alsReading) {
/* Create event */
sample.fdata = getLuxFromAlsData(xfer->txrxBuf[0] | (xfer->txrxBuf[1] << 8));
if (mData.lastAlsSample.idata != sample.idata) {
osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), sample.vptr, NULL);
mData.lastAlsSample.fdata = sample.fdata;
}
}
mData.alsReading = false;
break;
}
xfer->inUse = false;
}
/*
* Main driver entry points
*/
static bool init_app(uint32_t myTid)
{
INFO_PRINT("started\n");
/* Set up driver private data */
mData.tid = myTid;
mData.alsOn = false;
mData.alsReading = false;
mData.lastAlsSample.idata = ISL29034_ALS_INVALID;
i2cMasterRequest(ISL29034_I2C_BUS_ID, ISL29034_I2C_SPEED);
/* Register sensors */
mData.alsHandle = sensorRegister(&sensorInfoAls, &sensorOpsAls, NULL, false);
osEventSubscribe(myTid, EVT_APP_START);
return true;
}
static void end_app(void)
{
INFO_PRINT("ended\n");
sensorUnregister(mData.alsHandle);
i2cMasterRelease(ISL29034_I2C_BUS_ID);
}
static void handle_event(uint32_t evtType, const void* evtData)
{
struct I2cTransfer *xfer;
switch (evtType) {
case EVT_APP_START:
osEventUnsubscribe(mData.tid, EVT_APP_START);
xfer = allocXfer(SENSOR_STATE_VERIFY_ID);
if (xfer != NULL) {
xfer->txrxBuf[0] = ISL29034_REG_ID;
i2cMasterTxRx(ISL29034_I2C_BUS_ID, ISL29034_I2C_ADDR, xfer->txrxBuf, 1, xfer->txrxBuf, 1, i2cCallback, xfer);
}
break;
case EVT_SENSOR_I2C:
handle_i2c_event((struct I2cTransfer *)evtData);
break;
case EVT_SENSOR_ALS_TIMER:
/* Start sampling for a value */
if (!mData.alsReading) {
xfer = allocXfer(SENSOR_STATE_SAMPLING);
if (xfer != NULL) {
xfer->txrxBuf[0] = ISL29034_REG_DATA_L;
i2cMasterTxRx(ISL29034_I2C_BUS_ID, ISL29034_I2C_ADDR, xfer->txrxBuf, 1, xfer->txrxBuf, 2, i2cCallback, xfer);
}
}
mData.alsReading = true;
break;
}
}
INTERNAL_APP_INIT(APP_ID_MAKE(NANOHUB_VENDOR_STMICRO, 4), ISL29034_APP_VERSION, init_app, end_app, handle_event);