blob: 3c59011bf292965f77b7f4f215198f0e725a0d4d [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;
codeworkxf1be2fe2012-03-24 17:38:29 +010040
41char const*const PANEL_FILE
42 = "/sys/class/backlight/panel/brightness";
43
codeworkx9eb34162012-05-23 20:22:25 +020044#ifndef EXYNOS4210_TABLET
codeworkxf1be2fe2012-03-24 17:38:29 +010045char const*const BUTTON_FILE
46 = "/sys/class/sec/sec_touchkey/brightness";
R. Andrew Ohanaec03b1b2012-04-01 00:30:55 -070047#endif
codeworkxf1be2fe2012-03-24 17:38:29 +010048
sbrissen898047a2012-05-25 09:40:54 -040049#ifdef LED_NOTIFICATION
50static char const RED_LED_DIR[] = "/sys/class/leds/red";
51static char const BLUE_LED_DIR[] = "/sys/class/leds/blue";
52#endif // LED_NOTIFICATION
codeworkxf1be2fe2012-03-24 17:38:29 +010053void init_globals(void)
54{
55 // init the mutex
56 pthread_mutex_init(&g_lock, NULL);
57}
58
sbrissen898047a2012-05-25 09:40:54 -040059#ifdef LED_NOTIFICATION
60static struct led_state {
61 unsigned int enabled;
62 int delay_on, delay_off;
63} battery_red, battery_blue, notifications_red, notifications_blue;
64#endif // LED_NOTIFICATION
codeworkxf1be2fe2012-03-24 17:38:29 +010065static int
66write_int(char const* path, int value)
67{
68 int fd;
69 static int already_warned = 0;
70
71 fd = open(path, O_RDWR);
72 if (fd >= 0) {
73 char buffer[20];
74 int bytes = sprintf(buffer, "%d\n", value);
75 int amt = write(fd, buffer, bytes);
76 close(fd);
77 return amt == -1 ? -errno : 0;
78 } else {
79 if (already_warned == 0) {
Daniel Hillenbrand29deaa02012-07-22 15:34:24 +020080 ALOGE("write_int failed to open %s\n", path);
codeworkxf1be2fe2012-03-24 17:38:29 +010081 already_warned = 1;
82 }
83 return -errno;
84 }
85}
86
sbrissen898047a2012-05-25 09:40:54 -040087#ifdef LED_NOTIFICATION
88static int write_str(char const *path, char const *str)
89{
90 int fd;
91 static int already_warned = 0;
92
93 ALOGV("write_str: path=\"%s\", str=\"%s\".", path, str);
94 fd = open(path, O_RDWR);
95
96 if (fd >= 0) {
97 int amt = write(fd, str, strlen(str));
98 close(fd);
99 return amt == -1 ? -errno : 0;
100 } else {
101 if (already_warned == 0) {
102 ALOGE("write_str failed to open %s\n", path);
103 already_warned = 1;
104 }
105 return -errno;
106 }
107}
108
109/* Should check for snprintf truncation, but as these functions only use
110 * internal paths, meh. */
111static int write_df_int(char const *dir, char const *file, int value)
112{
113 char path[PATH_MAX];
114 snprintf(path, sizeof(path), "%s/%s", dir, file);
115 return write_int(path, value);
116}
117
118static int write_df_str(char const *dir, char const *file, char const *str)
119{
120 char path[PATH_MAX];
121 snprintf(path, sizeof(path), "%s/%s", dir, file);
122 return write_str(path, str);
123}
124#endif // LED_NOTIFICATION
125
codeworkxf1be2fe2012-03-24 17:38:29 +0100126static int
127is_lit(struct light_state_t const* state)
128{
129 return state->color & 0x00ffffff;
130}
131
132static int
133rgb_to_brightness(struct light_state_t const* state)
134{
135 int color = state->color & 0x00ffffff;
136 return ((77*((color>>16)&0x00ff))
137 + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
138}
139
sbrissen898047a2012-05-25 09:40:54 -0400140#ifdef LED_NOTIFICATION
141static void comp_led_states(struct led_state *red, struct led_state *blue,
142 struct light_state_t const* state)
143{
144 unsigned int color = state->color;
145 int delay_on, delay_off;
146
147 switch (state->flashMode) {
148 case LIGHT_FLASH_TIMED:
149 delay_on = state->flashOnMS;
150 delay_off = state->flashOffMS;
151 break;
152 default:
153 ALOGI("Unsuported flashMode %d, default to NONE.", state->flashMode);
154 case LIGHT_FLASH_NONE:
155 delay_on = delay_off = 0;
156 break;
157 }
158
159 red->enabled = !!(color >> 16 & 0xff);
160 red->delay_on = delay_on;
161 red->delay_off = delay_off;
162
163 blue->enabled = !!(color & 0xff);
164 blue->delay_on = delay_on;
165 blue->delay_off = delay_off;
166
167 ALOGV("comp_led_states: red=(%u, %d, %d), blue=(%u, %d, %d).",
168 red->enabled, red->delay_on, red->delay_off, blue->enabled,
169 blue->delay_on, blue->delay_off);
170}
171
172static int set_led(char const *dir, struct led_state const *battery,
173 struct led_state const *notifications)
174{
175
176 struct led_state const *state = NULL;
177 int res;
178
179 if (notifications->enabled)
180 state = notifications;
181 else if (battery->enabled)
182 state = battery;
183
184 if (state != NULL) {
185 int delay_on = state->delay_on;
186 int delay_off = state->delay_off;
187
188 if (delay_on > 0 && delay_off > 0) {
189 /* Handling of blink_count is wrong in the kernel, blinking indefinitely
190 * for any non-zero value. TW lights just sets it to 1. */
191 if ((res = write_df_str(dir, "trigger", "notification")) < 0) return res;
192 if ((res = write_df_str(dir, "brightness", "255" )) < 0) return res;
193 if ((res = write_df_str(dir, "blink_count", "1" )) < 0) return res;
194 if ((res = write_df_int(dir, "delay_on", delay_on )) < 0) return res;
195 if ((res = write_df_int(dir, "delay_off", delay_off )) < 0) return res;
196 } else {
197 if ((res = write_df_str(dir, "trigger", "none")) < 0) return res;
198 if ((res = write_df_str(dir, "brightness", "255" )) < 0) return res;
199 }
200 } else {
201 if ((res = write_df_str(dir, "trigger", "none")) < 0) return res;
202 if ((res = write_df_str(dir, "brightness", "0" )) < 0) return res;
203 }
204
205 return 0;
206}
207#endif // LED_NOTIFICATION
208
codeworkxf1be2fe2012-03-24 17:38:29 +0100209static int
210set_light_backlight(struct light_device_t* dev,
211 struct light_state_t const* state)
212{
codeworkxf1be2fe2012-03-24 17:38:29 +0100213 int err = 0;
Sam Mortimer4c88bee2012-07-01 22:46:59 -0700214 static int s_previous_brightness = -1;
codeworkxf1be2fe2012-03-24 17:38:29 +0100215 int brightness = rgb_to_brightness(state);
216
217 pthread_mutex_lock(&g_lock);
218 err = write_int(PANEL_FILE, brightness);
codeworkxf1be2fe2012-03-24 17:38:29 +0100219 pthread_mutex_unlock(&g_lock);
220
221 return err;
222}
223
224static int
codeworkxf1be2fe2012-03-24 17:38:29 +0100225set_light_buttons(struct light_device_t* dev,
226 struct light_state_t const* state)
227{
R. Andrew Ohanaec03b1b2012-04-01 00:30:55 -0700228#ifdef EXYNOS4210_TABLET
229 return 0;
230#else
codeworkx7800b3b2013-03-02 18:23:50 +0000231 int err = 0;
232 int brightness = rgb_to_brightness(state);
Phil Tunstalle54debb2012-05-17 16:25:38 +0100233
codeworkxf1be2fe2012-03-24 17:38:29 +0100234 pthread_mutex_lock(&g_lock);
Daniel Hillenbrandebc06142013-08-23 22:08:04 +0200235 ALOGD("set_light_buttons: %d\n", brightness > 0 ? 1 : 2);
236 err = write_int(BUTTON_FILE, brightness > 0 ? 1 : 2);
codeworkxf1be2fe2012-03-24 17:38:29 +0100237 pthread_mutex_unlock(&g_lock);
238
239 return err;
R. Andrew Ohanaec03b1b2012-04-01 00:30:55 -0700240#endif
codeworkxf1be2fe2012-03-24 17:38:29 +0100241}
242
243static int
244set_light_battery(struct light_device_t* dev,
245 struct light_state_t const* state)
246{
sbrissen898047a2012-05-25 09:40:54 -0400247 int res = 0;
248
249#ifdef LED_NOTIFICATION
250 ALOGD("set_light_battery: color=%#010x, fM=%u, fOnMS=%d, fOffMs=%d.",
251 state->color, state->flashMode, state->flashOnMS, state->flashOffMS);
252
253 pthread_mutex_lock(&g_lock);
254
255 comp_led_states(&battery_red, &battery_blue, state);
256
257 if ((res = set_led(RED_LED_DIR, &battery_red, &notifications_red)) >= 0)
258 res = set_led(BLUE_LED_DIR, &battery_blue, &notifications_blue);
259
260 pthread_mutex_unlock(&g_lock);
261#endif // LED_NOTIFICATION
262
263 return res;
codeworkxf1be2fe2012-03-24 17:38:29 +0100264}
265
266static int
267set_light_notification(struct light_device_t* dev,
268 struct light_state_t const* state)
269{
sbrissen898047a2012-05-25 09:40:54 -0400270 int res = 0;
271
272#ifdef LED_NOTIFICATION
273 ALOGD("set_light_notification: color=%#010x, fM=%u, fOnMS=%d, fOffMs=%d.",
274 state->color, state->flashMode, state->flashOnMS, state->flashOffMS);
275
276 pthread_mutex_lock(&g_lock);
277
278 comp_led_states(&notifications_red, &notifications_blue, state);
279
280 if ((res = set_led(RED_LED_DIR, &battery_red, &notifications_red)) >= 0)
281 res = set_led(BLUE_LED_DIR, &battery_blue, &notifications_blue);
282
283 pthread_mutex_unlock(&g_lock);
284#endif // LED_NOTIFICATION
285
286 return res;
codeworkxf1be2fe2012-03-24 17:38:29 +0100287}
288
289static int
codeworkxf1be2fe2012-03-24 17:38:29 +0100290close_lights(struct light_device_t *dev)
291{
292 if (dev) {
293 free(dev);
294 }
295 return 0;
296}
297
298
299/******************************************************************************/
300static int open_lights(const struct hw_module_t* module, char const* name,
301 struct hw_device_t** device)
302{
303 int (*set_light)(struct light_device_t* dev,
304 struct light_state_t const* state);
305
306 if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
307 set_light = set_light_backlight;
308 }
codeworkxf1be2fe2012-03-24 17:38:29 +0100309 else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
310 set_light = set_light_buttons;
311 }
312 else if (0 == strcmp(LIGHT_ID_BATTERY, name)) {
313 set_light = set_light_battery;
314 }
315 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
316 set_light = set_light_notification;
317 }
codeworkxf1be2fe2012-03-24 17:38:29 +0100318 else {
319 return -EINVAL;
320 }
321
322 pthread_once(&g_init, init_globals);
323
324 struct light_device_t *dev = malloc(sizeof(struct light_device_t));
325 memset(dev, 0, sizeof(*dev));
326
327 dev->common.tag = HARDWARE_DEVICE_TAG;
328 dev->common.version = 0;
329 dev->common.module = (struct hw_module_t*)module;
330 dev->common.close = (int (*)(struct hw_device_t*))close_lights;
331 dev->set_light = set_light;
332
333 *device = (struct hw_device_t*)dev;
334 return 0;
335}
336
337
338static struct hw_module_methods_t lights_module_methods = {
339 .open = open_lights,
340};
341
Daniel Hillenbrand29deaa02012-07-22 15:34:24 +0200342struct hw_module_t HAL_MODULE_INFO_SYM = {
codeworkxf1be2fe2012-03-24 17:38:29 +0100343 .tag = HARDWARE_MODULE_TAG,
344 .version_major = 1,
345 .version_minor = 0,
346 .id = LIGHTS_HARDWARE_MODULE_ID,
347 .name = "Samsung Exynos4210 Lights Module",
348 .author = "The CyanogenMod Project",
349 .methods = &lights_module_methods,
350};