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