Home | History | Annotate | Download | only in lights
      1 /*
      2  * Copyright (C) 2014 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 <cutils/log.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 
     28 #include <sys/ioctl.h>
     29 #include <sys/types.h>
     30 
     31 #include <hardware/lights.h>
     32 #include <hardware/hardware.h>
     33 
     34 #define ALOG_ONCE(mask, op, ...)			\
     35 	do {					\
     36 		if (!(mask & op)) {		\
     37 			ALOGE(__VA_ARGS__);	\
     38 			mask |= op;		\
     39 		}				\
     40 	} while (0);
     41 #define OP_WRITE_OPEN		(1 << 0)
     42 #define OP_BRIGHTNESS_PATH	(1 << 1)
     43 #define OP_BRIGHTNESS_VALUE	(1 << 2)
     44 #define OP_BRIGHTNESS_WRITE	(1 << 3)
     45 #define OP_MAX_BRIGHTNESS_PATH	(1 << 4)
     46 #define OP_MAX_BRIGHTNESS_OPEN	(1 << 5)
     47 #define OP_MAX_BRIGHTNESS_READ	(1 << 6)
     48 
     49 struct dragon_lights {
     50 	struct light_device_t base;
     51 
     52 	pthread_mutex_t lock;
     53 	char const *sysfs_path;
     54 
     55 	int max_brightness;
     56 
     57 	unsigned long logged_failures;
     58 };
     59 
     60 static char const * kBacklightPath =
     61 	"/sys/class/backlight/lpm102a188a-backlight";
     62 static const int kNumBrightnessLevels = 16;
     63 static const int kBrightnessLevels[] =
     64 	{8, 25, 30, 40, 55, 65, 75, 85, 95, 105, 120, 135, 160, 180, 200, 220};
     65 
     66 static struct dragon_lights *to_dragon_lights(struct light_device_t *dev)
     67 {
     68 	return (struct dragon_lights *)dev;
     69 }
     70 
     71 static int write_brightness(struct dragon_lights *lights, int brightness)
     72 {
     73 	char buffer[20], path[PATH_MAX];
     74 	int fd, bytes, amt, ret = 0;
     75 
     76 	bytes = snprintf(path, sizeof(path), "%s/brightness",
     77 			 lights->sysfs_path);
     78 	if (bytes < 0 || (size_t)bytes >= sizeof(path)) {
     79 		ALOG_ONCE(lights->logged_failures, OP_BRIGHTNESS_PATH,
     80 			  "failed to create brightness path %d\n", bytes);
     81 		return -EINVAL;
     82 	}
     83 
     84 	fd = open(path, O_RDWR);
     85 	if (fd < 0) {
     86 		ALOG_ONCE(lights->logged_failures, OP_WRITE_OPEN,
     87 			  "write_int failed to open %s/%d\n", path, errno);
     88 		return -errno;
     89 	}
     90 
     91 	bytes = snprintf(buffer, sizeof(buffer), "%d\n", brightness);
     92 	if (bytes < 0 || (size_t)bytes >= sizeof(buffer)) {
     93 		ALOG_ONCE(lights->logged_failures, OP_BRIGHTNESS_VALUE,
     94 			  "failed to create brightness value %d/%d\n",
     95 			  brightness, bytes);
     96 		ret = -EINVAL;
     97 		goto out;
     98 	}
     99 	amt = write(fd, buffer, bytes);
    100 	if (amt != bytes) {
    101 		ALOG_ONCE(lights->logged_failures, OP_BRIGHTNESS_WRITE,
    102 			  "failed to write brightness value %d/%d\n", amt,
    103 			  bytes);
    104 		ret = amt == -1 ? -errno : -EINVAL;
    105 		goto out;
    106 	}
    107 
    108 out:
    109 	close(fd);
    110 	return ret;
    111 }
    112 
    113 static int read_max_brightness(struct dragon_lights *lights, int *value)
    114 {
    115 	char buffer[20], path[PATH_MAX];
    116 	int ret = 0, fd, bytes;
    117 
    118 	bytes = snprintf(path, sizeof(path), "%s/max_brightness",
    119 			 lights->sysfs_path);
    120 	if (bytes < 0 || (size_t)bytes >= sizeof(path)) {
    121 		ALOG_ONCE(lights->logged_failures, OP_MAX_BRIGHTNESS_PATH,
    122 			  "failed to create max_brightness path %d\n", bytes);
    123 		return -EINVAL;
    124 	}
    125 
    126 	fd = open(path, O_RDONLY);
    127 	if (fd < 0) {
    128 		ALOG_ONCE(lights->logged_failures, OP_MAX_BRIGHTNESS_OPEN,
    129 			  "failed to open max_brightness %s/%d\n", path, errno);
    130 		return -errno;
    131 	}
    132 
    133 	bytes = read(fd, buffer, sizeof(buffer));
    134 	if (bytes <= 0) {
    135 		ALOG_ONCE(lights->logged_failures, OP_MAX_BRIGHTNESS_READ,
    136 			  "failed to read max_brightness %s/%d\n", path, errno);
    137 		ret = -errno;
    138 		goto out;
    139 	}
    140 
    141 	*value = atoi(buffer);
    142 
    143 out:
    144 	close(fd);
    145 	return ret;
    146 }
    147 
    148 static int rgb_to_brightness(struct light_state_t const *state)
    149 {
    150 	int color = state->color & 0x00ffffff;
    151 	return ((77 * ((color >> 16) & 0x00ff))
    152 			+ (150 * ((color >> 8) & 0x00ff)) +
    153 			(29 * (color & 0x00ff))) >> 8;
    154 }
    155 
    156 static int set_light_backlight(struct light_device_t *dev,
    157 			       struct light_state_t const *state)
    158 {
    159 	struct dragon_lights *lights = to_dragon_lights(dev);
    160 	int err, brightness_idx;
    161 	int brightness = rgb_to_brightness(state);
    162 
    163 	if (brightness > 0) {
    164 		// Get the bin number for brightness (0 to kNumBrightnessLevels - 1)
    165 		brightness_idx = (brightness - 1) * kNumBrightnessLevels / 0xff;
    166 
    167 		// Get brightness level
    168 		brightness = kBrightnessLevels[brightness_idx];
    169 	}
    170 
    171 	pthread_mutex_lock(&lights->lock);
    172 	err = write_brightness(lights, brightness);
    173 	pthread_mutex_unlock(&lights->lock);
    174 
    175 	return err;
    176 }
    177 
    178 static int close_lights(struct hw_device_t *dev)
    179 {
    180 	struct dragon_lights *lights = (struct dragon_lights *)dev;
    181 	if (lights)
    182 		free(lights);
    183 	return 0;
    184 }
    185 
    186 static int open_lights(const struct hw_module_t *module, char const *name,
    187 		       struct hw_device_t **device)
    188 {
    189 	struct dragon_lights *lights;
    190 	int ret;
    191 
    192 	// Only support backlight at the moment
    193 	if (strcmp(LIGHT_ID_BACKLIGHT, name))
    194 		return -EINVAL;
    195 
    196 	lights = malloc(sizeof(*lights));
    197 	if (lights == NULL) {
    198 		ALOGE("failed to allocate lights memory");
    199 		return -ENOMEM;
    200 	}
    201 	memset(lights, 0, sizeof(*lights));
    202 
    203 	pthread_mutex_init(&lights->lock, NULL);
    204 
    205 	lights->sysfs_path = kBacklightPath;
    206 
    207 	ret = read_max_brightness(lights, &lights->max_brightness);
    208 	if (ret) {
    209 		close_lights((struct hw_device_t *)lights);
    210 		return ret;
    211 	}
    212 
    213 	lights->base.common.tag = HARDWARE_DEVICE_TAG;
    214 	lights->base.common.version = 0;
    215 	lights->base.common.module = (struct hw_module_t *)module;
    216 	lights->base.common.close = close_lights;
    217 	lights->base.set_light = set_light_backlight;
    218 
    219 	*device = (struct hw_device_t *)lights;
    220 	return 0;
    221 }
    222 
    223 static struct hw_module_methods_t lights_methods = {
    224 	.open =  open_lights,
    225 };
    226 
    227 struct hw_module_t HAL_MODULE_INFO_SYM = {
    228 	.tag = HARDWARE_MODULE_TAG,
    229 	.version_major = 1,
    230 	.version_minor = 0,
    231 	.id = LIGHTS_HARDWARE_MODULE_ID,
    232 	.name = "dragon lights module",
    233 	.author = "Google, Inc.",
    234 	.methods = &lights_methods,
    235 };
    236