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 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 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 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 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 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 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 222 is_lit(struct light_state_t const* state) 223 { 224 return state->color & 0x00ffffff; 225 } 226 227 static int 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 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 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 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 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 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 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 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 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 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 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 */ 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