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 <malloc.h>
     28 #include <pthread.h>
     29 
     30 #include <sys/ioctl.h>
     31 #include <sys/types.h>
     32 
     33 #include <hardware/lights.h>
     34 
     35 /******************************************************************************/
     36 
     37 #define MAX_PATH_SIZE 80
     38 
     39 static pthread_once_t g_init = PTHREAD_ONCE_INIT;
     40 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
     41 static struct light_state_t g_notification;
     42 static struct light_state_t g_battery;
     43 static int g_attention = 0;
     44 
     45 char const*const RED_LED_FILE
     46         = "/sys/class/leds/red/brightness";
     47 
     48 char const*const GREEN_LED_FILE
     49         = "/sys/class/leds/green/brightness";
     50 
     51 char const*const BLUE_LED_FILE
     52         = "/sys/class/leds/blue/brightness";
     53 
     54 char const*const WHITE_LED_FILE
     55         = "/sys/class/leds/white/brightness";
     56 
     57 char const*const LCD_FILE
     58         = "/sys/class/leds/lcd-backlight/brightness";
     59 
     60 char const*const LED_FREQ_FILE
     61         = "/sys/class/leds/%s/device/grpfreq";
     62 
     63 char const*const LED_PWM_FILE
     64         = "/sys/class/leds/%s/device/grppwm";
     65 
     66 char const*const LED_BLINK_FILE
     67         = "/sys/class/leds/%s/device/blink";
     68 
     69 char const*const LED_LOCK_UPDATE_FILE
     70         = "/sys/class/leds/%s/device/lock";
     71 
     72 /**
     73  * device methods
     74  */
     75 
     76 void init_globals(void)
     77 {
     78     // init the mutex
     79     pthread_mutex_init(&g_lock, NULL);
     80 }
     81 
     82 static int
     83 write_int(char const* path, int value)
     84 {
     85     int fd;
     86     static int already_warned = 0;
     87 
     88     fd = open(path, O_RDWR);
     89     if (fd >= 0) {
     90         char buffer[20];
     91         int bytes = sprintf(buffer, "%d\n", value);
     92         int amt = write(fd, buffer, bytes);
     93         close(fd);
     94         return amt == -1 ? -errno : 0;
     95     } else {
     96         if (already_warned == 0) {
     97             ALOGE("write_int failed to open %s\n", path);
     98             already_warned = 1;
     99         }
    100         return -errno;
    101     }
    102 }
    103 
    104 static int
    105 is_avail(char const* path)
    106 {
    107     int fd = open(path, O_RDWR);
    108     if (fd >= 0) {
    109         close(fd);
    110         return 1;
    111     } else {
    112         return 0;
    113     }
    114 }
    115 
    116 static int
    117 is_lit(struct light_state_t const* state)
    118 {
    119     return state->color & 0x00ffffff;
    120 }
    121 
    122 static int
    123 rgb_to_brightness(struct light_state_t const* state)
    124 {
    125     int color = state->color & 0x00ffffff;
    126     return ((77*((color>>16)&0x00ff))
    127             + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
    128 }
    129 
    130 static int
    131 set_light_backlight(struct light_device_t* dev,
    132         struct light_state_t const* state)
    133 {
    134     int err = 0;
    135     int brightness = rgb_to_brightness(state);
    136     pthread_mutex_lock(&g_lock);
    137     err = write_int(LCD_FILE, brightness);
    138     pthread_mutex_unlock(&g_lock);
    139     return err;
    140 }
    141 
    142 static int
    143 set_speaker_light_locked(struct light_device_t* dev,
    144         struct light_state_t const* state)
    145 {
    146     int 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