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 <cutils/log.h>
     23 
     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 
     37 #ifndef DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS
     38 #define DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS 0x80
     39 #endif
     40 
     41 /******************************************************************************/
     42 
     43 static pthread_once_t g_init = PTHREAD_ONCE_INIT;
     44 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
     45 static struct light_state_t g_notification;
     46 static struct light_state_t g_battery;
     47 static int g_last_backlight_mode = BRIGHTNESS_MODE_USER;
     48 static int g_attention = 0;
     49 
     50 char const*const RED_LED_FILE
     51         = "/sys/class/leds/red/brightness";
     52 
     53 char const*const GREEN_LED_FILE
     54         = "/sys/class/leds/green/brightness";
     55 
     56 char const*const BLUE_LED_FILE
     57         = "/sys/class/leds/blue/brightness";
     58 
     59 char const*const LCD_FILE
     60         = "/sys/class/leds/lcd-backlight/brightness";
     61 
     62 char const*const LCD_FILE2
     63         = "/sys/class/backlight/panel0-backlight/brightness";
     64 
     65 char const*const BUTTON_FILE
     66         = "/sys/class/leds/button-backlight/brightness";
     67 
     68 char const*const RED_BLINK_FILE
     69         = "/sys/class/leds/red/blink";
     70 
     71 char const*const GREEN_BLINK_FILE
     72         = "/sys/class/leds/green/blink";
     73 
     74 char const*const BLUE_BLINK_FILE
     75         = "/sys/class/leds/blue/blink";
     76 
     77 char const*const PERSISTENCE_FILE
     78         = "/sys/class/graphics/fb0/msm_fb_persist_mode";
     79 
     80 /**
     81  * device methods
     82  */
     83 
     84 void init_globals(void)
     85 {
     86     // init the mutex
     87     pthread_mutex_init(&g_lock, NULL);
     88 }
     89 
     90 static int
     91 write_int(char const* path, int value)
     92 {
     93     int fd;
     94     static int already_warned = 0;
     95 
     96     fd = open(path, O_RDWR);
     97     if (fd >= 0) {
     98         char buffer[20];
     99         int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
    100         ssize_t amt = write(fd, buffer, (size_t)bytes);
    101         close(fd);
    102         return amt == -1 ? -errno : 0;
    103     } else {
    104         if (already_warned == 0) {
    105             ALOGE("write_int failed to open %s\n", path);
    106             already_warned = 1;
    107         }
    108         return -errno;
    109     }
    110 }
    111 
    112 static int
    113 is_lit(struct light_state_t const* state)
    114 {
    115     return state->color & 0x00ffffff;
    116 }
    117 
    118 static int
    119 rgb_to_brightness(struct light_state_t const* state)
    120 {
    121     int color = state->color & 0x00ffffff;
    122     return ((77*((color>>16)&0x00ff))
    123             + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
    124 }
    125 
    126 static int
    127 set_light_backlight(struct light_device_t* dev,
    128         struct light_state_t const* state)
    129 {
    130     int err = 0;
    131     int brightness = rgb_to_brightness(state);
    132     unsigned int lpEnabled =
    133         state->brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE;
    134     if(!dev) {
    135         return -1;
    136     }
    137 
    138     pthread_mutex_lock(&g_lock);
    139     // Toggle low persistence mode state
    140     if ((g_last_backlight_mode != state->brightnessMode && lpEnabled) ||
    141         (!lpEnabled &&
    142          g_last_backlight_mode == BRIGHTNESS_MODE_LOW_PERSISTENCE)) {
    143         if ((err = write_int(PERSISTENCE_FILE, lpEnabled)) != 0) {
    144             ALOGE("%s: Failed to write to %s: %s\n", __FUNCTION__,
    145                    PERSISTENCE_FILE, strerror(errno));
    146         }
    147         if (lpEnabled != 0) {
    148             brightness = DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS;
    149         }
    150     }
    151 
    152     g_last_backlight_mode = state->brightnessMode;
    153 
    154     if (!err) {
    155         if (!access(LCD_FILE, F_OK)) {
    156             err = write_int(LCD_FILE, brightness);
    157         } else {
    158             err = write_int(LCD_FILE2, brightness);
    159         }
    160     }
    161 
    162     pthread_mutex_unlock(&g_lock);
    163     return err;
    164 }
    165 
    166 static int
    167 set_speaker_light_locked(struct light_device_t* dev,
    168         struct light_state_t const* state)
    169 {
    170     int red, green, blue;
    171     int blink;
    172     int onMS, offMS;
    173     unsigned int colorRGB;
    174 
    175     if(!dev) {
    176         return -1;
    177     }
    178 
    179     switch (state->flashMode) {
    180         case LIGHT_FLASH_TIMED:
    181             onMS = state->flashOnMS;
    182             offMS = state->flashOffMS;
    183             break;
    184         case LIGHT_FLASH_NONE:
    185         default:
    186             onMS = 0;
    187             offMS = 0;
    188             break;
    189     }
    190 
    191     colorRGB = state->color;
    192 
    193 #if 0
    194     ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n",
    195             state->flashMode, colorRGB, onMS, offMS);
    196 #endif
    197 
    198     red = (colorRGB >> 16) & 0xFF;
    199     green = (colorRGB >> 8) & 0xFF;
    200     blue = colorRGB & 0xFF;
    201 
    202     if (onMS > 0 && offMS > 0) {
    203         /*
    204          * if ON time == OFF time
    205          *   use blink mode 2
    206          * else
    207          *   use blink mode 1
    208          */
    209         if (onMS == offMS)
    210             blink = 2;
    211         else
    212             blink = 1;
    213     } else {
    214         blink = 0;
    215     }
    216 
    217     if (blink) {
    218         if (red) {
    219             if (write_int(RED_BLINK_FILE, blink))
    220                 write_int(RED_LED_FILE, 0);
    221 	}
    222         if (green) {
    223             if (write_int(GREEN_BLINK_FILE, blink))
    224                 write_int(GREEN_LED_FILE, 0);
    225 	}
    226         if (blue) {
    227             if (write_int(BLUE_BLINK_FILE, blink))
    228                 write_int(BLUE_LED_FILE, 0);
    229 	}
    230     } else {
    231         write_int(RED_LED_FILE, red);
    232         write_int(GREEN_LED_FILE, green);
    233         write_int(BLUE_LED_FILE, blue);
    234     }
    235 
    236     return 0;
    237 }
    238 
    239 static void
    240 handle_speaker_battery_locked(struct light_device_t* dev)
    241 {
    242     if (is_lit(&g_battery)) {
    243         set_speaker_light_locked(dev, &g_battery);
    244     } else {
    245         set_speaker_light_locked(dev, &g_notification);
    246     }
    247 }
    248 
    249 static int
    250 set_light_battery(struct light_device_t* dev,
    251         struct light_state_t const* state)
    252 {
    253     pthread_mutex_lock(&g_lock);
    254     g_battery = *state;
    255     handle_speaker_battery_locked(dev);
    256     pthread_mutex_unlock(&g_lock);
    257     return 0;
    258 }
    259 
    260 static int
    261 set_light_notifications(struct light_device_t* dev,
    262         struct light_state_t const* state)
    263 {
    264     pthread_mutex_lock(&g_lock);
    265     g_notification = *state;
    266     handle_speaker_battery_locked(dev);
    267     pthread_mutex_unlock(&g_lock);
    268     return 0;
    269 }
    270 
    271 static int
    272 set_light_attention(struct light_device_t* dev,
    273         struct light_state_t const* state)
    274 {
    275     pthread_mutex_lock(&g_lock);
    276     if (state->flashMode == LIGHT_FLASH_HARDWARE) {
    277         g_attention = state->flashOnMS;
    278     } else if (state->flashMode == LIGHT_FLASH_NONE) {
    279         g_attention = 0;
    280     }
    281     handle_speaker_battery_locked(dev);
    282     pthread_mutex_unlock(&g_lock);
    283     return 0;
    284 }
    285 
    286 static int
    287 set_light_buttons(struct light_device_t* dev,
    288         struct light_state_t const* state)
    289 {
    290     int err = 0;
    291     if(!dev) {
    292         return -1;
    293     }
    294     pthread_mutex_lock(&g_lock);
    295     err = write_int(BUTTON_FILE, state->color & 0xFF);
    296     pthread_mutex_unlock(&g_lock);
    297     return err;
    298 }
    299 
    300 /** Close the lights device */
    301 static int
    302 close_lights(struct light_device_t *dev)
    303 {
    304     if (dev) {
    305         free(dev);
    306     }
    307     return 0;
    308 }
    309 
    310 
    311 /******************************************************************************/
    312 
    313 /**
    314  * module methods
    315  */
    316 
    317 /** Open a new instance of a lights device using name */
    318 static int open_lights(const struct hw_module_t* module, char const* name,
    319         struct hw_device_t** device)
    320 {
    321     int (*set_light)(struct light_device_t* dev,
    322             struct light_state_t const* state);
    323 
    324     if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))
    325         set_light = set_light_backlight;
    326     else if (0 == strcmp(LIGHT_ID_BATTERY, name))
    327         set_light = set_light_battery;
    328     else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
    329         set_light = set_light_notifications;
    330     else if (0 == strcmp(LIGHT_ID_BUTTONS, name))
    331         set_light = set_light_buttons;
    332     else if (0 == strcmp(LIGHT_ID_ATTENTION, name))
    333         set_light = set_light_attention;
    334     else
    335         return -EINVAL;
    336 
    337     pthread_once(&g_init, init_globals);
    338 
    339     struct light_device_t *dev = malloc(sizeof(struct light_device_t));
    340 
    341     if(!dev)
    342         return -ENOMEM;
    343 
    344     memset(dev, 0, sizeof(*dev));
    345 
    346     dev->common.tag = HARDWARE_DEVICE_TAG;
    347     dev->common.version = LIGHTS_DEVICE_API_VERSION_2_0;
    348     dev->common.module = (struct hw_module_t*)module;
    349     dev->common.close = (int (*)(struct hw_device_t*))close_lights;
    350     dev->set_light = set_light;
    351 
    352     *device = (struct hw_device_t*)dev;
    353     return 0;
    354 }
    355 
    356 static struct hw_module_methods_t lights_module_methods = {
    357     .open =  open_lights,
    358 };
    359 
    360 /*
    361  * The lights Module
    362  */
    363 struct hw_module_t HAL_MODULE_INFO_SYM = {
    364     .tag = HARDWARE_MODULE_TAG,
    365     .version_major = 1,
    366     .version_minor = 0,
    367     .id = LIGHTS_HARDWARE_MODULE_ID,
    368     .name = "lights Module",
    369     .author = "Google, Inc.",
    370     .methods = &lights_module_methods,
    371 };
    372