1 /*
2 * Copyright (C) 2014 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 #define LOG_TAG "lights"
18
19 #include <cutils/log.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
28 #include <sys/ioctl.h>
29 #include <sys/types.h>
30
31 #include <hardware/lights.h>
32 #include <hardware/hardware.h>
33
34 #define ALOG_ONCE(mask, op, ...) \
35 do { \
36 if (!(mask & op)) { \
37 ALOGE(__VA_ARGS__); \
38 mask |= op; \
39 } \
40 } while (0);
41 #define OP_WRITE_OPEN (1 << 0)
42 #define OP_BRIGHTNESS_PATH (1 << 1)
43 #define OP_BRIGHTNESS_VALUE (1 << 2)
44 #define OP_BRIGHTNESS_WRITE (1 << 3)
45 #define OP_MAX_BRIGHTNESS_PATH (1 << 4)
46 #define OP_MAX_BRIGHTNESS_OPEN (1 << 5)
47 #define OP_MAX_BRIGHTNESS_READ (1 << 6)
48
49 struct dragon_lights {
50 struct light_device_t base;
51
52 pthread_mutex_t lock;
53 char const *sysfs_path;
54
55 int max_brightness;
56
57 unsigned long logged_failures;
58 };
59
60 static char const * kBacklightPath =
61 "/sys/class/backlight/lpm102a188a-backlight";
62 static const int kNumBrightnessLevels = 16;
63 static const int kBrightnessLevels[] =
64 {8, 25, 30, 40, 55, 65, 75, 85, 95, 105, 120, 135, 160, 180, 200, 220};
65
to_dragon_lights(struct light_device_t * dev)66 static struct dragon_lights *to_dragon_lights(struct light_device_t *dev)
67 {
68 return (struct dragon_lights *)dev;
69 }
70
write_brightness(struct dragon_lights * lights,int brightness)71 static int write_brightness(struct dragon_lights *lights, int brightness)
72 {
73 char buffer[20], path[PATH_MAX];
74 int fd, bytes, amt, ret = 0;
75
76 bytes = snprintf(path, sizeof(path), "%s/brightness",
77 lights->sysfs_path);
78 if (bytes < 0 || (size_t)bytes >= sizeof(path)) {
79 ALOG_ONCE(lights->logged_failures, OP_BRIGHTNESS_PATH,
80 "failed to create brightness path %d\n", bytes);
81 return -EINVAL;
82 }
83
84 fd = open(path, O_RDWR);
85 if (fd < 0) {
86 ALOG_ONCE(lights->logged_failures, OP_WRITE_OPEN,
87 "write_int failed to open %s/%d\n", path, errno);
88 return -errno;
89 }
90
91 bytes = snprintf(buffer, sizeof(buffer), "%d\n", brightness);
92 if (bytes < 0 || (size_t)bytes >= sizeof(buffer)) {
93 ALOG_ONCE(lights->logged_failures, OP_BRIGHTNESS_VALUE,
94 "failed to create brightness value %d/%d\n",
95 brightness, bytes);
96 ret = -EINVAL;
97 goto out;
98 }
99 amt = write(fd, buffer, bytes);
100 if (amt != bytes) {
101 ALOG_ONCE(lights->logged_failures, OP_BRIGHTNESS_WRITE,
102 "failed to write brightness value %d/%d\n", amt,
103 bytes);
104 ret = amt == -1 ? -errno : -EINVAL;
105 goto out;
106 }
107
108 out:
109 close(fd);
110 return ret;
111 }
112
read_max_brightness(struct dragon_lights * lights,int * value)113 static int read_max_brightness(struct dragon_lights *lights, int *value)
114 {
115 char buffer[20], path[PATH_MAX];
116 int ret = 0, fd, bytes;
117
118 bytes = snprintf(path, sizeof(path), "%s/max_brightness",
119 lights->sysfs_path);
120 if (bytes < 0 || (size_t)bytes >= sizeof(path)) {
121 ALOG_ONCE(lights->logged_failures, OP_MAX_BRIGHTNESS_PATH,
122 "failed to create max_brightness path %d\n", bytes);
123 return -EINVAL;
124 }
125
126 fd = open(path, O_RDONLY);
127 if (fd < 0) {
128 ALOG_ONCE(lights->logged_failures, OP_MAX_BRIGHTNESS_OPEN,
129 "failed to open max_brightness %s/%d\n", path, errno);
130 return -errno;
131 }
132
133 bytes = read(fd, buffer, sizeof(buffer));
134 if (bytes <= 0) {
135 ALOG_ONCE(lights->logged_failures, OP_MAX_BRIGHTNESS_READ,
136 "failed to read max_brightness %s/%d\n", path, errno);
137 ret = -errno;
138 goto out;
139 }
140
141 *value = atoi(buffer);
142
143 out:
144 close(fd);
145 return ret;
146 }
147
rgb_to_brightness(struct light_state_t const * state)148 static int rgb_to_brightness(struct light_state_t const *state)
149 {
150 int color = state->color & 0x00ffffff;
151 return ((77 * ((color >> 16) & 0x00ff))
152 + (150 * ((color >> 8) & 0x00ff)) +
153 (29 * (color & 0x00ff))) >> 8;
154 }
155
set_light_backlight(struct light_device_t * dev,struct light_state_t const * state)156 static int set_light_backlight(struct light_device_t *dev,
157 struct light_state_t const *state)
158 {
159 struct dragon_lights *lights = to_dragon_lights(dev);
160 int err, brightness_idx;
161 int brightness = rgb_to_brightness(state);
162
163 if (brightness > 0) {
164 // Get the bin number for brightness (0 to kNumBrightnessLevels - 1)
165 brightness_idx = (brightness - 1) * kNumBrightnessLevels / 0xff;
166
167 // Get brightness level
168 brightness = kBrightnessLevels[brightness_idx];
169 }
170
171 pthread_mutex_lock(&lights->lock);
172 err = write_brightness(lights, brightness);
173 pthread_mutex_unlock(&lights->lock);
174
175 return err;
176 }
177
close_lights(struct hw_device_t * dev)178 static int close_lights(struct hw_device_t *dev)
179 {
180 struct dragon_lights *lights = (struct dragon_lights *)dev;
181 if (lights)
182 free(lights);
183 return 0;
184 }
185
open_lights(const struct hw_module_t * module,char const * name,struct hw_device_t ** device)186 static int open_lights(const struct hw_module_t *module, char const *name,
187 struct hw_device_t **device)
188 {
189 struct dragon_lights *lights;
190 int ret;
191
192 // Only support backlight at the moment
193 if (strcmp(LIGHT_ID_BACKLIGHT, name))
194 return -EINVAL;
195
196 lights = malloc(sizeof(*lights));
197 if (lights == NULL) {
198 ALOGE("failed to allocate lights memory");
199 return -ENOMEM;
200 }
201 memset(lights, 0, sizeof(*lights));
202
203 pthread_mutex_init(&lights->lock, NULL);
204
205 lights->sysfs_path = kBacklightPath;
206
207 ret = read_max_brightness(lights, &lights->max_brightness);
208 if (ret) {
209 close_lights((struct hw_device_t *)lights);
210 return ret;
211 }
212
213 lights->base.common.tag = HARDWARE_DEVICE_TAG;
214 lights->base.common.version = 0;
215 lights->base.common.module = (struct hw_module_t *)module;
216 lights->base.common.close = close_lights;
217 lights->base.set_light = set_light_backlight;
218
219 *device = (struct hw_device_t *)lights;
220 return 0;
221 }
222
223 static struct hw_module_methods_t lights_methods = {
224 .open = open_lights,
225 };
226
227 struct hw_module_t HAL_MODULE_INFO_SYM = {
228 .tag = HARDWARE_MODULE_TAG,
229 .version_major = 1,
230 .version_minor = 0,
231 .id = LIGHTS_HARDWARE_MODULE_ID,
232 .name = "dragon lights module",
233 .author = "Google, Inc.",
234 .methods = &lights_methods,
235 };
236