| /* |
| * 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); |