601 lines
18 KiB
C
601 lines
18 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 <errno.h>
|
|
#include <float.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <eventnums.h>
|
|
#include <gpio.h>
|
|
#include <heap.h>
|
|
#include <hostIntf.h>
|
|
#include <isr.h>
|
|
#include <i2c.h>
|
|
#include <nanohubPacket.h>
|
|
#include <sensors.h>
|
|
#include <seos.h>
|
|
#include <timer.h>
|
|
#include <util.h>
|
|
|
|
#include <cpu/cpuMath.h>
|
|
|
|
#include <plat/exti.h>
|
|
#include <plat/gpio.h>
|
|
#include <plat/syscfg.h>
|
|
|
|
#define S3708_APP_ID APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 13)
|
|
#define S3708_APP_VERSION 1
|
|
|
|
#define I2C_BUS_ID 0
|
|
#define I2C_SPEED 400000
|
|
#define I2C_ADDR 0x20
|
|
|
|
#define S3708_REG_PAGE_SELECT 0xFF
|
|
|
|
#define S3708_REG_F01_DATA_BASE 0x06
|
|
#define S3708_INT_STATUS_LPWG 0x04
|
|
|
|
#define S3708_REG_DATA_BASE 0x08
|
|
#define S3708_REG_DATA_4_OFFSET 0x02
|
|
#define S3708_INT_STATUS_DOUBLE_TAP 0x03
|
|
|
|
#define S3708_REG_F01_CTRL_BASE 0x14
|
|
#define S3708_NORMAL_MODE 0x00
|
|
#define S3708_SLEEP_MODE 0x01
|
|
|
|
#define S3708_REG_CTRL_BASE 0x1b
|
|
#define S3708_REG_CTRL_20_OFFSET 0x07
|
|
#define S3708_REPORT_MODE_CONT 0x00
|
|
#define S3708_REPORT_MODE_LPWG 0x02
|
|
|
|
#define MAX_PENDING_I2C_REQUESTS 4
|
|
#define MAX_I2C_TRANSFER_SIZE 8
|
|
#define MAX_I2C_RETRY_DELAY 250000000ull // 250 milliseconds
|
|
#define MAX_I2C_RETRY_COUNT (15000000000ull / MAX_I2C_RETRY_DELAY) // 15 seconds
|
|
#define HACK_RETRY_SKIP_COUNT 1
|
|
|
|
#define DEFAULT_PROX_RATE_HZ SENSOR_HZ(5.0f)
|
|
#define DEFAULT_PROX_LATENCY 0.0
|
|
#define PROXIMITY_THRESH_NEAR 5.0f // distance in cm
|
|
|
|
#define EVT_SENSOR_PROX sensorGetMyEventType(SENS_TYPE_PROX)
|
|
|
|
#define ENABLE_DEBUG 0
|
|
|
|
#define VERBOSE_PRINT(fmt, ...) osLog(LOG_VERBOSE, "[DoubleTouch] " fmt, ##__VA_ARGS__)
|
|
#define INFO_PRINT(fmt, ...) osLog(LOG_INFO, "[DoubleTouch] " fmt, ##__VA_ARGS__)
|
|
#define ERROR_PRINT(fmt, ...) osLog(LOG_ERROR, "[DoubleTouch] " fmt, ##__VA_ARGS__)
|
|
#if ENABLE_DEBUG
|
|
#define DEBUG_PRINT(fmt, ...) osLog(LOG_DEBUG, "[DoubleTouch] " fmt, ##__VA_ARGS__)
|
|
#else
|
|
#define DEBUG_PRINT(fmt, ...) ((void)0)
|
|
#endif
|
|
|
|
|
|
#ifndef TOUCH_PIN
|
|
#error "TOUCH_PIN is not defined; please define in variant.h"
|
|
#endif
|
|
|
|
#ifndef TOUCH_IRQ
|
|
#error "TOUCH_IRQ is not defined; please define in variant.h"
|
|
#endif
|
|
|
|
enum SensorEvents
|
|
{
|
|
EVT_SENSOR_I2C = EVT_APP_START + 1,
|
|
EVT_SENSOR_TOUCH_INTERRUPT,
|
|
EVT_SENSOR_RETRY_TIMER,
|
|
};
|
|
|
|
enum TaskState
|
|
{
|
|
STATE_ENABLE_0,
|
|
STATE_ENABLE_1,
|
|
STATE_ENABLE_2,
|
|
STATE_DISABLE_0,
|
|
STATE_INT_HANDLE_0,
|
|
STATE_INT_HANDLE_1,
|
|
STATE_IDLE,
|
|
STATE_CANCELLED,
|
|
};
|
|
|
|
struct I2cTransfer
|
|
{
|
|
size_t tx;
|
|
size_t rx;
|
|
int err;
|
|
uint8_t txrxBuf[MAX_I2C_TRANSFER_SIZE];
|
|
uint8_t state;
|
|
bool inUse;
|
|
};
|
|
|
|
struct TaskStatistics {
|
|
uint64_t enabledTimestamp;
|
|
uint64_t proxEnabledTimestamp;
|
|
uint64_t lastProxFarTimestamp;
|
|
uint64_t totalEnabledTime;
|
|
uint64_t totalProxEnabledTime;
|
|
uint64_t totalProxFarTime;
|
|
uint32_t totalProxBecomesFar;
|
|
uint32_t totalProxBecomesNear;
|
|
};
|
|
|
|
enum ProxState {
|
|
PROX_STATE_UNKNOWN,
|
|
PROX_STATE_NEAR,
|
|
PROX_STATE_FAR
|
|
};
|
|
|
|
static struct TaskStruct
|
|
{
|
|
struct Gpio *pin;
|
|
struct ChainedIsr isr;
|
|
struct TaskStatistics stats;
|
|
struct I2cTransfer transfers[MAX_PENDING_I2C_REQUESTS];
|
|
uint32_t id;
|
|
uint32_t handle;
|
|
uint32_t retryTimerHandle;
|
|
uint32_t retryCnt;
|
|
uint32_t proxHandle;
|
|
enum ProxState proxState;
|
|
bool on;
|
|
bool gestureEnabled;
|
|
bool isrEnabled;
|
|
} mTask;
|
|
|
|
static inline void enableInterrupt(bool enable)
|
|
{
|
|
if (!mTask.isrEnabled && enable) {
|
|
extiEnableIntGpio(mTask.pin, EXTI_TRIGGER_FALLING);
|
|
extiChainIsr(TOUCH_IRQ, &mTask.isr);
|
|
} else if (mTask.isrEnabled && !enable) {
|
|
extiUnchainIsr(TOUCH_IRQ, &mTask.isr);
|
|
extiDisableIntGpio(mTask.pin);
|
|
}
|
|
mTask.isrEnabled = enable;
|
|
}
|
|
|
|
static bool touchIsr(struct ChainedIsr *localIsr)
|
|
{
|
|
struct TaskStruct *data = container_of(localIsr, struct TaskStruct, isr);
|
|
|
|
if (!extiIsPendingGpio(data->pin)) {
|
|
return false;
|
|
}
|
|
|
|
osEnqueuePrivateEvt(EVT_SENSOR_TOUCH_INTERRUPT, NULL, NULL, data->id);
|
|
|
|
extiClearPendingGpio(data->pin);
|
|
|
|
return true;
|
|
}
|
|
|
|
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);
|
|
// Do not print error for ENXIO since we expect there to be times where we
|
|
// cannot talk to the touch controller.
|
|
if (err == -ENXIO) {
|
|
DEBUG_PRINT("i2c error (tx: %d, rx: %d, err: %d)\n", tx, rx, err);
|
|
} else if (err != 0) {
|
|
ERROR_PRINT("i2c error (tx: %d, rx: %d, err: %d)\n", tx, rx, err);
|
|
}
|
|
}
|
|
|
|
static void retryTimerCallback(uint32_t timerId, void *cookie)
|
|
{
|
|
osEnqueuePrivateEvt(EVT_SENSOR_RETRY_TIMER, cookie, NULL, mTask.id);
|
|
}
|
|
|
|
// 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;
|
|
memset(mTask.transfers[i].txrxBuf, 0x00, sizeof(mTask.transfers[i].txrxBuf));
|
|
return &mTask.transfers[i];
|
|
}
|
|
}
|
|
|
|
ERROR_PRINT("Ran out of I2C buffers!");
|
|
return NULL;
|
|
}
|
|
|
|
// Helper function to initiate the I2C transfer. Returns true is the transaction
|
|
// was successfully register by I2C driver. Otherwise, returns false.
|
|
static bool performXfer(struct I2cTransfer *xfer, size_t txBytes, size_t rxBytes)
|
|
{
|
|
int ret;
|
|
|
|
if ((txBytes > MAX_I2C_TRANSFER_SIZE) || (rxBytes > MAX_I2C_TRANSFER_SIZE)) {
|
|
ERROR_PRINT("txBytes and rxBytes must be less than %d", MAX_I2C_TRANSFER_SIZE);
|
|
return false;
|
|
}
|
|
|
|
if (rxBytes) {
|
|
ret = i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, txBytes, xfer->txrxBuf, rxBytes, i2cCallback, xfer);
|
|
} else {
|
|
ret = i2cMasterTx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, txBytes, i2cCallback, xfer);
|
|
}
|
|
|
|
if (ret != 0) {
|
|
ERROR_PRINT("I2C transfer was not successful (error %d)!", ret);
|
|
}
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
// 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);
|
|
|
|
if (xfer != NULL) {
|
|
xfer->txrxBuf[0] = reg;
|
|
xfer->txrxBuf[1] = value;
|
|
return performXfer(xfer, 2, 0);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool setSleepEnable(bool enable, uint8_t state)
|
|
{
|
|
return writeRegister(S3708_REG_F01_CTRL_BASE, enable ? S3708_SLEEP_MODE : S3708_NORMAL_MODE, state);
|
|
}
|
|
|
|
static bool setReportingMode(uint8_t mode, uint8_t state)
|
|
{
|
|
struct I2cTransfer *xfer;
|
|
|
|
xfer = allocXfer(state);
|
|
if (xfer != NULL) {
|
|
xfer->txrxBuf[0] = S3708_REG_CTRL_BASE + S3708_REG_CTRL_20_OFFSET;
|
|
xfer->txrxBuf[1] = 0x00;
|
|
xfer->txrxBuf[2] = 0x00;
|
|
xfer->txrxBuf[3] = mode;
|
|
return performXfer(xfer, 4, 0);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void setRetryTimer()
|
|
{
|
|
mTask.retryCnt++;
|
|
if (mTask.retryCnt < MAX_I2C_RETRY_COUNT) {
|
|
mTask.retryTimerHandle = timTimerSet(MAX_I2C_RETRY_DELAY, 0, 50, retryTimerCallback, NULL, true);
|
|
if (!mTask.retryTimerHandle) {
|
|
ERROR_PRINT("failed to allocate timer");
|
|
}
|
|
} else {
|
|
ERROR_PRINT("could not communicate with touch controller");
|
|
}
|
|
}
|
|
|
|
static void setGesturePower(bool enable, bool skipI2c)
|
|
{
|
|
bool ret;
|
|
size_t i;
|
|
|
|
VERBOSE_PRINT("gesture: %d", enable);
|
|
|
|
// Cancel any pending I2C transactions by changing the callback state
|
|
for (i = 0; i < ARRAY_SIZE(mTask.transfers); i++) {
|
|
if (mTask.transfers[i].inUse) {
|
|
mTask.transfers[i].state = STATE_CANCELLED;
|
|
}
|
|
}
|
|
|
|
if (enable) {
|
|
mTask.retryCnt = 0;
|
|
|
|
// Set page number to 0x00
|
|
ret = writeRegister(S3708_REG_PAGE_SELECT, 0x00, STATE_ENABLE_0);
|
|
} else {
|
|
// Cancel any pending retries
|
|
if (mTask.retryTimerHandle) {
|
|
timTimerCancel(mTask.retryTimerHandle);
|
|
mTask.retryTimerHandle = 0;
|
|
}
|
|
|
|
if (skipI2c) {
|
|
ret = true;
|
|
} else {
|
|
// Reset to continuous reporting mode
|
|
ret = setReportingMode(S3708_REPORT_MODE_CONT, STATE_DISABLE_0);
|
|
}
|
|
}
|
|
|
|
if (ret) {
|
|
mTask.gestureEnabled = enable;
|
|
enableInterrupt(enable);
|
|
}
|
|
}
|
|
|
|
static void configProx(bool on) {
|
|
if (on) {
|
|
mTask.stats.proxEnabledTimestamp = sensorGetTime();
|
|
sensorRequest(mTask.id, mTask.proxHandle, DEFAULT_PROX_RATE_HZ,
|
|
DEFAULT_PROX_LATENCY);
|
|
osEventSubscribe(mTask.id, EVT_SENSOR_PROX);
|
|
} else {
|
|
sensorRelease(mTask.id, mTask.proxHandle);
|
|
osEventUnsubscribe(mTask.id, EVT_SENSOR_PROX);
|
|
|
|
mTask.stats.totalProxEnabledTime += sensorGetTime() - mTask.stats.proxEnabledTimestamp;
|
|
if (mTask.proxState == PROX_STATE_FAR) {
|
|
mTask.stats.totalProxFarTime += sensorGetTime() - mTask.stats.lastProxFarTimestamp;
|
|
}
|
|
}
|
|
mTask.proxState = PROX_STATE_UNKNOWN;
|
|
}
|
|
|
|
static bool callbackPower(bool on, void *cookie)
|
|
{
|
|
uint32_t enabledSeconds, proxEnabledSeconds, proxFarSeconds;
|
|
|
|
VERBOSE_PRINT("power: %d", on);
|
|
|
|
if (on) {
|
|
mTask.stats.enabledTimestamp = sensorGetTime();
|
|
} else {
|
|
mTask.stats.totalEnabledTime += sensorGetTime() - mTask.stats.enabledTimestamp;
|
|
}
|
|
|
|
enabledSeconds = U64_DIV_BY_U64_CONSTANT(mTask.stats.totalEnabledTime, 1000000000);
|
|
proxEnabledSeconds = U64_DIV_BY_U64_CONSTANT(mTask.stats.totalProxEnabledTime, 1000000000);
|
|
proxFarSeconds = U64_DIV_BY_U64_CONSTANT(mTask.stats.totalProxFarTime, 1000000000);
|
|
VERBOSE_PRINT("STATS: enabled %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32
|
|
", prox enabled %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32
|
|
", prox far %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32
|
|
", prox *->f %" PRIu32
|
|
", prox *->n %" PRIu32,
|
|
enabledSeconds / 3600, (enabledSeconds % 3600) / 60, enabledSeconds % 60,
|
|
proxEnabledSeconds / 3600, (proxEnabledSeconds % 3600) / 60, proxEnabledSeconds % 60,
|
|
proxFarSeconds / 3600, (proxFarSeconds % 3600) / 60, proxFarSeconds % 60,
|
|
mTask.stats.totalProxBecomesFar,
|
|
mTask.stats.totalProxBecomesNear);
|
|
|
|
// If the task is disabled, that means the AP is on and has switched the I2C
|
|
// mux. Therefore, no I2C transactions will succeed so skip them.
|
|
if (mTask.gestureEnabled) {
|
|
setGesturePower(false, true /* skipI2c */);
|
|
}
|
|
|
|
mTask.on = on;
|
|
configProx(on);
|
|
|
|
return sensorSignalInternalEvt(mTask.handle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, mTask.on, 0);
|
|
}
|
|
|
|
static bool callbackFirmwareUpload(void *cookie)
|
|
{
|
|
return sensorSignalInternalEvt(mTask.handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
|
|
}
|
|
|
|
static bool callbackSetRate(uint32_t rate, uint64_t latency, void *cookie)
|
|
{
|
|
return sensorSignalInternalEvt(mTask.handle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
|
|
}
|
|
|
|
static bool callbackFlush(void *cookie)
|
|
{
|
|
return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_DOUBLE_TOUCH), SENSOR_DATA_EVENT_FLUSH, NULL);
|
|
}
|
|
|
|
static const struct SensorInfo mSensorInfo = {
|
|
.sensorName = "Double Touch",
|
|
.sensorType = SENS_TYPE_DOUBLE_TOUCH,
|
|
.numAxis = NUM_AXIS_EMBEDDED,
|
|
.interrupt = NANOHUB_INT_WAKEUP,
|
|
.minSamples = 20
|
|
};
|
|
|
|
static const struct SensorOps mSensorOps =
|
|
{
|
|
.sensorPower = callbackPower,
|
|
.sensorFirmwareUpload = callbackFirmwareUpload,
|
|
.sensorSetRate = callbackSetRate,
|
|
.sensorFlush = callbackFlush,
|
|
};
|
|
|
|
static void processI2cResponse(struct I2cTransfer *xfer)
|
|
{
|
|
struct I2cTransfer *nextXfer;
|
|
union EmbeddedDataPoint sample;
|
|
|
|
switch (xfer->state) {
|
|
case STATE_ENABLE_0:
|
|
setSleepEnable(false, STATE_ENABLE_1);
|
|
break;
|
|
|
|
case STATE_ENABLE_1:
|
|
// HACK: DozeService reactivates pickup gesture before the screen
|
|
// comes on, so we need to wait for some time after enabling before
|
|
// trying to talk to touch controller. We may see the touch
|
|
// controller on the first few samples and then have communication
|
|
// switched off. So, wait HACK_RETRY_SKIP_COUNT samples before we
|
|
// consider the transaction.
|
|
if (mTask.retryCnt < HACK_RETRY_SKIP_COUNT) {
|
|
setRetryTimer();
|
|
} else {
|
|
setReportingMode(S3708_REPORT_MODE_LPWG, STATE_ENABLE_2);
|
|
}
|
|
break;
|
|
|
|
case STATE_ENABLE_2:
|
|
// Poll the GPIO line to see if it is low/active (it might have been
|
|
// low when we enabled the ISR, e.g. due to a pending touch event).
|
|
// Only do this after arming the LPWG, so it happens after we know
|
|
// that we can talk to the touch controller.
|
|
if (!gpioGet(mTask.pin)) {
|
|
osEnqueuePrivateEvt(EVT_SENSOR_TOUCH_INTERRUPT, NULL, NULL, mTask.id);
|
|
}
|
|
break;
|
|
|
|
case STATE_DISABLE_0:
|
|
setSleepEnable(true, STATE_IDLE);
|
|
break;
|
|
|
|
case STATE_INT_HANDLE_0:
|
|
// If the interrupt was from the LPWG function, read the function interrupt status register
|
|
if (xfer->txrxBuf[1] & S3708_INT_STATUS_LPWG) {
|
|
nextXfer = allocXfer(STATE_INT_HANDLE_1);
|
|
if (nextXfer != NULL) {
|
|
nextXfer->txrxBuf[0] = S3708_REG_DATA_BASE + S3708_REG_DATA_4_OFFSET;
|
|
performXfer(nextXfer, 1, 5);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STATE_INT_HANDLE_1:
|
|
// Verify the LPWG interrupt status
|
|
if (xfer->txrxBuf[0] & S3708_INT_STATUS_DOUBLE_TAP) {
|
|
DEBUG_PRINT("Sending event");
|
|
sample.idata = 1;
|
|
osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_DOUBLE_TOUCH), sample.vptr, NULL);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void handleI2cEvent(struct I2cTransfer *xfer)
|
|
{
|
|
if (xfer->err == 0) {
|
|
processI2cResponse(xfer);
|
|
} else if (xfer->state == STATE_ENABLE_0 || xfer->state == STATE_ENABLE_1) {
|
|
setRetryTimer();
|
|
}
|
|
|
|
xfer->inUse = false;
|
|
}
|
|
|
|
static void handleEvent(uint32_t evtType, const void* evtData)
|
|
{
|
|
struct I2cTransfer *xfer;
|
|
union EmbeddedDataPoint embeddedSample;
|
|
enum ProxState lastProxState;
|
|
int ret;
|
|
|
|
switch (evtType) {
|
|
case EVT_APP_START:
|
|
osEventUnsubscribe(mTask.id, EVT_APP_START);
|
|
ret = i2cMasterRequest(I2C_BUS_ID, I2C_SPEED);
|
|
// Since the i2c bus can be shared with other drivers, it is
|
|
// possible that one of the other drivers requested the bus first.
|
|
// Therefore, either 0 or -EBUSY is an acceptable return.
|
|
if ((ret < 0) && (ret != -EBUSY)) {
|
|
ERROR_PRINT("i2cMasterRequest() failed!");
|
|
}
|
|
|
|
sensorFind(SENS_TYPE_PROX, 0, &mTask.proxHandle);
|
|
|
|
sensorRegisterInitComplete(mTask.handle);
|
|
break;
|
|
|
|
case EVT_SENSOR_I2C:
|
|
handleI2cEvent((struct I2cTransfer *)evtData);
|
|
break;
|
|
|
|
case EVT_SENSOR_TOUCH_INTERRUPT:
|
|
if (mTask.on) {
|
|
// Read the interrupt status register
|
|
xfer = allocXfer(STATE_INT_HANDLE_0);
|
|
if (xfer != NULL) {
|
|
xfer->txrxBuf[0] = S3708_REG_F01_DATA_BASE;
|
|
performXfer(xfer, 1, 2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EVT_SENSOR_PROX:
|
|
if (mTask.on) {
|
|
// cast off the const, and cast to union
|
|
embeddedSample = (union EmbeddedDataPoint)((void*)evtData);
|
|
lastProxState = mTask.proxState;
|
|
mTask.proxState = (embeddedSample.fdata < PROXIMITY_THRESH_NEAR) ? PROX_STATE_NEAR : PROX_STATE_FAR;
|
|
|
|
if ((lastProxState != PROX_STATE_FAR) && (mTask.proxState == PROX_STATE_FAR)) {
|
|
++mTask.stats.totalProxBecomesFar;
|
|
mTask.stats.lastProxFarTimestamp = sensorGetTime();
|
|
setGesturePower(true, false);
|
|
} else if ((lastProxState != PROX_STATE_NEAR) && (mTask.proxState == PROX_STATE_NEAR)) {
|
|
++mTask.stats.totalProxBecomesNear;
|
|
if (lastProxState == PROX_STATE_FAR) {
|
|
mTask.stats.totalProxFarTime += sensorGetTime() - mTask.stats.lastProxFarTimestamp;
|
|
setGesturePower(false, false);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EVT_SENSOR_RETRY_TIMER:
|
|
if (mTask.on) {
|
|
// Set page number to 0x00
|
|
writeRegister(S3708_REG_PAGE_SELECT, 0x00, STATE_ENABLE_0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool startTask(uint32_t taskId)
|
|
{
|
|
mTask.id = taskId;
|
|
mTask.handle = sensorRegister(&mSensorInfo, &mSensorOps, NULL, false);
|
|
|
|
mTask.pin = gpioRequest(TOUCH_PIN);
|
|
gpioConfigInput(mTask.pin, GPIO_SPEED_LOW, GPIO_PULL_NONE);
|
|
syscfgSetExtiPort(mTask.pin);
|
|
mTask.isr.func = touchIsr;
|
|
|
|
mTask.stats.totalProxBecomesFar = 0;
|
|
mTask.stats.totalProxBecomesNear = 0;
|
|
|
|
osEventSubscribe(taskId, EVT_APP_START);
|
|
return true;
|
|
}
|
|
|
|
static void endTask(void)
|
|
{
|
|
enableInterrupt(false);
|
|
extiUnchainIsr(TOUCH_IRQ, &mTask.isr);
|
|
extiClearPendingGpio(mTask.pin);
|
|
gpioRelease(mTask.pin);
|
|
|
|
i2cMasterRelease(I2C_BUS_ID);
|
|
|
|
sensorUnregister(mTask.handle);
|
|
}
|
|
|
|
INTERNAL_APP_INIT(S3708_APP_ID, S3708_APP_VERSION, startTask, endTask, handleEvent);
|