• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014, 2017-2018 The  Linux Foundation. All rights reserved.
3  * Not a contribution
4  * Copyright (C) 2008 The Android Open Source Project
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 
20 // #define LOG_NDEBUG 0
21 
22 #include <log/log.h>
23 #include <cutils/properties.h>
24 
25 #include <stdint.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <pthread.h>
32 
33 #include <linux/msm_mdp.h>
34 
35 #include <sys/ioctl.h>
36 #include <sys/types.h>
37 
38 #include <hardware/lights.h>
39 
40 /*
41  * Change this to 1 to support battery notifications via BatteryService
42  */
43 #define LIGHTS_SUPPORT_BATTERY 0
44 #define CG_COLOR_ID_PROPERTY "ro.boot.hardware.color"
45 
46 /******************************************************************************/
47 static pthread_once_t g_init = PTHREAD_ONCE_INIT;
48 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
49 static struct light_state_t g_notification;
50 static struct light_state_t g_battery;
51 static int g_last_backlight_mode = BRIGHTNESS_MODE_USER;
52 static int g_attention = 0;
53 static int rgb_brightness_ratio = 255;
54 static bool g_has_persistence_node = false;
55 
56 char const*const RED_LED_FILE
57         = "/sys/class/leds/red/brightness";
58 
59 char const*const GREEN_LED_FILE
60         = "/sys/class/leds/green/brightness";
61 
62 char const*const BLUE_LED_FILE
63         = "/sys/class/leds/blue/brightness";
64 
65 char const*const LCD_FILE
66         = "/sys/class/backlight/panel0-backlight/brightness";
67 
68 char const*const PERSISTENCE_FILE
69         = "/sys/class/backlight/panel0-backlight/vr_mode";
70 
71 char const*const BUTTON_FILE
72         = "/sys/class/leds/button-backlight/brightness";
73 
74 char const*const RED_BLINK_FILE
75         = "/sys/class/leds/red/blink";
76 
77 char const*const GREEN_BLINK_FILE
78         = "/sys/class/leds/green/blink";
79 
80 char const*const BLUE_BLINK_FILE
81         = "/sys/class/leds/blue/blink";
82 
83 char const*const RED_ON_OFF_MS_FILE
84         = "/sys/class/leds/red/on_off_ms";
85 
86 char const*const GREEN_ON_OFF_MS_FILE
87         = "/sys/class/leds/green/on_off_ms";
88 
89 char const*const BLUE_ON_OFF_MS_FILE
90         = "/sys/class/leds/blue/on_off_ms";
91 
92 char const*const RED_RGB_START_FILE
93         = "/sys/class/leds/red/rgb_start";
94 
95 char const*const GREEN_RGB_START_FILE
96         = "/sys/class/leds/green/rgb_start";
97 
98 char const*const BLUE_RGB_START_FILE
99         = "/sys/class/leds/blue/rgb_start";
100 
101 /**
102  * device methods
103  */
104 
init_globals(void)105 void init_globals(void)
106 {
107     char color_id_prop[PROPERTY_VALUE_MAX] = {""};
108 
109     // init the mutex
110     pthread_mutex_init(&g_lock, NULL);
111 
112     // check CG color
113     property_get(CG_COLOR_ID_PROPERTY, color_id_prop, "DEF00");
114     if (strcmp(color_id_prop, "GRA00") == 0) {
115         rgb_brightness_ratio = 25;
116     } else if (strcmp(color_id_prop, "SLV00") == 0) {
117         rgb_brightness_ratio = 15;
118     } else if (strcmp(color_id_prop, "BLU00") == 0) {
119         rgb_brightness_ratio = 15;
120     } else {
121         rgb_brightness_ratio = 20;
122     }
123 }
124 
125 static int
write_int(char const * path,int value)126 write_int(char const* path, int value)
127 {
128     int fd;
129     static int already_warned = 0;
130 
131     fd = open(path, O_WRONLY);
132     if (fd >= 0) {
133         char buffer[20];
134         ssize_t amt;
135         size_t bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
136         if(bytes < sizeof(buffer)) {
137             amt = write(fd, buffer, bytes);
138         } else {
139             amt = -1;
140             errno = -EINVAL;
141         }
142         close(fd);
143         return amt == -1 ? -errno : 0;
144     } else {
145         if (already_warned == 0) {
146             ALOGE("write_int failed to open %s\n", path);
147             already_warned = 1;
148         }
149         return -errno;
150     }
151 }
152 
153 static int
write_double_int(char const * path,int value1,int value2)154 write_double_int(char const* path, int value1, int value2)
155 {
156     int fd;
157     static int already_warned = 0;
158 
159     fd = open(path, O_WRONLY);
160     if (fd >= 0) {
161         char buffer[20];
162         ssize_t amt;
163         size_t bytes = snprintf(buffer, sizeof(buffer), "%d %d\n", value1, value2);
164         if(bytes < sizeof(buffer)) {
165             amt = write(fd, buffer, bytes);
166         } else {
167             amt = -1;
168             errno = -EINVAL;
169         }
170         close(fd);
171         return amt == -1 ? -errno : 0;
172     } else {
173         if (already_warned == 0) {
174             ALOGE("write_int failed to open %s\n", path);
175             already_warned = 1;
176         }
177         return -errno;
178     }
179 }
180 
181 static int
is_lit(struct light_state_t const * state)182 is_lit(struct light_state_t const* state)
183 {
184     return state->color & 0x00ffffff;
185 }
186 
187 static int
rgb_to_brightness(struct light_state_t const * state)188 rgb_to_brightness(struct light_state_t const* state)
189 {
190     int color = state->color & 0x00ffffff;
191     return ((77*((color>>16)&0x00ff))
192             + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
193 }
194 
195 static int
set_light_backlight(struct light_device_t * dev,struct light_state_t const * state)196 set_light_backlight(struct light_device_t* dev,
197         struct light_state_t const* state)
198 {
199     int err = 0;
200     int brightness = rgb_to_brightness(state);
201     unsigned int lpEnabled =
202         state->brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE;
203     if(!dev) {
204         return -1;
205     }
206 
207     // The HAL requires that when low-persistence is requested, errors occur in only
208     // one case: if VR mode is unsupported.  So if the display just doesn't happen to
209     // be in the right state for VR mode (an error case in the SDM845 kernel driver),
210     // suppress the error.
211     bool suppress_persistence_error = false;
212 
213     pthread_mutex_lock(&g_lock);
214     // Toggle low persistence mode state
215     bool persistence_mode = ((g_last_backlight_mode != state->brightnessMode && lpEnabled) ||
216                             (!lpEnabled &&
217                             g_last_backlight_mode == BRIGHTNESS_MODE_LOW_PERSISTENCE));
218     bool cannot_handle_persistence = !g_has_persistence_node && persistence_mode;
219     if (g_has_persistence_node) {
220         if (persistence_mode) {
221             if ((err = write_int(PERSISTENCE_FILE, lpEnabled)) != 0) {
222                 ALOGE("%s: Failed to write to %s: %s\n", __FUNCTION__,
223                        PERSISTENCE_FILE, strerror(errno));
224                 suppress_persistence_error = true;
225             }
226         }
227         g_last_backlight_mode = state->brightnessMode;
228     }
229 
230     g_last_backlight_mode = state->brightnessMode;
231 
232     if (!err && !lpEnabled) {
233         err = write_int(LCD_FILE, brightness);
234     }
235 
236     pthread_mutex_unlock(&g_lock);
237     if (suppress_persistence_error) {
238         return 0;
239     } else {
240         return cannot_handle_persistence ? -ENOSYS : err;
241     }
242 }
243 
244 static int
set_speaker_light_locked(struct light_device_t * dev,struct light_state_t const * state)245 set_speaker_light_locked(struct light_device_t* dev,
246         struct light_state_t const* state)
247 {
248     int red, green, blue;
249     int blink;
250     int onMS, offMS;
251     unsigned int colorRGB;
252 
253     if(!dev) {
254         return -1;
255     }
256 
257     switch (state->flashMode) {
258         case LIGHT_FLASH_TIMED:
259             onMS = state->flashOnMS;
260             offMS = state->flashOffMS;
261             break;
262         case LIGHT_FLASH_NONE:
263         default:
264             onMS = 0;
265             offMS = 0;
266             break;
267     }
268 
269     colorRGB = state->color;
270 
271 #if 0
272     ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n",
273             state->flashMode, colorRGB, onMS, offMS);
274 #endif
275 
276     red = ((colorRGB >> 16) & 0xFF) * rgb_brightness_ratio / 255;
277     green = ((colorRGB >> 8) & 0xFF) * rgb_brightness_ratio / 255;
278     blue = (colorRGB & 0xFF) * rgb_brightness_ratio / 255;
279 
280     write_double_int(RED_ON_OFF_MS_FILE, onMS, offMS);
281     write_int(RED_LED_FILE, red);
282     write_double_int(GREEN_ON_OFF_MS_FILE, onMS, offMS);
283     write_int(GREEN_LED_FILE, green);
284     write_double_int(BLUE_ON_OFF_MS_FILE, onMS, offMS);
285     write_int(BLUE_LED_FILE, blue);
286 
287     if(!write_int(RED_RGB_START_FILE, 1))
288         if(!write_int(GREEN_RGB_START_FILE, 1))
289             if(!write_int(BLUE_RGB_START_FILE, 1))
290                 return -1;
291 
292     return 0;
293 }
294 
295 static void
handle_speaker_battery_locked(struct light_device_t * dev)296 handle_speaker_battery_locked(struct light_device_t* dev)
297 {
298     if (is_lit(&g_battery)) {
299         set_speaker_light_locked(dev, &g_battery);
300     } else {
301         set_speaker_light_locked(dev, &g_notification);
302     }
303 }
304 
305 static int
set_light_battery(struct light_device_t * dev,struct light_state_t const * state)306 set_light_battery(struct light_device_t* dev,
307         struct light_state_t const* state)
308 {
309     pthread_mutex_lock(&g_lock);
310     g_battery = *state;
311     handle_speaker_battery_locked(dev);
312     pthread_mutex_unlock(&g_lock);
313     return 0;
314 }
315 
316 static int
set_light_notifications(struct light_device_t * dev,struct light_state_t const * state)317 set_light_notifications(struct light_device_t* dev,
318         struct light_state_t const* state)
319 {
320     pthread_mutex_lock(&g_lock);
321     g_notification = *state;
322     handle_speaker_battery_locked(dev);
323     pthread_mutex_unlock(&g_lock);
324     return 0;
325 }
326 
327 static int
set_light_attention(struct light_device_t * dev,struct light_state_t const * state)328 set_light_attention(struct light_device_t* dev,
329         struct light_state_t const* state)
330 {
331     pthread_mutex_lock(&g_lock);
332     if (state->flashMode == LIGHT_FLASH_HARDWARE) {
333         g_attention = state->flashOnMS;
334     } else if (state->flashMode == LIGHT_FLASH_NONE) {
335         g_attention = 0;
336     }
337     handle_speaker_battery_locked(dev);
338     pthread_mutex_unlock(&g_lock);
339     return 0;
340 }
341 
342 static int
set_light_buttons(struct light_device_t * dev,struct light_state_t const * state)343 set_light_buttons(struct light_device_t* dev,
344         struct light_state_t const* state)
345 {
346     int err = 0;
347     if(!dev) {
348         return -1;
349     }
350     pthread_mutex_lock(&g_lock);
351     err = write_int(BUTTON_FILE, state->color & 0xFF);
352     pthread_mutex_unlock(&g_lock);
353     return err;
354 }
355 
356 /** Close the lights device */
357 static int
close_lights(struct light_device_t * dev)358 close_lights(struct light_device_t *dev)
359 {
360     if (dev) {
361         free(dev);
362     }
363     return 0;
364 }
365 
366 
367 /******************************************************************************/
368 
369 /**
370  * module methods
371  */
372 
373 /** 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)374 static int open_lights(const struct hw_module_t* module, char const* name,
375         struct hw_device_t** device)
376 {
377     int (*set_light)(struct light_device_t* dev,
378             struct light_state_t const* state);
379 
380     if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
381         g_has_persistence_node = !access(PERSISTENCE_FILE, F_OK);
382         set_light = set_light_backlight;
383     } else if (0 == strcmp(LIGHT_ID_BATTERY, name))
384         set_light = set_light_battery;
385     else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
386         set_light = set_light_notifications;
387     else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
388         if (!access(BUTTON_FILE, F_OK)) {
389           // enable light button when the file is present
390           set_light = set_light_buttons;
391         } else {
392           return -EINVAL;
393         }
394     }
395     else if (0 == strcmp(LIGHT_ID_ATTENTION, name))
396         set_light = set_light_attention;
397     else
398         return -EINVAL;
399 
400     pthread_once(&g_init, init_globals);
401 
402     struct light_device_t *dev = malloc(sizeof(struct light_device_t));
403 
404     if(!dev)
405         return -ENOMEM;
406 
407     memset(dev, 0, sizeof(*dev));
408 
409     dev->common.tag = HARDWARE_DEVICE_TAG;
410     dev->common.version = LIGHTS_DEVICE_API_VERSION_2_0;
411     dev->common.module = (struct hw_module_t*)module;
412     dev->common.close = (int (*)(struct hw_device_t*))close_lights;
413     dev->set_light = set_light;
414 
415     *device = (struct hw_device_t*)dev;
416     return 0;
417 }
418 
419 static struct hw_module_methods_t lights_module_methods = {
420     .open =  open_lights,
421 };
422 
423 /*
424  * The lights Module
425  */
426 struct hw_module_t HAL_MODULE_INFO_SYM = {
427     .tag = HARDWARE_MODULE_TAG,
428     .version_major = 1,
429     .version_minor = 0,
430     .id = LIGHTS_HARDWARE_MODULE_ID,
431     .name = "lights Module",
432     .author = "Google, Inc.",
433     .methods = &lights_module_methods,
434 };
435