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