Home | History | Annotate | Download | only in liblight
      1 /*
      2  * Copyright (C) 2014, 2017 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 #include <stdint.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <unistd.h>
     28 #include <errno.h>
     29 #include <fcntl.h>
     30 #include <pthread.h>
     31 
     32 #include <sys/ioctl.h>
     33 #include <sys/types.h>
     34 
     35 #include <hardware/lights.h>
     36 #include "lights_prv.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 int g_brightness_max = 0;
     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 LCD_FILE2
     65         = "/sys/class/backlight/panel0-backlight/brightness";
     66 
     67 char const*const BUTTON_FILE
     68         = "/sys/class/leds/button-backlight/brightness";
     69 
     70 char const*const RED_BLINK_FILE
     71         = "/sys/class/leds/red/blink";
     72 
     73 char const*const GREEN_BLINK_FILE
     74         = "/sys/class/leds/green/blink";
     75 
     76 char const*const BLUE_BLINK_FILE
     77         = "/sys/class/leds/blue/blink";
     78 
     79 char const*const PERSISTENCE_FILE
     80         = "/sys/class/graphics/fb0/msm_fb_persist_mode";
     81 
     82 /**
     83  * device methods
     84  */
     85 
     86 void init_globals(void)
     87 {
     88     // init the mutex
     89     pthread_mutex_init(&g_lock, NULL);
     90 }
     91 
     92 static int
     93 write_int(char const* path, int value)
     94 {
     95     int fd;
     96     static int already_warned = 0;
     97 
     98     fd = open(path, O_RDWR);
     99     if (fd >= 0) {
    100         char buffer[20];
    101         int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
    102         ssize_t amt = write(fd, buffer, (size_t)bytes);
    103         close(fd);
    104         return amt == -1 ? -errno : 0;
    105     } else {
    106         if (already_warned == 0) {
    107             ALOGE("write_int failed to open %s\n", path);
    108             already_warned = 1;
    109         }
    110         return -errno;
    111     }
    112 }
    113 
    114 static int
    115 is_lit(struct light_state_t const* state)
    116 {
    117     return state->color & 0x00ffffff;
    118 }
    119 
    120 static int
    121 rgb_to_brightness(struct light_state_t const* state)
    122 {
    123     int color = state->color & 0x00ffffff;
    124     return ((77*((color>>16)&0x00ff))
    125             + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
    126 }
    127 
    128 static int
    129 set_light_backlight(struct light_device_t* dev,
    130         struct light_state_t const* state)
    131 {
    132     int err = 0;
    133     int brightness = rgb_to_brightness(state);
    134     unsigned int lpEnabled =
    135         state->brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE;
    136     if(!dev) {
    137         return -1;
    138     }
    139 
    140     pthread_mutex_lock(&g_lock);
    141     // Toggle low persistence mode state
    142     if ((g_last_backlight_mode != state->brightnessMode && lpEnabled) ||
    143         (!lpEnabled &&
    144          g_last_backlight_mode == BRIGHTNESS_MODE_LOW_PERSISTENCE)) {
    145         if ((err = write_int(PERSISTENCE_FILE, lpEnabled)) != 0) {
    146             ALOGE("%s: Failed to write to %s: %s\n", __FUNCTION__,
    147                    PERSISTENCE_FILE, strerror(errno));
    148         }
    149         if (lpEnabled != 0) {
    150             brightness = DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS;
    151         }
    152     }
    153 
    154     g_last_backlight_mode = state->brightnessMode;
    155 
    156     if (!err) {
    157         if (!access(LCD_FILE, F_OK)) {
    158             err = write_int(LCD_FILE, brightness);
    159         } else {
    160             err = write_int(LCD_FILE2, brightness);
    161         }
    162     }
    163 
    164     pthread_mutex_unlock(&g_lock);
    165     return err;
    166 }
    167 
    168 static int
    169 set_light_backlight_ext(struct light_device_t* dev,
    170         struct light_state_t const* state)
    171 {
    172     int err = 0;
    173 
    174     if(!dev) {
    175         return -1;
    176     }
    177 
    178     int brightness = state->color & 0x00ffffff;
    179     pthread_mutex_lock(&g_lock);
    180 
    181     if (brightness >= 0 && brightness <= g_brightness_max) {
    182         set_brightness_ext_level(brightness);
    183     }
    184 
    185     pthread_mutex_unlock(&g_lock);
    186 
    187     return err;
    188 }
    189 
    190 static int
    191 set_speaker_light_locked(struct light_device_t* dev,
    192         struct light_state_t const* state)
    193 {
    194     int red, green, blue;
    195     int blink;
    196     int onMS, offMS;
    197     unsigned int colorRGB;
    198 
    199     if(!dev) {
    200         return -1;
    201     }
    202 
    203     switch (state->flashMode) {
    204         case LIGHT_FLASH_TIMED:
    205             onMS = state->flashOnMS;
    206             offMS = state->flashOffMS;
    207             break;
    208         case LIGHT_FLASH_NONE:
    209         default:
    210             onMS = 0;
    211             offMS = 0;
    212             break;
    213     }
    214 
    215     colorRGB = state->color;
    216 
    217 #if 0
    218     ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n",
    219             state->flashMode, colorRGB, onMS, offMS);
    220 #endif
    221 
    222     red = (colorRGB >> 16) & 0xFF;
    223     green = (colorRGB >> 8) & 0xFF;
    224     blue = colorRGB & 0xFF;
    225 
    226     if (onMS > 0 && offMS > 0) {
    227         /*
    228          * if ON time == OFF time
    229          *   use blink mode 2
    230          * else
    231          *   use blink mode 1
    232          */
    233         if (onMS == offMS)
    234             blink = 2;
    235         else
    236             blink = 1;
    237     } else {
    238         blink = 0;
    239     }
    240 
    241     if (blink) {
    242         if (red) {
    243             if (write_int(RED_BLINK_FILE, blink))
    244                 write_int(RED_LED_FILE, 0);
    245     }
    246         if (green) {
    247             if (write_int(GREEN_BLINK_FILE, blink))
    248                 write_int(GREEN_LED_FILE, 0);
    249     }
    250         if (blue) {
    251             if (write_int(BLUE_BLINK_FILE, blink))
    252                 write_int(BLUE_LED_FILE, 0);
    253     }
    254     } else {
    255         write_int(RED_LED_FILE, red);
    256         write_int(GREEN_LED_FILE, green);
    257         write_int(BLUE_LED_FILE, blue);
    258     }
    259 
    260     return 0;
    261 }
    262 
    263 static void
    264 handle_speaker_battery_locked(struct light_device_t* dev)
    265 {
    266     if (is_lit(&g_battery)) {
    267         set_speaker_light_locked(dev, &g_battery);
    268     } else {
    269         set_speaker_light_locked(dev, &g_notification);
    270     }
    271 }
    272 
    273 static int
    274 set_light_battery(struct light_device_t* dev,
    275         struct light_state_t const* state)
    276 {
    277     pthread_mutex_lock(&g_lock);
    278     g_battery = *state;
    279     handle_speaker_battery_locked(dev);
    280     pthread_mutex_unlock(&g_lock);
    281     return 0;
    282 }
    283 
    284 static int
    285 set_light_notifications(struct light_device_t* dev,
    286         struct light_state_t const* state)
    287 {
    288     pthread_mutex_lock(&g_lock);
    289     g_notification = *state;
    290     handle_speaker_battery_locked(dev);
    291     pthread_mutex_unlock(&g_lock);
    292     return 0;
    293 }
    294 
    295 static int
    296 set_light_attention(struct light_device_t* dev,
    297         struct light_state_t const* state)
    298 {
    299     pthread_mutex_lock(&g_lock);
    300     if (state->flashMode == LIGHT_FLASH_HARDWARE) {
    301         g_attention = state->flashOnMS;
    302     } else if (state->flashMode == LIGHT_FLASH_NONE) {
    303         g_attention = 0;
    304     }
    305     handle_speaker_battery_locked(dev);
    306     pthread_mutex_unlock(&g_lock);
    307     return 0;
    308 }
    309 
    310 static int
    311 set_light_buttons(struct light_device_t* dev,
    312         struct light_state_t const* state)
    313 {
    314     int err = 0;
    315     if(!dev) {
    316         return -1;
    317     }
    318     pthread_mutex_lock(&g_lock);
    319     err = write_int(BUTTON_FILE, state->color & 0xFF);
    320     pthread_mutex_unlock(&g_lock);
    321     return err;
    322 }
    323 
    324 /** Close the lights device */
    325 static int
    326 close_lights(struct light_device_t *dev)
    327 {
    328     if (dev) {
    329         free(dev);
    330     }
    331     return 0;
    332 }
    333 
    334 
    335 /******************************************************************************/
    336 
    337 /**
    338  * module methods
    339  */
    340 
    341 /** Open a new instance of a lights device using name */
    342 static int open_lights(const struct hw_module_t* module, char const* name,
    343         struct hw_device_t** device)
    344 {
    345     int (*set_light)(struct light_device_t* dev,
    346             struct light_state_t const* state);
    347 
    348     if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
    349         char property[PROPERTY_VALUE_MAX];
    350         property_get("persist.extend.brightness", property, "0");
    351 
    352         if(!(strncmp(property, "1", PROPERTY_VALUE_MAX)) ||
    353            !(strncmp(property, "true", PROPERTY_VALUE_MAX))) {
    354             property_get("persist.display.max_brightness", property, "255");
    355             g_brightness_max = atoi(property);
    356             set_brightness_ext_init();
    357             set_light = set_light_backlight_ext;
    358         } else
    359             set_light = set_light_backlight;
    360     } else if (0 == strcmp(LIGHT_ID_BATTERY, name))
    361         set_light = set_light_battery;
    362     else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
    363         set_light = set_light_notifications;
    364     else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
    365         if (!access(BUTTON_FILE, F_OK)) {
    366           // enable light button when the file is present
    367           set_light = set_light_buttons;
    368         } else {
    369           return -EINVAL;
    370         }
    371     }
    372     else if (0 == strcmp(LIGHT_ID_ATTENTION, name))
    373         set_light = set_light_attention;
    374     else
    375         return -EINVAL;
    376 
    377     pthread_once(&g_init, init_globals);
    378 
    379     struct light_device_t *dev = malloc(sizeof(struct light_device_t));
    380 
    381     if(!dev)
    382         return -ENOMEM;
    383 
    384     memset(dev, 0, sizeof(*dev));
    385 
    386     dev->common.tag = HARDWARE_DEVICE_TAG;
    387     dev->common.version = LIGHTS_DEVICE_API_VERSION_2_0;
    388     dev->common.module = (struct hw_module_t*)module;
    389     dev->common.close = (int (*)(struct hw_device_t*))close_lights;
    390     dev->set_light = set_light;
    391 
    392     *device = (struct hw_device_t*)dev;
    393     return 0;
    394 }
    395 
    396 static struct hw_module_methods_t lights_module_methods = {
    397     .open =  open_lights,
    398 };
    399 
    400 /*
    401  * The lights Module
    402  */
    403 struct hw_module_t HAL_MODULE_INFO_SYM = {
    404     .tag = HARDWARE_MODULE_TAG,
    405     .version_major = 1,
    406     .version_minor = 0,
    407     .id = LIGHTS_HARDWARE_MODULE_ID,
    408     .name = "lights Module",
    409     .author = "Google, Inc.",
    410     .methods = &lights_module_methods,
    411 };
    412