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