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 <timer.h>
24 #include <sensors.h>
25 #include <heap.h>
26 #include <hostIntf.h>
27 #include <isr.h>
28 #include <i2c.h>
29 #include <nanohubPacket.h>
30 #include <sensors.h>
31 #include <seos.h>
32 
33 #include <plat/exti.h>
34 #include <plat/gpio.h>
35 #include <plat/syscfg.h>
36 #include <variant/variant.h>
37 
38 #define AMS_TMD4903_APP_ID      APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 12)
39 #define AMS_TMD4903_APP_VERSION 14
40 
41 #ifndef PROX_INT_PIN
42 #error "PROX_INT_PIN is not defined; please define in variant.h"
43 #endif
44 
45 #ifndef PROX_IRQ
46 #error "PROX_IRQ is not defined; please define in variant.h"
47 #endif
48 
49 #define I2C_BUS_ID                             0
50 #define I2C_SPEED                              400000
51 #define I2C_ADDR                               0x39
52 
53 #define AMS_TMD4903_REG_ENABLE                 0x80
54 #define AMS_TMD4903_REG_ATIME                  0x81
55 #define AMS_TMD4903_REG_PTIME                  0x82
56 #define AMS_TMD4903_REG_WTIME                  0x83
57 #define AMS_TMD4903_REG_AILTL                  0x84
58 #define AMS_TMD4903_REG_AILTH                  0x85
59 #define AMS_TMD4903_REG_AIHTL                  0x86
60 #define AMS_TMD4903_REG_AIHTH                  0x87
61 #define AMS_TMD4903_REG_PILTL                  0x88
62 #define AMS_TMD4903_REG_PILTH                  0x89
63 #define AMS_TMD4903_REG_PIHTL                  0x8a
64 #define AMS_TMD4903_REG_PIHTH                  0x8b
65 #define AMS_TMD4903_REG_PERS                   0x8c
66 #define AMS_TMD4903_REG_CFG0                   0x8d
67 #define AMS_TMD4903_REG_PGCFG0                 0x8e
68 #define AMS_TMD4903_REG_PGCFG1                 0x8f
69 #define AMS_TMD4903_REG_CFG1                   0x90
70 #define AMS_TMD4903_REG_REVID                  0x91
71 #define AMS_TMD4903_REG_ID                     0x92
72 #define AMS_TMD4903_REG_STATUS                 0x93
73 #define AMS_TMD4903_REG_CDATAL                 0x94
74 #define AMS_TMD4903_REG_CDATAH                 0x95
75 #define AMS_TMD4903_REG_RDATAL                 0x96
76 #define AMS_TMD4903_REG_RDATAH                 0x97
77 #define AMS_TMD4903_REG_GDATAL                 0x98
78 #define AMS_TMD4903_REG_GDATAH                 0x99
79 #define AMS_TMD4903_REG_BDATAL                 0x9A
80 #define AMS_TMD4903_REG_BDATAH                 0x9B
81 #define AMS_TMD4903_REG_PDATAL                 0x9C
82 #define AMS_TMD4903_REG_PDATAH                 0x9D
83 #define AMS_TMD4903_REG_STATUS2                0x9E
84 #define AMS_TMD4903_REG_CFG4                   0xAC
85 #define AMS_TMD4903_REG_OFFSETNL               0xC0
86 #define AMS_TMD4903_REG_OFFSETNH               0xC1
87 #define AMS_TMD4903_REG_OFFSETSL               0xC2
88 #define AMS_TMD4903_REG_OFFSETSH               0xC3
89 #define AMS_TMD4903_REG_OFFSETWL               0xC4
90 #define AMS_TMD4903_REG_OFFSETWH               0xC5
91 #define AMS_TMD4903_REG_OFFSETEL               0xC6
92 #define AMS_TMD4903_REG_OFFSETEH               0xC7
93 #define AMS_TMD4903_REG_CALIB                  0xD7
94 #define AMS_TMD4903_REG_INTENAB                0xDD
95 #define AMS_TMD4903_REG_INTCLEAR               0xDE
96 
97 #define AMS_TMD4903_ID                         0xB8
98 
99 #define AMS_TMD4903_DEFAULT_RATE               SENSOR_HZ(5)
100 
101 #define AMS_TMD4903_ATIME_SETTING              0xdc
102 #define AMS_TMD4903_ATIME_MS                   ((256 - AMS_TMD4903_ATIME_SETTING) * 2.78) // in milliseconds
103 #define AMS_TMD4903_MAX_ALS_CHANNEL_COUNT      ((256 - AMS_TMD4903_ATIME_SETTING) * 1024) // in ALS data units
104 #define AMS_TMD4903_ALS_MAX_REPORT_VALUE       150000.0f // in lux
105 #define AMS_TMD4903_PTIME_SETTING              0x11
106 #define AMS_TMD4903_PGCFG0_SETTING             0x41 // pulse length: 8 us, pulse count: 2
107 #define AMS_TMD4903_PGCFG1_SETTING             0x04 // gain: 1x, drive: 50 mA
108 
109 #define AMS_TMD4903_ALS_DEBOUNCE_SAMPLES       5
110 #define AMS_TMD4903_ALS_GAIN_4X_THOLD          4000.0f
111 #define AMS_TMD4903_ALS_GAIN_16X_THOLD         1000.0f
112 #define AMS_TMD4903_ALS_GAIN_64X_THOLD         250.0f
113 
114 /* AMS_TMD4903_REG_ENABLE */
115 #define PROX_INT_ENABLE_BIT                    (1 << 5)
116 #define ALS_INT_ENABLE_BIT                     (1 << 4)
117 #define PROX_ENABLE_BIT                        (1 << 2)
118 #define ALS_ENABLE_BIT                         (1 << 1)
119 #define POWER_ON_BIT                           (1 << 0)
120 
121 /* AMS_TMD4903_REG_INTENAB */
122 #define CAL_INT_ENABLE_BIT                     (1 << 1)
123 
124 #define AMS_TMD4903_REPORT_NEAR_VALUE          0.0f // centimeters
125 #define AMS_TMD4903_REPORT_FAR_VALUE           5.0f // centimeters
126 #define AMS_TMD4903_PROX_THRESHOLD_HIGH        350  // value in PS_DATA
127 #define AMS_TMD4903_PROX_THRESHOLD_LOW         150  // value in PS_DATA
128 
129 #define AMS_TMD4903_ALS_INVALID                UINT32_MAX
130 
131 #define AMS_TMD4903_ALS_TIMER_DELAY            200000000ULL
132 
133 #define AMS_TMD4903_MAX_PENDING_I2C_REQUESTS   8
134 #define AMS_TMD4903_MAX_I2C_TRANSFER_SIZE      18
135 
136 #define MIN2(a,b) (((a) < (b)) ? (a) : (b))
137 #define MAX2(a,b) (((a) > (b)) ? (a) : (b))
138 
139 // NOTE: Define this to be 1 to enable streaming of proximity samples instead of
140 // using the interrupt
141 #define PROX_STREAMING 0
142 
143 #define INFO_PRINT(fmt, ...) do { \
144         osLog(LOG_INFO, "%s " fmt, "[TMD4903]", ##__VA_ARGS__); \
145     } while (0);
146 
147 #define DEBUG_PRINT(fmt, ...) do { \
148         if (enable_debug) {  \
149             INFO_PRINT(fmt, ##__VA_ARGS__); \
150         } \
151     } while (0);
152 
153 static const bool enable_debug = 0;
154 
155 /* Private driver events */
156 enum SensorEvents
157 {
158     EVT_SENSOR_I2C = EVT_APP_START + 1,
159     EVT_SENSOR_ALS_TIMER,
160     EVT_SENSOR_ALS_INTERRUPT,
161     EVT_SENSOR_PROX_INTERRUPT,
162 };
163 
164 /* I2C state machine */
165 enum SensorState
166 {
167     SENSOR_STATE_VERIFY_ID,
168     SENSOR_STATE_INIT_0,
169     SENSOR_STATE_INIT_1,
170     SENSOR_STATE_INIT_2,
171     SENSOR_STATE_FINISH_INIT,
172     SENSOR_STATE_START_PROX_CALIBRATION_0,
173     SENSOR_STATE_START_PROX_CALIBRATION_1,
174     SENSOR_STATE_FINISH_PROX_CALIBRATION_0,
175     SENSOR_STATE_FINISH_PROX_CALIBRATION_1,
176     SENSOR_STATE_POLL_STATUS,
177     SENSOR_STATE_ENABLING_ALS,
178     SENSOR_STATE_ENABLING_PROX,
179     SENSOR_STATE_DISABLING_ALS,
180     SENSOR_STATE_DISABLING_ALS_2,
181     SENSOR_STATE_DISABLING_PROX,
182     SENSOR_STATE_DISABLING_PROX_2,
183     SENSOR_STATE_DISABLING_PROX_3,
184     SENSOR_STATE_ALS_CHANGING_GAIN,
185     SENSOR_STATE_ALS_SAMPLING,
186     SENSOR_STATE_PROX_SAMPLING,
187     SENSOR_STATE_PROX_TRANSITION_0,
188     SENSOR_STATE_IDLE,
189 };
190 
191 enum ProxState
192 {
193     PROX_STATE_INIT,
194     PROX_STATE_NEAR,
195     PROX_STATE_FAR,
196 };
197 
198 enum ProxOffsetIndex
199 {
200     PROX_OFFSET_NORTH = 0,
201     PROX_OFFSET_SOUTH = 1,
202     PROX_OFFSET_WEST  = 2,
203     PROX_OFFSET_EAST  = 3
204 };
205 
206 enum AlsGain
207 {
208     ALS_GAIN_1X  = 0,
209     ALS_GAIN_4X  = 1,
210     ALS_GAIN_16X = 2,
211     ALS_GAIN_64X = 3
212 };
213 
214 struct AlsProxTransfer
215 {
216     size_t tx;
217     size_t rx;
218     int err;
219     uint8_t txrxBuf[AMS_TMD4903_MAX_I2C_TRANSFER_SIZE];
220     uint8_t state;
221     bool inUse;
222 };
223 
224 struct SensorData
225 {
226     struct Gpio *pin;
227     struct ChainedIsr isr;
228 
229     uint32_t tid;
230 
231     uint32_t alsHandle;
232     uint32_t proxHandle;
233     uint32_t alsTimerHandle;
234 
235     float alsOffset;
236 
237     union EmbeddedDataPoint lastAlsSample;
238 
239     struct AlsProxTransfer transfers[AMS_TMD4903_MAX_PENDING_I2C_REQUESTS];
240 
241     uint8_t lastProxState; // enum ProxState
242 
243     uint8_t alsGain;
244     uint8_t nextAlsGain;
245     uint8_t alsDebounceSamples;
246 
247     bool alsOn;
248     bool proxOn;
249     bool alsCalibrating;
250     bool proxCalibrating;
251     bool proxDirectMode;
252     bool alsChangingGain;
253     bool alsSkipSample;
254 };
255 
256 static struct SensorData mTask;
257 
258 struct AlsCalibrationData {
259     struct HostHubRawPacket header;
260     struct SensorAppEventHeader data_header;
261     float offset;
262 } __attribute__((packed));
263 
264 struct ProxCalibrationData {
265     struct HostHubRawPacket header;
266     struct SensorAppEventHeader data_header;
267     int32_t offsets[4];
268 } __attribute__((packed));
269 
270 static const uint32_t supportedRates[] =
271 {
272     SENSOR_HZ(5),
273     SENSOR_RATE_ONCHANGE,
274     0,
275 };
276 
277 static void i2cCallback(void *cookie, size_t tx, size_t rx, int err);
278 
279 /*
280  * Helper functions
281  */
282 
283 // Allocate a buffer and mark it as in use with the given state, or return NULL
284 // if no buffers available. Must *not* be called from interrupt context.
allocXfer(uint8_t state)285 static struct AlsProxTransfer *allocXfer(uint8_t state)
286 {
287     size_t i;
288 
289     for (i = 0; i < ARRAY_SIZE(mTask.transfers); i++) {
290         if (!mTask.transfers[i].inUse) {
291             mTask.transfers[i].inUse = true;
292             mTask.transfers[i].state = state;
293             return &mTask.transfers[i];
294         }
295     }
296 
297     INFO_PRINT("Ran out of i2c buffers!");
298     return NULL;
299 }
300 
301 // Helper function to write a one byte register. Returns true if we got a
302 // successful return value from i2cMasterTx().
writeRegister(uint8_t reg,uint8_t value,uint8_t state)303 static bool writeRegister(uint8_t reg, uint8_t value, uint8_t state)
304 {
305     struct AlsProxTransfer *xfer = allocXfer(state);
306     int ret = -1;
307 
308     if (xfer != NULL) {
309         xfer->txrxBuf[0] = reg;
310         xfer->txrxBuf[1] = value;
311         ret = i2cMasterTx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 2, i2cCallback, xfer);
312     }
313 
314     return (ret == 0);
315 }
316 
proxIsr(struct ChainedIsr * localIsr)317 static bool proxIsr(struct ChainedIsr *localIsr)
318 {
319     struct SensorData *data = container_of(localIsr, struct SensorData, isr);
320     uint8_t lastProxState = data->lastProxState;
321     union EmbeddedDataPoint sample;
322     bool pinState;
323 
324     if (!extiIsPendingGpio(data->pin)) {
325         return false;
326     }
327 
328     pinState = gpioGet(data->pin);
329 
330     if (data->proxOn) {
331 #if PROX_STREAMING
332         (void)sample;
333         (void)pinState;
334         (void)lastProxState;
335         if (!pinState)
336             osEnqueuePrivateEvt(EVT_SENSOR_PROX_INTERRUPT, NULL, NULL, mTask.tid);
337 #else
338         if (data->proxDirectMode) {
339             sample.fdata = (pinState) ? AMS_TMD4903_REPORT_FAR_VALUE : AMS_TMD4903_REPORT_NEAR_VALUE;
340             data->lastProxState = (pinState) ? PROX_STATE_FAR : PROX_STATE_NEAR;
341             if (data->lastProxState != lastProxState)
342                 osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL);
343         } else {
344             osEnqueuePrivateEvt(EVT_SENSOR_PROX_INTERRUPT, NULL, NULL, mTask.tid);
345         }
346 #endif
347     } else if (data->alsOn && data->alsCalibrating && !pinState) {
348         osEnqueuePrivateEvt(EVT_SENSOR_ALS_INTERRUPT, NULL, NULL, mTask.tid);
349     }
350 
351     extiClearPendingGpio(data->pin);
352     return true;
353 }
354 
enableInterrupt(struct Gpio * pin,struct ChainedIsr * isr,enum ExtiTrigger trigger)355 static bool enableInterrupt(struct Gpio *pin, struct ChainedIsr *isr, enum ExtiTrigger trigger)
356 {
357     extiEnableIntGpio(pin, trigger);
358     extiChainIsr(PROX_IRQ, isr);
359     return true;
360 }
361 
disableInterrupt(struct Gpio * pin,struct ChainedIsr * isr)362 static bool disableInterrupt(struct Gpio *pin, struct ChainedIsr *isr)
363 {
364     extiUnchainIsr(PROX_IRQ, isr);
365     extiDisableIntGpio(pin);
366     return true;
367 }
368 
i2cCallback(void * cookie,size_t tx,size_t rx,int err)369 static void i2cCallback(void *cookie, size_t tx, size_t rx, int err)
370 {
371     struct AlsProxTransfer *xfer = cookie;
372 
373     xfer->tx = tx;
374     xfer->rx = rx;
375     xfer->err = err;
376 
377     osEnqueuePrivateEvt(EVT_SENSOR_I2C, cookie, NULL, mTask.tid);
378     if (err != 0)
379         INFO_PRINT("i2c error (tx: %d, rx: %d, err: %d)\n", tx, rx, err);
380 }
381 
alsTimerCallback(uint32_t timerId,void * cookie)382 static void alsTimerCallback(uint32_t timerId, void *cookie)
383 {
384     osEnqueuePrivateEvt(EVT_SENSOR_ALS_TIMER, cookie, NULL, mTask.tid);
385 }
386 
getAlsGainFromSetting(uint8_t gainSetting)387 static inline uint32_t getAlsGainFromSetting(uint8_t gainSetting)
388 {
389     return 0x1 << (2 * gainSetting);
390 }
391 
getLuxFromAlsData(uint16_t c,uint16_t r,uint16_t g,uint16_t b)392 static float getLuxFromAlsData(uint16_t c, uint16_t r, uint16_t g, uint16_t b)
393 {
394     float lux;
395 
396     // check for channel saturation
397     if (c >= AMS_TMD4903_MAX_ALS_CHANNEL_COUNT || r >= AMS_TMD4903_MAX_ALS_CHANNEL_COUNT ||
398         g >= AMS_TMD4903_MAX_ALS_CHANNEL_COUNT || b >= AMS_TMD4903_MAX_ALS_CHANNEL_COUNT)
399        return AMS_TMD4903_ALS_MAX_REPORT_VALUE;
400 
401     lux = (ALS_GA_FACTOR *
402            ((c * ALS_C_COEFF) + (r * ALS_R_COEFF) + (g * ALS_G_COEFF) + (b * ALS_B_COEFF)) /
403         (AMS_TMD4903_ATIME_MS * getAlsGainFromSetting(mTask.alsGain))) * mTask.alsOffset;
404 
405     return MIN2(MAX2(0.0f, lux), AMS_TMD4903_ALS_MAX_REPORT_VALUE);
406 }
407 
checkForAlsAutoGain(float sample)408 static bool checkForAlsAutoGain(float sample)
409 {
410     if ((mTask.alsGain != ALS_GAIN_64X) && (sample < AMS_TMD4903_ALS_GAIN_64X_THOLD)) {
411         mTask.alsDebounceSamples = (mTask.nextAlsGain == ALS_GAIN_64X) ? (mTask.alsDebounceSamples + 1) : 1;
412         mTask.nextAlsGain = ALS_GAIN_64X;
413     } else if ((mTask.alsGain != ALS_GAIN_16X) && (sample >= AMS_TMD4903_ALS_GAIN_64X_THOLD) && (sample < AMS_TMD4903_ALS_GAIN_16X_THOLD)) {
414         mTask.alsDebounceSamples = (mTask.nextAlsGain == ALS_GAIN_16X) ? (mTask.alsDebounceSamples + 1) : 1;
415         mTask.nextAlsGain = ALS_GAIN_16X;
416     } else if ((mTask.alsGain != ALS_GAIN_4X) && (sample >= AMS_TMD4903_ALS_GAIN_16X_THOLD) && (sample < AMS_TMD4903_ALS_GAIN_4X_THOLD)) {
417         mTask.alsDebounceSamples = (mTask.nextAlsGain == ALS_GAIN_4X) ? (mTask.alsDebounceSamples + 1) : 1;
418         mTask.nextAlsGain = ALS_GAIN_4X;
419     } else if ((mTask.alsGain != ALS_GAIN_1X) && (sample >= AMS_TMD4903_ALS_GAIN_4X_THOLD)) {
420         mTask.alsDebounceSamples = (mTask.nextAlsGain == ALS_GAIN_1X) ? (mTask.alsDebounceSamples + 1) : 1;
421         mTask.nextAlsGain = ALS_GAIN_1X;
422     }
423 
424     return (mTask.alsDebounceSamples >= AMS_TMD4903_ALS_DEBOUNCE_SAMPLES);
425 }
426 
sendCalibrationResultAls(uint8_t status,float offset)427 static void sendCalibrationResultAls(uint8_t status, float offset) {
428     struct AlsCalibrationData *data = heapAlloc(sizeof(struct AlsCalibrationData));
429     if (!data) {
430         osLog(LOG_WARN, "Couldn't alloc als cal result pkt");
431         return;
432     }
433 
434     data->header.appId = AMS_TMD4903_APP_ID;
435     data->header.dataLen = (sizeof(struct AlsCalibrationData) - sizeof(struct HostHubRawPacket));
436     data->data_header.msgId = SENSOR_APP_MSG_ID_CAL_RESULT;
437     data->data_header.sensorType = SENS_TYPE_ALS;
438     data->data_header.status = status;
439     data->offset = offset;
440 
441     if (!osEnqueueEvtOrFree(EVT_APP_TO_HOST, data, heapFree))
442         osLog(LOG_WARN, "Couldn't send als cal result evt");
443 }
444 
sendCalibrationResultProx(uint8_t status,int16_t * offsets)445 static void sendCalibrationResultProx(uint8_t status, int16_t *offsets) {
446     int i;
447 
448     struct ProxCalibrationData *data = heapAlloc(sizeof(struct ProxCalibrationData));
449     if (!data) {
450         osLog(LOG_WARN, "Couldn't alloc prox cal result pkt");
451         return;
452     }
453 
454     data->header.appId = AMS_TMD4903_APP_ID;
455     data->header.dataLen = (sizeof(struct ProxCalibrationData) - sizeof(struct HostHubRawPacket));
456     data->data_header.msgId = SENSOR_APP_MSG_ID_CAL_RESULT;
457     data->data_header.sensorType = SENS_TYPE_PROX;
458     data->data_header.status = status;
459 
460     // The offsets are cast from int16_t to int32_t, so I can't use memcpy
461     for (i = 0; i < 4; i++)
462         data->offsets[i] = offsets[i];
463 
464     if (!osEnqueueEvtOrFree(EVT_APP_TO_HOST, data, heapFree))
465         osLog(LOG_WARN, "Couldn't send prox cal result evt");
466 }
467 
setMode(bool alsOn,bool proxOn,uint8_t state)468 static void setMode(bool alsOn, bool proxOn, uint8_t state)
469 {
470     uint8_t regEnable =
471         ((alsOn || proxOn) ? POWER_ON_BIT : 0) |
472         (alsOn ? ALS_ENABLE_BIT : 0) |
473         (proxOn ? (PROX_INT_ENABLE_BIT | PROX_ENABLE_BIT) : 0);
474     writeRegister(AMS_TMD4903_REG_ENABLE, regEnable, state);
475 }
476 
sensorPowerAls(bool on,void * cookie)477 static bool sensorPowerAls(bool on, void *cookie)
478 {
479     DEBUG_PRINT("sensorPowerAls: %d\n", on);
480 
481     if (on && !mTask.alsTimerHandle) {
482         mTask.alsTimerHandle = timTimerSet(AMS_TMD4903_ALS_TIMER_DELAY, 0, 50, alsTimerCallback, NULL, false);
483     } else if (!on && mTask.alsTimerHandle) {
484         timTimerCancel(mTask.alsTimerHandle);
485         mTask.alsTimerHandle = 0;
486     }
487 
488     mTask.lastAlsSample.idata = AMS_TMD4903_ALS_INVALID;
489     mTask.alsOn = on;
490     mTask.nextAlsGain = ALS_GAIN_4X;
491     mTask.alsChangingGain = false;
492     // skip first sample to make sure we get an entire integration cycle
493     mTask.alsSkipSample = true;
494     mTask.alsDebounceSamples = 0;
495 
496     setMode(on, mTask.proxOn, (on) ? SENSOR_STATE_ENABLING_ALS : SENSOR_STATE_DISABLING_ALS);
497     return true;
498 }
499 
sensorFirmwareAls(void * cookie)500 static bool sensorFirmwareAls(void *cookie)
501 {
502     return sensorSignalInternalEvt(mTask.alsHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
503 }
504 
sensorRateAls(uint32_t rate,uint64_t latency,void * cookie)505 static bool sensorRateAls(uint32_t rate, uint64_t latency, void *cookie)
506 {
507     if (rate == SENSOR_RATE_ONCHANGE)
508         rate = AMS_TMD4903_DEFAULT_RATE;
509 
510     DEBUG_PRINT("sensorRateAls: rate=%ld Hz latency=%lld ns\n", rate/1024, latency);
511 
512     return sensorSignalInternalEvt(mTask.alsHandle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
513 }
514 
sensorFlushAls(void * cookie)515 static bool sensorFlushAls(void *cookie)
516 {
517     return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), SENSOR_DATA_EVENT_FLUSH, NULL);
518 }
519 
sensorCalibrateAls(void * cookie)520 static bool sensorCalibrateAls(void *cookie)
521 {
522     DEBUG_PRINT("sensorCalibrateAls");
523 
524     if (mTask.alsOn || mTask.proxOn) {
525         INFO_PRINT("cannot calibrate while als or prox are active\n");
526         sendCalibrationResultAls(SENSOR_APP_EVT_STATUS_BUSY, 0.0f);
527         return false;
528     }
529 
530     mTask.alsOn = true;
531     mTask.lastAlsSample.idata = AMS_TMD4903_ALS_INVALID;
532     mTask.alsCalibrating = true;
533     mTask.alsOffset = 1.0f;
534     mTask.alsChangingGain = false;
535     mTask.alsSkipSample = false;
536 
537     extiClearPendingGpio(mTask.pin);
538     enableInterrupt(mTask.pin, &mTask.isr, EXTI_TRIGGER_FALLING);
539 
540     return writeRegister(AMS_TMD4903_REG_ENABLE,
541                          (POWER_ON_BIT | ALS_ENABLE_BIT | ALS_INT_ENABLE_BIT),
542                          SENSOR_STATE_IDLE);
543 }
544 
sensorCfgDataAls(void * data,void * cookie)545 static bool sensorCfgDataAls(void *data, void *cookie)
546 {
547     DEBUG_PRINT("sensorCfgDataAls");
548 
549     mTask.alsOffset = *(float*)data;
550 
551     INFO_PRINT("Received als cfg data: %d\n", (int)mTask.alsOffset);
552 
553     return true;
554 }
555 
sendLastSampleAls(void * cookie,uint32_t tid)556 static bool sendLastSampleAls(void *cookie, uint32_t tid) {
557     bool result = true;
558 
559     // If we don't end up doing anything here, the expectation is that we are powering up/haven't got the
560     // first sample yet, so the client will get a broadcast event soon
561     if (mTask.lastAlsSample.idata != AMS_TMD4903_ALS_INVALID) {
562         result = osEnqueuePrivateEvt(sensorGetMyEventType(SENS_TYPE_ALS), mTask.lastAlsSample.vptr, NULL, tid);
563     }
564     return result;
565 }
566 
sensorPowerProx(bool on,void * cookie)567 static bool sensorPowerProx(bool on, void *cookie)
568 {
569     DEBUG_PRINT("sensorPowerProx: %d\n", on);
570 
571     if (on) {
572         extiClearPendingGpio(mTask.pin);
573         enableInterrupt(mTask.pin, &mTask.isr, EXTI_TRIGGER_FALLING);
574     } else {
575         disableInterrupt(mTask.pin, &mTask.isr);
576         extiClearPendingGpio(mTask.pin);
577     }
578 
579     mTask.lastProxState = PROX_STATE_INIT;
580     mTask.proxOn = on;
581     mTask.proxDirectMode = false;
582 
583     setMode(mTask.alsOn, on, (on) ? SENSOR_STATE_ENABLING_PROX : SENSOR_STATE_DISABLING_PROX);
584     return true;
585 }
586 
sensorFirmwareProx(void * cookie)587 static bool sensorFirmwareProx(void *cookie)
588 {
589     return sensorSignalInternalEvt(mTask.proxHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
590 }
591 
sensorRateProx(uint32_t rate,uint64_t latency,void * cookie)592 static bool sensorRateProx(uint32_t rate, uint64_t latency, void *cookie)
593 {
594     if (rate == SENSOR_RATE_ONCHANGE)
595         rate = AMS_TMD4903_DEFAULT_RATE;
596 
597     DEBUG_PRINT("sensorRateProx: rate=%ld Hz latency=%lld ns\n", rate/1024, latency);
598 
599     return sensorSignalInternalEvt(mTask.proxHandle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
600 }
601 
sensorFlushProx(void * cookie)602 static bool sensorFlushProx(void *cookie)
603 {
604     return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), SENSOR_DATA_EVENT_FLUSH, NULL);
605 }
606 
sensorCalibrateProx(void * cookie)607 static bool sensorCalibrateProx(void *cookie)
608 {
609     int16_t failOffsets[4] = {0, 0, 0, 0};
610     DEBUG_PRINT("sensorCalibrateProx");
611 
612     if (mTask.alsOn || mTask.proxOn) {
613         INFO_PRINT("cannot calibrate while als or prox are active\n");
614         sendCalibrationResultProx(SENSOR_APP_EVT_STATUS_BUSY, failOffsets);
615         return false;
616     }
617 
618     mTask.lastProxState = PROX_STATE_INIT;
619     mTask.proxOn = true;
620     mTask.proxCalibrating = true;
621     mTask.proxDirectMode = false;
622 
623     extiClearPendingGpio(mTask.pin);
624     enableInterrupt(mTask.pin, &mTask.isr, EXTI_TRIGGER_FALLING);
625 
626     return writeRegister(AMS_TMD4903_REG_ENABLE, POWER_ON_BIT,
627                          SENSOR_STATE_START_PROX_CALIBRATION_0);
628 }
629 
sensorCfgDataProx(void * data,void * cookie)630 static bool sensorCfgDataProx(void *data, void *cookie)
631 {
632     struct AlsProxTransfer *xfer;
633     int32_t *offsets = (int32_t *) data;
634 
635     INFO_PRINT("Received cfg data: {%d, %d, %d, %d}\n",
636                 (int)offsets[0], (int)offsets[1], (int)offsets[2], (int)offsets[3]);
637 
638     xfer = allocXfer(SENSOR_STATE_IDLE);
639     if (xfer != NULL) {
640         xfer->txrxBuf[0] = AMS_TMD4903_REG_OFFSETNL;
641         *((int16_t*)&xfer->txrxBuf[1]) = offsets[0];
642         *((int16_t*)&xfer->txrxBuf[3]) = offsets[1];
643         *((int16_t*)&xfer->txrxBuf[5]) = offsets[2];
644         *((int16_t*)&xfer->txrxBuf[7]) = offsets[3];
645         i2cMasterTx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 9, i2cCallback, xfer);
646     }
647     return true;
648 }
649 
sendLastSampleProx(void * cookie,uint32_t tid)650 static bool sendLastSampleProx(void *cookie, uint32_t tid) {
651     union EmbeddedDataPoint sample;
652     bool result = true;
653 
654     // See note in sendLastSampleAls
655     if (mTask.lastProxState != PROX_STATE_INIT) {
656         sample.fdata = (mTask.lastProxState == PROX_STATE_NEAR) ? AMS_TMD4903_REPORT_NEAR_VALUE : AMS_TMD4903_REPORT_FAR_VALUE;
657         result = osEnqueuePrivateEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL, tid);
658     }
659     return result;
660 }
661 
662 static const struct SensorInfo sensorInfoAls =
663 {
664     .sensorName = "ALS",
665     .supportedRates = supportedRates,
666     .sensorType = SENS_TYPE_ALS,
667     .numAxis = NUM_AXIS_EMBEDDED,
668     .interrupt = NANOHUB_INT_NONWAKEUP,
669     .minSamples = 20
670 };
671 
672 static const struct SensorOps sensorOpsAls =
673 {
674     .sensorPower = sensorPowerAls,
675     .sensorFirmwareUpload = sensorFirmwareAls,
676     .sensorSetRate = sensorRateAls,
677     .sensorFlush = sensorFlushAls,
678     .sensorTriggerOndemand = NULL,
679     .sensorCalibrate = sensorCalibrateAls,
680     .sensorCfgData = sensorCfgDataAls,
681     .sensorSendOneDirectEvt = sendLastSampleAls
682 };
683 
684 static const struct SensorInfo sensorInfoProx =
685 {
686     .sensorName = "Proximity",
687     .supportedRates = supportedRates,
688     .sensorType = SENS_TYPE_PROX,
689     .numAxis = NUM_AXIS_EMBEDDED,
690     .interrupt = NANOHUB_INT_WAKEUP,
691     .minSamples = 300
692 };
693 
694 static const struct SensorOps sensorOpsProx =
695 {
696     .sensorPower = sensorPowerProx,
697     .sensorFirmwareUpload = sensorFirmwareProx,
698     .sensorSetRate = sensorRateProx,
699     .sensorFlush = sensorFlushProx,
700     .sensorTriggerOndemand = NULL,
701     .sensorCalibrate = sensorCalibrateProx,
702     .sensorCfgData = sensorCfgDataProx,
703     .sensorSendOneDirectEvt = sendLastSampleProx
704 };
705 
verifySensorId(const struct AlsProxTransfer * xfer)706 static void verifySensorId(const struct AlsProxTransfer *xfer)
707 {
708     struct AlsProxTransfer *nextXfer;
709     DEBUG_PRINT("REVID = 0x%02x, ID = 0x%02x\n", xfer->txrxBuf[0], xfer->txrxBuf[1]);
710 
711     // Check the sensor ID
712     if (xfer->err != 0 || xfer->txrxBuf[1] != AMS_TMD4903_ID) {
713         INFO_PRINT("not detected\n");
714         sensorUnregister(mTask.alsHandle);
715         sensorUnregister(mTask.proxHandle);
716         return;
717     }
718 
719     nextXfer = allocXfer(SENSOR_STATE_INIT_0);
720     if (nextXfer == NULL) {
721         return;
722     }
723 
724     // There is no SW reset on the AMS TMD4903, so we have to reset all registers manually
725     nextXfer->txrxBuf[0]  = AMS_TMD4903_REG_ENABLE;
726     nextXfer->txrxBuf[1]  = 0x00;                                          // REG_ENABLE - reset value from datasheet
727     nextXfer->txrxBuf[2]  = AMS_TMD4903_ATIME_SETTING;                     // REG_ATIME - 100 ms
728     nextXfer->txrxBuf[3]  = AMS_TMD4903_PTIME_SETTING;                     // REG_PTIME - 50 ms
729     nextXfer->txrxBuf[4]  = 0xff;                                          // REG_WTIME - reset value from datasheet
730     nextXfer->txrxBuf[5]  = 0x00;                                          // REG_AILTL - reset value from datasheet
731     nextXfer->txrxBuf[6]  = 0x00;                                          // REG_AILTH - reset value from datasheet
732     nextXfer->txrxBuf[7]  = 0x00;                                          // REG_AIHTL - reset value from datasheet
733     nextXfer->txrxBuf[8]  = 0x00;                                          // REG_AIHTH - reset value from datasheet
734     nextXfer->txrxBuf[9]  = (AMS_TMD4903_PROX_THRESHOLD_LOW & 0xFF);       // REG_PILTL
735     nextXfer->txrxBuf[10] = (AMS_TMD4903_PROX_THRESHOLD_LOW >> 8) & 0xFF;  // REG_PILTH
736     nextXfer->txrxBuf[11] = (AMS_TMD4903_PROX_THRESHOLD_HIGH & 0xFF);      // REG_PIHTL
737     nextXfer->txrxBuf[12] = (AMS_TMD4903_PROX_THRESHOLD_HIGH >> 8) & 0xFF; // REG_PIHTH
738     nextXfer->txrxBuf[13] = 0x00;                                          // REG_PERS - reset value from datasheet
739     nextXfer->txrxBuf[14] = 0xa0;                                          // REG_CFG0 - reset value from datasheet
740     nextXfer->txrxBuf[15] = AMS_TMD4903_PGCFG0_SETTING;                    // REG_PGCFG0
741     nextXfer->txrxBuf[16] = AMS_TMD4903_PGCFG1_SETTING;                    // REG_PGCFG1
742     nextXfer->txrxBuf[17] = mTask.alsGain;                                 // REG_CFG1
743 
744     i2cMasterTx(I2C_BUS_ID, I2C_ADDR, nextXfer->txrxBuf, 18, i2cCallback, nextXfer);
745 }
746 
handleAlsSample(const struct AlsProxTransfer * xfer)747 static void handleAlsSample(const struct AlsProxTransfer *xfer)
748 {
749     union EmbeddedDataPoint sample;
750     uint16_t c = *(uint16_t*)(xfer->txrxBuf);
751     uint16_t r = *(uint16_t*)(xfer->txrxBuf+2);
752     uint16_t g = *(uint16_t*)(xfer->txrxBuf+4);
753     uint16_t b = *(uint16_t*)(xfer->txrxBuf+6);
754 
755     if (mTask.alsOn) {
756         sample.fdata = getLuxFromAlsData(c, r, g, b);
757         DEBUG_PRINT("als sample ready: c=%u r=%u g=%u b=%u, gain=%dx, lux=%d\n", c, r, g, b,
758                     (int)getAlsGainFromSetting(mTask.alsGain), (int)sample.fdata);
759 
760         if (mTask.alsCalibrating) {
761             sendCalibrationResultAls(SENSOR_APP_EVT_STATUS_SUCCESS, sample.fdata);
762 
763             mTask.alsOn = false;
764             mTask.alsCalibrating = false;
765 
766             writeRegister(AMS_TMD4903_REG_ENABLE, 0, SENSOR_STATE_IDLE);
767         } else if (mTask.alsSkipSample) {
768             mTask.alsSkipSample = false;
769         } else if (!mTask.alsChangingGain) {
770             if (mTask.lastAlsSample.idata != sample.idata) {
771                 osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), sample.vptr, NULL);
772                 mTask.lastAlsSample.fdata = sample.fdata;
773             }
774 
775             if (checkForAlsAutoGain(sample.fdata)) {
776                 DEBUG_PRINT("Changing ALS gain from %dx to %dx\n", (int)getAlsGainFromSetting(mTask.alsGain),
777                             (int)getAlsGainFromSetting(mTask.nextAlsGain));
778                 if (writeRegister(AMS_TMD4903_REG_CFG1, mTask.nextAlsGain,
779                                   SENSOR_STATE_ALS_CHANGING_GAIN)) {
780                     mTask.alsChangingGain = true;
781                 }
782             }
783         }
784     }
785 }
786 
handleProxSample(const struct AlsProxTransfer * xfer)787 static void handleProxSample(const struct AlsProxTransfer *xfer)
788 {
789     union EmbeddedDataPoint sample;
790     uint16_t ps = *((uint16_t *) xfer->txrxBuf);
791     uint8_t lastProxState = mTask.lastProxState;
792 
793     DEBUG_PRINT("prox sample ready: prox=%u\n", ps);
794     if (mTask.proxOn) {
795 #if PROX_STREAMING
796         (void)lastProxState;
797         sample.fdata = ps;
798         osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL);
799 #else
800         // Lower the bar for "near" threshold so it reports "near" when the prox
801         // value is within the hysteresis threshold
802         if (ps > AMS_TMD4903_PROX_THRESHOLD_LOW) {
803             sample.fdata = AMS_TMD4903_REPORT_NEAR_VALUE;
804             mTask.lastProxState = PROX_STATE_NEAR;
805         } else {
806             sample.fdata = AMS_TMD4903_REPORT_FAR_VALUE;
807             mTask.lastProxState = PROX_STATE_FAR;
808         }
809 
810         if (mTask.lastProxState != lastProxState)
811             osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL);
812 #endif
813 
814 #if PROX_STREAMING
815         // reset proximity interrupts
816         writeRegister(AMS_TMD4903_REG_INTCLEAR, 0x60, SENSOR_STATE_IDLE);
817 #else
818         // The TMD4903 direct interrupt mode does not work properly if enabled while something is covering the sensor,
819         // so we need to wait until it is far.
820         if (mTask.lastProxState == PROX_STATE_FAR) {
821             disableInterrupt(mTask.pin, &mTask.isr);
822             extiClearPendingGpio(mTask.pin);
823 
824             // Switch to proximity interrupt direct mode
825             writeRegister(AMS_TMD4903_REG_CFG4, 0x27, SENSOR_STATE_PROX_TRANSITION_0);
826         } else {
827             // If we are in the "near" state, we cannot change to direct interrupt mode, so just clear the interrupt
828             writeRegister(AMS_TMD4903_REG_INTCLEAR, 0x60, SENSOR_STATE_IDLE);
829         }
830 #endif
831     }
832 }
833 
834 /*
835  * Sensor i2c state machine
836  */
handle_i2c_event(struct AlsProxTransfer * xfer)837 static void handle_i2c_event(struct AlsProxTransfer *xfer)
838 {
839     int i;
840     struct AlsProxTransfer *nextXfer;
841 
842     switch (xfer->state) {
843     case SENSOR_STATE_VERIFY_ID:
844         verifySensorId(xfer);
845         break;
846 
847     case SENSOR_STATE_INIT_0:
848         // Set REG_CFG4 to reset value from datasheet
849         writeRegister(AMS_TMD4903_REG_CFG4, 0x07, SENSOR_STATE_INIT_1);
850         break;
851 
852     case SENSOR_STATE_INIT_1:
853         nextXfer = allocXfer(SENSOR_STATE_INIT_2);
854         if (nextXfer != NULL) {
855             nextXfer->txrxBuf[0] = AMS_TMD4903_REG_OFFSETNL;
856             for (i = 0; i < 8; i++)
857                 nextXfer->txrxBuf[1+i] = 0x00;
858             i2cMasterTx(I2C_BUS_ID, I2C_ADDR, nextXfer->txrxBuf, 9, i2cCallback, nextXfer);
859         }
860         break;
861 
862     case SENSOR_STATE_INIT_2:
863         // Write REG_INTCLEAR to clear all interrupts
864         writeRegister(AMS_TMD4903_REG_INTCLEAR, 0xFA, SENSOR_STATE_FINISH_INIT);
865         break;
866 
867     case SENSOR_STATE_FINISH_INIT:
868         sensorRegisterInitComplete(mTask.alsHandle);
869         sensorRegisterInitComplete(mTask.proxHandle);
870         break;
871 
872     case SENSOR_STATE_START_PROX_CALIBRATION_0:
873         // Write REG_INTENAB to enable calibration interrupt
874         writeRegister(AMS_TMD4903_REG_INTENAB, CAL_INT_ENABLE_BIT,
875                       SENSOR_STATE_START_PROX_CALIBRATION_1);
876         break;
877 
878     case SENSOR_STATE_START_PROX_CALIBRATION_1:
879         // Write REG_CALIB to start calibration
880         writeRegister(AMS_TMD4903_REG_CALIB, 0x01, SENSOR_STATE_IDLE);
881         break;
882 
883     case SENSOR_STATE_FINISH_PROX_CALIBRATION_0:
884         disableInterrupt(mTask.pin, &mTask.isr);
885         extiClearPendingGpio(mTask.pin);
886 
887         mTask.proxOn = false;
888         mTask.proxCalibrating = false;
889 
890         INFO_PRINT("Calibration offsets = {%d, %d, %d, %d}\n", *((int16_t*)&xfer->txrxBuf[0]),
891                     *((int16_t*)&xfer->txrxBuf[2]), *((int16_t*)&xfer->txrxBuf[4]),
892                     *((int16_t*)&xfer->txrxBuf[6]));
893 
894         // Send calibration result
895         sendCalibrationResultProx(SENSOR_APP_EVT_STATUS_SUCCESS, (int16_t*)xfer->txrxBuf);
896 
897         // Write REG_INTENAB to disable all interrupts
898         writeRegister(AMS_TMD4903_REG_INTENAB, 0x00,
899                       SENSOR_STATE_FINISH_PROX_CALIBRATION_1);
900         break;
901 
902     case SENSOR_STATE_FINISH_PROX_CALIBRATION_1:
903         writeRegister(AMS_TMD4903_REG_ENABLE, 0, SENSOR_STATE_IDLE);
904         break;
905 
906     case SENSOR_STATE_ENABLING_ALS:
907         sensorSignalInternalEvt(mTask.alsHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0);
908         break;
909 
910     case SENSOR_STATE_ENABLING_PROX:
911         sensorSignalInternalEvt(mTask.proxHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0);
912         break;
913 
914     case SENSOR_STATE_DISABLING_ALS:
915         // Reset AGAIN to 4x
916         mTask.alsGain = ALS_GAIN_4X;
917         writeRegister(AMS_TMD4903_REG_CFG1, mTask.alsGain, SENSOR_STATE_DISABLING_ALS_2);
918         break;
919 
920     case SENSOR_STATE_DISABLING_ALS_2:
921         sensorSignalInternalEvt(mTask.alsHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, false, 0);
922         break;
923 
924     case SENSOR_STATE_DISABLING_PROX:
925         // Write REG_CFG4 to the reset value from datasheet
926         writeRegister(AMS_TMD4903_REG_CFG4, 0x07, SENSOR_STATE_DISABLING_PROX_2);
927         break;
928 
929     case SENSOR_STATE_DISABLING_PROX_2:
930         // Write REG_INTCLEAR to clear proximity interrupts
931         writeRegister(AMS_TMD4903_REG_INTCLEAR, 0x60, SENSOR_STATE_DISABLING_PROX_3);
932         break;
933 
934     case SENSOR_STATE_DISABLING_PROX_3:
935         sensorSignalInternalEvt(mTask.proxHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, false, 0);
936         break;
937 
938     case SENSOR_STATE_ALS_CHANGING_GAIN:
939         if (mTask.alsOn) {
940             mTask.alsChangingGain = false;
941             mTask.alsGain = mTask.nextAlsGain;
942             mTask.alsDebounceSamples = 0;
943             mTask.alsSkipSample = true;
944         }
945         break;
946 
947     case SENSOR_STATE_ALS_SAMPLING:
948         handleAlsSample(xfer);
949         break;
950 
951     case SENSOR_STATE_PROX_SAMPLING:
952         handleProxSample(xfer);
953         break;
954 
955     case SENSOR_STATE_PROX_TRANSITION_0:
956         if (mTask.proxOn) {
957             mTask.proxDirectMode = true;
958             extiClearPendingGpio(mTask.pin);
959             enableInterrupt(mTask.pin, &mTask.isr, EXTI_TRIGGER_BOTH);
960         }
961         break;
962 
963     default:
964         break;
965     }
966 
967     xfer->inUse = false;
968 }
969 
970 /*
971  * Main driver entry points
972  */
973 
init_app(uint32_t myTid)974 static bool init_app(uint32_t myTid)
975 {
976     /* Set up driver private data */
977     mTask.tid = myTid;
978     mTask.alsOn = false;
979     mTask.proxOn = false;
980     mTask.lastAlsSample.idata = AMS_TMD4903_ALS_INVALID;
981     mTask.lastProxState = PROX_STATE_INIT;
982     mTask.proxCalibrating = false;
983     mTask.alsOffset = 1.0f;
984     mTask.alsGain = ALS_GAIN_4X;
985 
986     mTask.pin = gpioRequest(PROX_INT_PIN);
987     gpioConfigInput(mTask.pin, GPIO_SPEED_LOW, GPIO_PULL_NONE);
988     syscfgSetExtiPort(mTask.pin);
989     mTask.isr.func = proxIsr;
990 
991     mTask.alsHandle = sensorRegister(&sensorInfoAls, &sensorOpsAls, NULL, false);
992     mTask.proxHandle = sensorRegister(&sensorInfoProx, &sensorOpsProx, NULL, false);
993 
994     osEventSubscribe(myTid, EVT_APP_START);
995 
996     return true;
997 }
998 
end_app(void)999 static void end_app(void)
1000 {
1001     disableInterrupt(mTask.pin, &mTask.isr);
1002     extiUnchainIsr(PROX_IRQ, &mTask.isr);
1003     extiClearPendingGpio(mTask.pin);
1004     gpioRelease(mTask.pin);
1005 
1006     sensorUnregister(mTask.alsHandle);
1007     sensorUnregister(mTask.proxHandle);
1008 
1009     i2cMasterRelease(I2C_BUS_ID);
1010 }
1011 
handle_event(uint32_t evtType,const void * evtData)1012 static void handle_event(uint32_t evtType, const void* evtData)
1013 {
1014     struct AlsProxTransfer *xfer;
1015 
1016     switch (evtType) {
1017     case EVT_APP_START:
1018         i2cMasterRequest(I2C_BUS_ID, I2C_SPEED);
1019 
1020         // Read the ID
1021         xfer = allocXfer(SENSOR_STATE_VERIFY_ID);
1022         if (xfer != NULL) {
1023             xfer->txrxBuf[0] = AMS_TMD4903_REG_REVID;
1024             i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 1, xfer->txrxBuf, 2, i2cCallback, xfer);
1025         }
1026         break;
1027 
1028     case EVT_SENSOR_I2C:
1029         // Dropping const here (we own this memory)
1030         handle_i2c_event((struct AlsProxTransfer *) evtData);
1031         break;
1032 
1033     case EVT_SENSOR_ALS_INTERRUPT:
1034         disableInterrupt(mTask.pin, &mTask.isr);
1035         extiClearPendingGpio(mTask.pin);
1036         // NOTE: fall-through to initiate read of ALS data registers
1037 
1038     case EVT_SENSOR_ALS_TIMER:
1039         xfer = allocXfer(SENSOR_STATE_ALS_SAMPLING);
1040         if (xfer != NULL) {
1041             xfer->txrxBuf[0] = AMS_TMD4903_REG_CDATAL;
1042             i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 1, xfer->txrxBuf, 8, i2cCallback, xfer);
1043         }
1044         break;
1045 
1046     case EVT_SENSOR_PROX_INTERRUPT:
1047         xfer = allocXfer(SENSOR_STATE_PROX_SAMPLING);
1048         if (xfer != NULL) {
1049             if (mTask.proxCalibrating) {
1050                 xfer->txrxBuf[0] = AMS_TMD4903_REG_OFFSETNL;
1051                 xfer->state = SENSOR_STATE_FINISH_PROX_CALIBRATION_0;
1052                 i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 1, xfer->txrxBuf, 8, i2cCallback, xfer);
1053             } else {
1054                 xfer->txrxBuf[0] = AMS_TMD4903_REG_PDATAL;
1055                 i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 1, xfer->txrxBuf, 2, i2cCallback, xfer);
1056             }
1057         }
1058         break;
1059 
1060     }
1061 }
1062 
1063 INTERNAL_APP_INIT(AMS_TMD4903_APP_ID, AMS_TMD4903_APP_VERSION, init_app, end_app, handle_event);
1064