blob: 2d22b7cdc8aa2550b8bfebfadf61b0bfa55e66f0 [file] [log] [blame]
codeworkxf1be2fe2012-03-24 17:38:29 +01001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 * Copyright (C) 2011 The CyanogenMod Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19#define LOG_TAG "lights"
20#define LOG_NDEBUG 0
21
22#include <cutils/log.h>
23
24#include <stdint.h>
25#include <string.h>
26#include <unistd.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <pthread.h>
30
31#include <sys/ioctl.h>
32#include <sys/types.h>
33
34#include <hardware/lights.h>
35
36/******************************************************************************/
37
38static pthread_once_t g_init = PTHREAD_ONCE_INIT;
39static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
40static int g_enable_touchlight = -1;
41
42char const*const PANEL_FILE
43 = "/sys/class/backlight/panel/brightness";
44
codeworkx9eb34162012-05-23 20:22:25 +020045#ifndef EXYNOS4210_TABLET
codeworkxf1be2fe2012-03-24 17:38:29 +010046char const*const BUTTON_FILE
47 = "/sys/class/sec/sec_touchkey/brightness";
R. Andrew Ohanaec03b1b2012-04-01 00:30:55 -070048#endif
codeworkxf1be2fe2012-03-24 17:38:29 +010049
sbrissen5e44bae2012-05-25 09:40:54 -040050#ifdef LED_NOTIFICATION
51static char const RED_LED_DIR[] = "/sys/class/leds/red";
52static char const BLUE_LED_DIR[] = "/sys/class/leds/blue";
53#endif // LED_NOTIFICATION
codeworkxf1be2fe2012-03-24 17:38:29 +010054void init_globals(void)
55{
56 // init the mutex
57 pthread_mutex_init(&g_lock, NULL);
58}
59
60void
61load_settings()
62{
63 FILE* fp = fopen("/data/.disable_touchlight", "r");
64 if (!fp) {
65 g_enable_touchlight = 1;
66 } else {
67 g_enable_touchlight = (int)(fgetc(fp));
68 if (g_enable_touchlight == '1')
69 g_enable_touchlight = 1;
70 else
71 g_enable_touchlight = 0;
72
73 fclose(fp);
74 }
75}
76
sbrissen5e44bae2012-05-25 09:40:54 -040077#ifdef LED_NOTIFICATION
78static struct led_state {
79 unsigned int enabled;
80 int delay_on, delay_off;
81} battery_red, battery_blue, notifications_red, notifications_blue;
82#endif // LED_NOTIFICATION
codeworkxf1be2fe2012-03-24 17:38:29 +010083static int
84write_int(char const* path, int value)
85{
86 int fd;
87 static int already_warned = 0;
88
89 fd = open(path, O_RDWR);
90 if (fd >= 0) {
91 char buffer[20];
92 int bytes = sprintf(buffer, "%d\n", value);
93 int amt = write(fd, buffer, bytes);
94 close(fd);
95 return amt == -1 ? -errno : 0;
96 } else {
97 if (already_warned == 0) {
Daniel Hillenbrand29deaa02012-07-22 15:34:24 +020098 ALOGE("write_int failed to open %s\n", path);
codeworkxf1be2fe2012-03-24 17:38:29 +010099 already_warned = 1;
100 }
101 return -errno;
102 }
103}
104
sbrissen5e44bae2012-05-25 09:40:54 -0400105#ifdef LED_NOTIFICATION
106static int write_str(char const *path, char const *str)
107{
108 int fd;
109 static int already_warned = 0;
110
Daniel Hillenbrand29deaa02012-07-22 15:34:24 +0200111 ALOGV("write_str: path=\"%s\", str=\"%s\".", path, str);
sbrissen5e44bae2012-05-25 09:40:54 -0400112 fd = open(path, O_RDWR);
113
114 if (fd >= 0) {
115 int amt = write(fd, str, strlen(str));
116 close(fd);
117 return amt == -1 ? -errno : 0;
118 } else {
119 if (already_warned == 0) {
Daniel Hillenbrand29deaa02012-07-22 15:34:24 +0200120 ALOGE("write_str failed to open %s\n", path);
sbrissen5e44bae2012-05-25 09:40:54 -0400121 already_warned = 1;
122 }
123 return -errno;
124 }
125}
126
127/* Should check for snprintf truncation, but as these functions only use
128 * internal paths, meh. */
129static int write_df_int(char const *dir, char const *file, int value)
130{
131 char path[PATH_MAX];
132 snprintf(path, sizeof(path), "%s/%s", dir, file);
133 return write_int(path, value);
134}
135
136static int write_df_str(char const *dir, char const *file, char const *str)
137{
138 char path[PATH_MAX];
139 snprintf(path, sizeof(path), "%s/%s", dir, file);
140 return write_str(path, str);
141}
142#endif // LED_NOTIFICATION
143
codeworkxf1be2fe2012-03-24 17:38:29 +0100144static int
145is_lit(struct light_state_t const* state)
146{
147 return state->color & 0x00ffffff;
148}
149
150static int
151rgb_to_brightness(struct light_state_t const* state)
152{
153 int color = state->color & 0x00ffffff;
154 return ((77*((color>>16)&0x00ff))
155 + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
156}
157
sbrissen5e44bae2012-05-25 09:40:54 -0400158#ifdef LED_NOTIFICATION
159static void comp_led_states(struct led_state *red, struct led_state *blue,
160 struct light_state_t const* state)
161{
162 unsigned int color = state->color;
163 int delay_on, delay_off;
164
165 switch (state->flashMode) {
166 case LIGHT_FLASH_TIMED:
167 delay_on = state->flashOnMS;
168 delay_off = state->flashOffMS;
169 break;
170 default:
Daniel Hillenbrand29deaa02012-07-22 15:34:24 +0200171 ALOGI("Unsuported flashMode %d, default to NONE.", state->flashMode);
sbrissen5e44bae2012-05-25 09:40:54 -0400172 case LIGHT_FLASH_NONE:
173 delay_on = delay_off = 0;
174 break;
175 }
176
177 red->enabled = !!(color >> 16 & 0xff);
178 red->delay_on = delay_on;
179 red->delay_off = delay_off;
180
181 blue->enabled = !!(color & 0xff);
182 blue->delay_on = delay_on;
183 blue->delay_off = delay_off;
184
Daniel Hillenbrand29deaa02012-07-22 15:34:24 +0200185 ALOGV("comp_led_states: red=(%u, %d, %d), blue=(%u, %d, %d).",
sbrissen5e44bae2012-05-25 09:40:54 -0400186 red->enabled, red->delay_on, red->delay_off, blue->enabled,
187 blue->delay_on, blue->delay_off);
188}
189
190static int set_led(char const *dir, struct led_state const *battery,
191 struct led_state const *notifications)
192{
193
194 struct led_state const *state = NULL;
195 int res;
196
197 if (notifications->enabled)
198 state = notifications;
199 else if (battery->enabled)
200 state = battery;
201
202 if (state != NULL) {
203 int delay_on = state->delay_on;
204 int delay_off = state->delay_off;
205
206 if (delay_on > 0 && delay_off > 0) {
207 /* Handling of blink_count is wrong in the kernel, blinking indefinitely
208 * for any non-zero value. TW lights just sets it to 1. */
209 if ((res = write_df_str(dir, "trigger", "notification")) < 0) return res;
210 if ((res = write_df_str(dir, "brightness", "255" )) < 0) return res;
211 if ((res = write_df_str(dir, "blink_count", "1" )) < 0) return res;
212 if ((res = write_df_int(dir, "delay_on", delay_on )) < 0) return res;
213 if ((res = write_df_int(dir, "delay_off", delay_off )) < 0) return res;
214 } else {
215 if ((res = write_df_str(dir, "trigger", "none")) < 0) return res;
216 if ((res = write_df_str(dir, "brightness", "255" )) < 0) return res;
217 }
218 } else {
219 if ((res = write_df_str(dir, "trigger", "none")) < 0) return res;
220 if ((res = write_df_str(dir, "brightness", "0" )) < 0) return res;
221 }
222
223 return 0;
224}
225#endif // LED_NOTIFICATION
226
codeworkxf1be2fe2012-03-24 17:38:29 +0100227static int
228set_light_backlight(struct light_device_t* dev,
229 struct light_state_t const* state)
230{
codeworkxf1be2fe2012-03-24 17:38:29 +0100231 int err = 0;
Sam Mortimer4c88bee2012-07-01 22:46:59 -0700232 static int s_previous_brightness = -1;
codeworkxf1be2fe2012-03-24 17:38:29 +0100233 int brightness = rgb_to_brightness(state);
234
235 pthread_mutex_lock(&g_lock);
236 err = write_int(PANEL_FILE, brightness);
237
R. Andrew Ohanaec03b1b2012-04-01 00:30:55 -0700238#ifndef EXYNOS4210_TABLET
Sam Mortimer4c88bee2012-07-01 22:46:59 -0700239 if (!s_previous_brightness && (brightness > 0)) {
240 err = write_int(BUTTON_FILE, brightness > 0 ? 1 : 2);
241 s_previous_brightness = brightness;
242 }
R. Andrew Ohanaec03b1b2012-04-01 00:30:55 -0700243#endif
codeworkxf1be2fe2012-03-24 17:38:29 +0100244
245 pthread_mutex_unlock(&g_lock);
246
247 return err;
248}
249
250static int
codeworkxf1be2fe2012-03-24 17:38:29 +0100251set_light_buttons(struct light_device_t* dev,
252 struct light_state_t const* state)
253{
R. Andrew Ohanaec03b1b2012-04-01 00:30:55 -0700254#ifdef EXYNOS4210_TABLET
255 return 0;
256#else
codeworkx7800b3b2013-03-02 18:23:50 +0000257 int err = 0;
258 int brightness = rgb_to_brightness(state);
Phil Tunstalle54debb2012-05-17 16:25:38 +0100259
260 load_settings();
261
codeworkxf1be2fe2012-03-24 17:38:29 +0100262 pthread_mutex_lock(&g_lock);
codeworkx7800b3b2013-03-02 18:23:50 +0000263 if (brightness > 0) {
264 ALOGD("set_light_buttons on=%d\n", g_enable_touchlight ? 1 : 0);
265 err = write_int(BUTTON_FILE, g_enable_touchlight ? 1 : 0);
266 } else {
267 ALOGD("set_light_buttons off\n");
268 err = write_int(BUTTON_FILE, 0);
269 }
codeworkxf1be2fe2012-03-24 17:38:29 +0100270 pthread_mutex_unlock(&g_lock);
271
272 return err;
R. Andrew Ohanaec03b1b2012-04-01 00:30:55 -0700273#endif
codeworkxf1be2fe2012-03-24 17:38:29 +0100274}
275
276static int
277set_light_battery(struct light_device_t* dev,
278 struct light_state_t const* state)
279{
sbrissen5e44bae2012-05-25 09:40:54 -0400280 int res = 0;
281
282#ifdef LED_NOTIFICATION
Daniel Hillenbrand29deaa02012-07-22 15:34:24 +0200283 ALOGD("set_light_battery: color=%#010x, fM=%u, fOnMS=%d, fOffMs=%d.",
sbrissen5e44bae2012-05-25 09:40:54 -0400284 state->color, state->flashMode, state->flashOnMS, state->flashOffMS);
285
286 pthread_mutex_lock(&g_lock);
287
288 comp_led_states(&battery_red, &battery_blue, state);
289
290 if ((res = set_led(RED_LED_DIR, &battery_red, &notifications_red)) >= 0)
291 res = set_led(BLUE_LED_DIR, &battery_blue, &notifications_blue);
292
293 pthread_mutex_unlock(&g_lock);
294#endif // LED_NOTIFICATION
295
296 return res;
codeworkxf1be2fe2012-03-24 17:38:29 +0100297}
298
299static int
300set_light_notification(struct light_device_t* dev,
301 struct light_state_t const* state)
302{
sbrissen5e44bae2012-05-25 09:40:54 -0400303 int res = 0;
304
305#ifdef LED_NOTIFICATION
Daniel Hillenbrand29deaa02012-07-22 15:34:24 +0200306 ALOGD("set_light_notification: color=%#010x, fM=%u, fOnMS=%d, fOffMs=%d.",
sbrissen5e44bae2012-05-25 09:40:54 -0400307 state->color, state->flashMode, state->flashOnMS, state->flashOffMS);
308
309 pthread_mutex_lock(&g_lock);
310
311 comp_led_states(&notifications_red, &notifications_blue, state);
312
313 if ((res = set_led(RED_LED_DIR, &battery_red, &notifications_red)) >= 0)
314 res = set_led(BLUE_LED_DIR, &battery_blue, &notifications_blue);
315
316 pthread_mutex_unlock(&g_lock);
317#endif // LED_NOTIFICATION
318
319 return res;
codeworkxf1be2fe2012-03-24 17:38:29 +0100320}
321
322static int
codeworkxf1be2fe2012-03-24 17:38:29 +0100323close_lights(struct light_device_t *dev)
324{
325 if (dev) {
326 free(dev);
327 }
328 return 0;
329}
330
331
332/******************************************************************************/
333static int open_lights(const struct hw_module_t* module, char const* name,
334 struct hw_device_t** device)
335{
336 int (*set_light)(struct light_device_t* dev,
337 struct light_state_t const* state);
338
339 if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
340 set_light = set_light_backlight;
341 }
codeworkxf1be2fe2012-03-24 17:38:29 +0100342 else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
343 set_light = set_light_buttons;
344 }
345 else if (0 == strcmp(LIGHT_ID_BATTERY, name)) {
346 set_light = set_light_battery;
347 }
348 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
349 set_light = set_light_notification;
350 }
codeworkxf1be2fe2012-03-24 17:38:29 +0100351 else {
352 return -EINVAL;
353 }
354
355 pthread_once(&g_init, init_globals);
356
357 struct light_device_t *dev = malloc(sizeof(struct light_device_t));
358 memset(dev, 0, sizeof(*dev));
359
360 dev->common.tag = HARDWARE_DEVICE_TAG;
361 dev->common.version = 0;
362 dev->common.module = (struct hw_module_t*)module;
363 dev->common.close = (int (*)(struct hw_device_t*))close_lights;
364 dev->set_light = set_light;
365
366 *device = (struct hw_device_t*)dev;
367 return 0;
368}
369
370
371static struct hw_module_methods_t lights_module_methods = {
372 .open = open_lights,
373};
374
Daniel Hillenbrand29deaa02012-07-22 15:34:24 +0200375struct hw_module_t HAL_MODULE_INFO_SYM = {
codeworkxf1be2fe2012-03-24 17:38:29 +0100376 .tag = HARDWARE_MODULE_TAG,
377 .version_major = 1,
378 .version_minor = 0,
379 .id = LIGHTS_HARDWARE_MODULE_ID,
380 .name = "Samsung Exynos4210 Lights Module",
381 .author = "The CyanogenMod Project",
382 .methods = &lights_module_methods,
383};