1 /*
2 * Copyright (C) 2009 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 #define LOG_TAG "lights"
18
19 #include <cutils/log.h>
20
21 #include <stdint.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <pthread.h>
27
28 #include <sys/ioctl.h>
29 #include <sys/types.h>
30
31 #include <hardware/lights.h>
32
33 #define LIGHT_ATTENTION 1
34 #define LIGHT_NOTIFY 2
35
36 /******************************************************************************/
37 static struct light_state_t *g_notify;
38 static struct light_state_t *g_attention;
39 static pthread_once_t g_init = PTHREAD_ONCE_INIT;
40 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
41 static int g_backlight = 255;
42 static int g_buttons = 0;
43 struct led_prop {
44 const char *filename;
45 int fd;
46 };
47
48 struct led {
49 struct led_prop mode;
50 struct led_prop brightness;
51 struct led_prop blink;
52 struct led_prop color;
53 struct led_prop period;
54 };
55
56 enum {
57 JOGBALL_LED,
58 BUTTONS_LED,
59 AMBER_LED,
60 GREEN_LED,
61 BLUE_LED,
62 RED_LED,
63 LCD_BACKLIGHT,
64 NUM_LEDS,
65 };
66
67 struct led leds[NUM_LEDS] = {
68 [JOGBALL_LED] = {
69 .brightness = { "/sys/class/leds/jogball-backlight/brightness", 0},
70 .color = { "/sys/class/leds/jogball-backlight/color", 0},
71 .period = { "/sys/class/leds/jogball-backlight/period", 0},
72 },
73 [BUTTONS_LED] = {
74 .brightness = { "/sys/class/leds/button-backlight/brightness", 0},
75 },
76 [RED_LED] = {
77 .brightness = { "/sys/class/leds/red/brightness", 0},
78 .blink = { "/sys/class/leds/red/blink", 0},
79 },
80 [GREEN_LED] = {
81 .brightness = { "/sys/class/leds/green/brightness", 0},
82 .blink = { "/sys/class/leds/green/blink", 0},
83 },
84 [BLUE_LED] = {
85 .brightness = { "/sys/class/leds/blue/brightness", 0},
86 .blink = { "/sys/class/leds/blue/blink", 0},
87 },
88 [AMBER_LED] = {
89 .brightness = { "/sys/class/leds/amber/brightness", 0},
90 .blink = { "/sys/class/leds/amber/blink", 0},
91 },
92 [LCD_BACKLIGHT] = {
93 .brightness = { "/sys/class/leds/lcd-backlight/brightness", 0},
94 },
95 };
96
97 enum {
98 RGB_BLACK = 0x000000,
99 RGB_RED = 0xFF0000,
100 RGB_AMBER = 0xFFFF00, /* note this is actually RGB yellow */
101 RGB_GREEN = 0x00FF00,
102 RGB_BLUE = 0x0000FF,
103 RGB_WHITE = 0xFFFFFF,
104 RGB_PINK = 0xFFC0CB,
105 RGB_ORANGE = 0xFFA500,
106 RGB_YELLOW = 0xFFFF00,
107 RGB_PURPLE = 0x800080,
108 RGB_LT_BLUE = 0xADD8E6,
109 };
110
111 /**
112 * device methods
113 */
114
init_prop(struct led_prop * prop)115 static int init_prop(struct led_prop *prop)
116 {
117 int fd;
118
119 prop->fd = -1;
120 if (!prop->filename)
121 return 0;
122 fd = open(prop->filename, O_RDWR);
123 if (fd < 0) {
124 LOGE("init_prop: %s cannot be opened (%s)\n", prop->filename,
125 strerror(errno));
126 return -errno;
127 }
128
129 prop->fd = fd;
130 return 0;
131 }
132
close_prop(struct led_prop * prop)133 static void close_prop(struct led_prop *prop)
134 {
135 int fd;
136
137 if (prop->fd > 0)
138 close(prop->fd);
139 return;
140 }
141
init_globals(void)142 void init_globals(void)
143 {
144 int i;
145 pthread_mutex_init(&g_lock, NULL);
146
147 for (i = 0; i < NUM_LEDS; ++i) {
148 init_prop(&leds[i].brightness);
149 init_prop(&leds[i].blink);
150 init_prop(&leds[i].mode);
151 init_prop(&leds[i].color);
152 init_prop(&leds[i].period);
153 }
154 g_attention = malloc(sizeof(struct light_state_t));
155 memset(g_attention, 0, sizeof(*g_attention));
156 g_notify = malloc(sizeof(struct light_state_t));
157 memset(g_notify, 0, sizeof(*g_notify));
158 }
159
160 static int
write_int(struct led_prop * prop,int value)161 write_int(struct led_prop *prop, int value)
162 {
163 char buffer[20];
164 int bytes;
165 int amt;
166
167 if (prop->fd < 0)
168 return 0;
169
170 LOGV("%s %s: 0x%x\n", __func__, prop->filename, value);
171
172 bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
173 while (bytes > 0) {
174 amt = write(prop->fd, buffer, bytes);
175 if (amt < 0) {
176 if (errno == EINTR)
177 continue;
178 return -errno;
179 }
180 bytes -= amt;
181 }
182
183 return 0;
184 }
185
186 static int
write_rgb(struct led_prop * prop,int red,int green,int blue)187 write_rgb(struct led_prop *prop, int red, int green, int blue)
188 {
189 char buffer[20];
190 int bytes;
191 int amt;
192
193 if (prop->fd < 0)
194 return 0;
195
196 LOGV("%s %s: red:%d green:%d blue:%d\n",
197 __func__, prop->filename, red, green, blue);
198
199 bytes = snprintf(buffer, sizeof(buffer), "%d %d %d\n", red, green, blue);
200 while (bytes > 0) {
201 amt = write(prop->fd, buffer, bytes);
202 if (amt < 0) {
203 if (errno == EINTR)
204 continue;
205 return -errno;
206 }
207 bytes -= amt;
208 }
209
210 return 0;
211 }
212
213 static unsigned int
set_rgb(int red,int green,int blue)214 set_rgb(int red, int green, int blue)
215 {
216 return(((red << 16) & 0x00ff0000) |
217 ((green << 8) & 0x0000ff00) |
218 (blue & 0x000000ff));
219 }
220
221 static int
is_lit(struct light_state_t const * state)222 is_lit(struct light_state_t const* state)
223 {
224 return state->color & 0x00ffffff;
225 }
226
227 static int
set_trackball_light(struct light_state_t const * state)228 set_trackball_light(struct light_state_t const* state)
229 {
230 static int trackball_mode = 0;
231 int rc = 0;
232 int mode = state->flashMode;
233 int red, blue, green;
234 int period = 0;
235
236 if (state->flashMode == LIGHT_FLASH_HARDWARE) {
237 mode = state->flashOnMS;
238 period = state->flashOffMS;
239 }
240 LOGV("%s color=%08x mode=%d period %d\n", __func__,
241 state->color, mode, period);
242
243
244 if (mode != 0) {
245 red = (state->color >> 16) & 0xff;
246 green = (state->color >> 8) & 0xff;
247 blue = state->color & 0xff;
248
249 rc = write_rgb(&leds[JOGBALL_LED].color, red, green, blue);
250 if (rc != 0)
251 LOGE("set color failed rc = %d\n", rc);
252 if (period) {
253 rc = write_int(&leds[JOGBALL_LED].period, period);
254 if (rc != 0)
255 LOGE("set period failed rc = %d\n", rc);
256 }
257 }
258 // If the value isn't changing, don't set it, because this
259 // can reset the timer on the breathing mode, which looks bad.
260 if (trackball_mode == mode) {
261 return 0;
262 }
263 trackball_mode = mode;
264
265 return write_int(&leds[JOGBALL_LED].brightness, mode);
266 }
267
268 static void
handle_trackball_light_locked(int type)269 handle_trackball_light_locked(int type)
270 {
271 struct light_state_t *new_state = 0;
272 int attn_mode = 0;
273
274 if (g_attention->flashMode == LIGHT_FLASH_HARDWARE)
275 attn_mode = g_attention->flashOnMS;
276
277 LOGV("%s type %d attention %p notify %p\n",
278 __func__, type, g_attention, g_notify);
279
280 switch (type) {
281 case LIGHT_ATTENTION: {
282 if (attn_mode == 0) {
283 /* go back to notify state */
284 new_state = g_notify;
285 } else {
286 new_state = g_attention;
287 }
288 break;
289 }
290 case LIGHT_NOTIFY: {
291 if (attn_mode != 0) {
292 /* attention takes priority over notify state */
293 new_state = g_attention;
294 } else {
295 new_state = g_notify;
296 }
297 break;
298 }
299 }
300 if (new_state == 0) {
301 LOGE("%s: unknown type (%d)\n", __func__, type);
302 return;
303 }
304 LOGV("%s new state %p\n", __func__, new_state);
305 set_trackball_light(new_state);
306 return;
307 }
308
309 static int
rgb_to_brightness(struct light_state_t const * state)310 rgb_to_brightness(struct light_state_t const* state)
311 {
312 int color = state->color & 0x00ffffff;
313 return ((77*((color>>16)&0x00ff))
314 + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
315 }
316
317 static int
set_light_backlight(struct light_device_t * dev,struct light_state_t const * state)318 set_light_backlight(struct light_device_t* dev,
319 struct light_state_t const* state)
320 {
321 int err = 0;
322 int brightness = rgb_to_brightness(state);
323 LOGV("%s brightness=%d color=0x%08x",
324 __func__,brightness, state->color);
325 pthread_mutex_lock(&g_lock);
326 g_backlight = brightness;
327 err = write_int(&leds[LCD_BACKLIGHT].brightness, brightness);
328 pthread_mutex_unlock(&g_lock);
329 return err;
330 }
331
332 static int
set_light_keyboard(struct light_device_t * dev,struct light_state_t const * state)333 set_light_keyboard(struct light_device_t* dev,
334 struct light_state_t const* state)
335 {
336 /* nothing to do on mahimahi*/
337 return 0;
338 }
339
340 static int
set_light_buttons(struct light_device_t * dev,struct light_state_t const * state)341 set_light_buttons(struct light_device_t* dev,
342 struct light_state_t const* state)
343 {
344 int err = 0;
345 int on = is_lit(state);
346 pthread_mutex_lock(&g_lock);
347 g_buttons = on;
348 err = write_int(&leds[BUTTONS_LED].brightness, on?255:0);
349 pthread_mutex_unlock(&g_lock);
350 return err;
351 }
352
353 static int
set_speaker_light_locked(struct light_device_t * dev,struct light_state_t const * state)354 set_speaker_light_locked(struct light_device_t* dev,
355 struct light_state_t const* state)
356 {
357 int len;
358 unsigned int colorRGB;
359
360 /* Red = amber_led, blue or green = green_led */
361 colorRGB = state->color & 0xFFFFFF;
362
363 switch (state->flashMode) {
364 case LIGHT_FLASH_TIMED:
365 LOGV("set_led_state colorRGB=%08X, flashing\n", colorRGB);
366 switch (colorRGB) {
367 case RGB_RED:
368 write_int(&leds[RED_LED].blink, 1);
369 break;
370 case RGB_AMBER:
371 write_int(&leds[AMBER_LED].blink, 2);
372 break;
373 case RGB_GREEN:
374 write_int(&leds[GREEN_LED].blink, 1);
375 break;
376 case RGB_BLUE:
377 write_int(&leds[BLUE_LED].blink, 1);
378 break;
379 case RGB_BLACK: /*off*/
380 write_int(&leds[GREEN_LED].blink, 0);
381 write_int(&leds[RED_LED].blink, 0);
382 write_int(&leds[BLUE_LED].blink, 0);
383 write_int(&leds[AMBER_LED].blink, 0);
384 break;
385 break;
386 default:
387 LOGE("set_led_state colorRGB=%08X, unknown color\n",
388 colorRGB);
389 break;
390 }
391 break;
392 case LIGHT_FLASH_NONE:
393 LOGV("set_led_state colorRGB=%08X, on\n", colorRGB);
394 switch (colorRGB) {
395 case RGB_RED:
396 /*no support for red solid */
397 case RGB_AMBER:
398 write_int(&leds[AMBER_LED].brightness, 1);
399 write_int(&leds[GREEN_LED].brightness, 0);
400 write_int(&leds[BLUE_LED].brightness, 0);
401 break;
402 case RGB_GREEN:
403 write_int(&leds[GREEN_LED].brightness, 1);
404 write_int(&leds[AMBER_LED].brightness, 0);
405 write_int(&leds[BLUE_LED].brightness, 0);
406 break;
407 case RGB_BLUE:
408 write_int(&leds[BLUE_LED].brightness, 1);
409 write_int(&leds[GREEN_LED].brightness, 0);
410 write_int(&leds[AMBER_LED].brightness, 0);
411 break;
412 case RGB_BLACK: /*off*/
413 write_int(&leds[GREEN_LED].brightness, 0);
414 write_int(&leds[AMBER_LED].brightness, 0);
415 write_int(&leds[BLUE_LED].brightness, 0);
416 write_int(&leds[RED_LED].brightness, 0);
417 break;
418 default:
419 LOGE("set_led_state colorRGB=%08X, unknown color\n",
420 colorRGB);
421 break;
422 }
423 break;
424 default:
425 LOGE("set_led_state colorRGB=%08X, unknown mode %d\n",
426 colorRGB, state->flashMode);
427 }
428 return 0;
429 }
430
431 static int
set_light_battery(struct light_device_t * dev,struct light_state_t const * state)432 set_light_battery(struct light_device_t* dev,
433 struct light_state_t const* state)
434 {
435 pthread_mutex_lock(&g_lock);
436 LOGV("%s mode=%d color=0x%08x",
437 __func__,state->flashMode, state->color);
438 set_speaker_light_locked(dev, state);
439 pthread_mutex_unlock(&g_lock);
440 return 0;
441 }
442
443 static int
set_light_notifications(struct light_device_t * dev,struct light_state_t const * state)444 set_light_notifications(struct light_device_t* dev,
445 struct light_state_t const* state)
446 {
447 pthread_mutex_lock(&g_lock);
448
449 LOGV("%s mode=%d color=0x%08x On=%d Off=%d\n",
450 __func__,state->flashMode, state->color,
451 state->flashOnMS, state->flashOffMS);
452 /*
453 ** TODO Allow for user settings of color and interval
454 ** Setting 60% brightness
455 */
456 switch (state->color & 0x00FFFFFF) {
457 case RGB_BLACK:
458 g_notify->color = set_rgb(0, 0, 0);
459 break;
460 case RGB_WHITE:
461 g_notify->color = set_rgb(50, 127, 48);
462 break;
463 case RGB_RED:
464 g_notify->color = set_rgb(141, 0, 0);
465 break;
466 case RGB_GREEN:
467 g_notify->color = set_rgb(0, 141, 0);
468 break;
469 case RGB_BLUE:
470 g_notify->color = set_rgb(0, 0, 141);
471 break;
472 case RGB_PINK:
473 g_notify->color = set_rgb(141, 52, 58);
474 break;
475 case RGB_PURPLE:
476 g_notify->color = set_rgb(70, 0, 70);
477 break;
478 case RGB_ORANGE:
479 g_notify->color = set_rgb(141, 99, 0);
480 break;
481 case RGB_YELLOW:
482 g_notify->color = set_rgb(100, 141, 0);
483 break;
484 case RGB_LT_BLUE:
485 g_notify->color = set_rgb(35, 55, 98);
486 break;
487 default:
488 g_notify->color = state->color;
489 break;
490 }
491
492 if (state->flashMode != LIGHT_FLASH_NONE) {
493 g_notify->flashMode = LIGHT_FLASH_HARDWARE;
494 g_notify->flashOnMS = 7;
495 g_notify->flashOffMS = (state->flashOnMS + state->flashOffMS)/1000;
496 } else {
497 g_notify->flashOnMS = 0;
498 g_notify->flashOffMS = 0;
499 }
500 handle_trackball_light_locked(LIGHT_NOTIFY);
501
502 pthread_mutex_unlock(&g_lock);
503 return 0;
504 }
505
506 static int
set_light_attention(struct light_device_t * dev,struct light_state_t const * state)507 set_light_attention(struct light_device_t* dev,
508 struct light_state_t const* state)
509 {
510 unsigned int colorRGB;
511
512 LOGV("%s color=0x%08x mode=0x%08x submode=0x%08x",
513 __func__, state->color, state->flashMode, state->flashOnMS);
514
515 pthread_mutex_lock(&g_lock);
516 /* tune color for hardware*/
517 switch (state->color & 0x00FFFFFF) {
518 case RGB_WHITE:
519 colorRGB = set_rgb(101, 255, 96);
520 break;
521 case RGB_BLUE:
522 colorRGB = set_rgb(0, 0, 235);
523 break;
524 case RGB_BLACK:
525 colorRGB = set_rgb(0, 0, 0);
526 break;
527 default:
528 LOGE("%s colorRGB=%08X, unknown color\n",
529 __func__, state->color);
530 colorRGB = set_rgb(101, 255, 96);
531 break;
532 }
533 g_attention->flashMode = state->flashMode;
534 g_attention->flashOnMS = state->flashOnMS;
535 g_attention->color = colorRGB;
536 g_attention->flashOffMS = 0;
537 handle_trackball_light_locked(LIGHT_ATTENTION);
538
539 pthread_mutex_unlock(&g_lock);
540 return 0;
541 }
542
543
544 /** Close the lights device */
545 static int
close_lights(struct light_device_t * dev)546 close_lights(struct light_device_t *dev)
547 {
548 int i;
549
550 for (i = 0; i < NUM_LEDS; ++i) {
551 close_prop(&leds[i].brightness);
552 close_prop(&leds[i].blink);
553 close_prop(&leds[i].mode);
554 }
555
556 if (dev) {
557 free(dev);
558 }
559 return 0;
560 }
561
562
563 /******************************************************************************/
564
565 /**
566 * module methods
567 */
568
569 /** Open a new instance of a lights device using name */
open_lights(const struct hw_module_t * module,char const * name,struct hw_device_t ** device)570 static int open_lights(const struct hw_module_t* module, char const* name,
571 struct hw_device_t** device)
572 {
573 int (*set_light)(struct light_device_t* dev,
574 struct light_state_t const* state);
575
576 if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
577 set_light = set_light_backlight;
578 }
579 else if (0 == strcmp(LIGHT_ID_KEYBOARD, name)) {
580 set_light = set_light_keyboard;
581 }
582 else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
583 set_light = set_light_buttons;
584 }
585 else if (0 == strcmp(LIGHT_ID_BATTERY, name)) {
586 set_light = set_light_battery;
587 }
588 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
589 set_light = set_light_notifications;
590 }
591 else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) {
592 set_light = set_light_attention;
593 }
594 else {
595 return -EINVAL;
596 }
597
598 pthread_once(&g_init, init_globals);
599
600 struct light_device_t *dev = malloc(sizeof(struct light_device_t));
601 memset(dev, 0, sizeof(*dev));
602
603 dev->common.tag = HARDWARE_DEVICE_TAG;
604 dev->common.version = 0;
605 dev->common.module = (struct hw_module_t*)module;
606 dev->common.close = (int (*)(struct hw_device_t*))close_lights;
607 dev->set_light = set_light;
608
609 *device = (struct hw_device_t*)dev;
610 return 0;
611 }
612
613
614 static struct hw_module_methods_t lights_module_methods = {
615 .open = open_lights,
616 };
617
618 /*
619 * The lights Module
620 */
621 const struct hw_module_t HAL_MODULE_INFO_SYM = {
622 .tag = HARDWARE_MODULE_TAG,
623 .version_major = 1,
624 .version_minor = 0,
625 .id = LIGHTS_HARDWARE_MODULE_ID,
626 .name = "mahimahi lights Module",
627 .author = "Google, Inc.",
628 .methods = &lights_module_methods,
629 };
630