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