Home | History | Annotate | Download | only in liblights
      1 /*
      2  * Copyright (C) 2009 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 #define LOG_TAG "lights"
     18 
     19 #include <cutils/log.h>
     20 
     21 #include <stdint.h>
     22 #include <string.h>
     23 #include <unistd.h>
     24 #include <errno.h>
     25 #include <fcntl.h>
     26 #include <pthread.h>
     27 
     28 #include <sys/ioctl.h>
     29 #include <sys/types.h>
     30 
     31 #include <hardware/lights.h>
     32 
     33 #define LIGHT_ATTENTION	1
     34 #define LIGHT_NOTIFY 	2
     35 
     36 /******************************************************************************/
     37 static struct light_state_t *g_notify;
     38 static struct light_state_t *g_attention;
     39 static pthread_once_t g_init = PTHREAD_ONCE_INIT;
     40 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
     41 static int g_backlight = 255;
     42 static int g_buttons = 0;
     43 struct led_prop {
     44     const char *filename;
     45     int fd;
     46 };
     47 
     48 struct led {
     49     struct led_prop mode;
     50     struct led_prop brightness;
     51     struct led_prop blink;
     52     struct led_prop color;
     53     struct led_prop period;
     54 };
     55 
     56 enum {
     57     JOGBALL_LED,
     58     BUTTONS_LED,
     59     AMBER_LED,
     60     GREEN_LED,
     61     BLUE_LED,
     62     RED_LED,
     63     LCD_BACKLIGHT,
     64     NUM_LEDS,
     65 };
     66 
     67 struct led leds[NUM_LEDS] = {
     68     [JOGBALL_LED] = {
     69         .brightness = { "/sys/class/leds/jogball-backlight/brightness", 0},
     70         .color = { "/sys/class/leds/jogball-backlight/color", 0},
     71         .period = { "/sys/class/leds/jogball-backlight/period", 0},
     72     },
     73     [BUTTONS_LED] = {
     74         .brightness = { "/sys/class/leds/button-backlight/brightness", 0},
     75     },
     76     [RED_LED] = {
     77         .brightness = { "/sys/class/leds/red/brightness", 0},
     78         .blink = { "/sys/class/leds/red/blink", 0},
     79     },
     80     [GREEN_LED] = {
     81         .brightness = { "/sys/class/leds/green/brightness", 0},
     82         .blink = { "/sys/class/leds/green/blink", 0},
     83     },
     84     [BLUE_LED] = {
     85         .brightness = { "/sys/class/leds/blue/brightness", 0},
     86         .blink = { "/sys/class/leds/blue/blink", 0},
     87     },
     88     [AMBER_LED] = {
     89         .brightness = { "/sys/class/leds/amber/brightness", 0},
     90         .blink = { "/sys/class/leds/amber/blink", 0},
     91     },
     92     [LCD_BACKLIGHT] = {
     93         .brightness = { "/sys/class/leds/lcd-backlight/brightness", 0},
     94     },
     95 };
     96 
     97 enum {
     98     RGB_BLACK = 0x000000,
     99     RGB_RED = 0xFF0000,
    100     RGB_AMBER = 0xFFFF00,  /* note this is actually RGB yellow */
    101     RGB_GREEN = 0x00FF00,
    102     RGB_BLUE = 0x0000FF,
    103     RGB_WHITE = 0xFFFFFF,
    104     RGB_PINK = 0xFFC0CB,
    105     RGB_ORANGE = 0xFFA500,
    106     RGB_YELLOW = 0xFFFF00,
    107     RGB_PURPLE = 0x800080,
    108     RGB_LT_BLUE = 0xADD8E6,
    109 };
    110 
    111 /**
    112  * device methods
    113  */
    114 
    115 static int init_prop(struct led_prop *prop)
    116 {
    117     int fd;
    118 
    119     prop->fd = -1;
    120     if (!prop->filename)
    121         return 0;
    122     fd = open(prop->filename, O_RDWR);
    123     if (fd < 0) {
    124         LOGE("init_prop: %s cannot be opened (%s)\n", prop->filename,
    125              strerror(errno));
    126         return -errno;
    127     }
    128 
    129     prop->fd = fd;
    130     return 0;
    131 }
    132 
    133 static void close_prop(struct led_prop *prop)
    134 {
    135     int fd;
    136 
    137     if (prop->fd > 0)
    138         close(prop->fd);
    139     return;
    140 }
    141 
    142 void init_globals(void)
    143 {
    144     int i;
    145     pthread_mutex_init(&g_lock, NULL);
    146 
    147     for (i = 0; i < NUM_LEDS; ++i) {
    148         init_prop(&leds[i].brightness);
    149         init_prop(&leds[i].blink);
    150         init_prop(&leds[i].mode);
    151         init_prop(&leds[i].color);
    152         init_prop(&leds[i].period);
    153     }
    154     g_attention = malloc(sizeof(struct light_state_t));
    155     memset(g_attention, 0, sizeof(*g_attention));
    156     g_notify = malloc(sizeof(struct light_state_t));
    157     memset(g_notify, 0, sizeof(*g_notify));
    158 }
    159 
    160 static int
    161 write_int(struct led_prop *prop, int value)
    162 {
    163     char buffer[20];
    164     int bytes;
    165     int amt;
    166 
    167     if (prop->fd < 0)
    168         return 0;
    169 
    170     LOGV("%s %s: 0x%x\n", __func__, prop->filename, value);
    171 
    172     bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
    173     while (bytes > 0) {
    174         amt = write(prop->fd, buffer, bytes);
    175         if (amt < 0) {
    176             if (errno == EINTR)
    177                 continue;
    178             return -errno;
    179         }
    180         bytes -= amt;
    181     }
    182 
    183     return 0;
    184 }
    185 
    186 static int
    187 write_rgb(struct led_prop *prop, int red, int green, int blue)
    188 {
    189     char buffer[20];
    190     int bytes;
    191     int amt;
    192 
    193     if (prop->fd < 0)
    194         return 0;
    195 
    196     LOGV("%s %s: red:%d green:%d blue:%d\n",
    197           __func__, prop->filename, red, green, blue);
    198 
    199     bytes = snprintf(buffer, sizeof(buffer), "%d %d %d\n", red, green, blue);
    200     while (bytes > 0) {
    201         amt = write(prop->fd, buffer, bytes);
    202         if (amt < 0) {
    203             if (errno == EINTR)
    204                 continue;
    205             return -errno;
    206         }
    207         bytes -= amt;
    208     }
    209 
    210     return 0;
    211 }
    212 
    213 static unsigned int
    214 set_rgb(int red, int green, int blue)
    215 {
    216     return(((red << 16) & 0x00ff0000) |
    217            ((green << 8) & 0x0000ff00) |
    218            (blue & 0x000000ff));
    219 }
    220 
    221 static int
    222 is_lit(struct light_state_t const* state)
    223 {
    224     return state->color & 0x00ffffff;
    225 }
    226 
    227 static int
    228 set_trackball_light(struct light_state_t const* state)
    229 {
    230     static int trackball_mode = 0;
    231     int rc = 0;
    232     int mode = state->flashMode;
    233     int red, blue, green;
    234     int period = 0;
    235 
    236     if (state->flashMode == LIGHT_FLASH_HARDWARE) {
    237         mode = state->flashOnMS;
    238         period = state->flashOffMS;
    239     }
    240     LOGV("%s color=%08x mode=%d period %d\n", __func__,
    241         state->color, mode, period);
    242 
    243 
    244     if (mode != 0) {
    245         red = (state->color >> 16) & 0xff;
    246         green = (state->color >> 8) & 0xff;
    247         blue = state->color & 0xff;
    248 
    249         rc = write_rgb(&leds[JOGBALL_LED].color, red, green, blue);
    250         if (rc != 0)
    251             LOGE("set color failed rc = %d\n", rc);
    252         if (period) {
    253             rc = write_int(&leds[JOGBALL_LED].period, period);
    254             if (rc != 0)
    255                LOGE("set period failed rc = %d\n", rc);
    256         }
    257     }
    258     // If the value isn't changing, don't set it, because this
    259     // can reset the timer on the breathing mode, which looks bad.
    260     if (trackball_mode == mode) {
    261         return 0;
    262     }
    263     trackball_mode = mode;
    264 
    265 return write_int(&leds[JOGBALL_LED].brightness, mode);
    266 }
    267 
    268 static void
    269 handle_trackball_light_locked(int type)
    270 {
    271     struct light_state_t *new_state = 0;
    272     int attn_mode = 0;
    273 
    274     if (g_attention->flashMode == LIGHT_FLASH_HARDWARE)
    275         attn_mode = g_attention->flashOnMS;
    276 
    277     LOGV("%s type %d attention %p notify %p\n",
    278         __func__, type, g_attention, g_notify);
    279 
    280     switch (type) {
    281         case LIGHT_ATTENTION: {
    282             if (attn_mode == 0) {
    283                 /* go back to notify state */
    284                 new_state = g_notify;
    285             } else {
    286                new_state = g_attention;
    287             }
    288         break;
    289         }
    290         case LIGHT_NOTIFY: {
    291             if (attn_mode != 0) {
    292                 /* attention takes priority over notify state */
    293                 new_state = g_attention;
    294             } else {
    295                new_state = g_notify;
    296             }
    297         break;
    298         }
    299     }
    300     if (new_state == 0) {
    301         LOGE("%s: unknown type (%d)\n", __func__, type);
    302         return;
    303     }
    304     LOGV("%s new state %p\n", __func__, new_state);
    305     set_trackball_light(new_state);
    306     return;
    307 }
    308 
    309 static int
    310 rgb_to_brightness(struct light_state_t const* state)
    311 {
    312     int color = state->color & 0x00ffffff;
    313     return ((77*((color>>16)&0x00ff))
    314             + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
    315 }
    316 
    317 static int
    318 set_light_backlight(struct light_device_t* dev,
    319         struct light_state_t const* state)
    320 {
    321     int err = 0;
    322     int brightness = rgb_to_brightness(state);
    323     LOGV("%s brightness=%d color=0x%08x",
    324             __func__,brightness, state->color);
    325     pthread_mutex_lock(&g_lock);
    326     g_backlight = brightness;
    327     err = write_int(&leds[LCD_BACKLIGHT].brightness, brightness);
    328     pthread_mutex_unlock(&g_lock);
    329     return err;
    330 }
    331 
    332 static int
    333 set_light_keyboard(struct light_device_t* dev,
    334         struct light_state_t const* state)
    335 {
    336     /* nothing to do on mahimahi*/
    337     return 0;
    338 }
    339 
    340 static int
    341 set_light_buttons(struct light_device_t* dev,
    342         struct light_state_t const* state)
    343 {
    344     int err = 0;
    345     int on = is_lit(state);
    346     pthread_mutex_lock(&g_lock);
    347     g_buttons = on;
    348     err = write_int(&leds[BUTTONS_LED].brightness, on?255:0);
    349     pthread_mutex_unlock(&g_lock);
    350     return err;
    351 }
    352 
    353 static int
    354 set_speaker_light_locked(struct light_device_t* dev,
    355         struct light_state_t const* state)
    356 {
    357     int len;
    358     unsigned int colorRGB;
    359 
    360     /* Red = amber_led, blue or green = green_led */
    361     colorRGB = state->color & 0xFFFFFF;
    362 
    363     switch (state->flashMode) {
    364         case LIGHT_FLASH_TIMED:
    365             LOGV("set_led_state colorRGB=%08X, flashing\n", colorRGB);
    366             switch (colorRGB) {
    367                 case RGB_RED:
    368                     write_int(&leds[RED_LED].blink, 1);
    369                     break;
    370                 case RGB_AMBER:
    371                     write_int(&leds[AMBER_LED].blink, 2);
    372                     break;
    373                 case RGB_GREEN:
    374                     write_int(&leds[GREEN_LED].blink, 1);
    375                     break;
    376                 case RGB_BLUE:
    377                     write_int(&leds[BLUE_LED].blink, 1);
    378                     break;
    379                 case RGB_BLACK:  /*off*/
    380                     write_int(&leds[GREEN_LED].blink, 0);
    381                     write_int(&leds[RED_LED].blink, 0);
    382                     write_int(&leds[BLUE_LED].blink, 0);
    383                     write_int(&leds[AMBER_LED].blink, 0);
    384                     break;
    385                     break;
    386                 default:
    387                     LOGE("set_led_state colorRGB=%08X, unknown color\n",
    388                           colorRGB);
    389                     break;
    390             }
    391             break;
    392         case LIGHT_FLASH_NONE:
    393             LOGV("set_led_state colorRGB=%08X, on\n", colorRGB);
    394             switch (colorRGB) {
    395                case RGB_RED:
    396                     /*no support for red solid */
    397                case RGB_AMBER:
    398                     write_int(&leds[AMBER_LED].brightness, 1);
    399                     write_int(&leds[GREEN_LED].brightness, 0);
    400                     write_int(&leds[BLUE_LED].brightness, 0);
    401                     break;
    402                 case RGB_GREEN:
    403                     write_int(&leds[GREEN_LED].brightness, 1);
    404                     write_int(&leds[AMBER_LED].brightness, 0);
    405                     write_int(&leds[BLUE_LED].brightness, 0);
    406                    break;
    407                 case RGB_BLUE:
    408                     write_int(&leds[BLUE_LED].brightness, 1);
    409                     write_int(&leds[GREEN_LED].brightness, 0);
    410                     write_int(&leds[AMBER_LED].brightness, 0);
    411                    break;
    412                 case RGB_BLACK:  /*off*/
    413                     write_int(&leds[GREEN_LED].brightness, 0);
    414                     write_int(&leds[AMBER_LED].brightness, 0);
    415                     write_int(&leds[BLUE_LED].brightness, 0);
    416                     write_int(&leds[RED_LED].brightness, 0);
    417                     break;
    418                 default:
    419                     LOGE("set_led_state colorRGB=%08X, unknown color\n",
    420                           colorRGB);
    421                     break;
    422             }
    423             break;
    424         default:
    425             LOGE("set_led_state colorRGB=%08X, unknown mode %d\n",
    426                   colorRGB, state->flashMode);
    427     }
    428     return 0;
    429 }
    430 
    431 static int
    432 set_light_battery(struct light_device_t* dev,
    433         struct light_state_t const* state)
    434 {
    435     pthread_mutex_lock(&g_lock);
    436     LOGV("%s mode=%d color=0x%08x",
    437             __func__,state->flashMode, state->color);
    438     set_speaker_light_locked(dev, state);
    439     pthread_mutex_unlock(&g_lock);
    440     return 0;
    441 }
    442 
    443 static int
    444 set_light_notifications(struct light_device_t* dev,
    445         struct light_state_t const* state)
    446 {
    447     pthread_mutex_lock(&g_lock);
    448 
    449     LOGV("%s mode=%d color=0x%08x On=%d Off=%d\n",
    450             __func__,state->flashMode, state->color,
    451             state->flashOnMS, state->flashOffMS);
    452     /*
    453     ** TODO Allow for user settings of color and interval
    454     ** Setting 60% brightness
    455     */
    456     switch (state->color & 0x00FFFFFF) {
    457         case RGB_BLACK:
    458             g_notify->color = set_rgb(0, 0, 0);
    459             break;
    460         case RGB_WHITE:
    461             g_notify->color = set_rgb(50, 127, 48);
    462             break;
    463         case RGB_RED:
    464             g_notify->color = set_rgb(141, 0, 0);
    465             break;
    466         case RGB_GREEN:
    467             g_notify->color = set_rgb(0, 141, 0);
    468             break;
    469         case RGB_BLUE:
    470             g_notify->color = set_rgb(0, 0, 141);
    471             break;
    472         case RGB_PINK:
    473             g_notify->color = set_rgb(141, 52, 58);
    474             break;
    475         case RGB_PURPLE:
    476             g_notify->color = set_rgb(70, 0, 70);
    477             break;
    478         case RGB_ORANGE:
    479             g_notify->color = set_rgb(141, 99, 0);
    480             break;
    481         case RGB_YELLOW:
    482             g_notify->color = set_rgb(100, 141, 0);
    483             break;
    484         case RGB_LT_BLUE:
    485             g_notify->color = set_rgb(35, 55, 98);
    486             break;
    487         default:
    488             g_notify->color = state->color;
    489             break;
    490     }
    491 
    492     if (state->flashMode != LIGHT_FLASH_NONE) {
    493         g_notify->flashMode = LIGHT_FLASH_HARDWARE;
    494         g_notify->flashOnMS = 7;
    495         g_notify->flashOffMS = (state->flashOnMS + state->flashOffMS)/1000;
    496     } else {
    497         g_notify->flashOnMS = 0;
    498         g_notify->flashOffMS = 0;
    499     }
    500     handle_trackball_light_locked(LIGHT_NOTIFY);
    501 
    502     pthread_mutex_unlock(&g_lock);
    503     return 0;
    504 }
    505 
    506 static int
    507 set_light_attention(struct light_device_t* dev,
    508         struct light_state_t const* state)
    509 {
    510     unsigned int colorRGB;
    511 
    512     LOGV("%s color=0x%08x mode=0x%08x submode=0x%08x",
    513             __func__, state->color, state->flashMode, state->flashOnMS);
    514 
    515     pthread_mutex_lock(&g_lock);
    516     /* tune color for hardware*/
    517     switch (state->color & 0x00FFFFFF) {
    518         case RGB_WHITE:
    519             colorRGB = set_rgb(101, 255, 96);
    520             break;
    521         case RGB_BLUE:
    522             colorRGB = set_rgb(0, 0, 235);
    523             break;
    524         case RGB_BLACK:
    525             colorRGB = set_rgb(0, 0, 0);
    526             break;
    527         default:
    528             LOGE("%s colorRGB=%08X, unknown color\n",
    529                           __func__, state->color);
    530             colorRGB = set_rgb(101, 255, 96);
    531             break;
    532     }
    533     g_attention->flashMode = state->flashMode;
    534     g_attention->flashOnMS = state->flashOnMS;
    535     g_attention->color = colorRGB;
    536     g_attention->flashOffMS = 0;
    537     handle_trackball_light_locked(LIGHT_ATTENTION);
    538 
    539     pthread_mutex_unlock(&g_lock);
    540     return 0;
    541 }
    542 
    543 
    544 /** Close the lights device */
    545 static int
    546 close_lights(struct light_device_t *dev)
    547 {
    548     int i;
    549 
    550     for (i = 0; i < NUM_LEDS; ++i) {
    551         close_prop(&leds[i].brightness);
    552         close_prop(&leds[i].blink);
    553         close_prop(&leds[i].mode);
    554     }
    555 
    556     if (dev) {
    557         free(dev);
    558     }
    559     return 0;
    560 }
    561 
    562 
    563 /******************************************************************************/
    564 
    565 /**
    566  * module methods
    567  */
    568 
    569 /** Open a new instance of a lights device using name */
    570 static int open_lights(const struct hw_module_t* module, char const* name,
    571         struct hw_device_t** device)
    572 {
    573     int (*set_light)(struct light_device_t* dev,
    574             struct light_state_t const* state);
    575 
    576     if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
    577         set_light = set_light_backlight;
    578     }
    579     else if (0 == strcmp(LIGHT_ID_KEYBOARD, name)) {
    580         set_light = set_light_keyboard;
    581     }
    582     else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
    583         set_light = set_light_buttons;
    584     }
    585     else if (0 == strcmp(LIGHT_ID_BATTERY, name)) {
    586         set_light = set_light_battery;
    587     }
    588     else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
    589         set_light = set_light_notifications;
    590     }
    591     else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) {
    592         set_light = set_light_attention;
    593     }
    594     else {
    595         return -EINVAL;
    596     }
    597 
    598     pthread_once(&g_init, init_globals);
    599 
    600     struct light_device_t *dev = malloc(sizeof(struct light_device_t));
    601     memset(dev, 0, sizeof(*dev));
    602 
    603     dev->common.tag = HARDWARE_DEVICE_TAG;
    604     dev->common.version = 0;
    605     dev->common.module = (struct hw_module_t*)module;
    606     dev->common.close = (int (*)(struct hw_device_t*))close_lights;
    607     dev->set_light = set_light;
    608 
    609     *device = (struct hw_device_t*)dev;
    610     return 0;
    611 }
    612 
    613 
    614 static struct hw_module_methods_t lights_module_methods = {
    615     .open =  open_lights,
    616 };
    617 
    618 /*
    619  * The lights Module
    620  */
    621 const struct hw_module_t HAL_MODULE_INFO_SYM = {
    622     .tag = HARDWARE_MODULE_TAG,
    623     .version_major = 1,
    624     .version_minor = 0,
    625     .id = LIGHTS_HARDWARE_MODULE_ID,
    626     .name = "mahimahi lights Module",
    627     .author = "Google, Inc.",
    628     .methods = &lights_module_methods,
    629 };
    630