fe1e83f05184e1a9731f3ec6385ca2a736cdba7f
[GitHub/LineageOS/android_hardware_samsung.git] / exynos4 / exynos4210 / liblights / lights.c
1 /*
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
38 static pthread_once_t g_init = PTHREAD_ONCE_INIT;
39 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
40 static int g_enable_touchlight = -1;
41
42 char const*const PANEL_FILE
43 = "/sys/class/backlight/panel/brightness";
44
45 #ifndef EXYNOS4210_TABLET
46 char const*const BUTTON_FILE
47 = "/sys/class/sec/sec_touchkey/brightness";
48 #endif
49
50 #ifdef LED_NOTIFICATION
51 static char const RED_LED_DIR[] = "/sys/class/leds/red";
52 static char const BLUE_LED_DIR[] = "/sys/class/leds/blue";
53 #endif // LED_NOTIFICATION
54 void init_globals(void)
55 {
56 // init the mutex
57 pthread_mutex_init(&g_lock, NULL);
58 }
59
60 void
61 load_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
77 #ifdef LED_NOTIFICATION
78 static 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
83 static int
84 write_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) {
98 ALOGE("write_int failed to open %s\n", path);
99 already_warned = 1;
100 }
101 return -errno;
102 }
103 }
104
105 #ifdef LED_NOTIFICATION
106 static int write_str(char const *path, char const *str)
107 {
108 int fd;
109 static int already_warned = 0;
110
111 ALOGV("write_str: path=\"%s\", str=\"%s\".", path, str);
112 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) {
120 ALOGE("write_str failed to open %s\n", path);
121 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. */
129 static 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
136 static 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
144 static int
145 is_lit(struct light_state_t const* state)
146 {
147 return state->color & 0x00ffffff;
148 }
149
150 static int
151 rgb_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
158 #ifdef LED_NOTIFICATION
159 static 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:
171 ALOGI("Unsuported flashMode %d, default to NONE.", state->flashMode);
172 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
185 ALOGV("comp_led_states: red=(%u, %d, %d), blue=(%u, %d, %d).",
186 red->enabled, red->delay_on, red->delay_off, blue->enabled,
187 blue->delay_on, blue->delay_off);
188 }
189
190 static 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
227 static int
228 set_light_backlight(struct light_device_t* dev,
229 struct light_state_t const* state)
230 {
231 int err = 0;
232 static int s_previous_brightness = -1;
233 int brightness = rgb_to_brightness(state);
234
235 pthread_mutex_lock(&g_lock);
236 err = write_int(PANEL_FILE, brightness);
237
238 #ifndef EXYNOS4210_TABLET
239 if (!s_previous_brightness && (brightness > 0)) {
240 err = write_int(BUTTON_FILE, brightness > 0 ? 1 : 2);
241 s_previous_brightness = brightness;
242 }
243 #endif
244
245 pthread_mutex_unlock(&g_lock);
246
247 return err;
248 }
249
250 static int
251 set_light_buttons(struct light_device_t* dev,
252 struct light_state_t const* state)
253 {
254 #ifdef EXYNOS4210_TABLET
255 return 0;
256 #else
257
258 load_settings();
259
260 int err = 0;
261
262 pthread_mutex_lock(&g_lock);
263 ALOGD("set_light_button on=%d\n", g_enable_touchlight ? 1 : 0);
264 err = write_int(BUTTON_FILE, g_enable_touchlight ? 1 : 0);
265 pthread_mutex_unlock(&g_lock);
266
267 return err;
268 #endif
269 }
270
271 static int
272 set_light_battery(struct light_device_t* dev,
273 struct light_state_t const* state)
274 {
275 int res = 0;
276
277 #ifdef LED_NOTIFICATION
278 ALOGD("set_light_battery: color=%#010x, fM=%u, fOnMS=%d, fOffMs=%d.",
279 state->color, state->flashMode, state->flashOnMS, state->flashOffMS);
280
281 pthread_mutex_lock(&g_lock);
282
283 comp_led_states(&battery_red, &battery_blue, state);
284
285 if ((res = set_led(RED_LED_DIR, &battery_red, &notifications_red)) >= 0)
286 res = set_led(BLUE_LED_DIR, &battery_blue, &notifications_blue);
287
288 pthread_mutex_unlock(&g_lock);
289 #endif // LED_NOTIFICATION
290
291 return res;
292 }
293
294 static int
295 set_light_notification(struct light_device_t* dev,
296 struct light_state_t const* state)
297 {
298 int res = 0;
299
300 #ifdef LED_NOTIFICATION
301 ALOGD("set_light_notification: color=%#010x, fM=%u, fOnMS=%d, fOffMs=%d.",
302 state->color, state->flashMode, state->flashOnMS, state->flashOffMS);
303
304 pthread_mutex_lock(&g_lock);
305
306 comp_led_states(&notifications_red, &notifications_blue, state);
307
308 if ((res = set_led(RED_LED_DIR, &battery_red, &notifications_red)) >= 0)
309 res = set_led(BLUE_LED_DIR, &battery_blue, &notifications_blue);
310
311 pthread_mutex_unlock(&g_lock);
312 #endif // LED_NOTIFICATION
313
314 return res;
315 }
316
317 static int
318 close_lights(struct light_device_t *dev)
319 {
320 if (dev) {
321 free(dev);
322 }
323 return 0;
324 }
325
326
327 /******************************************************************************/
328 static int open_lights(const struct hw_module_t* module, char const* name,
329 struct hw_device_t** device)
330 {
331 int (*set_light)(struct light_device_t* dev,
332 struct light_state_t const* state);
333
334 if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
335 set_light = set_light_backlight;
336 }
337 else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
338 set_light = set_light_buttons;
339 }
340 else if (0 == strcmp(LIGHT_ID_BATTERY, name)) {
341 set_light = set_light_battery;
342 }
343 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
344 set_light = set_light_notification;
345 }
346 else {
347 return -EINVAL;
348 }
349
350 pthread_once(&g_init, init_globals);
351
352 struct light_device_t *dev = malloc(sizeof(struct light_device_t));
353 memset(dev, 0, sizeof(*dev));
354
355 dev->common.tag = HARDWARE_DEVICE_TAG;
356 dev->common.version = 0;
357 dev->common.module = (struct hw_module_t*)module;
358 dev->common.close = (int (*)(struct hw_device_t*))close_lights;
359 dev->set_light = set_light;
360
361 *device = (struct hw_device_t*)dev;
362 return 0;
363 }
364
365
366 static struct hw_module_methods_t lights_module_methods = {
367 .open = open_lights,
368 };
369
370 struct hw_module_t HAL_MODULE_INFO_SYM = {
371 .tag = HARDWARE_MODULE_TAG,
372 .version_major = 1,
373 .version_minor = 0,
374 .id = LIGHTS_HARDWARE_MODULE_ID,
375 .name = "Samsung Exynos4210 Lights Module",
376 .author = "The CyanogenMod Project",
377 .methods = &lights_module_methods,
378 };