1 /* 2 * Copyright (C) 2012 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 #define LOG_TAG "lights" 18 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <limits.h> 22 #include <pthread.h> 23 #include <sched.h> 24 #include <stdbool.h> 25 #include <stdint.h> 26 #include <string.h> 27 #include <sys/types.h> 28 #include <time.h> 29 30 #include <cutils/log.h> 31 #include <hardware/lights.h> 32 33 #define LED_SLOPE_UP_DEFAULT 450 34 #define LED_SLOPE_DOWN_DEFAULT 450 35 #define LED_BRIGHTNESS_OFF 0 36 #define LED_BRIGHTNESS_MAX 255 37 38 #define ALPHA_MASK 0xff000000 39 #define COLOR_MASK 0x00ffffff 40 41 #define NSEC_PER_MSEC 1000000ULL 42 #define NSEC_PER_SEC 1000000000ULL 43 44 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; 45 46 char const *const LCD_FILE = "/sys/class/backlight/pwm-backlight.0/brightness"; 47 const char *const LED_DIR = "/sys/class/leds/as3668"; 48 49 const char *const LED_COLOR_FILE = "color"; 50 const char *const LED_BRIGHTNESS_FILE = "brightness"; 51 const char *const LED_DELAY_ON_FILE = "delay_on"; 52 const char *const LED_DELAY_OFF_FILE = "delay_off"; 53 const char *const LED_TRIGGER_FILE = "trigger"; 54 const char *const LED_SLOPE_UP_FILE = "slope_up"; 55 const char *const LED_SLOPE_DOWN_FILE = "slope_down"; 56 57 enum LED_STATE { 58 OFF, 59 ON, 60 BLINK, 61 }; 62 63 struct as3668_led_info { 64 unsigned int color; 65 unsigned int delay_on; 66 unsigned int delay_off; 67 unsigned int slope_up; 68 unsigned int slope_down; 69 enum LED_STATE state; 70 }; 71 72 static int write_int(char const *path, int value) 73 { 74 int fd; 75 static int already_warned; 76 77 ALOGV("write_int: path %s, value %d", path, value); 78 fd = open(path, O_RDWR); 79 80 if (fd >= 0) { 81 char buffer[20]; 82 int bytes = sprintf(buffer, "%d\n", value); 83 int amt = write(fd, buffer, bytes); 84 close(fd); 85 return amt == -1 ? -errno : 0; 86 } else { 87 if (already_warned == 0) { 88 ALOGE("write_int failed to open %s\n", path); 89 already_warned = 1; 90 } 91 return -errno; 92 } 93 } 94 95 static int rgb_to_brightness(struct light_state_t const *state) 96 { 97 /* use max of the RGB components for brightness */ 98 int color = state->color & 0x00ffffff; 99 int red = (color >> 16) & 0x000000ff; 100 int green = (color >> 8) & 0x000000ff; 101 int blue = color & 0x000000ff; 102 103 int brightness = red; 104 if (green > brightness) 105 brightness = green; 106 if (blue > brightness) 107 brightness = blue; 108 109 return brightness; 110 } 111 112 static int set_light_backlight(struct light_device_t *dev, 113 struct light_state_t const *state) 114 { 115 int err = 0; 116 int brightness = rgb_to_brightness(state); 117 118 pthread_mutex_lock(&g_lock); 119 err = write_int(LCD_FILE, brightness); 120 121 pthread_mutex_unlock(&g_lock); 122 return err; 123 } 124 125 static int close_lights(struct hw_device_t *dev) 126 { 127 ALOGV("close_light is called"); 128 free(dev); 129 130 return 0; 131 } 132 133 /* For LEDs */ 134 static void set_led_colors(unsigned int color, struct as3668_led_info *leds) 135 { 136 unsigned int red; 137 unsigned int green; 138 unsigned int blue; 139 unsigned int white; 140 141 red = (color >> 16) & 0x000000ff; 142 green = (color >> 8) & 0x000000ff; 143 blue = color & 0x000000ff; 144 145 white = red; 146 if (green < white) 147 white = green; 148 if (blue < white) 149 white = blue; 150 151 color -= (white << 16) | (white << 8) | white; 152 153 leds->color = (color << 8) | white; 154 } 155 156 static void time_add(struct timespec *time, int sec, int nsec) 157 { 158 time->tv_nsec += nsec; 159 time->tv_sec += time->tv_nsec / NSEC_PER_SEC; 160 time->tv_nsec %= NSEC_PER_SEC; 161 time->tv_sec += sec; 162 } 163 164 static bool time_after(struct timespec *t) 165 { 166 struct timespec now; 167 168 clock_gettime(CLOCK_MONOTONIC, &now); 169 return now.tv_sec > t->tv_sec || (now.tv_sec == t->tv_sec && now.tv_nsec > t->tv_nsec); 170 } 171 172 static int led_sysfs_write(char *buf, const char *command, char *format, ...) 173 { 174 int fd; 175 char path_name[PATH_MAX]; 176 int err; 177 int len; 178 va_list args; 179 struct timespec timeout; 180 int ret; 181 182 err = sprintf(path_name, "%s/%s", LED_DIR, command); 183 if (err < 0) 184 return err; 185 186 clock_gettime(CLOCK_MONOTONIC, &timeout); 187 time_add(&timeout, 0, 100 * NSEC_PER_MSEC); 188 189 do { 190 fd = open(path_name, O_WRONLY); 191 err = -errno; 192 if (fd < 0) { 193 if (errno != EINTR && errno != EACCES && time_after(&timeout)) { 194 ALOGE("failed to open %s!", path_name); 195 return err; 196 } 197 sched_yield(); 198 } 199 } while (fd < 0); 200 201 va_start(args, format); 202 len = vsprintf(buf, format, args); 203 va_end(args); 204 if (len < 0) 205 return len; 206 207 err = write(fd, buf, len); 208 if (err == -1) 209 return -errno; 210 211 err = close(fd); 212 if (err == -1) 213 return -errno; 214 215 return 0; 216 } 217 218 static int write_leds(struct as3668_led_info *leds) 219 { 220 char buf[20]; 221 int err; 222 223 pthread_mutex_lock(&g_lock); 224 225 err = led_sysfs_write(buf, LED_SLOPE_UP_FILE, "%u", leds->slope_up); 226 if (err) 227 goto err_write_fail; 228 err = led_sysfs_write(buf, LED_SLOPE_DOWN_FILE, "%u", leds->slope_down); 229 if (err) 230 goto err_write_fail; 231 232 switch(leds->state) { 233 case OFF: 234 err = led_sysfs_write(buf, LED_BRIGHTNESS_FILE, "%d", 235 LED_BRIGHTNESS_OFF); 236 break; 237 case BLINK: 238 err = led_sysfs_write(buf, LED_TRIGGER_FILE, "%s", "timer"); 239 if (err) 240 goto err_write_fail; 241 err = led_sysfs_write(buf, LED_DELAY_ON_FILE, "%u", leds->delay_on); 242 if (err) 243 goto err_write_fail; 244 err = led_sysfs_write(buf, LED_DELAY_OFF_FILE, "%u", leds->delay_off); 245 if (err) 246 goto err_write_fail; 247 case ON: 248 err = led_sysfs_write(buf, LED_COLOR_FILE, "%x", leds->color); 249 if (err) 250 goto err_write_fail; 251 err = led_sysfs_write(buf, LED_BRIGHTNESS_FILE, "%d", 252 LED_BRIGHTNESS_MAX); 253 if (err) 254 goto err_write_fail; 255 default: 256 break; 257 } 258 259 err_write_fail: 260 pthread_mutex_unlock(&g_lock); 261 262 return err; 263 } 264 265 static int set_light_leds(struct light_state_t const *state, int type) 266 { 267 struct as3668_led_info leds; 268 unsigned int color; 269 270 memset(&leds, 0, sizeof(leds)); 271 leds.slope_up = LED_SLOPE_UP_DEFAULT; 272 leds.slope_down = LED_SLOPE_DOWN_DEFAULT; 273 274 switch (state->flashMode) { 275 case LIGHT_FLASH_NONE: 276 leds.state = OFF; 277 break; 278 case LIGHT_FLASH_TIMED: 279 case LIGHT_FLASH_HARDWARE: 280 if (state->flashOnMS < 0 || state->flashOffMS < 0) 281 return -EINVAL; 282 283 leds.delay_off = state->flashOffMS; 284 leds.delay_on = state->flashOnMS; 285 if (leds.delay_on <= leds.slope_up + leds.slope_down) 286 leds.delay_on = 1; 287 else 288 leds.delay_on -= leds.slope_up + leds.slope_down; 289 290 if (!(state->color & ALPHA_MASK)) { 291 leds.state = OFF; 292 break; 293 } 294 295 color = state->color & COLOR_MASK; 296 if (color == 0) { 297 leds.state = OFF; 298 break; 299 } 300 301 set_led_colors(color, &leds); 302 303 if (leds.delay_on == 0) 304 leds.state = OFF; 305 else if (leds.delay_off) 306 leds.state = BLINK; 307 else 308 leds.state = ON; 309 break; 310 default: 311 return -EINVAL; 312 } 313 314 return write_leds(&leds); 315 } 316 317 static int set_light_leds_notifications(struct light_device_t *dev, 318 struct light_state_t const *state) 319 { 320 return set_light_leds(state, 0); 321 } 322 323 static int set_light_leds_attention(struct light_device_t *dev, 324 struct light_state_t const *state) 325 { 326 return set_light_leds(state, 1); 327 } 328 329 static int open_lights(const struct hw_module_t *module, char const *name, 330 struct hw_device_t **device) 331 { 332 int (*set_light)(struct light_device_t *dev, 333 struct light_state_t const *state); 334 335 if (strcmp(LIGHT_ID_BACKLIGHT, name) == 0) 336 set_light = set_light_backlight; 337 else if (strcmp(LIGHT_ID_NOTIFICATIONS, name) == 0) 338 set_light = set_light_leds_notifications; 339 else if (strcmp(LIGHT_ID_ATTENTION, name) == 0) 340 set_light = set_light_leds_attention; 341 else 342 return -EINVAL; 343 344 struct light_device_t *dev = malloc(sizeof(struct light_device_t)); 345 if (!dev) 346 return -ENOMEM; 347 memset(dev, 0, sizeof(*dev)); 348 349 dev->common.tag = HARDWARE_DEVICE_TAG; 350 dev->common.version = 0; 351 dev->common.module = (struct hw_module_t *)module; 352 dev->common.close = close_lights; 353 dev->set_light = set_light; 354 355 *device = (struct hw_device_t *)dev; 356 357 return 0; 358 } 359 360 static struct hw_module_methods_t lights_module_methods = { 361 .open = open_lights, 362 }; 363 364 struct hw_module_t HAL_MODULE_INFO_SYM = { 365 .tag = HARDWARE_MODULE_TAG, 366 .version_major = 1, 367 .version_minor = 0, 368 .id = LIGHTS_HARDWARE_MODULE_ID, 369 .name = "lights Module", 370 .author = "Google, Inc.", 371 .methods = &lights_module_methods, 372 }; 373