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