• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
init_globals(void)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
write_int(char const * path,int value)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             LOGE("write_int failed to open %s\n", path);
117             already_warned = 1;
118         }
119         return -errno;
120     }
121 }
122 
123 static int
is_lit(struct light_state_t const * state)124 is_lit(struct light_state_t const* state)
125 {
126     return state->color & 0x00ffffff;
127 }
128 
129 static int
handle_trackball_light_locked(struct light_device_t * dev)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     LOGV("%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
rgb_to_brightness(struct light_state_t const * state)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
set_light_backlight(struct light_device_t * dev,struct light_state_t const * state)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
set_light_keyboard(struct light_device_t * dev,struct light_state_t const * state)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
set_light_buttons(struct light_device_t * dev,struct light_state_t const * state)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
set_speaker_light_locked(struct light_device_t * dev,struct light_state_t const * state)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     LOGD("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
handle_speaker_battery_locked(struct light_device_t * dev)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
set_light_battery(struct light_device_t * dev,struct light_state_t const * state)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
set_light_notifications(struct light_device_t * dev,struct light_state_t const * state)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     LOGV("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
set_light_attention(struct light_device_t * dev,struct light_state_t const * state)325 set_light_attention(struct light_device_t* dev,
326         struct light_state_t const* state)
327 {
328     pthread_mutex_lock(&g_lock);
329     LOGV("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
close_lights(struct light_device_t * dev)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 */
open_lights(const struct hw_module_t * module,char const * name,struct hw_device_t ** device)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 const 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