360 lines
9.2 KiB
C
360 lines
9.2 KiB
C
/*
|
|
* 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 <eventnums.h>
|
|
#include <heap.h>
|
|
#include <hostIntf.h>
|
|
#include <i2c.h>
|
|
#include <leds_gpio.h>
|
|
#include <nanohubPacket.h>
|
|
#include <sensors.h>
|
|
#include <seos.h>
|
|
#include <timer.h>
|
|
#include <util.h>
|
|
#include <variant/variant.h>
|
|
|
|
#define LP3943_LEDS_APP_ID APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 21)
|
|
#define LP3943_LEDS_APP_VERSION 1
|
|
|
|
#ifdef LP3943_I2C_BUS_ID
|
|
#define I2C_BUS_ID LP3943_I2C_BUS_ID
|
|
#else
|
|
#define I2C_BUS_ID 0
|
|
#endif
|
|
|
|
#ifdef LP3943_I2C_SPEED
|
|
#define I2C_SPEED LP3943_I2C_SPEED
|
|
#else
|
|
#define I2C_SPEED 400000
|
|
#endif
|
|
|
|
#ifdef LP3943_I2C_ADDR
|
|
#define I2C_ADDR LP3943_I2C_ADDR
|
|
#else
|
|
#define I2C_ADDR 0x60
|
|
#endif
|
|
|
|
#define LP3943_REG_PSC0 0x02
|
|
#define LP3943_REG_PWM0 0x03
|
|
#define LP3943_REG_PSC1 0x04
|
|
#define LP3943_REG_PWM1 0x05
|
|
#define LP3943_REG_LS0 0x06
|
|
#define LP3943_REG_LS1 0x07
|
|
#define LP3943_REG_LS2 0x08
|
|
#define LP3943_REG_LS3 0x09
|
|
|
|
#define LP3943_MAX_PENDING_I2C_REQUESTS 4
|
|
#define LP3943_MAX_I2C_TRANSFER_SIZE 2
|
|
#define LP3943_MAX_LED_NUM 16
|
|
#define LP3943_MAX_LED_SECTION 4
|
|
|
|
#ifndef LP3943_DBG_ENABLE
|
|
#define LP3943_DBG_ENABLE 0
|
|
#endif
|
|
#define LP3943_DBG_VALUE 0x55
|
|
|
|
enum LP3943SensorEvents
|
|
{
|
|
EVT_SENSOR_I2C = EVT_APP_START + 1,
|
|
EVT_SENSOR_LEDS_TIMER,
|
|
EVT_TEST,
|
|
};
|
|
|
|
enum LP3943TaskState
|
|
{
|
|
STATE_RESET,
|
|
STATE_CLEAN_LS1,
|
|
STATE_CLEAN_LS2,
|
|
STATE_FINISH_INIT,
|
|
STATE_LED,
|
|
};
|
|
|
|
struct I2cTransfer
|
|
{
|
|
size_t tx;
|
|
size_t rx;
|
|
int err;
|
|
uint8_t txrxBuf[LP3943_MAX_I2C_TRANSFER_SIZE];
|
|
uint8_t state;
|
|
bool inUse;
|
|
};
|
|
|
|
static struct LP3943Task
|
|
{
|
|
uint32_t id;
|
|
uint32_t sHandle;
|
|
uint32_t num;
|
|
bool ledsOn;
|
|
bool blink;
|
|
uint32_t ledsTimerHandle;
|
|
uint8_t led[LP3943_MAX_LED_SECTION];
|
|
|
|
struct I2cTransfer transfers[LP3943_MAX_PENDING_I2C_REQUESTS];
|
|
} mTask;
|
|
|
|
/* sensor callbacks from nanohub */
|
|
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, mTask.id);
|
|
if (err != 0)
|
|
osLog(LOG_INFO, "[LP3943] i2c error (tx: %d, rx: %d, err: %d)\n", tx, rx, err);
|
|
}
|
|
|
|
static void sensorLP3943TimerCallback(uint32_t timerId, void *data)
|
|
{
|
|
osEnqueuePrivateEvt(EVT_SENSOR_LEDS_TIMER, data, NULL, mTask.id);
|
|
}
|
|
|
|
static uint32_t ledsRates[] = {
|
|
SENSOR_HZ(0.1),
|
|
SENSOR_HZ(0.5),
|
|
SENSOR_HZ(1.0f),
|
|
SENSOR_HZ(2.0f),
|
|
0
|
|
};
|
|
|
|
// should match "supported rates in length"
|
|
static const uint64_t ledsRatesRateVals[] =
|
|
{
|
|
10 * 1000000000ULL,
|
|
2 * 1000000000ULL,
|
|
1 * 1000000000ULL,
|
|
1000000000ULL / 2,
|
|
};
|
|
|
|
// 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(mTask.transfers); i++) {
|
|
if (!mTask.transfers[i].inUse) {
|
|
mTask.transfers[i].inUse = true;
|
|
mTask.transfers[i].state = state;
|
|
return &mTask.transfers[i];
|
|
}
|
|
}
|
|
|
|
osLog(LOG_ERROR, "[LP3943]: Ran out of i2c buffers!");
|
|
return NULL;
|
|
}
|
|
|
|
// Helper function to release I2cTranfer structure.
|
|
static inline void releaseXfer(struct I2cTransfer *xfer)
|
|
{
|
|
xfer->inUse = false;
|
|
}
|
|
|
|
// 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(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 2, i2cCallback, xfer);
|
|
if (ret)
|
|
releaseXfer(xfer);
|
|
}
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
/* Sensor Operations */
|
|
static bool sensorLP3943Power(bool on, void *cookie)
|
|
{
|
|
if (mTask.ledsTimerHandle) {
|
|
timTimerCancel(mTask.ledsTimerHandle);
|
|
mTask.ledsTimerHandle = 0;
|
|
}
|
|
mTask.ledsOn = on;
|
|
return sensorSignalInternalEvt(mTask.sHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, on, 0);
|
|
}
|
|
|
|
static bool sensorLP3943FwUpload(void *cookie)
|
|
{
|
|
return sensorSignalInternalEvt(mTask.sHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
|
|
}
|
|
|
|
static bool sensorLP3943SetRate(uint32_t rate, uint64_t latency, void *cookie)
|
|
{
|
|
if (mTask.ledsTimerHandle)
|
|
timTimerCancel(mTask.ledsTimerHandle);
|
|
|
|
mTask.ledsTimerHandle = timTimerSet(sensorTimerLookupCommon(ledsRates,
|
|
ledsRatesRateVals, rate), 0, 50, sensorLP3943TimerCallback, NULL, false);
|
|
|
|
return sensorSignalInternalEvt(mTask.sHandle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
|
|
}
|
|
|
|
static bool sensorCfgDataLedsLP3943(void *cfg, void *cookie)
|
|
{
|
|
struct LedsCfg *lcfg = (struct LedsCfg *)cfg;
|
|
uint8_t laddr = LP3943_REG_LS0;
|
|
uint8_t lval;
|
|
uint8_t index;
|
|
uint8_t lnum;
|
|
|
|
if (lcfg->led_num >= mTask.num) {
|
|
osLog(LOG_INFO, "Wrong led number %"PRIu32"\n", lcfg->led_num);
|
|
return false;
|
|
}
|
|
index = lcfg->led_num >> 2;
|
|
lnum = (lcfg->led_num & 0x3) << 1;
|
|
lval = mTask.led[index];
|
|
laddr += index;
|
|
if (lcfg->value) {
|
|
lval |= (1 << lnum);
|
|
} else {
|
|
lval &= ~(1 << lnum);
|
|
}
|
|
|
|
writeRegister(laddr, lval, STATE_LED);
|
|
mTask.led[index] = lval;
|
|
osLog(LOG_INFO, "Set led[%"PRIu32"]=%"PRIu32"\n", lcfg->led_num, lcfg->value);
|
|
return true;
|
|
}
|
|
|
|
static void sensorLedsOnOff(bool flag)
|
|
{
|
|
uint8_t laddr = LP3943_REG_LS0;
|
|
uint8_t lval;
|
|
uint8_t index;
|
|
|
|
for (index=0; index < LP3943_MAX_LED_SECTION; index++) {
|
|
lval = flag ? mTask.led[index] : 0;
|
|
writeRegister(laddr + index, lval, STATE_LED);
|
|
}
|
|
}
|
|
|
|
static const struct SensorInfo sensorInfoLedsLP3943 = {
|
|
.sensorName = "Leds-LP3943",
|
|
.sensorType = SENS_TYPE_LEDS_I2C,
|
|
.supportedRates = ledsRates,
|
|
};
|
|
|
|
static const struct SensorOps sensorOpsLedsLP3943 = {
|
|
.sensorPower = sensorLP3943Power,
|
|
.sensorFirmwareUpload = sensorLP3943FwUpload,
|
|
.sensorSetRate = sensorLP3943SetRate,
|
|
.sensorCfgData = sensorCfgDataLedsLP3943,
|
|
};
|
|
|
|
static void handleI2cEvent(struct I2cTransfer *xfer)
|
|
{
|
|
switch (xfer->state) {
|
|
case STATE_RESET:
|
|
writeRegister(LP3943_REG_LS1, 0, STATE_CLEAN_LS1);
|
|
break;
|
|
|
|
case STATE_CLEAN_LS1:
|
|
writeRegister(LP3943_REG_LS2, 0, STATE_FINISH_INIT);
|
|
break;
|
|
|
|
case STATE_CLEAN_LS2:
|
|
writeRegister(LP3943_REG_LS3, 0, STATE_FINISH_INIT);
|
|
break;
|
|
|
|
case STATE_FINISH_INIT:
|
|
if (xfer->err != 0) {
|
|
osLog(LOG_INFO, "[LP3943] not detected\n");
|
|
} else {
|
|
osLog(LOG_INFO, "[LP3943] detected\n");
|
|
sensorRegisterInitComplete(mTask.sHandle);
|
|
if (LP3943_DBG_ENABLE) {
|
|
mTask.ledsOn = true;
|
|
mTask.led[0] = LP3943_DBG_VALUE;
|
|
osEnqueuePrivateEvt(EVT_TEST, NULL, NULL, mTask.id);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STATE_LED:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
releaseXfer(xfer);
|
|
}
|
|
|
|
static void handleEvent(uint32_t evtType, const void* evtData)
|
|
{
|
|
switch (evtType) {
|
|
case EVT_APP_START:
|
|
osEventUnsubscribe(mTask.id, EVT_APP_START);
|
|
i2cMasterRequest(I2C_BUS_ID, I2C_SPEED);
|
|
|
|
/* Reset Leds */
|
|
writeRegister(LP3943_REG_LS0, 0, STATE_RESET);
|
|
break;
|
|
|
|
case EVT_SENSOR_I2C:
|
|
handleI2cEvent((struct I2cTransfer *)evtData);
|
|
break;
|
|
|
|
case EVT_SENSOR_LEDS_TIMER:
|
|
if (!mTask.ledsOn)
|
|
break;
|
|
mTask.blink = !mTask.blink;
|
|
sensorLedsOnOff(mTask.blink);
|
|
break;
|
|
|
|
case EVT_TEST:
|
|
sensorLP3943SetRate(SENSOR_HZ(1), 0, NULL);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool startTask(uint32_t taskId)
|
|
{
|
|
mTask.id = taskId;
|
|
mTask.num = LP3943_MAX_LED_NUM;
|
|
memset(mTask.led, 0x00, LP3943_MAX_LED_SECTION);
|
|
mTask.ledsOn = mTask.blink = false;
|
|
|
|
/* Register sensors */
|
|
mTask.sHandle = sensorRegister(&sensorInfoLedsLP3943, &sensorOpsLedsLP3943, NULL, false);
|
|
|
|
osEventSubscribe(taskId, EVT_APP_START);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void endTask(void)
|
|
{
|
|
sensorUnregister(mTask.sHandle);
|
|
}
|
|
|
|
INTERNAL_APP_INIT(LP3943_LEDS_APP_ID, LP3943_LEDS_APP_VERSION, startTask, endTask, handleEvent);
|