blob: 33ce7504001e5f8efbdb12899260d86ad9003358 [file] [log] [blame]
Zhihui Wub26f95c2015-04-16 14:09:36 -07001/*
2 * Copyright (C) 2015 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#include <cutils/log.h>
18
19#include <stdint.h>
20#include <string.h>
21#include <unistd.h>
22#include <errno.h>
23#include <fcntl.h>
24#include <pthread.h>
25#include <malloc.h>
26
Ruben Brunke2f236b2016-01-16 02:49:02 -080027#include <linux/msm_mdp.h>
28
Zhihui Wub26f95c2015-04-16 14:09:36 -070029#include <sys/ioctl.h>
30#include <sys/types.h>
31
32#include <hardware/lights.h>
33
34/******************************************************************************/
35
Patrick Tjin70f9f782015-09-02 14:03:13 -070036/*
37 * Change this to 1 to support battery notifications via BatteryService
38 */
39#define LIGHTS_SUPPORT_BATTERY 0
40
Ruben Brunke2f236b2016-01-16 02:49:02 -080041#define DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS 255
42
Zhihui Wub26f95c2015-04-16 14:09:36 -070043static pthread_once_t g_init = PTHREAD_ONCE_INIT;
44static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
lijianzhao3b633ba2015-07-30 12:29:19 -070045static pthread_mutex_t g_lcd_lock = PTHREAD_MUTEX_INITIALIZER;
Zhihui Wub26f95c2015-04-16 14:09:36 -070046static struct light_state_t g_notification;
Ruben Brunke2f236b2016-01-16 02:49:02 -080047static int g_last_backlight_mode = BRIGHTNESS_MODE_USER;
Patrick Tjin70f9f782015-09-02 14:03:13 -070048#if LIGHTS_SUPPORT_BATTERY
Zhihui Wub26f95c2015-04-16 14:09:36 -070049static struct light_state_t g_battery;
Patrick Tjin70f9f782015-09-02 14:03:13 -070050#endif
Zhihui Wub26f95c2015-04-16 14:09:36 -070051
52char const*const RED_LED_FILE
53 = "/sys/class/leds/red/brightness";
54
55char const*const GREEN_LED_FILE
56 = "/sys/class/leds/green/brightness";
57
58char const*const BLUE_LED_FILE
59 = "/sys/class/leds/blue/brightness";
60
61char const*const LCD_FILE
62 = "/sys/class/leds/lcd-backlight/brightness";
63
Patrick Tjin9ecc93f2015-08-25 00:13:48 -070064char const*const RED_TIMER_FILE
65 = "/sys/class/leds/red/on_off_ms";
Zhihui Wub26f95c2015-04-16 14:09:36 -070066
Patrick Tjin9ecc93f2015-08-25 00:13:48 -070067char const*const GREEN_TIMER_FILE
68 = "/sys/class/leds/green/on_off_ms";
Zhihui Wub26f95c2015-04-16 14:09:36 -070069
Patrick Tjin9ecc93f2015-08-25 00:13:48 -070070char const*const BLUE_TIMER_FILE
71 = "/sys/class/leds/blue/on_off_ms";
72
73char const*const RGB_LOCK_FILE
74 = "/sys/class/leds/red/rgb_start";
75
Ruben Brunke2f236b2016-01-16 02:49:02 -080076char const*const DISPLAY_FB_DEV_PATH
77 = "/dev/graphics/fb0";
78
Patrick Tjin9ecc93f2015-08-25 00:13:48 -070079enum led_type {
80 LED_NOTIFICATION = 0,
81 LED_BATTERY,
82};
Zhihui Wub26f95c2015-04-16 14:09:36 -070083
84/**
85 * device methods
86 */
87
88void init_globals(void)
89{
90 // init the mutex
91 pthread_mutex_init(&g_lock, NULL);
lijianzhao3b633ba2015-07-30 12:29:19 -070092 pthread_mutex_init(&g_lcd_lock, NULL);
Zhihui Wub26f95c2015-04-16 14:09:36 -070093}
94
95static int
96write_int(char const* path, int value)
97{
98 int fd;
99 static int already_warned = 0;
100
Patrick Tjin936ce8c2015-08-29 02:01:12 -0700101 fd = open(path, O_WRONLY);
Zhihui Wub26f95c2015-04-16 14:09:36 -0700102 if (fd >= 0) {
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700103 char buffer[20] = {0,};
Zhihui Wub26f95c2015-04-16 14:09:36 -0700104 int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
105 ssize_t amt = write(fd, buffer, (size_t)bytes);
106 close(fd);
107 return amt == -1 ? -errno : 0;
108 } else {
109 if (already_warned == 0) {
110 ALOGE("write_int failed to open %s\n", path);
111 already_warned = 1;
112 }
113 return -errno;
114 }
115}
116
117static int
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700118write_on_off(char const* path, int on, int off)
119{
120 int fd;
121 static int already_warned = 0;
122
Patrick Tjin936ce8c2015-08-29 02:01:12 -0700123 fd = open(path, O_WRONLY);
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700124 if (fd >= 0) {
125 char buffer[32] = {0,};
126 int bytes = snprintf(buffer, sizeof(buffer), "%d %d\n", on, off);
127 int amt = write(fd, buffer, bytes);
128 close(fd);
129 return amt == -1 ? -errno : 0;
130 } else {
131 if (already_warned == 0) {
132 ALOGE("write_int failed to open %s\n", path);
133 already_warned = 1;
134 }
135 return -errno;
136 }
137}
138
139static int
Zhihui Wub26f95c2015-04-16 14:09:36 -0700140is_lit(struct light_state_t const* state)
141{
142 return state->color & 0x00ffffff;
143}
144
145static int
146rgb_to_brightness(struct light_state_t const* state)
147{
148 int color = state->color & 0x00ffffff;
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700149 return ((77 * ((color >> 16) & 0x00ff))
150 + (150 * ((color >> 8) & 0x00ff)) + (29 * (color & 0x00ff))) >> 8;
Zhihui Wub26f95c2015-04-16 14:09:36 -0700151}
152
153static int
154set_light_backlight(struct light_device_t* dev,
155 struct light_state_t const* state)
156{
157 int err = 0;
158 int brightness = rgb_to_brightness(state);
Ruben Brunke2f236b2016-01-16 02:49:02 -0800159 unsigned int lpEnabled = state->brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE;
Zhihui Wub26f95c2015-04-16 14:09:36 -0700160
161 if(!dev) {
162 return -1;
163 }
164
lijianzhao3b633ba2015-07-30 12:29:19 -0700165 pthread_mutex_lock(&g_lcd_lock);
Ruben Brunke2f236b2016-01-16 02:49:02 -0800166
167 // If we're not in lp mode and it has been enabled or if we are in lp mode
168 // and it has been disabled send an ioctl to the display with the update
169 if ((g_last_backlight_mode != state->brightnessMode && lpEnabled) ||
170 (!lpEnabled && g_last_backlight_mode == BRIGHTNESS_MODE_LOW_PERSISTENCE)) {
171 int fd = -1;
172 fd = open(DISPLAY_FB_DEV_PATH, O_RDWR);
173 if (fd >= 0) {
174 if ((err = ioctl(fd, MSMFB_SET_PERSISTENCE_MODE, &lpEnabled)) != 0) {
175 ALOGE("%s: Failed in ioctl call to %s: %s\n", __FUNCTION__, DISPLAY_FB_DEV_PATH,
176 strerror(errno));
177 err = -1;
178 }
179 close(fd);
180
181 brightness = DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS;
182 } else {
183 ALOGE("%s: Failed to open %s: %s\n", __FUNCTION__, DISPLAY_FB_DEV_PATH,
184 strerror(errno));
185 err = -1;
186 }
187 }
188
189
190 g_last_backlight_mode = state->brightnessMode;
191
192 if (!err) {
193 err = write_int(LCD_FILE, brightness);
194 }
195
lijianzhao3b633ba2015-07-30 12:29:19 -0700196 pthread_mutex_unlock(&g_lcd_lock);
Zhihui Wub26f95c2015-04-16 14:09:36 -0700197 return err;
198}
199
200static int
201set_speaker_light_locked(struct light_device_t* dev,
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700202 struct light_state_t const* state, enum led_type type)
Zhihui Wub26f95c2015-04-16 14:09:36 -0700203{
204 int red, green, blue;
205 int blink;
206 int onMS, offMS;
207 unsigned int colorRGB;
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700208 int override = 0;
Zhihui Wub26f95c2015-04-16 14:09:36 -0700209
210 if(!dev) {
211 return -1;
212 }
213
Patrick Tjin70f9f782015-09-02 14:03:13 -0700214#if LIGHTS_SUPPORT_BATTERY
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700215 // Ensure that LED notifications override charging LED.
216 if (type == LED_BATTERY && is_lit(&g_notification)) {
217 state = &g_notification;
218 override = 1;
219 }
220
Patrick Tjinc06511b2015-08-27 00:34:17 -0700221 // When turning off the notification LED, restore the battery
222 // notification state.
223 if (type == LED_NOTIFICATION && !is_lit(&g_notification)) {
224 state = &g_battery;
225 override = 1;
226 }
Patrick Tjin70f9f782015-09-02 14:03:13 -0700227#endif
Patrick Tjinc06511b2015-08-27 00:34:17 -0700228
Zhihui Wub26f95c2015-04-16 14:09:36 -0700229 switch (state->flashMode) {
230 case LIGHT_FLASH_TIMED:
Patrick Tjinc06511b2015-08-27 00:34:17 -0700231 case LIGHT_FLASH_HARDWARE:
Zhihui Wub26f95c2015-04-16 14:09:36 -0700232 onMS = state->flashOnMS;
233 offMS = state->flashOffMS;
234 break;
235 case LIGHT_FLASH_NONE:
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700236 // if the light is lit, use some time on and no time off
237 if (is_lit(state)) {
238 onMS = 1;
239 offMS = 0;
240 break;
241 }
242 // fall through
Zhihui Wub26f95c2015-04-16 14:09:36 -0700243 default:
244 onMS = 0;
245 offMS = 0;
246 break;
247 }
248
249 colorRGB = state->color;
250
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700251 ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, "
252 "offMS=%d, type %s%c\n",
253 state->flashMode, colorRGB, onMS, offMS,
254 type == LED_BATTERY ? "BATTERY" : "NOTIFICATION",
255 override ? '*' : ' ');
Zhihui Wub26f95c2015-04-16 14:09:36 -0700256 red = (colorRGB >> 16) & 0xFF;
257 green = (colorRGB >> 8) & 0xFF;
258 blue = colorRGB & 0xFF;
259
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700260 if (onMS == 0) {
Patrick Tjinc06511b2015-08-27 00:34:17 -0700261 red = 0;
262 green = 0;
263 blue = 0;
264 }
Zhihui Wub26f95c2015-04-16 14:09:36 -0700265
Patrick Tjinc06511b2015-08-27 00:34:17 -0700266 write_int(RGB_LOCK_FILE, 0);
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700267
268 write_int(RED_LED_FILE, red);
269 write_int(GREEN_LED_FILE, green);
270 write_int(BLUE_LED_FILE, blue);
271
Patrick Tjinc06511b2015-08-27 00:34:17 -0700272 write_on_off(RED_TIMER_FILE, onMS, offMS);
273 write_on_off(GREEN_TIMER_FILE, onMS, offMS);
274 write_on_off(BLUE_TIMER_FILE, onMS, offMS);
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700275
Patrick Tjinc06511b2015-08-27 00:34:17 -0700276 write_int(RGB_LOCK_FILE, 1);
Zhihui Wub26f95c2015-04-16 14:09:36 -0700277
278 return 0;
279}
280
Patrick Tjin70f9f782015-09-02 14:03:13 -0700281#if LIGHTS_SUPPORT_BATTERY
Zhihui Wub26f95c2015-04-16 14:09:36 -0700282static int
283set_light_battery(struct light_device_t* dev,
284 struct light_state_t const* state)
285{
286 if(!dev) {
287 return -1;
288 }
289
290 pthread_mutex_lock(&g_lock);
291 g_battery = *state;
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700292 set_speaker_light_locked(dev, &g_battery, LED_BATTERY);
Zhihui Wub26f95c2015-04-16 14:09:36 -0700293 pthread_mutex_unlock(&g_lock);
294 return 0;
295}
Patrick Tjin70f9f782015-09-02 14:03:13 -0700296#endif
Zhihui Wub26f95c2015-04-16 14:09:36 -0700297
298static int
299set_light_notifications(struct light_device_t* dev,
300 struct light_state_t const* state)
301{
302 if(!dev) {
303 return -1;
304 }
305
306 pthread_mutex_lock(&g_lock);
307 g_notification = *state;
Patrick Tjin9ecc93f2015-08-25 00:13:48 -0700308 set_speaker_light_locked(dev, &g_notification, LED_NOTIFICATION);
Zhihui Wub26f95c2015-04-16 14:09:36 -0700309 pthread_mutex_unlock(&g_lock);
310 return 0;
311}
312
Zhihui Wub26f95c2015-04-16 14:09:36 -0700313/** Close the lights device */
314static int
315close_lights(struct light_device_t *dev)
316{
317 if (dev) {
318 free(dev);
319 }
320 return 0;
321}
322
323
324/******************************************************************************/
325
326/**
327 * module methods
328 */
329
330/** Open a new instance of a lights device using name */
331static int open_lights(const struct hw_module_t* module, char const* name,
332 struct hw_device_t** device)
333{
334 int (*set_light)(struct light_device_t* dev,
335 struct light_state_t const* state);
336
337 if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))
338 set_light = set_light_backlight;
Patrick Tjin70f9f782015-09-02 14:03:13 -0700339#if LIGHTS_SUPPORT_BATTERY
Zhihui Wub26f95c2015-04-16 14:09:36 -0700340 else if (0 == strcmp(LIGHT_ID_BATTERY, name))
341 set_light = set_light_battery;
Patrick Tjin70f9f782015-09-02 14:03:13 -0700342#endif
Zhihui Wub26f95c2015-04-16 14:09:36 -0700343 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
344 set_light = set_light_notifications;
Zhihui Wub26f95c2015-04-16 14:09:36 -0700345 else
346 return -EINVAL;
347
348 pthread_once(&g_init, init_globals);
349
350 struct light_device_t *dev = malloc(sizeof(struct light_device_t));
351
352 if(!dev)
353 return -ENOMEM;
354
355 memset(dev, 0, sizeof(*dev));
356
357 dev->common.tag = HARDWARE_DEVICE_TAG;
Ruben Brunke2f236b2016-01-16 02:49:02 -0800358 dev->common.version = LIGHTS_DEVICE_API_VERSION_2_0;
Zhihui Wub26f95c2015-04-16 14:09:36 -0700359 dev->common.module = (struct hw_module_t*)module;
360 dev->common.close = (int (*)(struct hw_device_t*))close_lights;
361 dev->set_light = set_light;
362
363 *device = (struct hw_device_t*)dev;
364 return 0;
365}
366
367static struct hw_module_methods_t lights_module_methods = {
368 .open = open_lights,
369};
370
371/*
372 * The lights Module
373 */
374struct hw_module_t HAL_MODULE_INFO_SYM = {
375 .tag = HARDWARE_MODULE_TAG,
376 .version_major = 1,
377 .version_minor = 0,
378 .id = LIGHTS_HARDWARE_MODULE_ID,
379 .name = "lights Module",
380 .author = "Google, Inc.",
381 .methods = &lights_module_methods,
382};