1/*
2 * Copyright 2013 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_TAG "lights"
19#define DEBUG 0
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
37static pthread_once_t g_init = PTHREAD_ONCE_INIT;
38static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
39
40char const *const LCD_FILE           = "/sys/class/leds/lcd-backlight/brightness";
41char const *const RED_LED_FILE       = "/sys/class/leds/red/brightness";
42char const *const GREEN_LED_FILE     = "/sys/class/leds/green/brightness";
43char const *const BLUE_LED_FILE      = "/sys/class/leds/blue/brightness";
44char const *const RED_TIMEOUT_FILE   = "/sys/class/leds/red/on_off_ms";
45char const *const GREEN_TIMEOUT_FILE = "/sys/class/leds/green/on_off_ms";
46char const *const BLUE_TIMEOUT_FILE  = "/sys/class/leds/blue/on_off_ms";
47char const *const RGB_LOCKED_FILE    = "/sys/class/leds/red/rgb_start";
48
49/**
50 * device methods
51 */
52
53void init_globals(void)
54{
55    // init the mutex
56    pthread_mutex_init(&g_lock, NULL);
57}
58
59static int write_int(char const* path, int value)
60{
61    int fd;
62    static int already_warned = 0;
63
64    fd = open(path, O_RDWR);
65    if (fd >= 0) {
66        char buffer[32] = {0,};
67        int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
68        int amt = write(fd, buffer, bytes);
69        close(fd);
70        return amt == -1 ? -errno : 0;
71    } else {
72        if (already_warned == 0) {
73            ALOGE("write_int failed to open %s\n", path);
74            already_warned = 1;
75        }
76        return -errno;
77    }
78}
79
80static int write_on_off(char const* path, int on, int off)
81{
82    int fd;
83    static int already_warned = 0;
84
85    fd = open(path, O_RDWR);
86    if (fd >= 0) {
87        char buffer[32] = {0,};
88        int bytes = snprintf(buffer, sizeof(buffer), "%d %d\n", on, off);
89        int amt = write(fd, buffer, bytes);
90        close(fd);
91        return amt == -1 ? -errno : 0;
92    } else {
93        if (already_warned == 0) {
94            ALOGE("write_int failed to open %s\n", path);
95            already_warned = 1;
96        }
97        return -errno;
98    }
99}
100
101static int rgb_to_brightness(struct light_state_t const* state)
102{
103    int color = state->color & 0x00ffffff;
104
105    return ((77 * ((color >> 16) & 0x00ff))
106            + (150 * ((color >> 8) & 0x00ff)) + (29 * (color & 0x00ff))) >> 8;
107}
108
109static int set_light_backlight(struct light_device_t* dev,
110        struct light_state_t const* state)
111{
112    int err = 0;
113    int brightness = rgb_to_brightness(state);
114
115    pthread_mutex_lock(&g_lock);
116    err = write_int(LCD_FILE, brightness);
117    pthread_mutex_unlock(&g_lock);
118
119    return err;
120}
121
122static int set_light_locked(struct light_state_t const* state, int type)
123{
124    int len;
125    int red, green, blue;
126    int onMS, offMS;
127    unsigned int colorRGB;
128
129    switch (state->flashMode) {
130    case LIGHT_FLASH_TIMED:
131    case LIGHT_FLASH_HARDWARE:
132        onMS = state->flashOnMS;
133        offMS = state->flashOffMS;
134        break;
135    case LIGHT_FLASH_NONE:
136    default:
137        onMS = 0;
138        offMS = 0;
139        break;
140    }
141
142    colorRGB = state->color;
143
144#if DEBUG
145    ALOGD("set_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n",
146            state->flashMode, colorRGB, onMS, offMS);
147#endif
148
149    red = (colorRGB >> 16) & 0xFF;
150    green = (colorRGB >> 8) & 0xFF;
151    blue = colorRGB & 0xFF;
152
153    // due to limitation of driver
154    if (onMS == 0) {
155        red = 0;
156        green = 0;
157        blue = 0;
158    }
159
160    write_int(RGB_LOCKED_FILE, 0);
161
162    write_int(RED_LED_FILE, red);
163    write_int(GREEN_LED_FILE, green);
164    write_int(BLUE_LED_FILE, blue);
165
166    write_on_off(RED_TIMEOUT_FILE, onMS, offMS);
167    write_on_off(GREEN_TIMEOUT_FILE, onMS, offMS);
168    write_on_off(BLUE_TIMEOUT_FILE, onMS, offMS);
169
170    write_int(RGB_LOCKED_FILE, 1);
171
172    return 0;
173}
174
175static int set_light_notifications(struct light_device_t* dev,
176        struct light_state_t const* state)
177{
178    pthread_mutex_lock(&g_lock);
179    set_light_locked(state, 0);
180    pthread_mutex_unlock(&g_lock);
181
182    return 0;
183}
184
185static int set_light_attention(struct light_device_t* dev,
186        struct light_state_t const* state)
187{
188    pthread_mutex_lock(&g_lock);
189    set_light_locked(state, 1);
190    pthread_mutex_unlock(&g_lock);
191
192    return 0;
193}
194
195
196/** Close the lights device */
197static int close_lights(struct light_device_t *dev)
198{
199    if (dev)
200        free(dev);
201
202    return 0;
203}
204
205/******************************************************************************/
206
207/**
208 * module methods
209 */
210
211/** Open a new instance of a lights device using name */
212static int open_lights(const struct hw_module_t* module, char const* name,
213        struct hw_device_t** device)
214{
215    int (*set_light)(struct light_device_t* dev,
216            struct light_state_t const* state);
217
218    if (!strcmp(LIGHT_ID_BACKLIGHT, name))
219        set_light = set_light_backlight;
220    else if (!strcmp(LIGHT_ID_NOTIFICATIONS, name))
221        set_light = set_light_notifications;
222    else if (!strcmp(LIGHT_ID_ATTENTION, name))
223        set_light = set_light_attention;
224    else
225        return -EINVAL;
226
227    pthread_once(&g_init, init_globals);
228
229    struct light_device_t *dev = malloc(sizeof(struct light_device_t));
230    memset(dev, 0, sizeof(*dev));
231
232    dev->common.tag = HARDWARE_DEVICE_TAG;
233    dev->common.version = 0;
234    dev->common.module = (struct hw_module_t*)module;
235    dev->common.close = (int (*)(struct hw_device_t*))close_lights;
236    dev->set_light = set_light;
237
238    *device = (struct hw_device_t*)dev;
239
240    return 0;
241}
242
243static struct hw_module_methods_t lights_module_methods = {
244    .open =  open_lights,
245};
246
247/*
248 * The lights Module
249 */
250struct hw_module_t HAL_MODULE_INFO_SYM = {
251    .tag = HARDWARE_MODULE_TAG,
252    .version_major = 1,
253    .version_minor = 0,
254    .id = LIGHTS_HARDWARE_MODULE_ID,
255    .name = "lights Module",
256    .author = "Google, Inc.",
257    .methods = &lights_module_methods,
258};
259