blob: a1cad470c12aa472b4021d9b0afa5150f195e23a [file] [log] [blame]
Colin Crossebf2cdf2012-03-12 13:58:29 -07001/*
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"
Veeren Mandaliacf994372012-07-06 17:23:17 -070018
Colin Crossebf2cdf2012-03-12 13:58:29 -070019#include <errno.h>
20#include <fcntl.h>
Veeren Mandaliacf994372012-07-06 17:23:17 -070021#include <limits.h>
Colin Crossebf2cdf2012-03-12 13:58:29 -070022#include <pthread.h>
Veeren Mandaliacf994372012-07-06 17:23:17 -070023#include <sched.h>
24#include <stdbool.h>
25#include <stdint.h>
26#include <string.h>
Colin Crossebf2cdf2012-03-12 13:58:29 -070027#include <sys/types.h>
Veeren Mandaliacf994372012-07-06 17:23:17 -070028#include <time.h>
29
30#include <cutils/log.h>
Colin Crossebf2cdf2012-03-12 13:58:29 -070031#include <hardware/lights.h>
32
Paul Eastham1a6deea2012-09-13 16:38:57 -070033#define LED_SLOPE_UP_DEFAULT 450
34#define LED_SLOPE_DOWN_DEFAULT 450
Veeren Mandaliacf994372012-07-06 17:23:17 -070035#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
Colin Crossebf2cdf2012-03-12 13:58:29 -070044static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
45
46char const *const LCD_FILE = "/sys/class/backlight/pwm-backlight.0/brightness";
Veeren Mandaliacf994372012-07-06 17:23:17 -070047const char *const LED_DIR = "/sys/class/leds/as3668";
48
49const char *const LED_COLOR_FILE = "color";
50const char *const LED_BRIGHTNESS_FILE = "brightness";
51const char *const LED_DELAY_ON_FILE = "delay_on";
52const char *const LED_DELAY_OFF_FILE = "delay_off";
53const char *const LED_TRIGGER_FILE = "trigger";
54const char *const LED_SLOPE_UP_FILE = "slope_up";
55const char *const LED_SLOPE_DOWN_FILE = "slope_down";
56
57enum LED_STATE {
58 OFF,
59 ON,
60 BLINK,
61};
62
63struct as3668_led_info {
64 unsigned int color;
Paul Eastham1a6deea2012-09-13 16:38:57 -070065 unsigned int delay_on;
66 unsigned int delay_off;
Veeren Mandaliacf994372012-07-06 17:23:17 -070067 unsigned int slope_up;
68 unsigned int slope_down;
69 enum LED_STATE state;
70};
Colin Crossebf2cdf2012-03-12 13:58:29 -070071
72static 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
95static 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
112static 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
125static int close_lights(struct hw_device_t *dev)
126{
127 ALOGV("close_light is called");
128 free(dev);
129
130 return 0;
131}
132
Veeren Mandaliacf994372012-07-06 17:23:17 -0700133/* For LEDs */
134static 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
156static 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
164static 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
172static 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
218static 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;
Paul Eastham1a6deea2012-09-13 16:38:57 -0700241 err = led_sysfs_write(buf, LED_DELAY_ON_FILE, "%u", leds->delay_on);
Veeren Mandaliacf994372012-07-06 17:23:17 -0700242 if (err)
243 goto err_write_fail;
Paul Eastham1a6deea2012-09-13 16:38:57 -0700244 err = led_sysfs_write(buf, LED_DELAY_OFF_FILE, "%u", leds->delay_off);
Veeren Mandaliacf994372012-07-06 17:23:17 -0700245 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
259err_write_fail:
260 pthread_mutex_unlock(&g_lock);
261
262 return err;
263}
264
265static 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:
Paul Eastham1a6deea2012-09-13 16:38:57 -0700280 if (state->flashOnMS < 0 || state->flashOffMS < 0)
Veeren Mandaliacf994372012-07-06 17:23:17 -0700281 return -EINVAL;
282
Paul Eastham1a6deea2012-09-13 16:38:57 -0700283 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
Veeren Mandaliacf994372012-07-06 17:23:17 -0700290 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
317static 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
323static 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
Colin Crossebf2cdf2012-03-12 13:58:29 -0700329static 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;
Veeren Mandaliacf994372012-07-06 17:23:17 -0700337 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;
Colin Crossebf2cdf2012-03-12 13:58:29 -0700341 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
360static struct hw_module_methods_t lights_module_methods = {
361 .open = open_lights,
362};
363
364struct 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};