Home | History | Annotate | Download | only in liblights
      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 #define LOG_TAG "lights"
     20 
     21 #include <cutils/log.h>
     22 
     23 #include <stdint.h>
     24 #include <string.h>
     25 #include <unistd.h>
     26 #include <errno.h>
     27 #include <fcntl.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 static pthread_once_t g_init = PTHREAD_ONCE_INIT;
     38 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
     39 static int g_haveTrackballLight = 0;
     40 static struct light_state_t g_notification;
     41 static struct light_state_t g_battery;
     42 static int g_backlight = 255;
     43 static int g_trackball = -1;
     44 static int g_buttons = 0;
     45 static int g_attention = 0;
     46 static int g_haveAmberLed = 0;
     47 
     48 char const*const TRACKBALL_FILE
     49         = "/sys/class/leds/jogball-backlight/brightness";
     50 
     51 char const*const RED_LED_FILE
     52         = "/sys/class/leds/red/brightness";
     53 
     54 char const*const GREEN_LED_FILE
     55         = "/sys/class/leds/green/brightness";
     56 
     57 char const*const BLUE_LED_FILE
     58         = "/sys/class/leds/blue/brightness";
     59 
     60 char const*const AMBER_LED_FILE
     61         = "/sys/class/leds/amber/brightness";
     62 
     63 char const*const LCD_FILE
     64         = "/sys/class/leds/lcd-backlight/brightness";
     65 
     66 char const*const RED_FREQ_FILE
     67         = "/sys/class/leds/red/device/grpfreq";
     68 
     69 char const*const RED_PWM_FILE
     70         = "/sys/class/leds/red/device/grppwm";
     71 
     72 char const*const RED_BLINK_FILE
     73         = "/sys/class/leds/red/device/blink";
     74 
     75 char const*const AMBER_BLINK_FILE
     76         = "/sys/class/leds/amber/blink";
     77 
     78 char const*const KEYBOARD_FILE
     79         = "/sys/class/leds/keyboard-backlight/brightness";
     80 
     81 char const*const BUTTON_FILE
     82         = "/sys/class/leds/button-backlight/brightness";
     83 
     84 /**
     85  * device methods
     86  */
     87 
     88 void init_globals(void)
     89 {
     90     // init the mutex
     91     pthread_mutex_init(&g_lock, NULL);
     92 
     93     // figure out if we have the trackball LED or not
     94     g_haveTrackballLight = (access(TRACKBALL_FILE, W_OK) == 0) ? 1 : 0;
     95 
     96     /* figure out if we have the amber LED or not.
     97        If yes, just support green and amber.         */
     98     g_haveAmberLed = (access(AMBER_LED_FILE, W_OK) == 0) ? 1 : 0;
     99 }
    100 
    101 static int
    102 write_int(char const* path, int value)
    103 {
    104     int fd;
    105     static int already_warned = 0;
    106 
    107     fd = open(path, O_RDWR);
    108     if (fd >= 0) {
    109         char buffer[20];
    110         int bytes = sprintf(buffer, "%d\n", value);
    111         int amt = write(fd, buffer, bytes);
    112         close(fd);
    113         return amt == -1 ? -errno : 0;
    114     } else {
    115         if (already_warned == 0) {
    116             ALOGE("write_int failed to open %s\n", path);
    117             already_warned = 1;
    118         }
    119         return -errno;
    120     }
    121 }
    122 
    123 static int
    124 is_lit(struct light_state_t const* state)
    125 {
    126     return state->color & 0x00ffffff;
    127 }
    128 
    129 static int
    130 handle_trackball_light_locked(struct light_device_t* dev)
    131 {
    132     int mode = g_attention;
    133 
    134     if (mode == 7 && g_backlight) {
    135         mode = 0;
    136     }
    137     ALOGV("%s g_backlight = %d, mode = %d, g_attention = %d\n",
    138         __func__, g_backlight, mode, g_attention);
    139 
    140     // If the value isn't changing, don't set it, because this
    141     // can reset the timer on the breathing mode, which looks bad.
    142     if (g_trackball == mode) {
    143         return 0;
    144     }
    145 
    146     return write_int(TRACKBALL_FILE, mode);
    147 }
    148 
    149 static int
    150 rgb_to_brightness(struct light_state_t const* state)
    151 {
    152     int color = state->color & 0x00ffffff;
    153     return ((77*((color>>16)&0x00ff))
    154             + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
    155 }
    156 
    157 static int
    158 set_light_backlight(struct light_device_t* dev,
    159         struct light_state_t const* state)
    160 {
    161     int err = 0;
    162     int brightness = rgb_to_brightness(state);
    163     pthread_mutex_lock(&g_lock);
    164     g_backlight = brightness;
    165     err = write_int(LCD_FILE, brightness);
    166     if (g_haveTrackballLight) {
    167         handle_trackball_light_locked(dev);
    168     }
    169     pthread_mutex_unlock(&g_lock);
    170     return err;
    171 }
    172 
    173 static int
    174 set_light_keyboard(struct light_device_t* dev,
    175         struct light_state_t const* state)
    176 {
    177     int err = 0;
    178     int on = is_lit(state);
    179     pthread_mutex_lock(&g_lock);
    180     err = write_int(KEYBOARD_FILE, on?255:0);
    181     pthread_mutex_unlock(&g_lock);
    182     return err;
    183 }
    184 
    185 static int
    186 set_light_buttons(struct light_device_t* dev,
    187         struct light_state_t const* state)
    188 {
    189     int err = 0;
    190     int on = is_lit(state);
    191     pthread_mutex_lock(&g_lock);
    192     g_buttons = on;
    193     err = write_int(BUTTON_FILE, on?255:0);
    194     pthread_mutex_unlock(&g_lock);
    195     return err;
    196 }
    197 
    198 static int
    199 set_speaker_light_locked(struct light_device_t* dev,
    200         struct light_state_t const* state)
    201 {
    202     int len;
    203     int alpha, red, green, blue;
    204     int blink, freq, pwm;
    205     int onMS, offMS;
    206     unsigned int colorRGB;
    207 
    208     switch (state->flashMode) {
    209         case LIGHT_FLASH_TIMED:
    210             onMS = state->flashOnMS;
    211             offMS = state->flashOffMS;
    212             break;
    213         case LIGHT_FLASH_NONE:
    214         default:
    215             onMS = 0;
    216             offMS = 0;
    217             break;
    218     }
    219 
    220     colorRGB = state->color;
    221 
    222 #if 0
    223     ALOGD("set_speaker_light_locked colorRGB=%08X, onMS=%d, offMS=%d\n",
    224             colorRGB, onMS, offMS);
    225 #endif
    226 
    227     red = (colorRGB >> 16) & 0xFF;
    228     green = (colorRGB >> 8) & 0xFF;
    229     blue = colorRGB & 0xFF;
    230 
    231     if (!g_haveAmberLed) {
    232         write_int(RED_LED_FILE, red);
    233         write_int(GREEN_LED_FILE, green);
    234         write_int(BLUE_LED_FILE, blue);
    235     } else {
    236         /* all of related red led is replaced by amber */
    237         if (red) {
    238             write_int(AMBER_LED_FILE, 1);
    239             write_int(GREEN_LED_FILE, 0);
    240         } else if (green) {
    241             write_int(AMBER_LED_FILE, 0);
    242             write_int(GREEN_LED_FILE, 1);
    243         } else {
    244             write_int(GREEN_LED_FILE, 0);
    245             write_int(AMBER_LED_FILE, 0);
    246         }
    247     }
    248 
    249     if (onMS > 0 && offMS > 0) {
    250         int totalMS = onMS + offMS;
    251 
    252         // the LED appears to blink about once per second if freq is 20
    253         // 1000ms / 20 = 50
    254         freq = totalMS / 50;
    255         // pwm specifies the ratio of ON versus OFF
    256         // pwm = 0 -> always off
    257         // pwm = 255 => always on
    258         pwm = (onMS * 255) / totalMS;
    259 
    260         // the low 4 bits are ignored, so round up if necessary
    261         if (pwm > 0 && pwm < 16)
    262             pwm = 16;
    263 
    264         blink = 1;
    265     } else {
    266         blink = 0;
    267         freq = 0;
    268         pwm = 0;
    269     }
    270 
    271     if (!g_haveAmberLed) {
    272         if (blink) {
    273             write_int(RED_FREQ_FILE, freq);
    274             write_int(RED_PWM_FILE, pwm);
    275         }
    276         write_int(RED_BLINK_FILE, blink);
    277     } else {
    278         write_int(AMBER_BLINK_FILE, blink);
    279     }
    280 
    281     return 0;
    282 }
    283 
    284 static void
    285 handle_speaker_battery_locked(struct light_device_t* dev)
    286 {
    287     if (is_lit(&g_battery)) {
    288         set_speaker_light_locked(dev, &g_battery);
    289     } else {
    290         set_speaker_light_locked(dev, &g_notification);
    291     }
    292 }
    293 
    294 static int
    295 set_light_battery(struct light_device_t* dev,
    296         struct light_state_t const* state)
    297 {
    298     pthread_mutex_lock(&g_lock);
    299     g_battery = *state;
    300     if (g_haveTrackballLight) {
    301         set_speaker_light_locked(dev, state);
    302     }
    303     handle_speaker_battery_locked(dev);
    304     pthread_mutex_unlock(&g_lock);
    305     return 0;
    306 }
    307 
    308 static int
    309 set_light_notifications(struct light_device_t* dev,
    310         struct light_state_t const* state)
    311 {
    312     pthread_mutex_lock(&g_lock);
    313     g_notification = *state;
    314     ALOGV("set_light_notifications g_trackball=%d color=0x%08x",
    315             g_trackball, state->color);
    316     if (g_haveTrackballLight) {
    317         handle_trackball_light_locked(dev);
    318     }
    319     handle_speaker_battery_locked(dev);
    320     pthread_mutex_unlock(&g_lock);
    321     return 0;
    322 }
    323 
    324 static int
    325 set_light_attention(struct light_device_t* dev,
    326         struct light_state_t const* state)
    327 {
    328     pthread_mutex_lock(&g_lock);
    329     ALOGV("set_light_attention g_trackball=%d color=0x%08x",
    330             g_trackball, state->color);
    331     if (state->flashMode == LIGHT_FLASH_HARDWARE) {
    332         g_attention = state->flashOnMS;
    333     } else if (state->flashMode == LIGHT_FLASH_NONE) {
    334         g_attention = 0;
    335     }
    336     if (g_haveTrackballLight) {
    337         handle_trackball_light_locked(dev);
    338     }
    339     pthread_mutex_unlock(&g_lock);
    340     return 0;
    341 }
    342 
    343 
    344 /** Close the lights device */
    345 static int
    346 close_lights(struct light_device_t *dev)
    347 {
    348     if (dev) {
    349         free(dev);
    350     }
    351     return 0;
    352 }
    353 
    354 
    355 /******************************************************************************/
    356 
    357 /**
    358  * module methods
    359  */
    360 
    361 /** Open a new instance of a lights device using name */
    362 static int open_lights(const struct hw_module_t* module, char const* name,
    363         struct hw_device_t** device)
    364 {
    365     int (*set_light)(struct light_device_t* dev,
    366             struct light_state_t const* state);
    367 
    368     if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
    369         set_light = set_light_backlight;
    370     }
    371     else if (0 == strcmp(LIGHT_ID_KEYBOARD, name)) {
    372         set_light = set_light_keyboard;
    373     }
    374     else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
    375         set_light = set_light_buttons;
    376     }
    377     else if (0 == strcmp(LIGHT_ID_BATTERY, name)) {
    378         set_light = set_light_battery;
    379     }
    380     else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
    381         set_light = set_light_notifications;
    382     }
    383     else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) {
    384         set_light = set_light_attention;
    385     }
    386     else {
    387         return -EINVAL;
    388     }
    389 
    390     pthread_once(&g_init, init_globals);
    391 
    392     struct light_device_t *dev = malloc(sizeof(struct light_device_t));
    393     memset(dev, 0, sizeof(*dev));
    394 
    395     dev->common.tag = HARDWARE_DEVICE_TAG;
    396     dev->common.version = 0;
    397     dev->common.module = (struct hw_module_t*)module;
    398     dev->common.close = (int (*)(struct hw_device_t*))close_lights;
    399     dev->set_light = set_light;
    400 
    401     *device = (struct hw_device_t*)dev;
    402     return 0;
    403 }
    404 
    405 
    406 static struct hw_module_methods_t lights_module_methods = {
    407     .open =  open_lights,
    408 };
    409 
    410 /*
    411  * The lights Module
    412  */
    413 struct hw_module_t HAL_MODULE_INFO_SYM = {
    414     .tag = HARDWARE_MODULE_TAG,
    415     .version_major = 1,
    416     .version_minor = 0,
    417     .id = LIGHTS_HARDWARE_MODULE_ID,
    418     .name = "QCT MSM7K lights Module",
    419     .author = "Google, Inc.",
    420     .methods = &lights_module_methods,
    421 };
    422