• 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_TAG "lights"
19 
20 #include <cutils/log.h>
21 
22 #include <stdint.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <pthread.h>
28 
29 #include <sys/ioctl.h>
30 #include <sys/types.h>
31 
32 #include <hardware/lights.h>
33 
34 /******************************************************************************/
35 
36 static pthread_once_t g_init = PTHREAD_ONCE_INIT;
37 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
38 static int g_haveTrackballLight = 0;
39 static struct light_state_t g_notification;
40 static struct light_state_t g_battery;
41 static int g_backlight = 255;
42 static int g_trackball = -1;
43 static int g_buttons = 0;
44 static int g_attention = 0;
45 static int g_haveAmberLed = 0;
46 
47 char const*const TRACKBALL_FILE
48         = "/sys/class/leds/jogball-backlight/brightness";
49 
50 char const*const RED_LED_FILE
51         = "/sys/class/leds/red/brightness";
52 
53 char const*const GREEN_LED_FILE
54         = "/sys/class/leds/green/brightness";
55 
56 char const*const BLUE_LED_FILE
57         = "/sys/class/leds/blue/brightness";
58 
59 char const*const AMBER_LED_FILE
60         = "/sys/class/leds/amber/brightness";
61 
62 char const*const LCD_FILE
63         = "/sys/class/leds/lcd-backlight/brightness";
64 
65 char const*const RED_FREQ_FILE
66         = "/sys/class/leds/red/device/grpfreq";
67 
68 char const*const RED_PWM_FILE
69         = "/sys/class/leds/red/device/grppwm";
70 
71 char const*const RED_BLINK_FILE
72         = "/sys/class/leds/red/device/blink";
73 
74 char const*const AMBER_BLINK_FILE
75         = "/sys/class/leds/amber/blink";
76 
77 char const*const KEYBOARD_FILE
78         = "/sys/class/leds/keyboard-backlight/brightness";
79 
80 char const*const BUTTON_FILE
81         = "/sys/class/leds/button-backlight/brightness";
82 
83 /**
84  * device methods
85  */
86 
init_globals(void)87 void init_globals(void)
88 {
89     // init the mutex
90     pthread_mutex_init(&g_lock, NULL);
91 
92     // figure out if we have the trackball LED or not
93     g_haveTrackballLight = (access(TRACKBALL_FILE, W_OK) == 0) ? 1 : 0;
94 
95     /* figure out if we have the amber LED or not.
96        If yes, just support green and amber.         */
97     g_haveAmberLed = (access(AMBER_LED_FILE, W_OK) == 0) ? 1 : 0;
98 }
99 
100 static int
write_int(char const * path,int value)101 write_int(char const* path, int value)
102 {
103     int fd;
104     static int already_warned = 0;
105 
106     fd = open(path, O_RDWR);
107     if (fd >= 0) {
108         char buffer[20];
109         int bytes = sprintf(buffer, "%d\n", value);
110         int amt = write(fd, buffer, bytes);
111         close(fd);
112         return amt == -1 ? -errno : 0;
113     } else {
114         if (already_warned == 0) {
115             LOGE("write_int failed to open %s\n", path);
116             already_warned = 1;
117         }
118         return -errno;
119     }
120 }
121 
122 static int
is_lit(struct light_state_t const * state)123 is_lit(struct light_state_t const* state)
124 {
125     return state->color & 0x00ffffff;
126 }
127 
128 static int
handle_trackball_light_locked(struct light_device_t * dev)129 handle_trackball_light_locked(struct light_device_t* dev)
130 {
131     int mode = g_attention;
132 
133     if (mode == 7 && g_backlight) {
134         mode = 0;
135     }
136     LOGV("%s g_backlight = %d, mode = %d, g_attention = %d\n",
137         __func__, g_backlight, mode, g_attention);
138 
139     // If the value isn't changing, don't set it, because this
140     // can reset the timer on the breathing mode, which looks bad.
141     if (g_trackball == mode) {
142         return 0;
143     }
144 
145     return write_int(TRACKBALL_FILE, mode);
146 }
147 
148 static int
rgb_to_brightness(struct light_state_t const * state)149 rgb_to_brightness(struct light_state_t const* state)
150 {
151     int color = state->color & 0x00ffffff;
152     return ((77*((color>>16)&0x00ff))
153             + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
154 }
155 
156 static int
set_light_backlight(struct light_device_t * dev,struct light_state_t const * state)157 set_light_backlight(struct light_device_t* dev,
158         struct light_state_t const* state)
159 {
160     int err = 0;
161     int brightness = rgb_to_brightness(state);
162     pthread_mutex_lock(&g_lock);
163     g_backlight = brightness;
164     err = write_int(LCD_FILE, brightness);
165     if (g_haveTrackballLight) {
166         handle_trackball_light_locked(dev);
167     }
168     pthread_mutex_unlock(&g_lock);
169     return err;
170 }
171 
172 static int
set_light_keyboard(struct light_device_t * dev,struct light_state_t const * state)173 set_light_keyboard(struct light_device_t* dev,
174         struct light_state_t const* state)
175 {
176     int err = 0;
177     int on = is_lit(state);
178     pthread_mutex_lock(&g_lock);
179     err = write_int(KEYBOARD_FILE, on?255:0);
180     pthread_mutex_unlock(&g_lock);
181     return err;
182 }
183 
184 static int
set_light_buttons(struct light_device_t * dev,struct light_state_t const * state)185 set_light_buttons(struct light_device_t* dev,
186         struct light_state_t const* state)
187 {
188     int err = 0;
189     int on = is_lit(state);
190     pthread_mutex_lock(&g_lock);
191     g_buttons = on;
192     err = write_int(BUTTON_FILE, on?255:0);
193     pthread_mutex_unlock(&g_lock);
194     return err;
195 }
196 
197 static int
set_speaker_light_locked(struct light_device_t * dev,struct light_state_t const * state)198 set_speaker_light_locked(struct light_device_t* dev,
199         struct light_state_t const* state)
200 {
201     int len;
202     int alpha, red, green, blue;
203     int blink, freq, pwm;
204     int onMS, offMS;
205     unsigned int colorRGB;
206 
207     switch (state->flashMode) {
208         case LIGHT_FLASH_TIMED:
209             onMS = state->flashOnMS;
210             offMS = state->flashOffMS;
211             break;
212         case LIGHT_FLASH_NONE:
213         default:
214             onMS = 0;
215             offMS = 0;
216             break;
217     }
218 
219     colorRGB = state->color;
220 
221 #if 0
222     LOGD("set_led_state colorRGB=%08X, onMS=%d, offMS=%d\n",
223             colorRGB, onMS, offMS);
224 #endif
225 
226     red = (colorRGB >> 16) & 0xFF;
227     green = (colorRGB >> 8) & 0xFF;
228     blue = colorRGB & 0xFF;
229 
230     if (!g_haveAmberLed) {
231         write_int(RED_LED_FILE, red);
232         write_int(GREEN_LED_FILE, green);
233         write_int(BLUE_LED_FILE, blue);
234     } else {
235         /* all of related red led is replaced by amber */
236         if (red) {
237             write_int(AMBER_LED_FILE, 1);
238             write_int(GREEN_LED_FILE, 0);
239         } else if (green) {
240             write_int(AMBER_LED_FILE, 0);
241             write_int(GREEN_LED_FILE, 1);
242         } else {
243             write_int(GREEN_LED_FILE, 0);
244             write_int(AMBER_LED_FILE, 0);
245         }
246     }
247 
248     if (onMS > 0 && offMS > 0) {
249         int totalMS = onMS + offMS;
250 
251         // the LED appears to blink about once per second if freq is 20
252         // 1000ms / 20 = 50
253         freq = totalMS / 50;
254         // pwm specifies the ratio of ON versus OFF
255         // pwm = 0 -> always off
256         // pwm = 255 => always on
257         pwm = (onMS * 255) / totalMS;
258 
259         // the low 4 bits are ignored, so round up if necessary
260         if (pwm > 0 && pwm < 16)
261             pwm = 16;
262 
263         blink = 1;
264     } else {
265         blink = 0;
266         freq = 0;
267         pwm = 0;
268     }
269 
270     if (!g_haveAmberLed) {
271         if (blink) {
272             write_int(RED_FREQ_FILE, freq);
273             write_int(RED_PWM_FILE, pwm);
274         }
275         write_int(RED_BLINK_FILE, blink);
276     } else {
277         write_int(AMBER_BLINK_FILE, blink);
278     }
279 
280     return 0;
281 }
282 
283 static void
handle_speaker_battery_locked(struct light_device_t * dev)284 handle_speaker_battery_locked(struct light_device_t* dev)
285 {
286     if (is_lit(&g_battery)) {
287         set_speaker_light_locked(dev, &g_battery);
288     } else {
289         set_speaker_light_locked(dev, &g_notification);
290     }
291 }
292 
293 static int
set_light_battery(struct light_device_t * dev,struct light_state_t const * state)294 set_light_battery(struct light_device_t* dev,
295         struct light_state_t const* state)
296 {
297     pthread_mutex_lock(&g_lock);
298     g_battery = *state;
299     if (g_haveTrackballLight) {
300         set_speaker_light_locked(dev, state);
301     }
302     handle_speaker_battery_locked(dev);
303     pthread_mutex_unlock(&g_lock);
304     return 0;
305 }
306 
307 static int
set_light_notifications(struct light_device_t * dev,struct light_state_t const * state)308 set_light_notifications(struct light_device_t* dev,
309         struct light_state_t const* state)
310 {
311     pthread_mutex_lock(&g_lock);
312     g_notification = *state;
313     LOGV("set_light_notifications g_trackball=%d color=0x%08x",
314             g_trackball, state->color);
315     if (g_haveTrackballLight) {
316         handle_trackball_light_locked(dev);
317     }
318     handle_speaker_battery_locked(dev);
319     pthread_mutex_unlock(&g_lock);
320     return 0;
321 }
322 
323 static int
set_light_attention(struct light_device_t * dev,struct light_state_t const * state)324 set_light_attention(struct light_device_t* dev,
325         struct light_state_t const* state)
326 {
327     pthread_mutex_lock(&g_lock);
328     g_notification = *state;
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