1/* 2 * Copyright (C) 2014, 2017 The Linux Foundation. All rights reserved. 3 * Not a contribution 4 * Copyright (C) 2008 The Android Open Source Project 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 20// #define LOG_NDEBUG 0 21 22#include <cutils/log.h> 23#include <cutils/properties.h> 24#include <stdint.h> 25#include <stdlib.h> 26#include <string.h> 27#include <unistd.h> 28#include <errno.h> 29#include <fcntl.h> 30#include <pthread.h> 31 32#include <sys/ioctl.h> 33#include <sys/types.h> 34 35#include <hardware/lights.h> 36#include "lights_prv.h" 37 38#ifndef DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS 39#define DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS 0x80 40#endif 41 42/******************************************************************************/ 43 44static pthread_once_t g_init = PTHREAD_ONCE_INIT; 45static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; 46static struct light_state_t g_notification; 47static struct light_state_t g_battery; 48static int g_last_backlight_mode = BRIGHTNESS_MODE_USER; 49static int g_attention = 0; 50static int g_brightness_max = 0; 51 52char const*const RED_LED_FILE 53 = "/sys/class/leds/red/brightness"; 54 55char const*const GREEN_LED_FILE 56 = "/sys/class/leds/green/brightness"; 57 58char const*const BLUE_LED_FILE 59 = "/sys/class/leds/blue/brightness"; 60 61char const*const LCD_FILE 62 = "/sys/class/leds/lcd-backlight/brightness"; 63 64char const*const LCD_FILE2 65 = "/sys/class/backlight/panel0-backlight/brightness"; 66 67char const*const BUTTON_FILE 68 = "/sys/class/leds/button-backlight/brightness"; 69 70char const*const RED_BLINK_FILE 71 = "/sys/class/leds/red/blink"; 72 73char const*const GREEN_BLINK_FILE 74 = "/sys/class/leds/green/blink"; 75 76char const*const BLUE_BLINK_FILE 77 = "/sys/class/leds/blue/blink"; 78 79char const*const PERSISTENCE_FILE 80 = "/sys/class/graphics/fb0/msm_fb_persist_mode"; 81 82/** 83 * device methods 84 */ 85 86void init_globals(void) 87{ 88 // init the mutex 89 pthread_mutex_init(&g_lock, NULL); 90} 91 92static int 93write_int(char const* path, int value) 94{ 95 int fd; 96 static int already_warned = 0; 97 98 fd = open(path, O_RDWR); 99 if (fd >= 0) { 100 char buffer[20]; 101 int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value); 102 ssize_t amt = write(fd, buffer, (size_t)bytes); 103 close(fd); 104 return amt == -1 ? -errno : 0; 105 } else { 106 if (already_warned == 0) { 107 ALOGE("write_int failed to open %s\n", path); 108 already_warned = 1; 109 } 110 return -errno; 111 } 112} 113 114static int 115is_lit(struct light_state_t const* state) 116{ 117 return state->color & 0x00ffffff; 118} 119 120static int 121rgb_to_brightness(struct light_state_t const* state) 122{ 123 int color = state->color & 0x00ffffff; 124 return ((77*((color>>16)&0x00ff)) 125 + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8; 126} 127 128static int 129set_light_backlight(struct light_device_t* dev, 130 struct light_state_t const* state) 131{ 132 int err = 0; 133 int brightness = rgb_to_brightness(state); 134 unsigned int lpEnabled = 135 state->brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE; 136 if(!dev) { 137 return -1; 138 } 139 140 pthread_mutex_lock(&g_lock); 141 // Toggle low persistence mode state 142 if ((g_last_backlight_mode != state->brightnessMode && lpEnabled) || 143 (!lpEnabled && 144 g_last_backlight_mode == BRIGHTNESS_MODE_LOW_PERSISTENCE)) { 145 if ((err = write_int(PERSISTENCE_FILE, lpEnabled)) != 0) { 146 ALOGE("%s: Failed to write to %s: %s\n", __FUNCTION__, 147 PERSISTENCE_FILE, strerror(errno)); 148 } 149 if (lpEnabled != 0) { 150 brightness = DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS; 151 } 152 } 153 154 g_last_backlight_mode = state->brightnessMode; 155 156 if (!err) { 157 if (!access(LCD_FILE, F_OK)) { 158 err = write_int(LCD_FILE, brightness); 159 } else { 160 err = write_int(LCD_FILE2, brightness); 161 } 162 } 163 164 pthread_mutex_unlock(&g_lock); 165 return err; 166} 167 168static int 169set_light_backlight_ext(struct light_device_t* dev, 170 struct light_state_t const* state) 171{ 172 int err = 0; 173 174 if(!dev) { 175 return -1; 176 } 177 178 int brightness = state->color & 0x00ffffff; 179 pthread_mutex_lock(&g_lock); 180 181 if (brightness >= 0 && brightness <= g_brightness_max) { 182 set_brightness_ext_level(brightness); 183 } 184 185 pthread_mutex_unlock(&g_lock); 186 187 return err; 188} 189 190static int 191set_speaker_light_locked(struct light_device_t* dev, 192 struct light_state_t const* state) 193{ 194 int red, green, blue; 195 int blink; 196 int onMS, offMS; 197 unsigned int colorRGB; 198 199 if(!dev) { 200 return -1; 201 } 202 203 switch (state->flashMode) { 204 case LIGHT_FLASH_TIMED: 205 onMS = state->flashOnMS; 206 offMS = state->flashOffMS; 207 break; 208 case LIGHT_FLASH_NONE: 209 default: 210 onMS = 0; 211 offMS = 0; 212 break; 213 } 214 215 colorRGB = state->color; 216 217#if 0 218 ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n", 219 state->flashMode, colorRGB, onMS, offMS); 220#endif 221 222 red = (colorRGB >> 16) & 0xFF; 223 green = (colorRGB >> 8) & 0xFF; 224 blue = colorRGB & 0xFF; 225 226 if (onMS > 0 && offMS > 0) { 227 /* 228 * if ON time == OFF time 229 * use blink mode 2 230 * else 231 * use blink mode 1 232 */ 233 if (onMS == offMS) 234 blink = 2; 235 else 236 blink = 1; 237 } else { 238 blink = 0; 239 } 240 241 if (blink) { 242 if (red) { 243 if (write_int(RED_BLINK_FILE, blink)) 244 write_int(RED_LED_FILE, 0); 245 } 246 if (green) { 247 if (write_int(GREEN_BLINK_FILE, blink)) 248 write_int(GREEN_LED_FILE, 0); 249 } 250 if (blue) { 251 if (write_int(BLUE_BLINK_FILE, blink)) 252 write_int(BLUE_LED_FILE, 0); 253 } 254 } else { 255 write_int(RED_LED_FILE, red); 256 write_int(GREEN_LED_FILE, green); 257 write_int(BLUE_LED_FILE, blue); 258 } 259 260 return 0; 261} 262 263static void 264handle_speaker_battery_locked(struct light_device_t* dev) 265{ 266 if (is_lit(&g_battery)) { 267 set_speaker_light_locked(dev, &g_battery); 268 } else { 269 set_speaker_light_locked(dev, &g_notification); 270 } 271} 272 273static int 274set_light_battery(struct light_device_t* dev, 275 struct light_state_t const* state) 276{ 277 pthread_mutex_lock(&g_lock); 278 g_battery = *state; 279 handle_speaker_battery_locked(dev); 280 pthread_mutex_unlock(&g_lock); 281 return 0; 282} 283 284static int 285set_light_notifications(struct light_device_t* dev, 286 struct light_state_t const* state) 287{ 288 pthread_mutex_lock(&g_lock); 289 g_notification = *state; 290 handle_speaker_battery_locked(dev); 291 pthread_mutex_unlock(&g_lock); 292 return 0; 293} 294 295static int 296set_light_attention(struct light_device_t* dev, 297 struct light_state_t const* state) 298{ 299 pthread_mutex_lock(&g_lock); 300 if (state->flashMode == LIGHT_FLASH_HARDWARE) { 301 g_attention = state->flashOnMS; 302 } else if (state->flashMode == LIGHT_FLASH_NONE) { 303 g_attention = 0; 304 } 305 handle_speaker_battery_locked(dev); 306 pthread_mutex_unlock(&g_lock); 307 return 0; 308} 309 310static int 311set_light_buttons(struct light_device_t* dev, 312 struct light_state_t const* state) 313{ 314 int err = 0; 315 if(!dev) { 316 return -1; 317 } 318 pthread_mutex_lock(&g_lock); 319 err = write_int(BUTTON_FILE, state->color & 0xFF); 320 pthread_mutex_unlock(&g_lock); 321 return err; 322} 323 324/** Close the lights device */ 325static int 326close_lights(struct light_device_t *dev) 327{ 328 if (dev) { 329 free(dev); 330 } 331 return 0; 332} 333 334 335/******************************************************************************/ 336 337/** 338 * module methods 339 */ 340 341/** Open a new instance of a lights device using name */ 342static int open_lights(const struct hw_module_t* module, char const* name, 343 struct hw_device_t** device) 344{ 345 int (*set_light)(struct light_device_t* dev, 346 struct light_state_t const* state); 347 348 if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) { 349 char property[PROPERTY_VALUE_MAX]; 350 property_get("persist.extend.brightness", property, "0"); 351 352 if(!(strncmp(property, "1", PROPERTY_VALUE_MAX)) || 353 !(strncmp(property, "true", PROPERTY_VALUE_MAX))) { 354 property_get("persist.display.max_brightness", property, "255"); 355 g_brightness_max = atoi(property); 356 set_brightness_ext_init(); 357 set_light = set_light_backlight_ext; 358 } else 359 set_light = set_light_backlight; 360 } else if (0 == strcmp(LIGHT_ID_BATTERY, name)) 361 set_light = set_light_battery; 362 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) 363 set_light = set_light_notifications; 364 else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) 365 set_light = set_light_buttons; 366 else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) 367 set_light = set_light_attention; 368 else 369 return -EINVAL; 370 371 pthread_once(&g_init, init_globals); 372 373 struct light_device_t *dev = malloc(sizeof(struct light_device_t)); 374 375 if(!dev) 376 return -ENOMEM; 377 378 memset(dev, 0, sizeof(*dev)); 379 380 dev->common.tag = HARDWARE_DEVICE_TAG; 381 dev->common.version = LIGHTS_DEVICE_API_VERSION_2_0; 382 dev->common.module = (struct hw_module_t*)module; 383 dev->common.close = (int (*)(struct hw_device_t*))close_lights; 384 dev->set_light = set_light; 385 386 *device = (struct hw_device_t*)dev; 387 return 0; 388} 389 390static struct hw_module_methods_t lights_module_methods = { 391 .open = open_lights, 392}; 393 394/* 395 * The lights Module 396 */ 397struct hw_module_t HAL_MODULE_INFO_SYM = { 398 .tag = HARDWARE_MODULE_TAG, 399 .version_major = 1, 400 .version_minor = 0, 401 .id = LIGHTS_HARDWARE_MODULE_ID, 402 .name = "lights Module", 403 .author = "Google, Inc.", 404 .methods = &lights_module_methods, 405}; 406