1 /*
2 * Copyright (C) 2008 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 /*
18 * Based on htc/flounder/lights/lights.h
19 */
20
21 #define LOG_TAG "lights"
22
23 #include <malloc.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <cutils/log.h>
28 #include <hardware/lights.h>
29 #include <hardware/hardware.h>
30 #include <gpio.h>
31
32 /* List of supported lights */
33 typedef enum {
34 NOTIFICATIONS_TYPE,
35 LIGHTS_TYPE_NUM
36 } light_type_t;
37
38 /* Light device data structure */
39 struct light_device_ext_t {
40 /* Base device */
41 struct light_device_t base_dev;
42 /* Physical pin */
43 int pin;
44 /* Current state of the light device */
45 struct light_state_t state;
46 /* Number of device references */
47 int refs;
48 /* Synchronization attributes */
49 pthread_t flash_thread;
50 pthread_cond_t flash_cond;
51 pthread_mutex_t flash_signal_mutex;
52 pthread_mutex_t write_mutex;
53 /* Transform function to apply on value */
54 int (*transform)(int);
55 };
56
57 static int64_t const ONE_MS_IN_NS = 1000000LL;
58 static int64_t const ONE_S_IN_NS = 1000000000LL;
59
60 /*
61 * Platform version strings used to identify board versions
62 */
63 static char * const EDISON_ARDUINO_PLATFORM_VERSION = "arduino";
64 static char * const MINNOWBOARD_TURBOT_PLATFORM_VERSION = "Turbot";
65
66 /*
67 * Pin constants
68 * Please add a pin to EDISON_ARDUINO_PINS, EDISON_MINIBOARD_PINS &
69 * MINNOWBOARD_MAX_PINS when you add a new light type
70 */
71 static int const EDISON_ARDUINO_PINS[LIGHTS_TYPE_NUM] = {13};
72 static int const EDISON_MINIBOARD_PINS[LIGHTS_TYPE_NUM] = {31};
73 static int const MINNOWBOARD_MAX_PINS[LIGHTS_TYPE_NUM] = {21};
74 static int const MINNOWBOARD_TURBOT_PINS[LIGHTS_TYPE_NUM] = {27};
75
76 /*
77 * Array of light devices with write_mutex statically initialized
78 * to be able to synchronize the open_lights & close_lights functions
79 */
80 struct light_device_ext_t light_devices[] = {
81 [ 0 ... (LIGHTS_TYPE_NUM - 1) ] = { .write_mutex = PTHREAD_MUTEX_INITIALIZER }
82 };
83
84 /*
85 * Set the GPIO value
86 * @param pin physical pin of the GPIO
87 * @param value what value to set
88 * @return 0 if success, error code otherwise
89 */
set_gpio_value(int pin,int value)90 static int set_gpio_value(int pin, int value)
91 {
92 mraa_gpio_context gpio = NULL;
93 int rc = 0;
94
95 if ((value != 0) && (value != 1)) {
96 return EINVAL;
97 }
98
99 gpio = mraa_gpio_init(pin);
100 if (gpio == NULL) {
101 return EPERM;
102 }
103
104 if (mraa_gpio_dir(gpio, MRAA_GPIO_OUT) != MRAA_SUCCESS) {
105 rc = EPERM;
106 goto close_gpio;
107 }
108
109 if (mraa_gpio_write(gpio, value) != MRAA_SUCCESS) {
110 rc = EPERM;
111 }
112
113 close_gpio:
114 if (mraa_gpio_close(gpio) != MRAA_SUCCESS) {
115 rc = EPERM;
116 }
117
118 return rc;
119 }
120
121 /*
122 * Invert value
123 * @param value what value to invert
124 * @return value inverted
125 */
invert_value(int value)126 static int invert_value(int value) {
127 return value ? 0 : 1;
128 }
129
130 /*
131 * Get current timestamp in nanoseconds
132 * @return time in nanoseconds
133 */
get_timestamp_monotonic()134 int64_t get_timestamp_monotonic()
135 {
136 struct timespec ts = {0, 0};
137
138 if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
139 return ONE_S_IN_NS * ts.tv_sec + ts.tv_nsec;
140 }
141
142 return -1;
143 }
144
145 /*
146 * Populates a timespec data structure from a int64_t timestamp
147 * @param out what timespec to populate
148 * @param target_ns timestamp in nanoseconds
149 */
set_timestamp(struct timespec * out,int64_t target_ns)150 void set_timestamp(struct timespec *out, int64_t target_ns)
151 {
152 out->tv_sec = target_ns / ONE_S_IN_NS;
153 out->tv_nsec = target_ns % ONE_S_IN_NS;
154 }
155
156 /*
157 * pthread routine which flashes an LED
158 * @param flash_param light device pointer
159 */
flash_routine(void * flash_param)160 static void * flash_routine (void *flash_param)
161 {
162 struct light_device_ext_t *dev = (struct light_device_ext_t *)flash_param;
163 struct light_state_t *flash_state;
164 int color = 0, rc = 0;
165 struct timespec target_time;
166 int64_t timestamp, period;
167
168 if (dev == NULL) {
169 ALOGE("%s: Cannot flash a NULL light device", __func__);
170 return NULL;
171 }
172
173 flash_state = &dev->state;
174
175 pthread_mutex_lock(&dev->flash_signal_mutex);
176
177 color = flash_state->color;
178
179 /* Light flashing loop */
180 while (flash_state->flashMode) {
181 rc = set_gpio_value(dev->pin, color);
182 if (rc != 0) {
183 ALOGE("%s: Cannot set light color", __func__);
184 goto mutex_unlock;
185 }
186
187 timestamp = get_timestamp_monotonic();
188 if (timestamp < 0) {
189 ALOGE("%s: Cannot get time from monotonic clock", __func__);
190 goto mutex_unlock;
191 }
192
193 if (color) {
194 color = 0;
195 period = flash_state->flashOnMS * ONE_MS_IN_NS;
196 } else {
197 color = 1;
198 period = flash_state->flashOffMS * ONE_MS_IN_NS;
199 }
200
201 /* check for overflow */
202 if (timestamp > LLONG_MAX - period) {
203 ALOGE("%s: Timestamp overflow", __func__);
204 goto mutex_unlock;
205 }
206
207 timestamp += period;
208
209 /* sleep until target_time or the cond var is signaled */
210 set_timestamp(&target_time, timestamp);
211 rc = pthread_cond_timedwait(&dev->flash_cond, &dev->flash_signal_mutex, &target_time);
212 if ((rc != 0) && (rc != ETIMEDOUT)) {
213 ALOGE("%s: pthread_cond_timedwait returned an error", __func__);
214 goto mutex_unlock;
215 }
216 }
217
218 mutex_unlock:
219 pthread_mutex_unlock(&dev->flash_signal_mutex);
220
221 return NULL;
222 }
223
224 /*
225 * Check lights flash state
226 * @param state pointer to the state to check
227 * @return 0 if success, error code otherwise
228 */
check_flash_state(struct light_state_t const * state)229 static int check_flash_state(struct light_state_t const *state)
230 {
231 int64_t ns = 0;
232
233 if ((state->flashOffMS < 0) || (state->flashOnMS < 0)) {
234 return EINVAL;
235 }
236
237 if ((state->flashOffMS == 0) && (state->flashOnMS) == 0) {
238 return EINVAL;
239 }
240
241 /* check for overflow in ns */
242 ns = state->flashOffMS * ONE_MS_IN_NS;
243 if (ns / ONE_MS_IN_NS != state->flashOffMS) {
244 return EINVAL;
245 }
246 ns = state->flashOnMS * ONE_MS_IN_NS;
247 if (ns / ONE_MS_IN_NS != state->flashOnMS) {
248 return EINVAL;
249 }
250
251 return 0;
252 }
253
254 /*
255 * Generic function for setting the state of the light
256 * @param base_dev light device data structure
257 * @param state what state to set
258 * @return 0 if success, error code otherwise
259 */
set_light_generic(struct light_device_t * base_dev,struct light_state_t const * state)260 static int set_light_generic(struct light_device_t *base_dev,
261 struct light_state_t const *state)
262 {
263 struct light_device_ext_t *dev = (struct light_device_ext_t *)base_dev;
264 struct light_state_t *current_state;
265 int rc = 0;
266
267 if (dev == NULL) {
268 ALOGE("%s: Cannot set state for NULL device", __func__);
269 return EINVAL;
270 }
271
272 current_state = &dev->state;
273
274 pthread_mutex_lock(&dev->write_mutex);
275
276 if (dev->refs == 0) {
277 ALOGE("%s: The light device is not opened", __func__);
278 pthread_mutex_unlock(&dev->write_mutex);
279 return EINVAL;
280 }
281
282 ALOGV("%s: flashMode:%x, color:%x", __func__, state->flashMode, state->color);
283
284 if (current_state->flashMode) {
285 /* destroy flashing thread */
286 pthread_mutex_lock(&dev->flash_signal_mutex);
287 current_state->flashMode = LIGHT_FLASH_NONE;
288 pthread_cond_signal(&dev->flash_cond);
289 pthread_mutex_unlock(&dev->flash_signal_mutex);
290 pthread_join(dev->flash_thread, NULL);
291 }
292
293 *current_state = *state;
294 if (dev->transform != NULL) {
295 current_state->color = dev->transform(current_state->color);
296 }
297
298 if (current_state->flashMode) {
299 /* start flashing thread */
300 if (check_flash_state(current_state) == 0) {
301 rc = pthread_create(&dev->flash_thread, NULL,
302 flash_routine, (void *)dev);
303 if (rc != 0) {
304 ALOGE("%s: Cannot create flashing thread", __func__);
305 current_state->flashMode = LIGHT_FLASH_NONE;
306 }
307 } else {
308 ALOGE("%s: Flash state is invalid", __func__);
309 current_state->flashMode = LIGHT_FLASH_NONE;
310 }
311 } else {
312 rc = set_gpio_value(dev->pin, current_state->color);
313 if (rc != 0) {
314 ALOGE("%s: Cannot set light color.", __func__);
315 }
316 }
317
318 pthread_mutex_unlock(&dev->write_mutex);
319
320 return rc;
321 }
322
323 /*
324 * Initialize light synchronization resources
325 * @param cond what condition variable to initialize
326 * @param signal_mutex what mutex (associated with the condvar) to initialize
327 * @return 0 if success, error code otherwise
328 */
init_light_sync_resources(pthread_cond_t * cond,pthread_mutex_t * signal_mutex)329 static int init_light_sync_resources(pthread_cond_t *cond,
330 pthread_mutex_t *signal_mutex)
331 {
332 int rc = 0;
333 pthread_condattr_t condattr;
334
335 rc = pthread_condattr_init(&condattr);
336 if (rc != 0) {
337 ALOGE("%s: Cannot initialize the pthread condattr", __func__);
338 return rc;
339 }
340
341 rc = pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
342 if (rc != 0) {
343 ALOGE("%s: Cannot set the clock of condattr to monotonic", __func__);
344 goto destroy_condattr;
345 }
346
347 rc = pthread_cond_init(cond, &condattr);
348 if (rc != 0) {
349 ALOGE("%s: Cannot intialize the pthread structure", __func__);
350 goto destroy_condattr;
351 }
352
353 rc = pthread_mutex_init(signal_mutex, NULL);
354 if (rc != 0) {
355 ALOGE("%s: Cannot initialize the mutex associated with the pthread cond", __func__);
356 goto destroy_cond;
357 }
358
359 pthread_condattr_destroy(&condattr);
360 return rc;
361
362 destroy_cond:
363 pthread_cond_destroy(cond);
364 destroy_condattr:
365 pthread_condattr_destroy(&condattr);
366 return rc;
367 }
368
369 /*
370 * Free light synchronization resources
371 * @param cond what condition variable to free
372 * @param signal_mutex what mutex (associated with the condvar) to free
373 */
free_light_sync_resources(pthread_cond_t * cond,pthread_mutex_t * signal_mutex)374 static void free_light_sync_resources(pthread_cond_t *cond,
375 pthread_mutex_t *signal_mutex)
376 {
377 pthread_mutex_destroy(signal_mutex);
378 pthread_cond_destroy(cond);
379 }
380
381 /*
382 * Close the lights module
383 * @param base_dev light device data structure
384 * @return 0 if success, error code otherwise
385 */
close_lights(struct light_device_t * base_dev)386 static int close_lights(struct light_device_t *base_dev)
387 {
388 struct light_device_ext_t *dev = (struct light_device_ext_t *)base_dev;
389 int rc = 0;
390
391 if (dev == NULL) {
392 ALOGE("%s: Cannot deallocate a NULL light device", __func__);
393 return EINVAL;
394 }
395
396 pthread_mutex_lock(&dev->write_mutex);
397
398 if (dev->refs == 0) {
399 /* the light device is not open */
400 rc = EINVAL;
401 goto mutex_unlock;
402 } else if (dev->refs > 1) {
403 goto dec_refs;
404 }
405
406 if (dev->state.flashMode) {
407 /* destroy flashing thread */
408 pthread_mutex_lock(&dev->flash_signal_mutex);
409 dev->state.flashMode = LIGHT_FLASH_NONE;
410 pthread_cond_signal(&dev->flash_cond);
411 pthread_mutex_unlock(&dev->flash_signal_mutex);
412 pthread_join(dev->flash_thread, NULL);
413 }
414
415 free_light_sync_resources(&dev->flash_cond,
416 &dev->flash_signal_mutex);
417
418 dec_refs:
419 dev->refs--;
420
421 mutex_unlock:
422 pthread_mutex_unlock(&dev->write_mutex);
423
424 return rc;
425 }
426
427 /*
428 * Module initialization routine which detects the LEDs' GPIOs
429 * @param type light device type
430 * @return 0 if success, error code otherwise
431 */
init_module(int type)432 static int init_module(int type)
433 {
434 const char *platform_version = NULL;
435
436 if (type < 0 || type >= LIGHTS_TYPE_NUM) {
437 return EINVAL;
438 }
439
440 light_devices[type].transform = NULL;
441
442 switch(mraa_get_platform_type()) {
443 case MRAA_INTEL_EDISON_FAB_C:
444 platform_version = mraa_get_platform_version(MRAA_MAIN_PLATFORM_OFFSET);
445 if ((platform_version != NULL) && (strncmp(platform_version,
446 EDISON_ARDUINO_PLATFORM_VERSION,
447 strlen(EDISON_ARDUINO_PLATFORM_VERSION)) == 0)) {
448 light_devices[type].pin = EDISON_ARDUINO_PINS[type];
449 } else {
450 light_devices[type].pin = EDISON_MINIBOARD_PINS[type];
451 }
452 break;
453 case MRAA_INTEL_MINNOWBOARD_MAX:
454 platform_version = mraa_get_platform_version(MRAA_MAIN_PLATFORM_OFFSET);
455 if ((platform_version != NULL) && (strncmp(platform_version,
456 MINNOWBOARD_TURBOT_PLATFORM_VERSION,
457 strlen(MINNOWBOARD_TURBOT_PLATFORM_VERSION)) == 0)) {
458 light_devices[type].pin = MINNOWBOARD_TURBOT_PINS[type];
459 light_devices[type].transform = invert_value;
460 } else {
461 light_devices[type].pin = MINNOWBOARD_MAX_PINS[type];
462 }
463 break;
464 default:
465 ALOGE("%s: Hardware platform not supported", __func__);
466 return EINVAL;
467 }
468
469 return 0;
470 }
471
472 /*
473 * Open a new lights device instance by name
474 * @param module associated hw module data structure
475 * @param name lights device name
476 * @param device where to store the pointer of the allocated device
477 * @return 0 if success, error code otherwise
478 */
open_lights(const struct hw_module_t * module,char const * name,struct hw_device_t ** device)479 static int open_lights(const struct hw_module_t *module, char const *name,
480 struct hw_device_t **device)
481 {
482 struct light_device_ext_t *dev;
483 int rc = 0, type = -1;
484
485 ALOGV("%s: Opening %s lights module", __func__, name);
486
487 if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
488 type = NOTIFICATIONS_TYPE;
489 } else {
490 return EINVAL;
491 }
492
493 dev = (struct light_device_ext_t *)(light_devices + type);
494
495 pthread_mutex_lock(&dev->write_mutex);
496
497 if (dev->refs != 0) {
498 /* already opened; nothing to do */
499 goto inc_refs;
500 }
501
502 rc = init_module(type);
503 if (rc != 0) {
504 ALOGE("%s: Failed to initialize lights module", __func__);
505 goto mutex_unlock;
506 }
507
508 rc = init_light_sync_resources(&dev->flash_cond,
509 &dev->flash_signal_mutex);
510 if (rc != 0) {
511 goto mutex_unlock;
512 }
513
514 dev->base_dev.common.tag = HARDWARE_DEVICE_TAG;
515 dev->base_dev.common.version = 0;
516 dev->base_dev.common.module = (struct hw_module_t *)module;
517 dev->base_dev.common.close = (int (*)(struct hw_device_t *))close_lights;
518 dev->base_dev.set_light = set_light_generic;
519
520 inc_refs:
521 dev->refs++;
522 *device = (struct hw_device_t *)dev;
523
524 mutex_unlock:
525 pthread_mutex_unlock(&dev->write_mutex);
526 return rc;
527 }
528
529 static struct hw_module_methods_t lights_methods =
530 {
531 .open = open_lights,
532 };
533
534 struct hw_module_t HAL_MODULE_INFO_SYM =
535 {
536 .tag = HARDWARE_MODULE_TAG,
537 .version_major = 1,
538 .version_minor = 0,
539 .id = LIGHTS_HARDWARE_MODULE_ID,
540 .name = "Edison lights module",
541 .author = "Intel",
542 .methods = &lights_methods,
543 };
544