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 static pthread_once_t g_init = PTHREAD_ONCE_INIT; 43 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; 44 static struct light_state_t g_notification; 45 static struct light_state_t g_battery; 46 static int g_last_backlight_mode = BRIGHTNESS_MODE_USER; 47 static int g_attention = 0; 48 static int rgb_brightness_ratio = 255; 49 50 char const*const RED_LED_FILE 51 = "/sys/class/leds/red/brightness"; 52 53 char const*const GREEN_LED_FILE 54 = "/sys/class/leds/green/brightness"; 55 56 char const*const BLUE_LED_FILE 57 = "/sys/class/leds/blue/brightness"; 58 59 char const*const LCD_FILE 60 = "/sys/class/leds/lcd-backlight/brightness"; 61 62 char const*const PERSISTENCE_FILE 63 = "/sys/class/leds/lcd-backlight/low_persistence"; 64 65 char const*const BUTTON_FILE 66 = "/sys/class/leds/button-backlight/brightness"; 67 68 char const*const RED_BLINK_FILE 69 = "/sys/class/leds/red/blink"; 70 71 char const*const GREEN_BLINK_FILE 72 = "/sys/class/leds/green/blink"; 73 74 char const*const BLUE_BLINK_FILE 75 = "/sys/class/leds/blue/blink"; 76 77 char const*const RED_ON_OFF_MS_FILE 78 = "/sys/class/leds/red/on_off_ms"; 79 80 char const*const GREEN_ON_OFF_MS_FILE 81 = "/sys/class/leds/green/on_off_ms"; 82 83 char const*const BLUE_ON_OFF_MS_FILE 84 = "/sys/class/leds/blue/on_off_ms"; 85 86 char const*const RED_RGB_START_FILE 87 = "/sys/class/leds/red/rgb_start"; 88 89 char const*const GREEN_RGB_START_FILE 90 = "/sys/class/leds/green/rgb_start"; 91 92 char const*const BLUE_RGB_START_FILE 93 = "/sys/class/leds/blue/rgb_start"; 94 95 /** 96 * device methods 97 */ 98 99 void init_globals(void) 100 { 101 char color_id_prop[PROPERTY_VALUE_MAX] = {""}; 102 103 // init the mutex 104 pthread_mutex_init(&g_lock, NULL); 105 106 // check CG color 107 property_get(CG_COLOR_ID_PROPERTY, color_id_prop, "DEF00"); 108 if (strcmp(color_id_prop, "GRA00") == 0) { 109 rgb_brightness_ratio = 25; 110 } else if (strcmp(color_id_prop, "SLV00") == 0) { 111 rgb_brightness_ratio = 15; 112 } else if (strcmp(color_id_prop, "BLU00") == 0) { 113 rgb_brightness_ratio = 15; 114 } else { 115 rgb_brightness_ratio = 20; 116 } 117 } 118 119 static int 120 write_int(char const* path, int value) 121 { 122 int fd; 123 static int already_warned = 0; 124 125 fd = open(path, O_WRONLY); 126 if (fd >= 0) { 127 char buffer[20]; 128 size_t bytes = snprintf(buffer, sizeof(buffer), "%d\n", value); 129 if(bytes >= sizeof(buffer)) return -EINVAL; 130 ssize_t amt = write(fd, buffer, bytes); 131 close(fd); 132 return amt == -1 ? -errno : 0; 133 } else { 134 if (already_warned == 0) { 135 ALOGE("write_int failed to open %s\n", path); 136 already_warned = 1; 137 } 138 return -errno; 139 } 140 } 141 142 static int 143 write_double_int(char const* path, int value1, int value2) 144 { 145 int fd; 146 static int already_warned = 0; 147 148 fd = open(path, O_WRONLY); 149 if (fd >= 0) { 150 char buffer[20]; 151 size_t bytes = snprintf(buffer, sizeof(buffer), "%d %d\n", value1, value2); 152 if(bytes >= sizeof(buffer)) return -EINVAL; 153 ssize_t amt = write(fd, buffer, bytes); 154 close(fd); 155 return amt == -1 ? -errno : 0; 156 } else { 157 if (already_warned == 0) { 158 ALOGE("write_int failed to open %s\n", path); 159 already_warned = 1; 160 } 161 return -errno; 162 } 163 } 164 165 static int 166 is_lit(struct light_state_t const* state) 167 { 168 return state->color & 0x00ffffff; 169 } 170 171 static int 172 rgb_to_brightness(struct light_state_t const* state) 173 { 174 int color = state->color & 0x00ffffff; 175 return ((77*((color>>16)&0x00ff)) 176 + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8; 177 } 178 179 static int 180 set_light_backlight(struct light_device_t* dev, 181 struct light_state_t const* state) 182 { 183 int err = 0; 184 int brightness = rgb_to_brightness(state); 185 unsigned int lpEnabled = state->brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE; 186 if(!dev) { 187 return -1; 188 } 189 190 pthread_mutex_lock(&g_lock); 191 192 // If we're not in lp mode and it has been enabled or if we are in lp mode 193 // and it has been disabled send an ioctl to the display with the update 194 if ((g_last_backlight_mode != state->brightnessMode && lpEnabled) || 195 (!lpEnabled && g_last_backlight_mode == BRIGHTNESS_MODE_LOW_PERSISTENCE)) { 196 if ((err = write_int(PERSISTENCE_FILE, lpEnabled)) != 0) { 197 ALOGE("%s: Failed to write to %s: %s\n", __FUNCTION__, PERSISTENCE_FILE, 198 strerror(errno)); 199 } 200 if (lpEnabled != 0) { 201 // This is defined in BoardConfig.mk. 202 brightness = DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS; 203 } 204 } 205 206 g_last_backlight_mode = state->brightnessMode; 207 208 if (!err) { 209 err = write_int(LCD_FILE, brightness); 210 } 211 212 pthread_mutex_unlock(&g_lock); 213 return err; 214 } 215 216 static int 217 set_speaker_light_locked(struct light_device_t* dev, 218 struct light_state_t const* state) 219 { 220 int red, green, blue; 221 int blink; 222 int onMS, offMS; 223 unsigned int colorRGB; 224 225 if(!dev) { 226 return -1; 227 } 228 229 switch (state->flashMode) { 230 case LIGHT_FLASH_TIMED: 231 onMS = state->flashOnMS; 232 offMS = state->flashOffMS; 233 break; 234 case LIGHT_FLASH_NONE: 235 default: 236 onMS = 0; 237 offMS = 0; 238 break; 239 } 240 241 colorRGB = state->color; 242 243 #if 0 244 ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n", 245 state->flashMode, colorRGB, onMS, offMS); 246 #endif 247 248 red = ((colorRGB >> 16) & 0xFF) * rgb_brightness_ratio / 255; 249 green = ((colorRGB >> 8) & 0xFF) * rgb_brightness_ratio / 255; 250 blue = (colorRGB & 0xFF) * rgb_brightness_ratio / 255; 251 252 write_double_int(RED_ON_OFF_MS_FILE, onMS, offMS); 253 write_int(RED_LED_FILE, red); 254 write_double_int(GREEN_ON_OFF_MS_FILE, onMS, offMS); 255 write_int(GREEN_LED_FILE, green); 256 write_double_int(BLUE_ON_OFF_MS_FILE, onMS, offMS); 257 write_int(BLUE_LED_FILE, blue); 258 259 if(!write_int(RED_RGB_START_FILE, 1)) 260 if(!write_int(GREEN_RGB_START_FILE, 1)) 261 if(!write_int(BLUE_RGB_START_FILE, 1)) 262 return -1; 263 264 return 0; 265 } 266 267 static void 268 handle_speaker_battery_locked(struct light_device_t* dev) 269 { 270 if (is_lit(&g_battery)) { 271 set_speaker_light_locked(dev, &g_battery); 272 } else { 273 set_speaker_light_locked(dev, &g_notification); 274 } 275 } 276 277 #if LIGHTS_SUPPORT_BATTERY 278 static int 279 set_light_battery(struct light_device_t* dev, 280 struct light_state_t const* state) 281 { 282 pthread_mutex_lock(&g_lock); 283 g_battery = *state; 284 handle_speaker_battery_locked(dev); 285 pthread_mutex_unlock(&g_lock); 286 return 0; 287 } 288 #endif 289 290 static int 291 set_light_notifications(struct light_device_t* dev, 292 struct light_state_t const* state) 293 { 294 pthread_mutex_lock(&g_lock); 295 g_notification = *state; 296 handle_speaker_battery_locked(dev); 297 pthread_mutex_unlock(&g_lock); 298 return 0; 299 } 300 301 static int 302 set_light_attention(struct light_device_t* dev, 303 struct light_state_t const* state) 304 { 305 pthread_mutex_lock(&g_lock); 306 if (state->flashMode == LIGHT_FLASH_HARDWARE) { 307 g_attention = state->flashOnMS; 308 } else if (state->flashMode == LIGHT_FLASH_NONE) { 309 g_attention = 0; 310 } 311 handle_speaker_battery_locked(dev); 312 pthread_mutex_unlock(&g_lock); 313 return 0; 314 } 315 316 static int 317 set_light_buttons(struct light_device_t* dev, 318 struct light_state_t const* state) 319 { 320 int err = 0; 321 if(!dev) { 322 return -1; 323 } 324 pthread_mutex_lock(&g_lock); 325 err = write_int(BUTTON_FILE, state->color & 0xFF); 326 pthread_mutex_unlock(&g_lock); 327 return err; 328 } 329 330 /** Close the lights device */ 331 static int 332 close_lights(struct light_device_t *dev) 333 { 334 if (dev) { 335 free(dev); 336 } 337 return 0; 338 } 339 340 341 /******************************************************************************/ 342 343 /** 344 * module methods 345 */ 346 347 /** Open a new instance of a lights device using name */ 348 static int open_lights(const struct hw_module_t* module, char const* name, 349 struct hw_device_t** device) 350 { 351 int (*set_light)(struct light_device_t* dev, 352 struct light_state_t const* state); 353 354 if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) 355 set_light = set_light_backlight; 356 #if LIGHTS_SUPPORT_BATTERY 357 else if (0 == strcmp(LIGHT_ID_BATTERY, name)) 358 set_light = set_light_battery; 359 #endif 360 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) 361 set_light = set_light_notifications; 362 else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) 363 set_light = set_light_buttons; 364 else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) 365 set_light = set_light_attention; 366 else 367 return -EINVAL; 368 369 pthread_once(&g_init, init_globals); 370 371 struct light_device_t *dev = malloc(sizeof(struct light_device_t)); 372 373 if(!dev) 374 return -ENOMEM; 375 376 memset(dev, 0, sizeof(*dev)); 377 378 dev->common.tag = HARDWARE_DEVICE_TAG; 379 dev->common.version = LIGHTS_DEVICE_API_VERSION_2_0; 380 dev->common.module = (struct hw_module_t*)module; 381 dev->common.close = (int (*)(struct hw_device_t*))close_lights; 382 dev->set_light = set_light; 383 384 *device = (struct hw_device_t*)dev; 385 return 0; 386 } 387 388 static struct hw_module_methods_t lights_module_methods = { 389 .open = open_lights, 390 }; 391 392 /* 393 * The lights Module 394 */ 395 struct hw_module_t HAL_MODULE_INFO_SYM = { 396 .tag = HARDWARE_MODULE_TAG, 397 .version_major = 1, 398 .version_minor = 0, 399 .id = LIGHTS_HARDWARE_MODULE_ID, 400 .name = "lights Module", 401 .author = "Google, Inc.", 402 .methods = &lights_module_methods, 403 }; 404