• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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