1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 * Copyright (C) 2014 The  Linux Foundation. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19// #define LOG_NDEBUG 0
20
21#include <cutils/log.h>
22
23#include <stdint.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <pthread.h>
30
31#include <sys/ioctl.h>
32#include <sys/types.h>
33
34#include <hardware/lights.h>
35
36/******************************************************************************/
37
38static pthread_once_t g_init = PTHREAD_ONCE_INIT;
39static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
40static struct light_state_t g_notification;
41static struct light_state_t g_battery;
42static int g_attention = 0;
43
44char const*const RED_LED_FILE
45        = "/sys/class/leds/red/brightness";
46
47char const*const GREEN_LED_FILE
48        = "/sys/class/leds/green/brightness";
49
50char const*const BLUE_LED_FILE
51        = "/sys/class/leds/blue/brightness";
52
53char const*const BLUETOOTH_LED_FILE
54        = "/sys/class/leds/bt/brightness";
55
56char const*const LCD_FILE
57        = "/sys/class/leds/lcd-backlight/brightness";
58
59char const*const BUTTON_FILE
60        = "/sys/class/leds/button-backlight/brightness";
61
62char const*const RED_BLINK_FILE
63        = "/sys/class/leds/red/blink";
64
65char const*const GREEN_BLINK_FILE
66        = "/sys/class/leds/green/blink";
67
68char const*const BLUE_BLINK_FILE
69        = "/sys/class/leds/blue/blink";
70
71/**
72 * device methods
73 */
74
75void init_globals(void)
76{
77    // init the mutex
78    pthread_mutex_init(&g_lock, NULL);
79}
80
81static int
82write_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 = snprintf(buffer, sizeof(buffer), "%d\n", value);
91        ssize_t amt = write(fd, buffer, (size_t)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
103static int
104is_lit(struct light_state_t const* state)
105{
106    return state->color & 0x00ffffff;
107}
108
109static int
110rgb_to_brightness(struct light_state_t const* state)
111{
112    int color = state->color & 0x00ffffff;
113    return ((77*((color>>16)&0x00ff))
114            + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
115}
116
117static int
118set_light_backlight(struct light_device_t* dev,
119        struct light_state_t const* state)
120{
121    int err = 0;
122    int brightness = rgb_to_brightness(state);
123    if(!dev) {
124        return -1;
125    }
126    pthread_mutex_lock(&g_lock);
127    err = write_int(LCD_FILE, brightness);
128    pthread_mutex_unlock(&g_lock);
129    return err;
130}
131
132static int
133set_speaker_light_locked(struct light_device_t* dev,
134        struct light_state_t const* state)
135{
136    int red, green, blue;
137    int blink;
138    int onMS, offMS;
139    unsigned int colorRGB;
140
141    if(!dev) {
142        return -1;
143    }
144
145    switch (state->flashMode) {
146        case LIGHT_FLASH_TIMED:
147            onMS = state->flashOnMS;
148            offMS = state->flashOffMS;
149            break;
150        case LIGHT_FLASH_NONE:
151        default:
152            onMS = 0;
153            offMS = 0;
154            break;
155    }
156
157    colorRGB = state->color;
158
159#if 0
160    ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n",
161            state->flashMode, colorRGB, onMS, offMS);
162#endif
163
164    red = (colorRGB >> 16) & 0xFF;
165    green = (colorRGB >> 8) & 0xFF;
166    blue = colorRGB & 0xFF;
167
168    if (onMS > 0 && offMS > 0) {
169        /*
170         * if ON time == OFF time
171         *   use blink mode 2
172         * else
173         *   use blink mode 1
174         */
175        if (onMS == offMS)
176            blink = 2;
177        else
178            blink = 1;
179    } else {
180        blink = 0;
181    }
182
183    if (blink) {
184        if (red) {
185            if (write_int(RED_BLINK_FILE, blink))
186                write_int(RED_LED_FILE, 0);
187	}
188        if (green) {
189            if (write_int(GREEN_BLINK_FILE, blink))
190                write_int(GREEN_LED_FILE, 0);
191	}
192        if (blue) {
193            if (write_int(BLUE_BLINK_FILE, blink))
194                write_int(BLUE_LED_FILE, 0);
195	}
196    } else {
197        write_int(RED_LED_FILE, red);
198        write_int(GREEN_LED_FILE, green);
199        write_int(BLUE_LED_FILE, blue);
200    }
201
202    return 0;
203}
204
205static void
206handle_speaker_battery_locked(struct light_device_t* dev)
207{
208    if (is_lit(&g_battery)) {
209        set_speaker_light_locked(dev, &g_battery);
210    } else {
211        set_speaker_light_locked(dev, &g_notification);
212    }
213}
214
215static int
216set_light_battery(struct light_device_t* dev,
217        struct light_state_t const* state)
218{
219    pthread_mutex_lock(&g_lock);
220    g_battery = *state;
221    handle_speaker_battery_locked(dev);
222    pthread_mutex_unlock(&g_lock);
223    return 0;
224}
225
226static int
227set_light_notifications(struct light_device_t* dev,
228        struct light_state_t const* state)
229{
230    pthread_mutex_lock(&g_lock);
231    g_notification = *state;
232    handle_speaker_battery_locked(dev);
233    pthread_mutex_unlock(&g_lock);
234    return 0;
235}
236
237static int
238set_light_attention(struct light_device_t* dev,
239        struct light_state_t const* state)
240{
241    pthread_mutex_lock(&g_lock);
242    if (state->flashMode == LIGHT_FLASH_HARDWARE) {
243        g_attention = state->flashOnMS;
244    } else if (state->flashMode == LIGHT_FLASH_NONE) {
245        g_attention = 0;
246    }
247    handle_speaker_battery_locked(dev);
248    pthread_mutex_unlock(&g_lock);
249    return 0;
250}
251
252static int
253set_light_buttons(struct light_device_t* dev,
254        struct light_state_t const* state)
255{
256    int err = 0;
257    if(!dev) {
258        return -1;
259    }
260    pthread_mutex_lock(&g_lock);
261    err = write_int(BUTTON_FILE, state->color & 0xFF);
262    pthread_mutex_unlock(&g_lock);
263    return err;
264}
265
266static int
267set_light_bluetooth(struct light_device_t* dev,
268        struct light_state_t const* state)
269{
270    int err = 0;
271    if(!dev) {
272        return -1;
273    }
274    pthread_mutex_lock(&g_lock);
275    err = write_int(BLUETOOTH_LED_FILE, state->color & 0xFF);
276    pthread_mutex_unlock(&g_lock);
277    return err;
278}
279
280/** Close the lights device */
281static int
282close_lights(struct light_device_t *dev)
283{
284    if (dev) {
285        free(dev);
286    }
287    return 0;
288}
289
290
291/******************************************************************************/
292
293/**
294 * module methods
295 */
296
297/** Open a new instance of a lights device using name */
298static int open_lights(const struct hw_module_t* module, char const* name,
299        struct hw_device_t** device)
300{
301    int (*set_light)(struct light_device_t* dev,
302            struct light_state_t const* state);
303
304    if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))
305        set_light = set_light_backlight;
306    else if (0 == strcmp(LIGHT_ID_BATTERY, name))
307        set_light = set_light_battery;
308    else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
309        set_light = set_light_notifications;
310    else if (0 == strcmp(LIGHT_ID_BUTTONS, name))
311        set_light = set_light_buttons;
312    else if (0 == strcmp(LIGHT_ID_ATTENTION, name))
313        set_light = set_light_attention;
314    else if (0 == strcmp(LIGHT_ID_BLUETOOTH, name))
315        set_light = set_light_bluetooth;
316    else
317        return -EINVAL;
318
319    pthread_once(&g_init, init_globals);
320
321    struct light_device_t *dev = malloc(sizeof(struct light_device_t));
322
323    if(!dev)
324        return -ENOMEM;
325
326    memset(dev, 0, sizeof(*dev));
327
328    dev->common.tag = HARDWARE_DEVICE_TAG;
329    dev->common.version = 0;
330    dev->common.module = (struct hw_module_t*)module;
331    dev->common.close = (int (*)(struct hw_device_t*))close_lights;
332    dev->set_light = set_light;
333
334    *device = (struct hw_device_t*)dev;
335    return 0;
336}
337
338static struct hw_module_methods_t lights_module_methods = {
339    .open =  open_lights,
340};
341
342/*
343 * The lights Module
344 */
345struct hw_module_t HAL_MODULE_INFO_SYM = {
346    .tag = HARDWARE_MODULE_TAG,
347    .version_major = 1,
348    .version_minor = 0,
349    .id = LIGHTS_HARDWARE_MODULE_ID,
350    .name = "lights Module",
351    .author = "Google, Inc.",
352    .methods = &lights_module_methods,
353};
354