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