Home | History | Annotate | Download | only in liblights
      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