1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdlib.h>
18#include <string.h>
19#include <float.h>
20
21#include <eventnums.h>
22#include <gpio.h>
23#include <heap.h>
24#include <hostIntf.h>
25#include <isr.h>
26#include <nanohubPacket.h>
27#include <sensors.h>
28#include <seos.h>
29#include <timer.h>
30#include <plat/gpio.h>
31#include <plat/exti.h>
32#include <plat/syscfg.h>
33#include <variant/variant.h>
34
35#define APP_VERSION 2
36
37#define HALL_REPORT_OPENED_VALUE  0
38#define HALL_REPORT_CLOSED_VALUE  1
39#define HALL_DEBOUNCE_TIMER_DELAY 25000000ULL // 25 milliseconds
40
41#ifndef HALL_PIN
42#error "HALL_PIN is not defined; please define in variant.h"
43#endif
44
45#ifndef HALL_IRQ
46#error "HALL_IRQ is not defined; please define in variant.h"
47#endif
48
49
50static struct SensorTask
51{
52    struct Gpio *pin;
53    struct ChainedIsr isr;
54
55    uint32_t id;
56    uint32_t sensorHandle;
57    uint32_t debounceTimerHandle;
58
59    int32_t prevReportedValue;
60
61    bool on;
62} mTask;
63
64static void debounceTimerCallback(uint32_t timerId, void *cookie)
65{
66    union EmbeddedDataPoint sample;
67    bool prevPinState = (bool)cookie;
68    bool pinState = gpioGet(mTask.pin);
69
70    if (mTask.on) {
71        if (pinState == prevPinState) {
72            sample.idata = pinState ? HALL_REPORT_OPENED_VALUE :
73                HALL_REPORT_CLOSED_VALUE;
74
75            if (sample.idata != mTask.prevReportedValue) {
76                mTask.prevReportedValue = sample.idata;
77                osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_HALL), sample.vptr, NULL);
78            }
79        }
80    }
81}
82
83static bool hallIsr(struct ChainedIsr *localIsr)
84{
85    struct SensorTask *data = container_of(localIsr, struct SensorTask, isr);
86    bool pinState = gpioGet(data->pin);
87
88    if (!extiIsPendingGpio(data->pin)) {
89        return false;
90    }
91
92    if (data->on) {
93        if (mTask.debounceTimerHandle)
94            timTimerCancel(mTask.debounceTimerHandle);
95
96        mTask.debounceTimerHandle = timTimerSet(HALL_DEBOUNCE_TIMER_DELAY, 0, 50, debounceTimerCallback, (void*)pinState, true /* oneShot */);
97    }
98
99
100    extiClearPendingGpio(data->pin);
101    return true;
102}
103
104static bool enableInterrupt(struct Gpio *pin, struct ChainedIsr *isr)
105{
106    gpioConfigInput(pin, GPIO_SPEED_LOW, GPIO_PULL_NONE);
107    syscfgSetExtiPort(pin);
108    extiEnableIntGpio(pin, EXTI_TRIGGER_BOTH);
109    extiChainIsr(HALL_IRQ, isr);
110    return true;
111}
112
113static bool disableInterrupt(struct Gpio *pin, struct ChainedIsr *isr)
114{
115    extiUnchainIsr(HALL_IRQ, isr);
116    extiDisableIntGpio(pin);
117    return true;
118}
119
120static const uint32_t supportedRates[] =
121{
122    SENSOR_RATE_ONCHANGE,
123    0
124};
125
126static const struct SensorInfo mSensorInfo =
127{
128    .sensorName = "Hall",
129    .supportedRates = supportedRates,
130    .sensorType = SENS_TYPE_HALL,
131    .numAxis = NUM_AXIS_EMBEDDED,
132    .interrupt = NANOHUB_INT_WAKEUP,
133    .minSamples = 20
134};
135
136static bool hallPower(bool on, void *cookie)
137{
138    if (on) {
139        extiClearPendingGpio(mTask.pin);
140        enableInterrupt(mTask.pin, &mTask.isr);
141    } else {
142        disableInterrupt(mTask.pin, &mTask.isr);
143        extiClearPendingGpio(mTask.pin);
144    }
145
146    mTask.on = on;
147    mTask.prevReportedValue = -1;
148
149    if (mTask.debounceTimerHandle) {
150        timTimerCancel(mTask.debounceTimerHandle);
151        mTask.debounceTimerHandle = 0;
152    }
153
154    return sensorSignalInternalEvt(mTask.sensorHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, on, 0);
155}
156
157static bool hallFirmwareUpload(void *cookie)
158{
159    return sensorSignalInternalEvt(mTask.sensorHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
160}
161
162static bool hallSetRate(uint32_t rate, uint64_t latency, void *cookie)
163{
164    // report initial state of hall interrupt pin
165    if (mTask.on) {
166        union EmbeddedDataPoint sample;
167        bool pinState = gpioGet(mTask.pin);
168        sample.idata = pinState ? HALL_REPORT_OPENED_VALUE :
169            HALL_REPORT_CLOSED_VALUE;
170        osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_HALL), sample.vptr, NULL);
171    }
172
173    return sensorSignalInternalEvt(mTask.sensorHandle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
174}
175
176static bool hallFlush(void *cookie)
177{
178    return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_HALL), SENSOR_DATA_EVENT_FLUSH, NULL);
179}
180
181static bool hallSendLastSample(void *cookie, uint32_t tid)
182{
183    union EmbeddedDataPoint sample;
184    bool result = true;
185
186    if (mTask.prevReportedValue != -1) {
187        sample.idata = mTask.prevReportedValue;
188        result = osEnqueuePrivateEvt(sensorGetMyEventType(SENS_TYPE_HALL), sample.vptr, NULL, tid);
189    }
190
191    return result;
192}
193
194static const struct SensorOps mSensorOps =
195{
196    .sensorPower = hallPower,
197    .sensorFirmwareUpload = hallFirmwareUpload,
198    .sensorSetRate = hallSetRate,
199    .sensorFlush = hallFlush,
200    .sensorSendOneDirectEvt = hallSendLastSample
201};
202
203static void handleEvent(uint32_t evtType, const void* evtData)
204{
205}
206
207static bool startTask(uint32_t taskId)
208{
209    mTask.id = taskId;
210    mTask.sensorHandle = sensorRegister(&mSensorInfo, &mSensorOps, NULL, true);
211    mTask.prevReportedValue = -1;
212    mTask.pin = gpioRequest(HALL_PIN);
213    mTask.isr.func = hallIsr;
214
215    return true;
216}
217
218static void endTask(void)
219{
220    disableInterrupt(mTask.pin, &mTask.isr);
221    extiUnchainIsr(HALL_IRQ, &mTask.isr);
222    extiClearPendingGpio(mTask.pin);
223    gpioRelease(mTask.pin);
224    sensorUnregister(mTask.sensorHandle);
225}
226
227INTERNAL_APP_INIT(APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 6), APP_VERSION, startTask, endTask, handleEvent);
228