Commit | Line | Data |
---|---|---|
f3fa1980 S |
1 | /* |
2 | * Copyright (C) 2016 The M.A.D. Team | |
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 <stdlib.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 struct light_state_t g_attention; | |
40 | static struct light_state_t g_notification; | |
41 | static struct light_state_t g_battery; | |
42 | ||
43 | char const*const RED_LED_FILE | |
44 | = "/sys/class/leds/red/brightness"; | |
45 | ||
46 | char const*const GREEN_LED_FILE | |
47 | = "/sys/class/leds/green/brightness"; | |
48 | ||
49 | char const*const BLUE_LED_FILE | |
50 | = "/sys/class/leds/blue/brightness"; | |
51 | ||
52 | char const*const LCD_FILE | |
53 | = "/sys/class/leds/lcd-backlight/brightness"; | |
54 | ||
55 | char const*const BUTTONS_FILE | |
56 | = "/sys/class/leds/button-backlight/brightness"; | |
57 | ||
58 | /** | |
59 | * device methods | |
60 | */ | |
61 | ||
62 | void init_globals(void) | |
63 | { | |
64 | // init the mutex | |
65 | pthread_mutex_init(&g_lock, NULL); | |
66 | } | |
67 | ||
68 | static int | |
69 | write_int(char const* path, int value) | |
70 | { | |
71 | int fd; | |
72 | static int already_warned = 0; | |
73 | ||
74 | fd = open(path, O_RDWR); | |
75 | if (fd >= 0) { | |
76 | char buffer[20]; | |
77 | int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value); | |
78 | ssize_t amt = write(fd, buffer, (size_t)bytes); | |
79 | close(fd); | |
80 | return amt == -1 ? -errno : 0; | |
81 | } else { | |
82 | if (already_warned == 0) { | |
83 | ALOGE("write_int failed to open %s\n", path); | |
84 | already_warned = 1; | |
85 | } | |
86 | return -errno; | |
87 | } | |
88 | } | |
89 | ||
90 | static int | |
91 | is_lit(struct light_state_t const* state) | |
92 | { | |
93 | return state->color & 0x00ffffff; | |
94 | } | |
95 | ||
96 | static int | |
97 | rgb_to_brightness(struct light_state_t const* state) | |
98 | { | |
99 | int color = state->color & 0x00ffffff; | |
100 | return ((77*((color>>16)&0x00ff)) | |
101 | + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8; | |
102 | } | |
103 | ||
104 | static int | |
105 | set_light_backlight(struct light_device_t* dev, | |
106 | struct light_state_t const* state) | |
107 | { | |
108 | if (!dev) { | |
109 | return -1; | |
110 | } | |
111 | int err = 0; | |
112 | int brightness = rgb_to_brightness(state); | |
113 | pthread_mutex_lock(&g_lock); | |
114 | err = write_int(LCD_FILE, brightness); | |
115 | pthread_mutex_unlock(&g_lock); | |
116 | return err; | |
117 | } | |
118 | ||
119 | static int | |
120 | set_light_buttons(struct light_device_t* dev, | |
121 | struct light_state_t const* state) | |
122 | { | |
123 | if (!dev) { | |
124 | return -1; | |
125 | } | |
126 | int err = 0; | |
127 | int on = is_lit(state); | |
128 | pthread_mutex_lock(&g_lock); | |
129 | err = write_int(BUTTONS_FILE, on ? 255 : 0); | |
130 | pthread_mutex_unlock(&g_lock); | |
131 | return err; | |
132 | } | |
133 | ||
134 | static int | |
135 | set_speaker_light_locked(struct light_device_t* dev, | |
136 | struct light_state_t const* state) | |
137 | { | |
138 | int red, green, blue, blink; | |
139 | int onMS, offMS; | |
140 | unsigned int colorRGB; | |
141 | ||
142 | if (!dev) { | |
143 | return -1; | |
144 | } | |
145 | ||
146 | switch (state->flashMode) { | |
147 | case LIGHT_FLASH_TIMED: | |
148 | onMS = state->flashOnMS; | |
149 | offMS = state->flashOffMS; | |
150 | break; | |
151 | case LIGHT_FLASH_NONE: | |
152 | default: | |
153 | onMS = 0; | |
154 | offMS = 0; | |
155 | break; | |
156 | } | |
157 | ||
158 | colorRGB = state->color; | |
159 | ||
160 | ALOGV("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n", | |
161 | state->flashMode, colorRGB, onMS, offMS); | |
162 | ||
163 | red = (colorRGB >> 16) & 0xFF; | |
164 | green = (colorRGB >> 8) & 0xFF; | |
165 | blue = colorRGB & 0xFF; | |
166 | blink = onMS > 0 && offMS > 0; | |
167 | ||
168 | write_int(RED_LED_FILE, 0); | |
169 | write_int(GREEN_LED_FILE, 0); | |
170 | write_int(BLUE_LED_FILE, 0); | |
171 | ||
172 | if (blink) { | |
173 | if (red >= 128) { | |
174 | write_int(RED_LED_FILE, 128); | |
175 | } | |
176 | if (green >= 128) { | |
177 | write_int(GREEN_LED_FILE, 128); | |
178 | } | |
179 | if (blue >= 128) { | |
180 | write_int(BLUE_LED_FILE, 128); | |
181 | } | |
182 | } | |
183 | else { | |
184 | if (red >= 128) { | |
185 | write_int(RED_LED_FILE, 255); | |
186 | } | |
187 | if (green >= 128) { | |
188 | write_int(GREEN_LED_FILE, 255); | |
189 | } | |
190 | if (blue >= 128) { | |
191 | write_int(BLUE_LED_FILE, 255); | |
192 | } | |
193 | } | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | static void | |
199 | handle_speaker_light_locked(struct light_device_t* dev) | |
200 | { | |
201 | if (is_lit(&g_attention)) { | |
202 | set_speaker_light_locked(dev, &g_attention); | |
203 | } else if (is_lit(&g_notification)) { | |
204 | set_speaker_light_locked(dev, &g_notification); | |
205 | } else { | |
206 | set_speaker_light_locked(dev, &g_battery); | |
207 | } | |
208 | } | |
209 | ||
210 | static int | |
211 | set_light_battery(struct light_device_t* dev, | |
212 | struct light_state_t const* state) | |
213 | { | |
214 | pthread_mutex_lock(&g_lock); | |
215 | g_battery = *state; | |
216 | handle_speaker_light_locked(dev); | |
217 | pthread_mutex_unlock(&g_lock); | |
218 | return 0; | |
219 | } | |
220 | ||
221 | static int | |
222 | set_light_notifications(struct light_device_t* dev, | |
223 | struct light_state_t const* state) | |
224 | { | |
225 | pthread_mutex_lock(&g_lock); | |
226 | g_notification = *state; | |
227 | handle_speaker_light_locked(dev); | |
228 | pthread_mutex_unlock(&g_lock); | |
229 | return 0; | |
230 | } | |
231 | ||
232 | static int | |
233 | set_light_attention(struct light_device_t* dev, | |
234 | struct light_state_t const* state) | |
235 | { | |
236 | pthread_mutex_lock(&g_lock); | |
237 | g_attention = *state; | |
238 | handle_speaker_light_locked(dev); | |
239 | pthread_mutex_unlock(&g_lock); | |
240 | return 0; | |
241 | } | |
242 | ||
243 | ||
244 | /** Close the lights device */ | |
245 | static int | |
246 | close_lights(struct light_device_t *dev) | |
247 | { | |
248 | if (dev) { | |
249 | free(dev); | |
250 | } | |
251 | return 0; | |
252 | } | |
253 | ||
254 | ||
255 | /******************************************************************************/ | |
256 | ||
257 | /** | |
258 | * module methods | |
259 | */ | |
260 | ||
261 | /** Open a new instance of a lights device using name */ | |
262 | static int open_lights(const struct hw_module_t* module, char const* name, | |
263 | struct hw_device_t** device) | |
264 | { | |
265 | int (*set_light)(struct light_device_t* dev, | |
266 | struct light_state_t const* state); | |
267 | ||
268 | if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) | |
269 | set_light = set_light_backlight; | |
270 | else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) | |
271 | set_light = set_light_buttons; | |
272 | else if (0 == strcmp(LIGHT_ID_BATTERY, name)) | |
273 | set_light = set_light_battery; | |
274 | else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) | |
275 | set_light = set_light_notifications; | |
276 | else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) | |
277 | set_light = set_light_attention; | |
278 | else | |
279 | return -EINVAL; | |
280 | ||
281 | pthread_once(&g_init, init_globals); | |
282 | ||
283 | struct light_device_t *dev = malloc(sizeof(struct light_device_t)); | |
284 | ||
285 | if (!dev) | |
286 | return -ENOMEM; | |
287 | ||
288 | memset(dev, 0, sizeof(*dev)); | |
289 | ||
290 | dev->common.tag = HARDWARE_DEVICE_TAG; | |
291 | dev->common.version = 0; | |
292 | dev->common.module = (struct hw_module_t*)module; | |
293 | dev->common.close = (int (*)(struct hw_device_t*))close_lights; | |
294 | dev->set_light = set_light; | |
295 | ||
296 | *device = (struct hw_device_t*)dev; | |
297 | return 0; | |
298 | } | |
299 | ||
300 | ||
301 | static struct hw_module_methods_t lights_module_methods = { | |
302 | .open = open_lights, | |
303 | }; | |
304 | ||
305 | /* | |
306 | * The lights Module | |
307 | */ | |
308 | struct hw_module_t HAL_MODULE_INFO_SYM = { | |
309 | .tag = HARDWARE_MODULE_TAG, | |
310 | .version_major = 1, | |
311 | .version_minor = 0, | |
312 | .id = LIGHTS_HARDWARE_MODULE_ID, | |
313 | .name = "M.A.D. Lights Module", | |
314 | .author = "M.A.D. Team", | |
315 | .methods = &lights_module_methods, | |
316 | }; |