1/* 2 * Copyright (C) 2008 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 18// #define LOG_NDEBUG 0 19#define LOG_TAG "lights" 20 21#include <cutils/log.h> 22 23#include <stdint.h> 24#include <string.h> 25#include <unistd.h> 26#include <errno.h> 27#include <fcntl.h> 28#include <pthread.h> 29 30#include <sys/ioctl.h> 31#include <sys/types.h> 32 33#include <hardware/lights.h> 34 35/******************************************************************************/ 36 37static pthread_once_t g_init = PTHREAD_ONCE_INIT; 38static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; 39static int g_haveTrackballLight = 0; 40static struct light_state_t g_notification; 41static struct light_state_t g_battery; 42static int g_backlight = 255; 43static int g_trackball = -1; 44static int g_buttons = 0; 45static int g_attention = 0; 46static int g_haveAmberLed = 0; 47 48char const*const TRACKBALL_FILE 49 = "/sys/class/leds/jogball-backlight/brightness"; 50 51char const*const RED_LED_FILE 52 = "/sys/class/leds/red/brightness"; 53 54char const*const GREEN_LED_FILE 55 = "/sys/class/leds/green/brightness"; 56 57char const*const BLUE_LED_FILE 58 = "/sys/class/leds/blue/brightness"; 59 60char const*const AMBER_LED_FILE 61 = "/sys/class/leds/amber/brightness"; 62 63char const*const LCD_FILE 64 = "/sys/class/leds/lcd-backlight/brightness"; 65 66char const*const RED_FREQ_FILE 67 = "/sys/class/leds/red/device/grpfreq"; 68 69char const*const RED_PWM_FILE 70 = "/sys/class/leds/red/device/grppwm"; 71 72char const*const RED_BLINK_FILE 73 = "/sys/class/leds/red/device/blink"; 74 75char const*const AMBER_BLINK_FILE 76 = "/sys/class/leds/amber/blink"; 77 78char const*const KEYBOARD_FILE 79 = "/sys/class/leds/keyboard-backlight/brightness"; 80 81char const*const BUTTON_FILE 82 = "/sys/class/leds/button-backlight/brightness"; 83 84/** 85 * device methods 86 */ 87 88void init_globals(void) 89{ 90 // init the mutex 91 pthread_mutex_init(&g_lock, NULL); 92 93 // figure out if we have the trackball LED or not 94 g_haveTrackballLight = (access(TRACKBALL_FILE, W_OK) == 0) ? 1 : 0; 95 96 /* figure out if we have the amber LED or not. 97 If yes, just support green and amber. */ 98 g_haveAmberLed = (access(AMBER_LED_FILE, W_OK) == 0) ? 1 : 0; 99} 100 101static int 102write_int(char const* path, int value) 103{ 104 int fd; 105 static int already_warned = 0; 106 107 fd = open(path, O_RDWR); 108 if (fd >= 0) { 109 char buffer[20]; 110 int bytes = sprintf(buffer, "%d\n", value); 111 int amt = write(fd, buffer, bytes); 112 close(fd); 113 return amt == -1 ? -errno : 0; 114 } else { 115 if (already_warned == 0) { 116 ALOGE("write_int failed to open %s\n", path); 117 already_warned = 1; 118 } 119 return -errno; 120 } 121} 122 123static int 124is_lit(struct light_state_t const* state) 125{ 126 return state->color & 0x00ffffff; 127} 128 129static int 130handle_trackball_light_locked(struct light_device_t* dev) 131{ 132 int mode = g_attention; 133 134 if (mode == 7 && g_backlight) { 135 mode = 0; 136 } 137 ALOGV("%s g_backlight = %d, mode = %d, g_attention = %d\n", 138 __func__, g_backlight, mode, g_attention); 139 140 // If the value isn't changing, don't set it, because this 141 // can reset the timer on the breathing mode, which looks bad. 142 if (g_trackball == mode) { 143 return 0; 144 } 145 146 return write_int(TRACKBALL_FILE, mode); 147} 148 149static int 150rgb_to_brightness(struct light_state_t const* state) 151{ 152 int color = state->color & 0x00ffffff; 153 return ((77*((color>>16)&0x00ff)) 154 + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8; 155} 156 157static int 158set_light_backlight(struct light_device_t* dev, 159 struct light_state_t const* state) 160{ 161 int err = 0; 162 int brightness = rgb_to_brightness(state); 163 pthread_mutex_lock(&g_lock); 164 g_backlight = brightness; 165 err = write_int(LCD_FILE, brightness); 166 if (g_haveTrackballLight) { 167 handle_trackball_light_locked(dev); 168 } 169 pthread_mutex_unlock(&g_lock); 170 return err; 171} 172 173static int 174set_light_keyboard(struct light_device_t* dev, 175 struct light_state_t const* state) 176{ 177 int err = 0; 178 int on = is_lit(state); 179 pthread_mutex_lock(&g_lock); 180 err = write_int(KEYBOARD_FILE, on?255:0); 181 pthread_mutex_unlock(&g_lock); 182 return err; 183} 184 185static int 186set_light_buttons(struct light_device_t* dev, 187 struct light_state_t const* state) 188{ 189 int err = 0; 190 int on = is_lit(state); 191 pthread_mutex_lock(&g_lock); 192 g_buttons = on; 193 err = write_int(BUTTON_FILE, on?255:0); 194 pthread_mutex_unlock(&g_lock); 195 return err; 196} 197 198static int 199set_speaker_light_locked(struct light_device_t* dev, 200 struct light_state_t const* state) 201{ 202 int len; 203 int alpha, red, green, blue; 204 int blink, freq, pwm; 205 int onMS, offMS; 206 unsigned int colorRGB; 207 208 switch (state->flashMode) { 209 case LIGHT_FLASH_TIMED: 210 onMS = state->flashOnMS; 211 offMS = state->flashOffMS; 212 break; 213 case LIGHT_FLASH_NONE: 214 default: 215 onMS = 0; 216 offMS = 0; 217 break; 218 } 219 220 colorRGB = state->color; 221 222#if 0 223 ALOGD("set_speaker_light_locked colorRGB=%08X, onMS=%d, offMS=%d\n", 224 colorRGB, onMS, offMS); 225#endif 226 227 red = (colorRGB >> 16) & 0xFF; 228 green = (colorRGB >> 8) & 0xFF; 229 blue = colorRGB & 0xFF; 230 231 if (!g_haveAmberLed) { 232 write_int(RED_LED_FILE, red); 233 write_int(GREEN_LED_FILE, green); 234 write_int(BLUE_LED_FILE, blue); 235 } else { 236 /* all of related red led is replaced by amber */ 237 if (red) { 238 write_int(AMBER_LED_FILE, 1); 239 write_int(GREEN_LED_FILE, 0); 240 } else if (green) { 241 write_int(AMBER_LED_FILE, 0); 242 write_int(GREEN_LED_FILE, 1); 243 } else { 244 write_int(GREEN_LED_FILE, 0); 245 write_int(AMBER_LED_FILE, 0); 246 } 247 } 248 249 if (onMS > 0 && offMS > 0) { 250 int totalMS = onMS + offMS; 251 252 // the LED appears to blink about once per second if freq is 20 253 // 1000ms / 20 = 50 254 freq = totalMS / 50; 255 // pwm specifies the ratio of ON versus OFF 256 // pwm = 0 -> always off 257 // pwm = 255 => always on 258 pwm = (onMS * 255) / totalMS; 259 260 // the low 4 bits are ignored, so round up if necessary 261 if (pwm > 0 && pwm < 16) 262 pwm = 16; 263 264 blink = 1; 265 } else { 266 blink = 0; 267 freq = 0; 268 pwm = 0; 269 } 270 271 if (!g_haveAmberLed) { 272 if (blink) { 273 write_int(RED_FREQ_FILE, freq); 274 write_int(RED_PWM_FILE, pwm); 275 } 276 write_int(RED_BLINK_FILE, blink); 277 } else { 278 write_int(AMBER_BLINK_FILE, blink); 279 } 280 281 return 0; 282} 283 284static void 285handle_speaker_battery_locked(struct light_device_t* dev) 286{ 287 if (is_lit(&g_battery)) { 288 set_speaker_light_locked(dev, &g_battery); 289 } else { 290 set_speaker_light_locked(dev, &g_notification); 291 } 292} 293 294static int 295set_light_battery(struct light_device_t* dev, 296 struct light_state_t const* state) 297{ 298 pthread_mutex_lock(&g_lock); 299 g_battery = *state; 300 if (g_haveTrackballLight) { 301 set_speaker_light_locked(dev, state); 302 } 303 handle_speaker_battery_locked(dev); 304 pthread_mutex_unlock(&g_lock); 305 return 0; 306} 307 308static int 309set_light_notifications(struct light_device_t* dev, 310 struct light_state_t const* state) 311{ 312 pthread_mutex_lock(&g_lock); 313 g_notification = *state; 314 ALOGV("set_light_notifications g_trackball=%d color=0x%08x", 315 g_trackball, state->color); 316 if (g_haveTrackballLight) { 317 handle_trackball_light_locked(dev); 318 } 319 handle_speaker_battery_locked(dev); 320 pthread_mutex_unlock(&g_lock); 321 return 0; 322} 323 324static int 325set_light_attention(struct light_device_t* dev, 326 struct light_state_t const* state) 327{ 328 pthread_mutex_lock(&g_lock); 329 ALOGV("set_light_attention g_trackball=%d color=0x%08x", 330 g_trackball, state->color); 331 if (state->flashMode == LIGHT_FLASH_HARDWARE) { 332 g_attention = state->flashOnMS; 333 } else if (state->flashMode == LIGHT_FLASH_NONE) { 334 g_attention = 0; 335 } 336 if (g_haveTrackballLight) { 337 handle_trackball_light_locked(dev); 338 } 339 pthread_mutex_unlock(&g_lock); 340 return 0; 341} 342 343 344/** Close the lights device */ 345static int 346close_lights(struct light_device_t *dev) 347{ 348 if (dev) { 349 free(dev); 350 } 351 return 0; 352} 353 354 355/******************************************************************************/ 356 357/** 358 * module methods 359 */ 360 361/** Open a new instance of a lights device using name */ 362static int open_lights(const struct hw_module_t* module, char const* name, 363 struct hw_device_t** device) 364{ 365 int (*set_light)(struct light_device_t* dev, 366 struct light_state_t const* state); 367 368 if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) { 369 set_light = set_light_backlight; 370 } 371 else if (0 == strcmp(LIGHT_ID_KEYBOARD, name)) { 372 set_light = set_light_keyboard; 373 } 374 else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) { 375 set_light = set_light_buttons; 376 } 377 else if (0 == strcmp(LIGHT_ID_BATTERY, name)) { 378 set_light = set_light_battery; 379 } 380 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) { 381 set_light = set_light_notifications; 382 } 383 else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) { 384 set_light = set_light_attention; 385 } 386 else { 387 return -EINVAL; 388 } 389 390 pthread_once(&g_init, init_globals); 391 392 struct light_device_t *dev = malloc(sizeof(struct light_device_t)); 393 memset(dev, 0, sizeof(*dev)); 394 395 dev->common.tag = HARDWARE_DEVICE_TAG; 396 dev->common.version = 0; 397 dev->common.module = (struct hw_module_t*)module; 398 dev->common.close = (int (*)(struct hw_device_t*))close_lights; 399 dev->set_light = set_light; 400 401 *device = (struct hw_device_t*)dev; 402 return 0; 403} 404 405 406static struct hw_module_methods_t lights_module_methods = { 407 .open = open_lights, 408}; 409 410/* 411 * The lights Module 412 */ 413struct hw_module_t HAL_MODULE_INFO_SYM = { 414 .tag = HARDWARE_MODULE_TAG, 415 .version_major = 1, 416 .version_minor = 0, 417 .id = LIGHTS_HARDWARE_MODULE_ID, 418 .name = "QCT MSM7K lights Module", 419 .author = "Google, Inc.", 420 .methods = &lights_module_methods, 421}; 422