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 <timer.h>
20 #include <heap.h>
21 #include <plat/rtc.h>
22 #include <plat/syscfg.h>
23 #include <hostIntf.h>
24 #include <nanohubPacket.h>
25 #include <floatRt.h>
26
27 #include <seos.h>
28
29 #include <nanohub_math.h>
30 #include <sensors.h>
31 #include <limits.h>
32
33 #define WINDOW_ORIENTATION_APP_VERSION 2
34
35 #define LOG_TAG "[WO]"
36
37 #define LOGV(fmt, ...) do { \
38 osLog(LOG_VERBOSE, LOG_TAG " " fmt, ##__VA_ARGS__); \
39 } while (0);
40
41 #define LOGW(fmt, ...) do { \
42 osLog(LOG_WARN, LOG_TAG " " fmt, ##__VA_ARGS__); \
43 } while (0);
44
45 #define LOGI(fmt, ...) do { \
46 osLog(LOG_INFO, LOG_TAG " " fmt, ##__VA_ARGS__); \
47 } while (0);
48
49 #define LOGD(fmt, ...) do { \
50 if (DBG_ENABLE) { \
51 osLog(LOG_DEBUG, LOG_TAG " " fmt, ##__VA_ARGS__); \
52 } \
53 } while (0);
54
55 #define DBG_ENABLE 0
56
57 #define ACCEL_MIN_RATE_HZ SENSOR_HZ(15) // 15 HZ
58 #define ACCEL_MAX_LATENCY_NS 40000000ull // 40 ms in nsec
59
60 // all time units in usec, angles in degrees
61 #define RADIANS_TO_DEGREES (180.0f / M_PI)
62
63 #define NS2US(x) ((x) >> 10) // convert nsec to approx usec
64
65 #define PROPOSAL_MIN_SETTLE_TIME NS2US(40000000ull) // 40 ms
66 #define PROPOSAL_MAX_SETTLE_TIME NS2US(400000000ull) // 400 ms
67 #define PROPOSAL_TILT_ANGLE_KNEE 20 // 20 deg
68 #define PROPOSAL_SETTLE_TIME_SLOPE NS2US(12000000ull) // 12 ms/deg
69
70 #define PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED NS2US(500000000ull) // 500 ms
71 #define PROPOSAL_MIN_TIME_SINCE_SWING_ENDED NS2US(300000000ull) // 300 ms
72 #define PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED NS2US(500000000ull) // 500 ms
73
74 #define FLAT_ANGLE 80
75 #define FLAT_TIME NS2US(1000000000ull) // 1 sec
76
77 #define SWING_AWAY_ANGLE_DELTA 20
78 #define SWING_TIME NS2US(300000000ull) // 300 ms
79
80 #define MAX_FILTER_DELTA_TIME NS2US(1000000000ull) // 1 sec
81 #define FILTER_TIME_CONSTANT NS2US(200000000ull) // 200 ms
82
83 #define NEAR_ZERO_MAGNITUDE 1.0f // m/s^2
84 #define ACCELERATION_TOLERANCE 4.0f
85 #define STANDARD_GRAVITY 9.8f
86 #define MIN_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY - ACCELERATION_TOLERANCE)
87 #define MAX_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY + ACCELERATION_TOLERANCE)
88
89 #define MAX_TILT 80
90 #define TILT_OVERHEAD_ENTER -40
91 #define TILT_OVERHEAD_EXIT -15
92
93 #define ADJACENT_ORIENTATION_ANGLE_GAP 45
94
95 // TILT_HISTORY_SIZE has to be greater than the time constant
96 // max(FLAT_TIME, SWING_TIME) multiplied by the highest accel sample rate after
97 // interpolation (1.0 / MIN_ACCEL_INTERVAL).
98 #define TILT_HISTORY_SIZE 64
99 #define TILT_REFERENCE_PERIOD NS2US(1800000000000ull) // 30 min
100 #define TILT_REFERENCE_BACKOFF NS2US(300000000000ull) // 5 min
101
102 // Allow up to 2.5x of the desired rate (ACCEL_MIN_RATE_HZ)
103 // The concerns are complexity and (not so much) the size of tilt_history.
104 #define MIN_ACCEL_INTERVAL NS2US(26666667ull) // 26.7 ms for 37.5 Hz
105
106 #define EVT_SENSOR_ACC_DATA_RDY sensorGetMyEventType(SENS_TYPE_ACCEL)
107 #define EVT_SENSOR_WIN_ORIENTATION_DATA_RDY sensorGetMyEventType(SENS_TYPE_WIN_ORIENTATION)
108
109 static int8_t Tilt_Tolerance[4][2] = {
110 /* ROTATION_0 */ { -25, 70 },
111 /* ROTATION_90 */ { -25, 65 },
112 /* ROTATION_180 */ { -25, 60 },
113 /* ROTATION_270 */ { -25, 65 }
114 };
115
116 struct WindowOrientationTask {
117 uint32_t tid;
118 uint32_t handle;
119 uint32_t accelHandle;
120
121 uint64_t last_filtered_time;
122 struct TripleAxisDataPoint last_filtered_sample;
123
124 uint64_t tilt_reference_time;
125 uint64_t accelerating_time;
126 uint64_t predicted_rotation_time;
127 uint64_t flat_time;
128 uint64_t swinging_time;
129
130 uint32_t tilt_history_time[TILT_HISTORY_SIZE];
131 int tilt_history_index;
132 int8_t tilt_history[TILT_HISTORY_SIZE];
133
134 int8_t current_rotation;
135 int8_t prev_valid_rotation;
136 int8_t proposed_rotation;
137 int8_t predicted_rotation;
138
139 bool flat;
140 bool swinging;
141 bool accelerating;
142 bool overhead;
143 };
144
145 static struct WindowOrientationTask mTask;
146
147 static const struct SensorInfo mSi =
148 {
149 .sensorName = "Window Orientation",
150 .sensorType = SENS_TYPE_WIN_ORIENTATION,
151 .numAxis = NUM_AXIS_EMBEDDED,
152 .interrupt = NANOHUB_INT_NONWAKEUP,
153 .minSamples = 20
154 };
155
isTiltAngleAcceptable(int rotation,int8_t tilt_angle)156 static bool isTiltAngleAcceptable(int rotation, int8_t tilt_angle)
157 {
158 return ((tilt_angle >= Tilt_Tolerance[rotation][0])
159 && (tilt_angle <= Tilt_Tolerance[rotation][1]));
160 }
161
isOrientationAngleAcceptable(int current_rotation,int rotation,int orientation_angle)162 static bool isOrientationAngleAcceptable(int current_rotation, int rotation,
163 int orientation_angle)
164 {
165 // If there is no current rotation, then there is no gap.
166 // The gap is used only to introduce hysteresis among advertised orientation
167 // changes to avoid flapping.
168 int lower_bound, upper_bound;
169
170 LOGD("current %d, new %d, orientation %d",
171 (int)current_rotation, (int)rotation, (int)orientation_angle);
172
173 if (current_rotation >= 0) {
174 // If the specified rotation is the same or is counter-clockwise
175 // adjacent to the current rotation, then we set a lower bound on the
176 // orientation angle.
177 // For example, if currentRotation is ROTATION_0 and proposed is
178 // ROTATION_90, then we want to check orientationAngle > 45 + GAP / 2.
179 if ((rotation == current_rotation)
180 || (rotation == (current_rotation + 1) % 4)) {
181 lower_bound = rotation * 90 - 45
182 + ADJACENT_ORIENTATION_ANGLE_GAP / 2;
183 if (rotation == 0) {
184 if ((orientation_angle >= 315)
185 && (orientation_angle < lower_bound + 360)) {
186 return false;
187 }
188 } else {
189 if (orientation_angle < lower_bound) {
190 return false;
191 }
192 }
193 }
194
195 // If the specified rotation is the same or is clockwise adjacent,
196 // then we set an upper bound on the orientation angle.
197 // For example, if currentRotation is ROTATION_0 and rotation is
198 // ROTATION_270, then we want to check orientationAngle < 315 - GAP / 2.
199 if ((rotation == current_rotation)
200 || (rotation == (current_rotation + 3) % 4)) {
201 upper_bound = rotation * 90 + 45
202 - ADJACENT_ORIENTATION_ANGLE_GAP / 2;
203 if (rotation == 0) {
204 if ((orientation_angle <= 45)
205 && (orientation_angle > upper_bound)) {
206 return false;
207 }
208 } else {
209 if (orientation_angle > upper_bound) {
210 return false;
211 }
212 }
213 }
214 }
215 return true;
216 }
217
isPredictedRotationAcceptable(uint64_t now,int8_t tilt_angle)218 static bool isPredictedRotationAcceptable(uint64_t now, int8_t tilt_angle)
219 {
220 // piecewise linear settle_time qualification:
221 // settle_time_needed =
222 // 1) PROPOSAL_MIN_SETTLE_TIME, for |tilt_angle| < PROPOSAL_TILT_ANGLE_KNEE.
223 // 2) linearly increasing with |tilt_angle| at slope PROPOSAL_SETTLE_TIME_SLOPE
224 // until it reaches PROPOSAL_MAX_SETTLE_TIME.
225 int abs_tilt = (tilt_angle >= 0) ? tilt_angle : -tilt_angle;
226 uint64_t settle_time_needed = PROPOSAL_MIN_SETTLE_TIME;
227 if (abs_tilt > PROPOSAL_TILT_ANGLE_KNEE) {
228 settle_time_needed += PROPOSAL_SETTLE_TIME_SLOPE
229 * (abs_tilt - PROPOSAL_TILT_ANGLE_KNEE);
230 }
231 if (settle_time_needed > PROPOSAL_MAX_SETTLE_TIME) {
232 settle_time_needed = PROPOSAL_MAX_SETTLE_TIME;
233 }
234 LOGD("settle_time_needed ~%llu (msec), settle_time ~%llu (msec)",
235 settle_time_needed >> 10, (now - mTask.predicted_rotation_time) >> 10);
236
237 // The predicted rotation must have settled long enough.
238 if (now < mTask.predicted_rotation_time + settle_time_needed) {
239 LOGD("...rejected by settle_time");
240 return false;
241 }
242
243 // The last flat state (time since picked up) must have been sufficiently
244 // long ago.
245 if (now < mTask.flat_time + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED) {
246 LOGD("...rejected by flat_time");
247 return false;
248 }
249
250 // The last swing state (time since last movement to put down) must have
251 // been sufficiently long ago.
252 if (now < mTask.swinging_time + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED) {
253 LOGD("...rejected by swing_time");
254 return false;
255 }
256
257 // The last acceleration state must have been sufficiently long ago.
258 if (now < mTask.accelerating_time
259 + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED) {
260 LOGD("...rejected by acceleration_time");
261 return false;
262 }
263
264 // Looks good!
265 return true;
266 }
267
clearPredictedRotation()268 static void clearPredictedRotation()
269 {
270 mTask.predicted_rotation = -1;
271 mTask.predicted_rotation_time = 0;
272 }
273
clearTiltHistory()274 static void clearTiltHistory()
275 {
276 mTask.tilt_history_time[0] = 0;
277 mTask.tilt_history_index = 1;
278 mTask.tilt_reference_time = 0;
279 }
280
reset()281 static void reset()
282 {
283 mTask.last_filtered_time = 0;
284 mTask.proposed_rotation = -1;
285
286 mTask.flat_time = 0;
287 mTask.flat = false;
288
289 mTask.swinging_time = 0;
290 mTask.swinging = false;
291
292 mTask.accelerating_time = 0;
293 mTask.accelerating = false;
294
295 mTask.overhead = false;
296
297 clearPredictedRotation();
298 clearTiltHistory();
299 }
300
updatePredictedRotation(uint64_t now,int rotation)301 static void updatePredictedRotation(uint64_t now, int rotation)
302 {
303 if (mTask.predicted_rotation != rotation) {
304 mTask.predicted_rotation = rotation;
305 mTask.predicted_rotation_time = now;
306 }
307 }
308
isAccelerating(float magnitude)309 static bool isAccelerating(float magnitude)
310 {
311 return ((magnitude < MIN_ACCELERATION_MAGNITUDE)
312 || (magnitude > MAX_ACCELERATION_MAGNITUDE));
313 }
314
addTiltHistoryEntry(uint64_t now,int8_t tilt)315 static void addTiltHistoryEntry(uint64_t now, int8_t tilt)
316 {
317 uint64_t old_reference_time, delta;
318 size_t i;
319 int index;
320
321 if (mTask.tilt_reference_time == 0) {
322 // set reference_time after reset()
323
324 mTask.tilt_reference_time = now - 1;
325 } else if (mTask.tilt_reference_time + TILT_REFERENCE_PERIOD < now) {
326 // uint32_t tilt_history_time[] is good up to 71 min (2^32 * 1e-6 sec).
327 // proactively shift reference_time every 30 min,
328 // all history entries are within 4.3sec interval (15Hz x 64 samples)
329
330 old_reference_time = mTask.tilt_reference_time;
331 mTask.tilt_reference_time = now - TILT_REFERENCE_BACKOFF;
332
333 delta = mTask.tilt_reference_time - old_reference_time;
334 for (i = 0; i < TILT_HISTORY_SIZE; ++i) {
335 mTask.tilt_history_time[i] = (mTask.tilt_history_time[i] > delta)
336 ? (mTask.tilt_history_time[i] - delta) : 0;
337 }
338 }
339
340 index = mTask.tilt_history_index;
341 mTask.tilt_history[index] = tilt;
342 mTask.tilt_history_time[index] = now - mTask.tilt_reference_time;
343
344 index = ((index + 1) == TILT_HISTORY_SIZE) ? 0 : (index + 1);
345 mTask.tilt_history_index = index;
346 mTask.tilt_history_time[index] = 0;
347 }
348
nextTiltHistoryIndex(int index)349 static int nextTiltHistoryIndex(int index)
350 {
351 int next = (index == 0) ? (TILT_HISTORY_SIZE - 1): (index - 1);
352 return ((mTask.tilt_history_time[next] != 0) ? next : -1);
353 }
354
isFlat(uint64_t now)355 static bool isFlat(uint64_t now)
356 {
357 int i = mTask.tilt_history_index;
358 for (; (i = nextTiltHistoryIndex(i)) >= 0;) {
359 if (mTask.tilt_history[i] < FLAT_ANGLE) {
360 break;
361 }
362 if (mTask.tilt_reference_time + mTask.tilt_history_time[i] + FLAT_TIME <= now) {
363 // Tilt has remained greater than FLAT_ANGLE for FLAT_TIME.
364 return true;
365 }
366 }
367 return false;
368 }
369
isSwinging(uint64_t now,int8_t tilt)370 static bool isSwinging(uint64_t now, int8_t tilt)
371 {
372 int i = mTask.tilt_history_index;
373 for (; (i = nextTiltHistoryIndex(i)) >= 0;) {
374 if (mTask.tilt_reference_time + mTask.tilt_history_time[i] + SWING_TIME
375 < now) {
376 break;
377 }
378 if (mTask.tilt_history[i] + SWING_AWAY_ANGLE_DELTA <= tilt) {
379 // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME.
380 // This is one-sided protection. No latency will be added when
381 // picking up the device and rotating.
382 return true;
383 }
384 }
385 return false;
386 }
387
add_samples(struct TripleAxisDataEvent * ev)388 static bool add_samples(struct TripleAxisDataEvent *ev)
389 {
390 int i, tilt_tmp;
391 int orientation_angle, nearest_rotation;
392 float x, y, z, alpha, magnitude;
393 uint64_t now_nsec = ev->referenceTime, now;
394 uint64_t then, time_delta;
395 struct TripleAxisDataPoint *last_sample;
396 size_t sampleCnt = ev->samples[0].firstSample.numSamples;
397 bool skip_sample;
398 bool accelerating, flat, swinging;
399 bool change_detected;
400 int8_t old_proposed_rotation, proposed_rotation;
401 int8_t tilt_angle;
402
403 for (i = 0; i < sampleCnt; i++) {
404
405 x = ev->samples[i].x;
406 y = ev->samples[i].y;
407 z = ev->samples[i].z;
408
409 // Apply a low-pass filter to the acceleration up vector in cartesian space.
410 // Reset the orientation listener state if the samples are too far apart in time.
411
412 now_nsec += i > 0 ? ev->samples[i].deltaTime : 0;
413 now = NS2US(now_nsec); // convert to ~usec
414
415 last_sample = &mTask.last_filtered_sample;
416 then = mTask.last_filtered_time;
417 time_delta = now - then;
418
419 if ((now < then) || (now > then + MAX_FILTER_DELTA_TIME)) {
420 reset();
421 skip_sample = true;
422 } else {
423 // alpha is the weight on the new sample
424 alpha = floatFromUint64(time_delta) / floatFromUint64(FILTER_TIME_CONSTANT + time_delta);
425 x = alpha * (x - last_sample->x) + last_sample->x;
426 y = alpha * (y - last_sample->y) + last_sample->y;
427 z = alpha * (z - last_sample->z) + last_sample->z;
428
429 skip_sample = false;
430 }
431
432 // poor man's interpolator for reduced complexity:
433 // drop samples when input sampling rate is 2.5x higher than requested
434 if (!skip_sample && (time_delta < MIN_ACCEL_INTERVAL)) {
435 skip_sample = true;
436 } else {
437 mTask.last_filtered_time = now;
438 mTask.last_filtered_sample.x = x;
439 mTask.last_filtered_sample.y = y;
440 mTask.last_filtered_sample.z = z;
441 }
442
443 accelerating = false;
444 flat = false;
445 swinging = false;
446
447 if (!skip_sample) {
448 // Calculate the magnitude of the acceleration vector.
449 magnitude = sqrtf(x * x + y * y + z * z);
450
451 if (magnitude < NEAR_ZERO_MAGNITUDE) {
452 LOGD("Ignoring sensor data, magnitude too close to zero.");
453 clearPredictedRotation();
454 } else {
455 // Determine whether the device appears to be undergoing
456 // external acceleration.
457 if (isAccelerating(magnitude)) {
458 accelerating = true;
459 mTask.accelerating_time = now;
460 }
461
462 // Calculate the tilt angle.
463 // This is the angle between the up vector and the x-y plane
464 // (the plane of the screen) in a range of [-90, 90] degrees.
465 // -90 degrees: screen horizontal and facing the ground (overhead)
466 // 0 degrees: screen vertical
467 // 90 degrees: screen horizontal and facing the sky (on table)
468 tilt_tmp = (int)(asinf(z / magnitude) * RADIANS_TO_DEGREES);
469 tilt_tmp = (tilt_tmp > 127) ? 127 : tilt_tmp;
470 tilt_tmp = (tilt_tmp < -128) ? -128 : tilt_tmp;
471 tilt_angle = tilt_tmp;
472 addTiltHistoryEntry(now, tilt_angle);
473
474 // Determine whether the device appears to be flat or swinging.
475 if (isFlat(now)) {
476 flat = true;
477 mTask.flat_time = now;
478 }
479 if (isSwinging(now, tilt_angle)) {
480 swinging = true;
481 mTask.swinging_time = now;
482 }
483
484 // If the tilt angle is too close to horizontal then we cannot
485 // determine the orientation angle of the screen.
486 if (tilt_angle <= TILT_OVERHEAD_ENTER) {
487 mTask.overhead = true;
488 } else if (tilt_angle >= TILT_OVERHEAD_EXIT) {
489 mTask.overhead = false;
490 }
491
492 if (mTask.overhead) {
493 LOGD("Ignoring sensor data, device is overhead: %d", (int)tilt_angle);
494 clearPredictedRotation();
495 } else if (fabsf(tilt_angle) > MAX_TILT) {
496 LOGD("Ignoring sensor data, tilt angle too high: %d", (int)tilt_angle);
497 clearPredictedRotation();
498 } else {
499 // Calculate the orientation angle.
500 // This is the angle between the x-y projection of the up
501 // vector onto the +y-axis, increasing clockwise in a range
502 // of [0, 360] degrees.
503 orientation_angle = (int)(-atan2f(-x, y) * RADIANS_TO_DEGREES);
504 if (orientation_angle < 0) {
505 // atan2 returns [-180, 180]; normalize to [0, 360]
506 orientation_angle += 360;
507 }
508
509 // Find the nearest rotation.
510 nearest_rotation = (orientation_angle + 45) / 90;
511 if (nearest_rotation == 4) {
512 nearest_rotation = 0;
513 }
514 // Determine the predicted orientation.
515 if (isTiltAngleAcceptable(nearest_rotation, tilt_angle)
516 && isOrientationAngleAcceptable(mTask.current_rotation,
517 nearest_rotation,
518 orientation_angle)) {
519 LOGD("Predicted: tilt %d, orientation %d, predicted %d",
520 (int)tilt_angle, (int)orientation_angle, (int)mTask.predicted_rotation);
521 updatePredictedRotation(now, nearest_rotation);
522 } else {
523 LOGD("Ignoring sensor data, no predicted rotation: "
524 "tilt %d, orientation %d",
525 (int)tilt_angle, (int)orientation_angle);
526 clearPredictedRotation();
527 }
528 }
529 }
530
531 mTask.flat = flat;
532 mTask.swinging = swinging;
533 mTask.accelerating = accelerating;
534
535 // Determine new proposed rotation.
536 old_proposed_rotation = mTask.proposed_rotation;
537 if ((mTask.predicted_rotation < 0)
538 || isPredictedRotationAcceptable(now, tilt_angle)) {
539
540 mTask.proposed_rotation = mTask.predicted_rotation;
541 }
542 proposed_rotation = mTask.proposed_rotation;
543
544 if ((proposed_rotation != old_proposed_rotation)
545 && (proposed_rotation >= 0)) {
546 mTask.current_rotation = proposed_rotation;
547
548 change_detected = (proposed_rotation != mTask.prev_valid_rotation);
549 mTask.prev_valid_rotation = proposed_rotation;
550
551 if (change_detected) {
552 return true;
553 }
554 }
555 }
556 }
557
558 return false;
559 }
560
561
windowOrientationPower(bool on,void * cookie)562 static bool windowOrientationPower(bool on, void *cookie)
563 {
564 if (on == false && mTask.accelHandle != 0) {
565 sensorRelease(mTask.tid, mTask.accelHandle);
566 mTask.accelHandle = 0;
567 osEventUnsubscribe(mTask.tid, EVT_SENSOR_ACC_DATA_RDY);
568 }
569
570 sensorSignalInternalEvt(mTask.handle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, on, 0);
571
572 return true;
573 }
574
windowOrientationSetRate(uint32_t rate,uint64_t latency,void * cookie)575 static bool windowOrientationSetRate(uint32_t rate, uint64_t latency, void *cookie)
576 {
577 int i;
578
579 if (mTask.accelHandle == 0) {
580 for (i = 0; sensorFind(SENS_TYPE_ACCEL, i, &mTask.accelHandle) != NULL; i++) {
581 if (sensorRequest(mTask.tid, mTask.accelHandle, ACCEL_MIN_RATE_HZ, ACCEL_MAX_LATENCY_NS)) {
582 // clear hysteresis
583 mTask.current_rotation = -1;
584 mTask.prev_valid_rotation = -1;
585 reset();
586 osEventSubscribe(mTask.tid, EVT_SENSOR_ACC_DATA_RDY);
587 break;
588 }
589 }
590 }
591
592 if (mTask.accelHandle != 0)
593 sensorSignalInternalEvt(mTask.handle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
594
595 return true;
596 }
597
windowOrientationFirmwareUpload(void * cookie)598 static bool windowOrientationFirmwareUpload(void *cookie)
599 {
600 sensorSignalInternalEvt(mTask.handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG,
601 1, 0);
602 return true;
603 }
604
windowOrientationFlush(void * cookie)605 static bool windowOrientationFlush(void *cookie)
606 {
607 return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_WIN_ORIENTATION), SENSOR_DATA_EVENT_FLUSH, NULL);
608 }
609
windowOrientationHandleEvent(uint32_t evtType,const void * evtData)610 static void windowOrientationHandleEvent(uint32_t evtType, const void* evtData)
611 {
612 struct TripleAxisDataEvent *ev;
613 union EmbeddedDataPoint sample;
614 bool rotation_changed;
615
616 if (evtData == SENSOR_DATA_EVENT_FLUSH)
617 return;
618
619 switch (evtType) {
620 case EVT_SENSOR_ACC_DATA_RDY:
621 ev = (struct TripleAxisDataEvent *)evtData;
622 rotation_changed = add_samples(ev);
623
624 if (rotation_changed) {
625 LOGV("rotation changed to: ******* %d *******\n",
626 (int)mTask.proposed_rotation);
627
628 // send a single int32 here so no memory alloc/free needed.
629 sample.idata = mTask.proposed_rotation;
630 if (!osEnqueueEvt(EVT_SENSOR_WIN_ORIENTATION_DATA_RDY, sample.vptr, NULL)) {
631 LOGW("osEnqueueEvt failure");
632 }
633 }
634 break;
635 }
636 }
637
638 static const struct SensorOps mSops =
639 {
640 .sensorPower = windowOrientationPower,
641 .sensorFirmwareUpload = windowOrientationFirmwareUpload,
642 .sensorSetRate = windowOrientationSetRate,
643 .sensorFlush = windowOrientationFlush,
644 };
645
window_orientation_start(uint32_t tid)646 static bool window_orientation_start(uint32_t tid)
647 {
648 mTask.tid = tid;
649
650 mTask.current_rotation = -1;
651 mTask.prev_valid_rotation = -1;
652 reset();
653
654 mTask.handle = sensorRegister(&mSi, &mSops, NULL, true);
655
656 return true;
657 }
658
windowOrientationEnd()659 static void windowOrientationEnd()
660 {
661 }
662
663 INTERNAL_APP_INIT(
664 APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 3),
665 WINDOW_ORIENTATION_APP_VERSION,
666 window_orientation_start,
667 windowOrientationEnd,
668 windowOrientationHandleEvent);
669