Home | History | Annotate | Download | only in liblight
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 
     18 // #define LOG_NDEBUG 0
     19 
     20 #include <cutils/log.h>
     21 
     22 #include <stdint.h>
     23 #include <string.h>
     24 #include <unistd.h>
     25 #include <errno.h>
     26 #include <fcntl.h>
     27 #include <pthread.h>
     28 
     29 #include <sys/ioctl.h>
     30 #include <sys/types.h>
     31 
     32 #include <hardware/lights.h>
     33 
     34 /******************************************************************************/
     35 
     36 #define MAX_PATH_SIZE 80
     37 
     38 static pthread_once_t g_init = PTHREAD_ONCE_INIT;
     39 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
     40 static struct light_state_t g_notification;
     41 static struct light_state_t g_battery;
     42 static int g_attention = 0;
     43 
     44 char const*const RED_LED_FILE
     45         = "/sys/class/leds/red/brightness";
     46 
     47 char const*const GREEN_LED_FILE
     48         = "/sys/class/leds/green/brightness";
     49 
     50 char const*const BLUE_LED_FILE
     51         = "/sys/class/leds/blue/brightness";
     52 
     53 char const*const WHITE_LED_FILE
     54         = "/sys/class/leds/white/brightness";
     55 
     56 char const*const LCD_FILE
     57         = "/sys/class/leds/lcd-backlight/brightness";
     58 
     59 char const*const LED_FREQ_FILE
     60         = "/sys/class/leds/%s/device/grpfreq";
     61 
     62 char const*const LED_PWM_FILE
     63         = "/sys/class/leds/%s/device/grppwm";
     64 
     65 char const*const LED_BLINK_FILE
     66         = "/sys/class/leds/%s/device/blink";
     67 
     68 char const*const LED_LOCK_UPDATE_FILE
     69         = "/sys/class/leds/%s/device/lock";
     70 
     71 /**
     72  * device methods
     73  */
     74 
     75 void init_globals(void)
     76 {
     77     // init the mutex
     78     pthread_mutex_init(&g_lock, NULL);
     79 }
     80 
     81 static int
     82 write_int(char const* path, int value)
     83 {
     84     int fd;
     85     static int already_warned = 0;
     86 
     87     fd = open(path, O_RDWR);
     88     if (fd >= 0) {
     89         char buffer[20];
     90         int bytes = sprintf(buffer, "%d\n", value);
     91         int amt = write(fd, buffer, bytes);
     92         close(fd);
     93         return amt == -1 ? -errno : 0;
     94     } else {
     95         if (already_warned == 0) {
     96             ALOGE("write_int failed to open %s\n", path);
     97             already_warned = 1;
     98         }
     99         return -errno;
    100     }
    101 }
    102 
    103 static int
    104 is_avail(char const* path)
    105 {
    106     int fd = open(path, O_RDWR);
    107     if (fd >= 0) {
    108         close(fd);
    109         return 1;
    110     } else {
    111         return 0;
    112     }
    113 }
    114 
    115 static int
    116 is_lit(struct light_state_t const* state)
    117 {
    118     return state->color & 0x00ffffff;
    119 }
    120 
    121 static int
    122 rgb_to_brightness(struct light_state_t const* state)
    123 {
    124     int color = state->color & 0x00ffffff;
    125     return ((77*((color>>16)&0x00ff))
    126             + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
    127 }
    128 
    129 static int
    130 set_light_backlight(struct light_device_t* dev,
    131         struct light_state_t const* state)
    132 {
    133     int err = 0;
    134     int brightness = rgb_to_brightness(state);
    135     pthread_mutex_lock(&g_lock);
    136     err = write_int(LCD_FILE, brightness);
    137     pthread_mutex_unlock(&g_lock);
    138     return err;
    139 }
    140 
    141 static int
    142 set_speaker_light_locked(struct light_device_t* dev,
    143         struct light_state_t const* state)
    144 {
    145     int len;
    146     int alpha, rgb;
    147     int blink, freq, pwm;
    148     int onMS, offMS;
    149     unsigned int colorRGB;
    150 
    151     switch (state->flashMode) {
    152         case LIGHT_FLASH_TIMED:
    153             onMS = state->flashOnMS;
    154             offMS = state->flashOffMS;
    155             break;
    156         case LIGHT_FLASH_NONE:
    157         default:
    158             onMS = 0;
    159             offMS = 0;
    160             break;
    161     }
    162 
    163     colorRGB = state->color;
    164 
    165 #if 0
    166     ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n",
    167             state->flashMode, colorRGB, onMS, offMS);
    168 #endif
    169 
    170     if (onMS > 0 && offMS > 0) {
    171         int totalMS = onMS + offMS;
    172 
    173         // the LED appears to blink about once per second if freq is 20
    174         // 1000ms / 20 = 50
    175         freq = totalMS / 50;
    176         // pwm specifies the ratio of ON versus OFF
    177         // pwm = 0 -> always off
    178         // pwm = 255 => always on
    179         pwm = (onMS * 255) / totalMS;
    180 
    181         // the low 4 bits are ignored, so round up if necessary
    182         if (pwm > 0 && pwm < 16)
    183             pwm = 16;
    184 
    185         blink = 1;
    186     } else {
    187         blink = 0;
    188         freq = 0;
    189         pwm = 0;
    190     }
    191 
    192     // Prefer RGB LEDs, fallback to white LED
    193     rgb = is_avail(RED_LED_FILE) && is_avail(GREEN_LED_FILE) && is_avail(BLUE_LED_FILE);
    194 
    195     char lock_update_file[MAX_PATH_SIZE];
    196     char freq_file[MAX_PATH_SIZE];
    197     char pwm_file[MAX_PATH_SIZE];
    198     char blink_file[MAX_PATH_SIZE];
    199     sprintf(lock_update_file, LED_LOCK_UPDATE_FILE, rgb ? "red" : "white");
    200     sprintf(freq_file, LED_FREQ_FILE, rgb ? "red" : "white");
    201     sprintf(pwm_file, LED_PWM_FILE, rgb ? "red" : "white");
    202     sprintf(blink_file, LED_BLINK_FILE, rgb ? "red" : "white");
    203 
    204     write_int(lock_update_file, 1); // for LED On/Off synchronization
    205 
    206     if (rgb) {
    207         write_int(RED_LED_FILE, (colorRGB >> 16) & 0xFF);
    208         write_int(GREEN_LED_FILE, (colorRGB >> 8) & 0xFF);
    209         write_int(BLUE_LED_FILE, colorRGB & 0xFF);
    210     } else {
    211         // See hardware/libhardware/include/hardware/lights.h
    212         int brightness = ((77 * ((colorRGB >> 16) & 0xFF)) +
    213                           (150 * ((colorRGB >> 8) & 0xFF)) +
    214                           (29 * (colorRGB & 0xFF))) >> 8;
    215         write_int(WHITE_LED_FILE, (int) brightness);
    216     }
    217 
    218     if (blink) {
    219         write_int(freq_file, freq);
    220         write_int(pwm_file, pwm);
    221     }
    222     write_int(blink_file, blink);
    223 
    224     write_int(lock_update_file, 0);
    225 
    226     return 0;
    227 }
    228 
    229 static void
    230 handle_speaker_battery_locked(struct light_device_t* dev)
    231 {
    232     if (is_lit(&g_battery)) {
    233         set_speaker_light_locked(dev, &g_battery);
    234     } else {
    235         set_speaker_light_locked(dev, &g_notification);
    236     }
    237 }
    238 
    239 static int
    240 set_light_notifications(struct light_device_t* dev,
    241         struct light_state_t const* state)
    242 {
    243     pthread_mutex_lock(&g_lock);
    244     g_notification = *state;
    245     handle_speaker_battery_locked(dev);
    246     pthread_mutex_unlock(&g_lock);
    247     return 0;
    248 }
    249 
    250 static int
    251 set_light_attention(struct light_device_t* dev,
    252         struct light_state_t const* state)
    253 {
    254     pthread_mutex_lock(&g_lock);
    255     if (state->flashMode == LIGHT_FLASH_HARDWARE) {
    256         g_attention = state->flashOnMS;
    257     } else if (state->flashMode == LIGHT_FLASH_NONE) {
    258         g_attention = 0;
    259     }
    260     handle_speaker_battery_locked(dev);
    261     pthread_mutex_unlock(&g_lock);
    262     return 0;
    263 }
    264 
    265 
    266 /** Close the lights device */
    267 static int
    268 close_lights(struct light_device_t *dev)
    269 {
    270     if (dev) {
    271         free(dev);
    272     }
    273     return 0;
    274 }
    275 
    276 
    277 /******************************************************************************/
    278 
    279 /**
    280  * module methods
    281  */
    282 
    283 /** Open a new instance of a lights device using name */
    284 static int open_lights(const struct hw_module_t* module, char const* name,
    285         struct hw_device_t** device)
    286 {
    287     int (*set_light)(struct light_device_t* dev,
    288             struct light_state_t const* state);
    289 
    290     if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))
    291         set_light = set_light_backlight;
    292     else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
    293         set_light = set_light_notifications;
    294     else if (0 == strcmp(LIGHT_ID_ATTENTION, name))
    295         set_light = set_light_attention;
    296     else
    297         return -EINVAL;
    298 
    299     pthread_once(&g_init, init_globals);
    300 
    301     struct light_device_t *dev = malloc(sizeof(struct light_device_t));
    302     memset(dev, 0, sizeof(*dev));
    303 
    304     dev->common.tag = HARDWARE_DEVICE_TAG;
    305     dev->common.version = 0;
    306     dev->common.module = (struct hw_module_t*)module;
    307     dev->common.close = (int (*)(struct hw_device_t*))close_lights;
    308     dev->set_light = set_light;
    309 
    310     *device = (struct hw_device_t*)dev;
    311     return 0;
    312 }
    313 
    314 static struct hw_module_methods_t lights_module_methods = {
    315     .open =  open_lights,
    316 };
    317 
    318 /*
    319  * The lights Module
    320  */
    321 struct hw_module_t HAL_MODULE_INFO_SYM = {
    322     .tag = HARDWARE_MODULE_TAG,
    323     .version_major = 1,
    324     .version_minor = 0,
    325     .id = LIGHTS_HARDWARE_MODULE_ID,
    326     .name = "lights Module",
    327     .author = "Google, Inc.",
    328     .methods = &lights_module_methods,
    329 };
    330