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
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 <malloc.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 #define MAX_PATH_SIZE 80
38
39 static pthread_once_t g_init = PTHREAD_ONCE_INIT;
40 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
41 static struct light_state_t g_notification;
42 static struct light_state_t g_battery;
43 static int g_attention = 0;
44
45 char const*const RED_LED_FILE
46 = "/sys/class/leds/red/brightness";
47
48 char const*const GREEN_LED_FILE
49 = "/sys/class/leds/green/brightness";
50
51 char const*const BLUE_LED_FILE
52 = "/sys/class/leds/blue/brightness";
53
54 char const*const WHITE_LED_FILE
55 = "/sys/class/leds/white/brightness";
56
57 char const*const LCD_FILE
58 = "/sys/class/leds/lcd-backlight/brightness";
59
60 char const*const LED_FREQ_FILE
61 = "/sys/class/leds/%s/device/grpfreq";
62
63 char const*const LED_PWM_FILE
64 = "/sys/class/leds/%s/device/grppwm";
65
66 char const*const LED_BLINK_FILE
67 = "/sys/class/leds/%s/device/blink";
68
69 char const*const LED_LOCK_UPDATE_FILE
70 = "/sys/class/leds/%s/device/lock";
71
72 /**
73 * device methods
74 */
75
init_globals(void)76 void init_globals(void)
77 {
78 // init the mutex
79 pthread_mutex_init(&g_lock, NULL);
80 }
81
82 static int
write_int(char const * path,int value)83 write_int(char const* path, int value)
84 {
85 int fd;
86 static int already_warned = 0;
87
88 fd = open(path, O_RDWR);
89 if (fd >= 0) {
90 char buffer[20];
91 int bytes = sprintf(buffer, "%d\n", value);
92 int amt = write(fd, buffer, bytes);
93 close(fd);
94 return amt == -1 ? -errno : 0;
95 } else {
96 if (already_warned == 0) {
97 ALOGE("write_int failed to open %s\n", path);
98 already_warned = 1;
99 }
100 return -errno;
101 }
102 }
103
104 static int
is_avail(char const * path)105 is_avail(char const* path)
106 {
107 int fd = open(path, O_RDWR);
108 if (fd >= 0) {
109 close(fd);
110 return 1;
111 } else {
112 return 0;
113 }
114 }
115
116 static int
is_lit(struct light_state_t const * state)117 is_lit(struct light_state_t const* state)
118 {
119 return state->color & 0x00ffffff;
120 }
121
122 static int
rgb_to_brightness(struct light_state_t const * state)123 rgb_to_brightness(struct light_state_t const* state)
124 {
125 int color = state->color & 0x00ffffff;
126 return ((77*((color>>16)&0x00ff))
127 + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
128 }
129
130 static int
set_light_backlight(struct light_device_t * dev,struct light_state_t const * state)131 set_light_backlight(struct light_device_t* dev,
132 struct light_state_t const* state)
133 {
134 int err = 0;
135 int brightness = rgb_to_brightness(state);
136 pthread_mutex_lock(&g_lock);
137 err = write_int(LCD_FILE, brightness);
138 pthread_mutex_unlock(&g_lock);
139 return err;
140 }
141
142 static int
set_speaker_light_locked(struct light_device_t * dev,struct light_state_t const * state)143 set_speaker_light_locked(struct light_device_t* dev,
144 struct light_state_t const* state)
145 {
146 int rgb;
147 int blink, freq, pwm;
148 int onMS, offMS;
149 unsigned int colorRGB;
150
151 switch (state->flashMode) {
152 case LIGHT_FLASH_TIMED:
153 onMS = state->flashOnMS;
154 offMS = state->flashOffMS;
155 break;
156 case LIGHT_FLASH_NONE:
157 default:
158 onMS = 0;
159 offMS = 0;
160 break;
161 }
162
163 colorRGB = state->color;
164
165 #if 0
166 ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n",
167 state->flashMode, colorRGB, onMS, offMS);
168 #endif
169
170 if (onMS > 0 && offMS > 0) {
171 int totalMS = onMS + offMS;
172
173 // the LED appears to blink about once per second if freq is 20
174 // 1000ms / 20 = 50
175 freq = totalMS / 50;
176 // pwm specifies the ratio of ON versus OFF
177 // pwm = 0 -> always off
178 // pwm = 255 => always on
179 pwm = (onMS * 255) / totalMS;
180
181 // the low 4 bits are ignored, so round up if necessary
182 if (pwm > 0 && pwm < 16)
183 pwm = 16;
184
185 blink = 1;
186 } else {
187 blink = 0;
188 freq = 0;
189 pwm = 0;
190 }
191
192 // Prefer RGB LEDs, fallback to white LED
193 rgb = is_avail(RED_LED_FILE) && is_avail(GREEN_LED_FILE) && is_avail(BLUE_LED_FILE);
194
195 char lock_update_file[MAX_PATH_SIZE];
196 char freq_file[MAX_PATH_SIZE];
197 char pwm_file[MAX_PATH_SIZE];
198 char blink_file[MAX_PATH_SIZE];
199 sprintf(lock_update_file, LED_LOCK_UPDATE_FILE, rgb ? "red" : "white");
200 sprintf(freq_file, LED_FREQ_FILE, rgb ? "red" : "white");
201 sprintf(pwm_file, LED_PWM_FILE, rgb ? "red" : "white");
202 sprintf(blink_file, LED_BLINK_FILE, rgb ? "red" : "white");
203
204 write_int(lock_update_file, 1); // for LED On/Off synchronization
205
206 if (rgb) {
207 write_int(RED_LED_FILE, (colorRGB >> 16) & 0xFF);
208 write_int(GREEN_LED_FILE, (colorRGB >> 8) & 0xFF);
209 write_int(BLUE_LED_FILE, colorRGB & 0xFF);
210 } else {
211 // See hardware/libhardware/include/hardware/lights.h
212 int brightness = ((77 * ((colorRGB >> 16) & 0xFF)) +
213 (150 * ((colorRGB >> 8) & 0xFF)) +
214 (29 * (colorRGB & 0xFF))) >> 8;
215 write_int(WHITE_LED_FILE, (int) brightness);
216 }
217
218 if (blink) {
219 write_int(freq_file, freq);
220 write_int(pwm_file, pwm);
221 }
222 write_int(blink_file, blink);
223
224 write_int(lock_update_file, 0);
225
226 return 0;
227 }
228
229 static void
handle_speaker_battery_locked(struct light_device_t * dev)230 handle_speaker_battery_locked(struct light_device_t* dev)
231 {
232 if (is_lit(&g_battery)) {
233 set_speaker_light_locked(dev, &g_battery);
234 } else {
235 set_speaker_light_locked(dev, &g_notification);
236 }
237 }
238
239 static int
set_light_notifications(struct light_device_t * dev,struct light_state_t const * state)240 set_light_notifications(struct light_device_t* dev,
241 struct light_state_t const* state)
242 {
243 pthread_mutex_lock(&g_lock);
244 g_notification = *state;
245 handle_speaker_battery_locked(dev);
246 pthread_mutex_unlock(&g_lock);
247 return 0;
248 }
249
250 static int
set_light_attention(struct light_device_t * dev,struct light_state_t const * state)251 set_light_attention(struct light_device_t* dev,
252 struct light_state_t const* state)
253 {
254 pthread_mutex_lock(&g_lock);
255 if (state->flashMode == LIGHT_FLASH_HARDWARE) {
256 g_attention = state->flashOnMS;
257 } else if (state->flashMode == LIGHT_FLASH_NONE) {
258 g_attention = 0;
259 }
260 handle_speaker_battery_locked(dev);
261 pthread_mutex_unlock(&g_lock);
262 return 0;
263 }
264
265
266 /** Close the lights device */
267 static int
close_lights(struct light_device_t * dev)268 close_lights(struct light_device_t *dev)
269 {
270 if (dev) {
271 free(dev);
272 }
273 return 0;
274 }
275
276
277 /******************************************************************************/
278
279 /**
280 * module methods
281 */
282
283 /** 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)284 static int open_lights(const struct hw_module_t* module, char const* name,
285 struct hw_device_t** device)
286 {
287 int (*set_light)(struct light_device_t* dev,
288 struct light_state_t const* state);
289
290 if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))
291 set_light = set_light_backlight;
292 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
293 set_light = set_light_notifications;
294 else if (0 == strcmp(LIGHT_ID_ATTENTION, name))
295 set_light = set_light_attention;
296 else
297 return -EINVAL;
298
299 pthread_once(&g_init, init_globals);
300
301 struct light_device_t *dev = malloc(sizeof(struct light_device_t));
302 memset(dev, 0, sizeof(*dev));
303
304 dev->common.tag = HARDWARE_DEVICE_TAG;
305 dev->common.version = 0;
306 dev->common.module = (struct hw_module_t*)module;
307 dev->common.close = (int (*)(struct hw_device_t*))close_lights;
308 dev->set_light = set_light;
309
310 *device = (struct hw_device_t*)dev;
311 return 0;
312 }
313
314 static struct hw_module_methods_t lights_module_methods = {
315 .open = open_lights,
316 };
317
318 /*
319 * The lights Module
320 */
321 struct hw_module_t HAL_MODULE_INFO_SYM = {
322 .tag = HARDWARE_MODULE_TAG,
323 .version_major = 1,
324 .version_minor = 0,
325 .id = LIGHTS_HARDWARE_MODULE_ID,
326 .name = "lights Module",
327 .author = "Google, Inc.",
328 .methods = &lights_module_methods,
329 };
330