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 <log/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 LCD_FILE
54        = "/sys/class/leds/lcd-backlight/brightness";
55
56char const*const BUTTON_FILE
57        = "/sys/class/leds/button-backlight/brightness";
58
59char const*const RED_BLINK_FILE
60        = "/sys/class/leds/red/blink";
61
62char const*const GREEN_BLINK_FILE
63        = "/sys/class/leds/green/blink";
64
65char const*const BLUE_BLINK_FILE
66        = "/sys/class/leds/blue/blink";
67
68/**
69 * device methods
70 */
71
72void init_globals(void)
73{
74    // init the mutex
75    pthread_mutex_init(&g_lock, NULL);
76}
77
78static int
79write_int(char const* path, int value)
80{
81    int fd;
82    static int already_warned = 0;
83
84    fd = open(path, O_RDWR);
85    if (fd >= 0) {
86        char buffer[20];
87        int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
88        ssize_t amt = write(fd, buffer, (size_t)bytes);
89        close(fd);
90        return amt == -1 ? -errno : 0;
91    } else {
92        if (already_warned == 0) {
93            ALOGE("write_int failed to open %s\n", path);
94            already_warned = 1;
95        }
96        return -errno;
97    }
98}
99
100static int
101is_lit(struct light_state_t const* state)
102{
103    return state->color & 0x00ffffff;
104}
105
106static int
107rgb_to_brightness(struct light_state_t const* state)
108{
109    int color = state->color & 0x00ffffff;
110    return ((77*((color>>16)&0x00ff))
111            + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
112}
113
114static int
115set_light_backlight(struct light_device_t* dev,
116        struct light_state_t const* state)
117{
118    int err = 0;
119    int brightness = rgb_to_brightness(state);
120    if(!dev) {
121        return -1;
122    }
123    pthread_mutex_lock(&g_lock);
124    err = write_int(LCD_FILE, brightness);
125    pthread_mutex_unlock(&g_lock);
126    return err;
127}
128
129static int
130set_speaker_light_locked(struct light_device_t* dev,
131        struct light_state_t const* state)
132{
133    int red, green, blue;
134    int blink;
135    int onMS, offMS;
136    unsigned int colorRGB;
137
138    if(!dev) {
139        return -1;
140    }
141
142    switch (state->flashMode) {
143        case LIGHT_FLASH_TIMED:
144            onMS = state->flashOnMS;
145            offMS = state->flashOffMS;
146            break;
147        case LIGHT_FLASH_NONE:
148        default:
149            onMS = 0;
150            offMS = 0;
151            break;
152    }
153
154    colorRGB = state->color;
155
156#if 0
157    ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n",
158            state->flashMode, colorRGB, onMS, offMS);
159#endif
160
161    red = (colorRGB >> 16) & 0xFF;
162    green = (colorRGB >> 8) & 0xFF;
163    blue = colorRGB & 0xFF;
164
165    if (onMS > 0 && offMS > 0) {
166        /*
167         * if ON time == OFF time
168         *   use blink mode 2
169         * else
170         *   use blink mode 1
171         */
172        if (onMS == offMS)
173            blink = 2;
174        else
175            blink = 1;
176    } else {
177        blink = 0;
178    }
179
180    if (blink) {
181        if (red) {
182            if (write_int(RED_BLINK_FILE, blink))
183                write_int(RED_LED_FILE, 0);
184	}
185        if (green) {
186            if (write_int(GREEN_BLINK_FILE, blink))
187                write_int(GREEN_LED_FILE, 0);
188	}
189        if (blue) {
190            if (write_int(BLUE_BLINK_FILE, blink))
191                write_int(BLUE_LED_FILE, 0);
192	}
193    } else {
194        write_int(RED_LED_FILE, red);
195        write_int(GREEN_LED_FILE, green);
196        write_int(BLUE_LED_FILE, blue);
197    }
198
199    return 0;
200}
201
202static void
203handle_speaker_battery_locked(struct light_device_t* dev)
204{
205    if (is_lit(&g_battery)) {
206        set_speaker_light_locked(dev, &g_battery);
207    } else {
208        set_speaker_light_locked(dev, &g_notification);
209    }
210}
211
212static int
213set_light_battery(struct light_device_t* dev,
214        struct light_state_t const* state)
215{
216    pthread_mutex_lock(&g_lock);
217    g_battery = *state;
218    handle_speaker_battery_locked(dev);
219    pthread_mutex_unlock(&g_lock);
220    return 0;
221}
222
223static int
224set_light_notifications(struct light_device_t* dev,
225        struct light_state_t const* state)
226{
227    pthread_mutex_lock(&g_lock);
228    g_notification = *state;
229    handle_speaker_battery_locked(dev);
230    pthread_mutex_unlock(&g_lock);
231    return 0;
232}
233
234static int
235set_light_attention(struct light_device_t* dev,
236        struct light_state_t const* state)
237{
238    pthread_mutex_lock(&g_lock);
239    if (state->flashMode == LIGHT_FLASH_HARDWARE) {
240        g_attention = state->flashOnMS;
241    } else if (state->flashMode == LIGHT_FLASH_NONE) {
242        g_attention = 0;
243    }
244    handle_speaker_battery_locked(dev);
245    pthread_mutex_unlock(&g_lock);
246    return 0;
247}
248
249static int
250set_light_buttons(struct light_device_t* dev,
251        struct light_state_t const* state)
252{
253    int err = 0;
254    if(!dev) {
255        return -1;
256    }
257    pthread_mutex_lock(&g_lock);
258    err = write_int(BUTTON_FILE, state->color & 0xFF);
259    pthread_mutex_unlock(&g_lock);
260    return err;
261}
262
263/** Close the lights device */
264static int
265close_lights(struct light_device_t *dev)
266{
267    if (dev) {
268        free(dev);
269    }
270    return 0;
271}
272
273
274/******************************************************************************/
275
276/**
277 * module methods
278 */
279
280/** Open a new instance of a lights device using name */
281static int open_lights(const struct hw_module_t* module, char const* name,
282        struct hw_device_t** device)
283{
284    int (*set_light)(struct light_device_t* dev,
285            struct light_state_t const* state);
286
287    if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))
288        set_light = set_light_backlight;
289    else if (0 == strcmp(LIGHT_ID_BATTERY, name))
290        set_light = set_light_battery;
291    else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
292        set_light = set_light_notifications;
293    else if (0 == strcmp(LIGHT_ID_BUTTONS, name))
294        set_light = set_light_buttons;
295    else if (0 == strcmp(LIGHT_ID_ATTENTION, name))
296        set_light = set_light_attention;
297    else
298        return -EINVAL;
299
300    pthread_once(&g_init, init_globals);
301
302    struct light_device_t *dev = malloc(sizeof(struct light_device_t));
303
304    if(!dev)
305        return -ENOMEM;
306
307    memset(dev, 0, sizeof(*dev));
308
309    dev->common.tag = HARDWARE_DEVICE_TAG;
310    dev->common.version = 0;
311    dev->common.module = (struct hw_module_t*)module;
312    dev->common.close = (int (*)(struct hw_device_t*))close_lights;
313    dev->set_light = set_light;
314
315    *device = (struct hw_device_t*)dev;
316    return 0;
317}
318
319static struct hw_module_methods_t lights_module_methods = {
320    .open =  open_lights,
321};
322
323/*
324 * The lights Module
325 */
326struct hw_module_t HAL_MODULE_INFO_SYM = {
327    .tag = HARDWARE_MODULE_TAG,
328    .version_major = 1,
329    .version_minor = 0,
330    .id = LIGHTS_HARDWARE_MODULE_ID,
331    .name = "lights Module",
332    .author = "Google, Inc.",
333    .methods = &lights_module_methods,
334};
335