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