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