blob: 61c6de26b27229ee9cb8490fc50aa347b39f7530 [file] [log] [blame]
Luca Stefani63bdfc12019-07-14 18:44:22 +02001/*
2 * Copyright (C) 2014 The CyanogenMod 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#include <cutils/log.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <pthread.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include <hardware/lights.h>
25
26/******************************************************************************/
27
28#define MAX_PATH_SIZE 80
29
30#define LED_LIGHT_OFF 0
31#define LED_LIGHT_ON 255
32
33enum {
34 ATTENTION = 0,
35 NOTIFICATION,
36 BATTERY,
37 LIGHT_MAX,
38};
39
40static pthread_once_t g_init = PTHREAD_ONCE_INIT;
41static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
42static struct light_state_t g_lights[LIGHT_MAX];
43
44#define LCD_BRIGHTNESS_FILE "/sys/class/backlight/panel0-backlight/brightness"
45
46#define GREEN_LED_BREATH_FILE "/sys/class/leds/green/breath"
47#define GREEN_LED_BRIGHTNESS_FILE "/sys/class/leds/green/brightness"
48#define GREEN_LED_DELAY_OFF_FILE "/sys/class/leds/green/delay_off"
49#define GREEN_LED_DELAY_ON_FILE "/sys/class/leds/green/delay_on"
50
51#define RED_LED_BREATH_FILE "/sys/class/leds/red/breath"
52#define RED_LED_BRIGHTNESS_FILE "/sys/class/leds/red/brightness"
53#define RED_LED_DELAY_OFF_FILE "/sys/class/leds/red/delay_off"
54#define RED_LED_DELAY_ON_FILE "/sys/class/leds/red/delay_on"
55
56/**
57 * device methods
58 */
59
60void init_globals(void) {
61 // init the mutex
62 pthread_mutex_init(&g_lock, NULL);
63}
64
65static int write_int(char const* path, int value) {
66 int fd;
67 static int already_warned = 0;
68
69 fd = open(path, O_RDWR);
70 if (fd >= 0) {
71 char buffer[20];
72 int bytes = sprintf(buffer, "%d\n", value);
73 int amt = write(fd, buffer, bytes);
74 close(fd);
75 return amt == -1 ? -errno : 0;
76 } else {
77 if (already_warned == 0) {
78 ALOGE("%s: failed to open %s\n", __func__, path);
79 already_warned = 1;
80 }
81 return -errno;
82 }
83}
84
85static int is_lit(struct light_state_t const* state) {
86 return state->color & 0x00ffffff;
87}
88
89static int rgb_to_brightness(struct light_state_t const* state) {
90 int color = state->color & 0x00ffffff;
91 return ((77 * ((color >> 16) & 0x00ff)) + (150 * ((color >> 8) & 0x00ff)) +
92 (29 * (color & 0x00ff))) >>
93 8;
94}
95
96static int set_light_backlight(__attribute__((unused)) struct light_device_t* dev,
97 struct light_state_t const* state) {
98 int err = 0;
99 int brightness = rgb_to_brightness(state);
100 pthread_mutex_lock(&g_lock);
101 err = write_int(LCD_BRIGHTNESS_FILE, brightness);
102 pthread_mutex_unlock(&g_lock);
103 return err;
104}
105
106static int set_light_locked(struct light_state_t const* state) {
107 int onMS, offMS;
108 int blink = 0;
109 int brightness_level;
110
111 switch (state->flashMode) {
112 case LIGHT_FLASH_TIMED:
113 case LIGHT_FLASH_HARDWARE:
114 offMS = state->flashOffMS;
115 onMS = state->flashOnMS;
116 break;
117 case LIGHT_FLASH_NONE:
118 default:
119 offMS = 0;
120 onMS = 0;
121 break;
122 }
123
124 if (onMS != 0 && offMS != 0) blink = 1;
125
126 if (is_lit(state))
127 brightness_level =
128 (state->color & 0xff000000) ? (state->color & 0xff000000) >> 24 : LED_LIGHT_ON;
129 else
130 brightness_level = LED_LIGHT_OFF;
131
132 if (blink) {
133 write_int(GREEN_LED_BREATH_FILE, 1);
134 write_int(GREEN_LED_DELAY_OFF_FILE, offMS);
135 write_int(GREEN_LED_DELAY_ON_FILE, onMS);
136 } else {
137 write_int(GREEN_LED_BRIGHTNESS_FILE, brightness_level);
138 }
139
140 return 0;
141}
142
143static int handle_led_prioritized_locked(struct light_state_t const* state) {
144 if (is_lit(&g_lights[ATTENTION]))
145 return set_light_locked(&g_lights[ATTENTION]);
146 else if (is_lit(&g_lights[NOTIFICATION]))
147 return set_light_locked(&g_lights[NOTIFICATION]);
148 else
149 return set_light_locked(state);
150}
151
152static int set_light_notifications(__attribute__((unused)) struct light_device_t* dev,
153 struct light_state_t const* state) {
154 int err = 0;
155 pthread_mutex_lock(&g_lock);
156 g_lights[NOTIFICATION] = *state;
157 err = handle_led_prioritized_locked(state);
158 pthread_mutex_unlock(&g_lock);
159 return err;
160}
161
162static int set_light_attention(__attribute__((unused)) struct light_device_t* dev,
163 struct light_state_t const* state) {
164 int err = 0;
165 pthread_mutex_lock(&g_lock);
166 g_lights[ATTENTION] = *state;
167 err = handle_led_prioritized_locked(state);
168 pthread_mutex_unlock(&g_lock);
169 return err;
170}
171
172static int set_light_battery(__attribute__((unused)) struct light_device_t* dev,
173 struct light_state_t const* state) {
174 int err = 0;
175
176 if (!dev) return -1;
177
178 pthread_mutex_lock(&g_lock);
179
180 int level = (state->color & 0xff000000) >> 24;
181
182 // sanity check
183 if (level < 0)
184 level = 0;
185 else if (level > 100)
186 level = 100;
187
188 // turn led off
189 write_int(GREEN_LED_BRIGHTNESS_FILE, LED_LIGHT_OFF);
190 write_int(RED_LED_BRIGHTNESS_FILE, LED_LIGHT_OFF);
191
192 if (is_lit(state)) {
193 if (level <= 15) {
194 write_int(RED_LED_BRIGHTNESS_FILE, 255);
195 } else if (level <= 99) {
196 write_int(GREEN_LED_BRIGHTNESS_FILE, 255);
197 write_int(RED_LED_BRIGHTNESS_FILE, 255);
198 } else {
199 write_int(GREEN_LED_BRIGHTNESS_FILE, 255);
200 }
201 }
202
203 pthread_mutex_unlock(&g_lock);
204 return err;
205}
206
207/** Close the lights device */
208static int close_lights(struct light_device_t* dev) {
209 if (dev) free(dev);
210
211 return 0;
212}
213
214/******************************************************************************/
215
216/**
217 * module methods
218 */
219
220/** Open a new instance of a lights device using name */
221static int open_lights(const struct hw_module_t* module, char const* name,
222 struct hw_device_t** device) {
223 int (*set_light)(struct light_device_t * dev, struct light_state_t const* state);
224
225 if (strcmp(LIGHT_ID_BACKLIGHT, name) == 0)
226 set_light = set_light_backlight;
227 else if (strcmp(LIGHT_ID_NOTIFICATIONS, name) == 0)
228 set_light = set_light_notifications;
229 else if (strcmp(LIGHT_ID_ATTENTION, name) == 0)
230 set_light = set_light_attention;
231 else if (strcmp(LIGHT_ID_BATTERY, name) == 0)
232 set_light = set_light_battery;
233 else
234 return -EINVAL;
235
236 pthread_once(&g_init, init_globals);
237
238 struct light_device_t* dev = malloc(sizeof(struct light_device_t));
239 memset(dev, 0, sizeof(*dev));
240
241 dev->common.tag = HARDWARE_DEVICE_TAG;
242 dev->common.version = 0;
243 dev->common.module = (struct hw_module_t*)module;
244 dev->common.close = (int (*)(struct hw_device_t*))close_lights;
245 dev->set_light = set_light;
246
247 *device = (struct hw_device_t*)dev;
248 return 0;
249}
250
251static struct hw_module_methods_t lights_module_methods = {
252 .open = open_lights,
253};
254
255/*
256 * The lights Module
257 */
258struct hw_module_t HAL_MODULE_INFO_SYM = {
259 .tag = HARDWARE_MODULE_TAG,
260 .version_major = 1,
261 .version_minor = 0,
262 .id = LIGHTS_HARDWARE_MODULE_ID,
263 .name = "MSM8916 lights Module",
264 .author = "Google, Inc.",
265 .methods = &lights_module_methods,
266};