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 
write_int(char const * path,int value)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 
rgb_to_brightness(struct light_state_t const * state)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 
set_light_backlight(struct light_device_t * dev,struct light_state_t const * state)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 
close_lights(struct hw_device_t * dev)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 */
set_led_colors(unsigned int color,struct as3668_led_info * 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 
time_add(struct timespec * time,int sec,int nsec)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 
time_after(struct timespec * t)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 
led_sysfs_write(char * buf,const char * command,char * format,...)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 
write_leds(struct as3668_led_info * leds)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 
set_light_leds(struct light_state_t const * state,int type)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 
set_light_leds_notifications(struct light_device_t * dev,struct light_state_t const * state)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 
set_light_leds_attention(struct light_device_t * dev,struct light_state_t const * state)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 
open_lights(const struct hw_module_t * module,char const * name,struct hw_device_t ** device)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