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