1 /*
2 * libbacklight - userspace interface to Linux backlight control
3 *
4 * Copyright © 2012 Intel Corporation
5 * Copyright 2010 Red Hat <mjg@redhat.com>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 *
27 * Authors:
28 * Matthew Garrett <mjg@redhat.com>
29 * Tiago Vignatti <vignatti@freedesktop.org>
30 */
31
32 #include "config.h"
33
34 #include "libbacklight.h"
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <linux/types.h>
40 #include <dirent.h>
41 #include <drm.h>
42 #include <fcntl.h>
43 #include <malloc.h>
44 #include <string.h>
45 #include <errno.h>
46
47 #include "shared/string-helpers.h"
48
backlight_get(struct backlight * backlight,char * node)49 static long backlight_get(struct backlight *backlight, char *node)
50 {
51 char buffer[100];
52 char *path;
53 int fd, value;
54 long ret;
55
56 if (asprintf(&path, "%s/%s", backlight->path, node) < 0)
57 return -ENOMEM;
58 fd = open(path, O_RDONLY);
59 if (fd < 0) {
60 ret = -1;
61 goto out;
62 }
63
64 ret = read(fd, &buffer, sizeof(buffer));
65 if (ret < 1) {
66 ret = -1;
67 goto out;
68 }
69
70 if (!safe_strtoint(buffer, &value)) {
71 ret = -1;
72 goto out;
73 }
74
75 ret = value;
76
77 out:
78 if (fd >= 0)
79 close(fd);
80 free(path);
81 return ret;
82 }
83
backlight_get_brightness(struct backlight * backlight)84 long backlight_get_brightness(struct backlight *backlight)
85 {
86 return backlight_get(backlight, "brightness");
87 }
88
backlight_get_max_brightness(struct backlight * backlight)89 long backlight_get_max_brightness(struct backlight *backlight)
90 {
91 return backlight_get(backlight, "max_brightness");
92 }
93
backlight_get_actual_brightness(struct backlight * backlight)94 long backlight_get_actual_brightness(struct backlight *backlight)
95 {
96 return backlight_get(backlight, "actual_brightness");
97 }
98
backlight_set_brightness(struct backlight * backlight,long brightness)99 long backlight_set_brightness(struct backlight *backlight, long brightness)
100 {
101 char *path;
102 char *buffer = NULL;
103 int fd;
104 long ret;
105
106 if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0)
107 return -ENOMEM;
108
109 fd = open(path, O_RDWR);
110 if (fd < 0) {
111 ret = -1;
112 goto out;
113 }
114
115 ret = read(fd, &buffer, sizeof(buffer));
116 if (ret < 1) {
117 ret = -1;
118 goto out;
119 }
120
121 if (asprintf(&buffer, "%ld", brightness) < 0) {
122 ret = -1;
123 goto out;
124 }
125
126 ret = write(fd, buffer, strlen(buffer));
127 if (ret < 0) {
128 ret = -1;
129 goto out;
130 }
131
132 ret = backlight_get_brightness(backlight);
133 backlight->brightness = ret;
134 out:
135 free(buffer);
136 free(path);
137 if (fd >= 0)
138 close(fd);
139 return ret;
140 }
141
backlight_destroy(struct backlight * backlight)142 void backlight_destroy(struct backlight *backlight)
143 {
144 if (!backlight)
145 return;
146
147 if (backlight->path)
148 free(backlight->path);
149
150 free(backlight);
151 }
152
backlight_init(struct udev_device * drm_device,uint32_t connector_type)153 struct backlight *backlight_init(struct udev_device *drm_device,
154 uint32_t connector_type)
155 {
156 const char *syspath = NULL;
157 char *pci_name = NULL;
158 char *chosen_path = NULL;
159 char *path = NULL;
160 DIR *backlights = NULL;
161 struct dirent *entry;
162 enum backlight_type type = 0;
163 char buffer[100];
164 struct backlight *backlight = NULL;
165 int ret;
166
167 if (!drm_device)
168 return NULL;
169
170 syspath = udev_device_get_syspath(drm_device);
171 if (!syspath)
172 return NULL;
173
174 if (asprintf(&path, "%s/%s", syspath, "device") < 0)
175 return NULL;
176
177 ret = readlink(path, buffer, sizeof(buffer) - 1);
178 free(path);
179 if (ret < 0)
180 return NULL;
181
182 buffer[ret] = '\0';
183 pci_name = basename(buffer);
184
185 if (connector_type <= 0)
186 return NULL;
187
188 backlights = opendir("/sys/class/backlight");
189 if (!backlights)
190 return NULL;
191
192 /* Find the "best" backlight for the device. Firmware
193 interfaces are preferred over platform interfaces are
194 preferred over raw interfaces. For raw interfaces we'll
195 check if the device ID in the form of pci match, while
196 for firmware interfaces we require the pci ID to
197 match. It's assumed that platform interfaces always match,
198 since we can't actually associate them with IDs.
199
200 A further awkwardness is that, while it's theoretically
201 possible for an ACPI interface to include support for
202 changing the backlight of external devices, it's unlikely
203 to ever be done. It's effectively impossible for a platform
204 interface to do so. So if we get asked about anything that
205 isn't LVDS or eDP, we pretty much have to require that the
206 control be supplied via a raw interface */
207
208 while ((entry = readdir(backlights))) {
209 char *backlight_path;
210 char *parent;
211 enum backlight_type entry_type;
212 int fd;
213
214 if (entry->d_name[0] == '.')
215 continue;
216
217 if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
218 entry->d_name) < 0)
219 goto err;
220
221 if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) {
222 free(backlight_path);
223 goto err;
224 }
225
226 fd = open(path, O_RDONLY);
227
228 if (fd < 0)
229 goto out;
230
231 ret = read (fd, &buffer, sizeof(buffer));
232 close (fd);
233
234 if (ret < 1)
235 goto out;
236
237 buffer[ret] = '\0';
238
239 if (!strncmp(buffer, "raw\n", sizeof(buffer)))
240 entry_type = BACKLIGHT_RAW;
241 else if (!strncmp(buffer, "platform\n", sizeof(buffer)))
242 entry_type = BACKLIGHT_PLATFORM;
243 else if (!strncmp(buffer, "firmware\n", sizeof(buffer)))
244 entry_type = BACKLIGHT_FIRMWARE;
245 else
246 goto out;
247
248 if (connector_type != DRM_MODE_CONNECTOR_LVDS &&
249 connector_type != DRM_MODE_CONNECTOR_eDP) {
250 /* External displays are assumed to require
251 gpu control at the moment */
252 if (entry_type != BACKLIGHT_RAW)
253 goto out;
254 }
255
256 free (path);
257
258 if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
259 goto err;
260
261 ret = readlink(path, buffer, sizeof(buffer) - 1);
262
263 if (ret < 0)
264 goto out;
265
266 buffer[ret] = '\0';
267
268 parent = basename(buffer);
269
270 /* Perform matching for raw and firmware backlights -
271 platform backlights have to be assumed to match */
272 if (entry_type == BACKLIGHT_RAW ||
273 entry_type == BACKLIGHT_FIRMWARE) {
274 if (!(pci_name && !strcmp(pci_name, parent)))
275 goto out;
276 }
277
278 if (entry_type < type)
279 goto out;
280
281 type = entry_type;
282
283 if (chosen_path)
284 free(chosen_path);
285 chosen_path = strdup(backlight_path);
286
287 out:
288 free(backlight_path);
289 free(path);
290 }
291
292 if (!chosen_path)
293 goto err;
294
295 backlight = malloc(sizeof(struct backlight));
296
297 if (!backlight)
298 goto err;
299
300 backlight->path = chosen_path;
301 backlight->type = type;
302
303 backlight->max_brightness = backlight_get_max_brightness(backlight);
304 if (backlight->max_brightness < 0)
305 goto err;
306
307 backlight->brightness = backlight_get_actual_brightness(backlight);
308 if (backlight->brightness < 0)
309 goto err;
310
311 closedir(backlights);
312 return backlight;
313 err:
314 closedir(backlights);
315 free (chosen_path);
316 free (backlight);
317 return NULL;
318 }
319