1 /*
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
27 #include <linux/msm_mdp.h>
28
29 #include <sys/ioctl.h>
30 #include <sys/types.h>
31
32 #include <hardware/lights.h>
33
34 /******************************************************************************/
35
36 /*
37 * Change this to 1 to support battery notifications via BatteryService
38 */
39 #define LIGHTS_SUPPORT_BATTERY 0
40
41 #define DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS 255
42
43 static pthread_once_t g_init = PTHREAD_ONCE_INIT;
44 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
45 static pthread_mutex_t g_lcd_lock = PTHREAD_MUTEX_INITIALIZER;
46 static struct light_state_t g_notification;
47 static int g_last_backlight_mode = BRIGHTNESS_MODE_USER;
48 #if LIGHTS_SUPPORT_BATTERY
49 static struct light_state_t g_battery;
50 #endif
51
52 char const*const RED_LED_FILE
53 = "/sys/class/leds/red/brightness";
54
55 char const*const GREEN_LED_FILE
56 = "/sys/class/leds/green/brightness";
57
58 char const*const BLUE_LED_FILE
59 = "/sys/class/leds/blue/brightness";
60
61 char const*const LCD_FILE
62 = "/sys/class/leds/lcd-backlight/brightness";
63
64 char const*const RED_TIMER_FILE
65 = "/sys/class/leds/red/on_off_ms";
66
67 char const*const GREEN_TIMER_FILE
68 = "/sys/class/leds/green/on_off_ms";
69
70 char const*const BLUE_TIMER_FILE
71 = "/sys/class/leds/blue/on_off_ms";
72
73 char const*const RGB_LOCK_FILE
74 = "/sys/class/leds/red/rgb_start";
75
76 char const*const DISPLAY_FB_DEV_PATH
77 = "/dev/graphics/fb0";
78
79 enum led_type {
80 LED_NOTIFICATION = 0,
81 LED_BATTERY,
82 };
83
84 /**
85 * device methods
86 */
87
init_globals(void)88 void init_globals(void)
89 {
90 // init the mutex
91 pthread_mutex_init(&g_lock, NULL);
92 pthread_mutex_init(&g_lcd_lock, NULL);
93 }
94
95 static int
write_int(char const * path,int value)96 write_int(char const* path, int value)
97 {
98 int fd;
99 static int already_warned = 0;
100
101 fd = open(path, O_WRONLY);
102 if (fd >= 0) {
103 char buffer[20] = {0,};
104 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
117 static int
write_on_off(char const * path,int on,int off)118 write_on_off(char const* path, int on, int off)
119 {
120 int fd;
121 static int already_warned = 0;
122
123 fd = open(path, O_WRONLY);
124 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
139 static int
is_lit(struct light_state_t const * state)140 is_lit(struct light_state_t const* state)
141 {
142 return state->color & 0x00ffffff;
143 }
144
145 static int
rgb_to_brightness(struct light_state_t const * state)146 rgb_to_brightness(struct light_state_t const* state)
147 {
148 int color = state->color & 0x00ffffff;
149 return ((77 * ((color >> 16) & 0x00ff))
150 + (150 * ((color >> 8) & 0x00ff)) + (29 * (color & 0x00ff))) >> 8;
151 }
152
153 static int
set_light_backlight(struct light_device_t * dev,struct light_state_t const * state)154 set_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);
159 unsigned int lpEnabled = state->brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE;
160
161 if(!dev) {
162 return -1;
163 }
164
165 pthread_mutex_lock(&g_lcd_lock);
166
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
196 pthread_mutex_unlock(&g_lcd_lock);
197 return err;
198 }
199
200 static int
set_speaker_light_locked(struct light_device_t * dev,struct light_state_t const * state,enum led_type type)201 set_speaker_light_locked(struct light_device_t* dev,
202 struct light_state_t const* state, enum led_type type)
203 {
204 int red, green, blue;
205 int blink;
206 int onMS, offMS;
207 unsigned int colorRGB;
208 int override = 0;
209
210 if(!dev) {
211 return -1;
212 }
213
214 #if LIGHTS_SUPPORT_BATTERY
215 // 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
221 // 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 }
227 #endif
228
229 switch (state->flashMode) {
230 case LIGHT_FLASH_TIMED:
231 case LIGHT_FLASH_HARDWARE:
232 onMS = state->flashOnMS;
233 offMS = state->flashOffMS;
234 break;
235 case LIGHT_FLASH_NONE:
236 // 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
243 default:
244 onMS = 0;
245 offMS = 0;
246 break;
247 }
248
249 colorRGB = state->color;
250
251 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 ? '*' : ' ');
256 red = (colorRGB >> 16) & 0xFF;
257 green = (colorRGB >> 8) & 0xFF;
258 blue = colorRGB & 0xFF;
259
260 if (onMS == 0) {
261 red = 0;
262 green = 0;
263 blue = 0;
264 }
265
266 write_int(RGB_LOCK_FILE, 0);
267
268 write_int(RED_LED_FILE, red);
269 write_int(GREEN_LED_FILE, green);
270 write_int(BLUE_LED_FILE, blue);
271
272 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);
275
276 write_int(RGB_LOCK_FILE, 1);
277
278 return 0;
279 }
280
281 #if LIGHTS_SUPPORT_BATTERY
282 static int
set_light_battery(struct light_device_t * dev,struct light_state_t const * state)283 set_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;
292 set_speaker_light_locked(dev, &g_battery, LED_BATTERY);
293 pthread_mutex_unlock(&g_lock);
294 return 0;
295 }
296 #endif
297
298 static int
set_light_notifications(struct light_device_t * dev,struct light_state_t const * state)299 set_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;
308 set_speaker_light_locked(dev, &g_notification, LED_NOTIFICATION);
309 pthread_mutex_unlock(&g_lock);
310 return 0;
311 }
312
313 /** Close the lights device */
314 static int
close_lights(struct light_device_t * dev)315 close_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 */
open_lights(const struct hw_module_t * module,char const * name,struct hw_device_t ** device)331 static 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;
339 #if LIGHTS_SUPPORT_BATTERY
340 else if (0 == strcmp(LIGHT_ID_BATTERY, name))
341 set_light = set_light_battery;
342 #endif
343 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
344 set_light = set_light_notifications;
345 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;
358 dev->common.version = LIGHTS_DEVICE_API_VERSION_2_0;
359 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
367 static struct hw_module_methods_t lights_module_methods = {
368 .open = open_lights,
369 };
370
371 /*
372 * The lights Module
373 */
374 struct 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 };
383