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 #include <cutils/log.h>
19 #include <cutils/properties.h>
20
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <pthread.h>
28
29 #include <linux/msm_mdp.h>
30
31 #include <sys/ioctl.h>
32 #include <sys/types.h>
33
34 #include <hardware/lights.h>
35
36 /*
37 * Change this to 1 to support battery notifications via BatteryService
38 */
39 #define LIGHTS_SUPPORT_BATTERY 0
40 #define CG_COLOR_ID_PROPERTY "ro.boot.hardware.color"
41
42 static pthread_once_t g_init = PTHREAD_ONCE_INIT;
43 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
44 static struct light_state_t g_notification;
45 static struct light_state_t g_battery;
46 static int g_last_backlight_mode = BRIGHTNESS_MODE_USER;
47 static int g_attention = 0;
48 static int rgb_brightness_ratio = 255;
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 LCD_FILE
60 = "/sys/class/leds/lcd-backlight/brightness";
61
62 char const*const PERSISTENCE_FILE
63 = "/sys/class/leds/lcd-backlight/low_persistence";
64
65 char const*const BUTTON_FILE
66 = "/sys/class/leds/button-backlight/brightness";
67
68 char const*const RED_BLINK_FILE
69 = "/sys/class/leds/red/blink";
70
71 char const*const GREEN_BLINK_FILE
72 = "/sys/class/leds/green/blink";
73
74 char const*const BLUE_BLINK_FILE
75 = "/sys/class/leds/blue/blink";
76
77 char const*const RED_ON_OFF_MS_FILE
78 = "/sys/class/leds/red/on_off_ms";
79
80 char const*const GREEN_ON_OFF_MS_FILE
81 = "/sys/class/leds/green/on_off_ms";
82
83 char const*const BLUE_ON_OFF_MS_FILE
84 = "/sys/class/leds/blue/on_off_ms";
85
86 char const*const RED_RGB_START_FILE
87 = "/sys/class/leds/red/rgb_start";
88
89 char const*const GREEN_RGB_START_FILE
90 = "/sys/class/leds/green/rgb_start";
91
92 char const*const BLUE_RGB_START_FILE
93 = "/sys/class/leds/blue/rgb_start";
94
95 /**
96 * device methods
97 */
98
init_globals(void)99 void init_globals(void)
100 {
101 char color_id_prop[PROPERTY_VALUE_MAX] = {""};
102
103 // init the mutex
104 pthread_mutex_init(&g_lock, NULL);
105
106 // check CG color
107 property_get(CG_COLOR_ID_PROPERTY, color_id_prop, "DEF00");
108 if (strcmp(color_id_prop, "GRA00") == 0) {
109 rgb_brightness_ratio = 25;
110 } else if (strcmp(color_id_prop, "SLV00") == 0) {
111 rgb_brightness_ratio = 15;
112 } else if (strcmp(color_id_prop, "BLU00") == 0) {
113 rgb_brightness_ratio = 15;
114 } else {
115 rgb_brightness_ratio = 20;
116 }
117 }
118
119 static int
write_int(char const * path,int value)120 write_int(char const* path, int value)
121 {
122 int fd;
123 static int already_warned = 0;
124
125 fd = open(path, O_WRONLY);
126 if (fd >= 0) {
127 char buffer[20];
128 size_t bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
129 if(bytes >= sizeof(buffer)) return -EINVAL;
130 ssize_t amt = write(fd, buffer, bytes);
131 close(fd);
132 return amt == -1 ? -errno : 0;
133 } else {
134 if (already_warned == 0) {
135 ALOGE("write_int failed to open %s\n", path);
136 already_warned = 1;
137 }
138 return -errno;
139 }
140 }
141
142 static int
write_double_int(char const * path,int value1,int value2)143 write_double_int(char const* path, int value1, int value2)
144 {
145 int fd;
146 static int already_warned = 0;
147
148 fd = open(path, O_WRONLY);
149 if (fd >= 0) {
150 char buffer[20];
151 size_t bytes = snprintf(buffer, sizeof(buffer), "%d %d\n", value1, value2);
152 if(bytes >= sizeof(buffer)) return -EINVAL;
153 ssize_t amt = write(fd, buffer, bytes);
154 close(fd);
155 return amt == -1 ? -errno : 0;
156 } else {
157 if (already_warned == 0) {
158 ALOGE("write_int failed to open %s\n", path);
159 already_warned = 1;
160 }
161 return -errno;
162 }
163 }
164
165 static int
is_lit(struct light_state_t const * state)166 is_lit(struct light_state_t const* state)
167 {
168 return state->color & 0x00ffffff;
169 }
170
171 static int
rgb_to_brightness(struct light_state_t const * state)172 rgb_to_brightness(struct light_state_t const* state)
173 {
174 int color = state->color & 0x00ffffff;
175 return ((77*((color>>16)&0x00ff))
176 + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
177 }
178
179 static int
set_light_backlight(struct light_device_t * dev,struct light_state_t const * state)180 set_light_backlight(struct light_device_t* dev,
181 struct light_state_t const* state)
182 {
183 int err = 0;
184 int brightness = rgb_to_brightness(state);
185 unsigned int lpEnabled = state->brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE;
186 if(!dev) {
187 return -1;
188 }
189
190 pthread_mutex_lock(&g_lock);
191
192 // If we're not in lp mode and it has been enabled or if we are in lp mode
193 // and it has been disabled send an ioctl to the display with the update
194 if ((g_last_backlight_mode != state->brightnessMode && lpEnabled) ||
195 (!lpEnabled && g_last_backlight_mode == BRIGHTNESS_MODE_LOW_PERSISTENCE)) {
196 if ((err = write_int(PERSISTENCE_FILE, lpEnabled)) != 0) {
197 ALOGE("%s: Failed to write to %s: %s\n", __FUNCTION__, PERSISTENCE_FILE,
198 strerror(errno));
199 }
200 if (lpEnabled != 0) {
201 // This is defined in BoardConfig.mk.
202 brightness = DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS;
203 }
204 }
205
206 g_last_backlight_mode = state->brightnessMode;
207
208 if (!err) {
209 err = write_int(LCD_FILE, brightness);
210 }
211
212 pthread_mutex_unlock(&g_lock);
213 return err;
214 }
215
216 static int
set_speaker_light_locked(struct light_device_t * dev,struct light_state_t const * state)217 set_speaker_light_locked(struct light_device_t* dev,
218 struct light_state_t const* state)
219 {
220 int red, green, blue;
221 int blink;
222 int onMS, offMS;
223 unsigned int colorRGB;
224
225 if(!dev) {
226 return -1;
227 }
228
229 switch (state->flashMode) {
230 case LIGHT_FLASH_TIMED:
231 onMS = state->flashOnMS;
232 offMS = state->flashOffMS;
233 break;
234 case LIGHT_FLASH_NONE:
235 default:
236 onMS = 0;
237 offMS = 0;
238 break;
239 }
240
241 colorRGB = state->color;
242
243 #if 0
244 ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n",
245 state->flashMode, colorRGB, onMS, offMS);
246 #endif
247
248 red = ((colorRGB >> 16) & 0xFF) * rgb_brightness_ratio / 255;
249 green = ((colorRGB >> 8) & 0xFF) * rgb_brightness_ratio / 255;
250 blue = (colorRGB & 0xFF) * rgb_brightness_ratio / 255;
251
252 write_double_int(RED_ON_OFF_MS_FILE, onMS, offMS);
253 write_int(RED_LED_FILE, red);
254 write_double_int(GREEN_ON_OFF_MS_FILE, onMS, offMS);
255 write_int(GREEN_LED_FILE, green);
256 write_double_int(BLUE_ON_OFF_MS_FILE, onMS, offMS);
257 write_int(BLUE_LED_FILE, blue);
258
259 if(!write_int(RED_RGB_START_FILE, 1))
260 if(!write_int(GREEN_RGB_START_FILE, 1))
261 if(!write_int(BLUE_RGB_START_FILE, 1))
262 return -1;
263
264 return 0;
265 }
266
267 static void
handle_speaker_battery_locked(struct light_device_t * dev)268 handle_speaker_battery_locked(struct light_device_t* dev)
269 {
270 if (is_lit(&g_battery)) {
271 set_speaker_light_locked(dev, &g_battery);
272 } else {
273 set_speaker_light_locked(dev, &g_notification);
274 }
275 }
276
277 #if LIGHTS_SUPPORT_BATTERY
278 static int
set_light_battery(struct light_device_t * dev,struct light_state_t const * state)279 set_light_battery(struct light_device_t* dev,
280 struct light_state_t const* state)
281 {
282 pthread_mutex_lock(&g_lock);
283 g_battery = *state;
284 handle_speaker_battery_locked(dev);
285 pthread_mutex_unlock(&g_lock);
286 return 0;
287 }
288 #endif
289
290 static int
set_light_notifications(struct light_device_t * dev,struct light_state_t const * state)291 set_light_notifications(struct light_device_t* dev,
292 struct light_state_t const* state)
293 {
294 pthread_mutex_lock(&g_lock);
295 g_notification = *state;
296 handle_speaker_battery_locked(dev);
297 pthread_mutex_unlock(&g_lock);
298 return 0;
299 }
300
301 static int
set_light_attention(struct light_device_t * dev,struct light_state_t const * state)302 set_light_attention(struct light_device_t* dev,
303 struct light_state_t const* state)
304 {
305 pthread_mutex_lock(&g_lock);
306 if (state->flashMode == LIGHT_FLASH_HARDWARE) {
307 g_attention = state->flashOnMS;
308 } else if (state->flashMode == LIGHT_FLASH_NONE) {
309 g_attention = 0;
310 }
311 handle_speaker_battery_locked(dev);
312 pthread_mutex_unlock(&g_lock);
313 return 0;
314 }
315
316 static int
set_light_buttons(struct light_device_t * dev,struct light_state_t const * state)317 set_light_buttons(struct light_device_t* dev,
318 struct light_state_t const* state)
319 {
320 int err = 0;
321 if(!dev) {
322 return -1;
323 }
324 pthread_mutex_lock(&g_lock);
325 err = write_int(BUTTON_FILE, state->color & 0xFF);
326 pthread_mutex_unlock(&g_lock);
327 return err;
328 }
329
330 /** Close the lights device */
331 static int
close_lights(struct light_device_t * dev)332 close_lights(struct light_device_t *dev)
333 {
334 if (dev) {
335 free(dev);
336 }
337 return 0;
338 }
339
340
341 /******************************************************************************/
342
343 /**
344 * module methods
345 */
346
347 /** 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)348 static int open_lights(const struct hw_module_t* module, char const* name,
349 struct hw_device_t** device)
350 {
351 int (*set_light)(struct light_device_t* dev,
352 struct light_state_t const* state);
353
354 if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))
355 set_light = set_light_backlight;
356 #if LIGHTS_SUPPORT_BATTERY
357 else if (0 == strcmp(LIGHT_ID_BATTERY, name))
358 set_light = set_light_battery;
359 #endif
360 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
361 set_light = set_light_notifications;
362 else if (0 == strcmp(LIGHT_ID_BUTTONS, name))
363 set_light = set_light_buttons;
364 else if (0 == strcmp(LIGHT_ID_ATTENTION, name))
365 set_light = set_light_attention;
366 else
367 return -EINVAL;
368
369 pthread_once(&g_init, init_globals);
370
371 struct light_device_t *dev = malloc(sizeof(struct light_device_t));
372
373 if(!dev)
374 return -ENOMEM;
375
376 memset(dev, 0, sizeof(*dev));
377
378 dev->common.tag = HARDWARE_DEVICE_TAG;
379 dev->common.version = LIGHTS_DEVICE_API_VERSION_2_0;
380 dev->common.module = (struct hw_module_t*)module;
381 dev->common.close = (int (*)(struct hw_device_t*))close_lights;
382 dev->set_light = set_light;
383
384 *device = (struct hw_device_t*)dev;
385 return 0;
386 }
387
388 static struct hw_module_methods_t lights_module_methods = {
389 .open = open_lights,
390 };
391
392 /*
393 * The lights Module
394 */
395 struct hw_module_t HAL_MODULE_INFO_SYM = {
396 .tag = HARDWARE_MODULE_TAG,
397 .version_major = 1,
398 .version_minor = 0,
399 .id = LIGHTS_HARDWARE_MODULE_ID,
400 .name = "lights Module",
401 .author = "Google, Inc.",
402 .methods = &lights_module_methods,
403 };
404