lights.c revision b514889338b512bdb2550f93c5514af2f1580866
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 struct light_state_t g_notification; 40static struct light_state_t g_battery; 41static int g_attention = 0; 42 43char const*const RED_LED_FILE 44 = "/sys/class/leds/red/brightness"; 45 46char const*const GREEN_LED_FILE 47 = "/sys/class/leds/green/brightness"; 48 49char const*const BLUE_LED_FILE 50 = "/sys/class/leds/blue/brightness"; 51 52char const*const LCD_FILE 53 = "/sys/class/leds/lcd-backlight/brightness"; 54 55/** 56 * device methods 57 */ 58 59void init_globals(void) 60{ 61 // init the mutex 62 pthread_mutex_init(&g_lock, NULL); 63} 64 65static int 66write_int(char const* path, int value) 67{ 68 int fd; 69 static int already_warned = 0; 70 71 fd = open(path, O_RDWR); 72 if (fd >= 0) { 73 char buffer[20]; 74 int bytes = sprintf(buffer, "%d\n", value); 75 int amt = write(fd, buffer, bytes); 76 close(fd); 77 return amt == -1 ? -errno : 0; 78 } else { 79 if (already_warned == 0) { 80 ALOGE("write_int failed to open %s\n", path); 81 already_warned = 1; 82 } 83 return -errno; 84 } 85} 86 87static int 88is_lit(struct light_state_t const* state) 89{ 90 return state->color & 0x00ffffff; 91} 92 93static int 94rgb_to_brightness(struct light_state_t const* state) 95{ 96 int color = state->color & 0x00ffffff; 97 return ((77*((color>>16)&0x00ff)) 98 + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8; 99} 100 101static int 102set_light_backlight(struct light_device_t* dev, 103 struct light_state_t const* state) 104{ 105 int err = 0; 106 int brightness = rgb_to_brightness(state); 107 pthread_mutex_lock(&g_lock); 108 err = write_int(LCD_FILE, brightness); 109 pthread_mutex_unlock(&g_lock); 110 return err; 111} 112 113static int 114set_speaker_light_locked(struct light_device_t* dev, 115 struct light_state_t const* state) 116{ 117 int len; 118 int alpha, red, green, blue; 119 int blink, freq, pwm; 120 int onMS, offMS; 121 unsigned int colorRGB; 122 123 switch (state->flashMode) { 124 case LIGHT_FLASH_TIMED: 125 onMS = state->flashOnMS; 126 offMS = state->flashOffMS; 127 break; 128 case LIGHT_FLASH_NONE: 129 default: 130 onMS = 0; 131 offMS = 0; 132 break; 133 } 134 135 colorRGB = state->color; 136 137#if 0 138 ALOGD("set_speaker_light_locked colorRGB=%08X, onMS=%d, offMS=%d\n", 139 colorRGB, onMS, offMS); 140#endif 141 142 red = (colorRGB >> 16) & 0xFF; 143 green = (colorRGB >> 8) & 0xFF; 144 blue = colorRGB & 0xFF; 145 146 // R, G, B value is among 0, 1, 2 147 if (red > 128) red = 2; 148 else if (red <= 128 && red > 0) red = 1; 149 if (green > 128) green = 2; 150 else if (green <= 128 && green > 0) green = 1; 151 if (blue > 128) blue = 2; 152 else if (blue <= 128 && blue > 0) red = 1; 153 154 write_int(RED_LED_FILE, red); 155 write_int(GREEN_LED_FILE, green); 156 write_int(BLUE_LED_FILE, blue); 157 158 // TODO 159 if (onMS > 0 && offMS > 0) { 160 int totalMS = onMS + offMS; 161 162 // the LED appears to blink about once per second if freq is 20 163 // 1000ms / 20 = 50 164 freq = totalMS / 50; 165 // pwm specifies the ratio of ON versus OFF 166 // pwm = 0 -> always off 167 // pwm = 255 => always on 168 pwm = (onMS * 255) / totalMS; 169 170 // the low 4 bits are ignored, so round up if necessary 171 if (pwm > 0 && pwm < 16) 172 pwm = 16; 173 174 blink = 1; 175 } else { 176 blink = 0; 177 freq = 0; 178 pwm = 0; 179 } 180 181 if (blink) { 182 write_int(RED_LED_FILE, freq); 183 } 184 185 return 0; 186} 187 188static void 189handle_speaker_battery_locked(struct light_device_t* dev) 190{ 191 if (is_lit(&g_battery)) { 192 set_speaker_light_locked(dev, &g_battery); 193 } else { 194 set_speaker_light_locked(dev, &g_notification); 195 } 196} 197 198static int 199set_light_battery(struct light_device_t* dev, 200 struct light_state_t const* state) 201{ 202 pthread_mutex_lock(&g_lock); 203 g_battery = *state; 204 handle_speaker_battery_locked(dev); 205 pthread_mutex_unlock(&g_lock); 206 return 0; 207} 208 209static int 210set_light_notifications(struct light_device_t* dev, 211 struct light_state_t const* state) 212{ 213 pthread_mutex_lock(&g_lock); 214 g_notification = *state; 215 handle_speaker_battery_locked(dev); 216 pthread_mutex_unlock(&g_lock); 217 return 0; 218} 219 220static int 221set_light_attention(struct light_device_t* dev, 222 struct light_state_t const* state) 223{ 224 pthread_mutex_lock(&g_lock); 225 if (state->flashMode == LIGHT_FLASH_HARDWARE) { 226 g_attention = state->flashOnMS; 227 } else if (state->flashMode == LIGHT_FLASH_NONE) { 228 g_attention = 0; 229 } 230 handle_speaker_battery_locked(dev); 231 pthread_mutex_unlock(&g_lock); 232 return 0; 233} 234 235 236/** Close the lights device */ 237static int 238close_lights(struct light_device_t *dev) 239{ 240 if (dev) { 241 free(dev); 242 } 243 return 0; 244} 245 246 247/******************************************************************************/ 248 249/** 250 * module methods 251 */ 252 253/** Open a new instance of a lights device using name */ 254static int open_lights(const struct hw_module_t* module, char const* name, 255 struct hw_device_t** device) 256{ 257 int (*set_light)(struct light_device_t* dev, 258 struct light_state_t const* state); 259 260 if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) 261 set_light = set_light_backlight; 262 else if (0 == strcmp(LIGHT_ID_BATTERY, name)) 263 set_light = set_light_battery; 264 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) 265 set_light = set_light_notifications; 266 else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) 267 set_light = set_light_attention; 268 else 269 return -EINVAL; 270 271 pthread_once(&g_init, init_globals); 272 273 struct light_device_t *dev = malloc(sizeof(struct light_device_t)); 274 memset(dev, 0, sizeof(*dev)); 275 276 dev->common.tag = HARDWARE_DEVICE_TAG; 277 dev->common.version = 0; 278 dev->common.module = (struct hw_module_t*)module; 279 dev->common.close = (int (*)(struct hw_device_t*))close_lights; 280 dev->set_light = set_light; 281 282 *device = (struct hw_device_t*)dev; 283 return 0; 284} 285 286static struct hw_module_methods_t lights_module_methods = { 287 .open = open_lights, 288}; 289 290/* 291 * The lights Module 292 */ 293struct hw_module_t HAL_MODULE_INFO_SYM = { 294 .tag = HARDWARE_MODULE_TAG, 295 .version_major = 1, 296 .version_minor = 0, 297 .id = LIGHTS_HARDWARE_MODULE_ID, 298 .name = "lights Module", 299 .author = "Google, Inc.", 300 .methods = &lights_module_methods, 301}; 302