blob: b2e19bf9f1e6b8f0f36f21e9cae61bb960d55e7b [file] [log] [blame]
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -05001/*
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#define _GNU_SOURCE
33
34#include <libbacklight.h>
35#include <stdio.h>
36#include <linux/types.h>
37#include <dirent.h>
38#include <drm/drm_mode.h>
39#include <fcntl.h>
40#include <malloc.h>
41#include <string.h>
42#include <errno.h>
43
44static const char *output_names[] = { "Unknown",
45 "VGA",
46 "DVI-I",
47 "DVI-D",
48 "DVI-A",
49 "Composite",
50 "SVIDEO",
51 "LVDS",
52 "Component",
53 "9-pin DIN",
54 "DisplayPort"
55 "HDMI Type A",
56 "HDMI Type B",
57 "TV",
58 "Embedded DisplayPort"
59};
60
61static long backlight_get(struct backlight *backlight, char *node)
62{
63 char buffer[100];
64 char *path;
65 int fd;
66 long value, ret;
67
68 if (asprintf(&path, "%s/%s", backlight->path, node) < 0)
69 return -ENOMEM
70;
71 fd = open(path, O_RDONLY);
72 if (fd < 0) {
73 ret = -1;
74 goto out;
75 }
76
77 ret = read(fd, &buffer, sizeof(buffer));
78 if (ret < 1) {
79 ret = -1;
80 goto out;
81 }
82
83 value = strtol(buffer, NULL, 10);
84 ret = value;
85out:
86 close(fd);
87 if (path)
88 free(path);
89 return ret;
90}
91
92long backlight_get_brightness(struct backlight *backlight)
93{
94 return backlight_get(backlight, "brightness");
95}
96
97long backlight_get_max_brightness(struct backlight *backlight)
98{
99 return backlight_get(backlight, "max_brightness");
100}
101
102long backlight_get_actual_brightness(struct backlight *backlight)
103{
104 return backlight_get(backlight, "actual_brightness");
105}
106
107long backlight_set_brightness(struct backlight *backlight, long brightness)
108{
109 char *path;
110 char *buffer = NULL;
111 int fd;
112 long ret;
113
114 if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0)
115 return -ENOMEM;
116
117 fd = open(path, O_RDWR);
118 if (fd < 0) {
119 ret = -1;
120 goto out;
121 }
122
123 ret = read(fd, &buffer, sizeof(buffer));
124 if (ret < 1) {
125 ret = -1;
126 goto out;
127 }
128
129 if (asprintf(&buffer, "%ld", brightness) < 0)
130 return -ENOMEM;
131
132 ret = write(fd, buffer, strlen(buffer));
133 if (ret < 0) {
134 ret = -1;
135 goto out;
136 }
137
138 ret = backlight_get_brightness(backlight);
139 backlight->brightness = ret;
140out:
141 if (buffer)
142 free(buffer);
143 if (path)
144 free(path);
145 close(fd);
146 return ret;
147}
148
149void backlight_destroy(struct backlight *backlight)
150{
151 if (!backlight)
152 return;
153
154 if (backlight->path)
155 free(backlight->path);
156
157 free(backlight);
158}
159
160struct backlight *backlight_init(struct udev_device *drm_device,
161 uint32_t connector_type)
162{
163 const char *syspath = NULL;
164 char *pci_name = NULL;
165 char *chosen_path = NULL;
166 char *path = NULL;
167 DIR *backlights;
168 struct dirent *entry;
169 enum backlight_type type = 0;
170 char buffer[100];
171 struct backlight *backlight;
172 int err, ret;
173
174 if (!drm_device)
175 return NULL;
176
177 syspath = udev_device_get_syspath(drm_device);
178 if (!syspath)
179 return NULL;
180
181 if (asprintf(&path, "%s/%s", syspath, "device") < 0)
182 return NULL;
183
184 ret = readlink(path, buffer, sizeof(buffer));
185 free(path);
186 if (ret < 0)
187 return NULL;
188
189 buffer[ret] = '\0';
190 pci_name = basename(buffer);
191
192 if (connector_type <= 0)
193 return NULL;
194
195 backlights = opendir("/sys/class/backlight");
196 if (!backlights)
197 return NULL;
198
199 /* Find the "best" backlight for the device. Firmware
200 interfaces are preferred over platform interfaces are
201 preferred over raw interfaces. For raw interfaces we'll
202 check if the device ID in the form of pci match, while
203 for firmware interfaces we require the pci ID to
204 match. It's assumed that platform interfaces always match,
205 since we can't actually associate them with IDs.
206
207 A further awkwardness is that, while it's theoretically
208 possible for an ACPI interface to include support for
209 changing the backlight of external devices, it's unlikely
210 to ever be done. It's effectively impossible for a platform
211 interface to do so. So if we get asked about anything that
212 isn't LVDS or eDP, we pretty much have to require that the
213 control be supplied via a raw interface */
214
215 while ((entry = readdir(backlights))) {
216 char *backlight_path;
217 char *parent;
218 enum backlight_type entry_type;
219 int fd;
220
221 if (entry->d_name[0] == '.')
222 continue;
223
224 if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
225 entry->d_name) < 0)
226 return NULL;
227
228 if (asprintf(&path, "%s/%s", backlight_path, "type") < 0)
229 return NULL;
230
231 fd = open(path, O_RDONLY);
232
233 if (fd < 0)
234 goto out;
235
236 ret = read (fd, &buffer, sizeof(buffer));
237 close (fd);
238
239 if (ret < 1)
240 goto out;
241
242 buffer[ret] = '\0';
243
244 if (!strncmp(buffer, "raw\n", sizeof(buffer)))
245 entry_type = BACKLIGHT_RAW;
246 else if (!strncmp(buffer, "platform\n", sizeof(buffer)))
247 entry_type = BACKLIGHT_PLATFORM;
248 else if (!strncmp(buffer, "firmware\n", sizeof(buffer)))
249 entry_type = BACKLIGHT_FIRMWARE;
250 else
251 goto out;
252
253 if (connector_type != DRM_MODE_CONNECTOR_LVDS &&
254 connector_type != DRM_MODE_CONNECTOR_eDP) {
255 /* External displays are assumed to require
256 gpu control at the moment */
257 if (entry_type != BACKLIGHT_RAW)
258 goto out;
259 }
260
261 free (path);
262
263 if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
264 return NULL;
265
266 ret = readlink(path, buffer, sizeof(buffer));
267
268 if (ret < 0)
269 goto out;
270
271 buffer[ret] = '\0';
272
273 parent = basename(buffer);
274
275 /* Perform matching for raw and firmware backlights -
276 platform backlights have to be assumed to match */
277 if (entry_type == BACKLIGHT_RAW ||
278 entry_type == BACKLIGHT_FIRMWARE) {
279 if (!(pci_name && !strcmp(pci_name, parent)))
280 goto out;
281 }
282
283 if (entry_type < type)
284 goto out;
285
286 type = entry_type;
287
288 if (chosen_path)
289 free(chosen_path);
290 chosen_path = strdup(backlight_path);
291
292 out:
293 free(backlight_path);
294 free(path);
295 }
296
297 if (!chosen_path)
298 return NULL;
299
300 backlight = malloc(sizeof(struct backlight));
301
302 if (!backlight)
303 goto err;
304
305 backlight->path = chosen_path;
306 backlight->type = type;
307
308 backlight->max_brightness = backlight_get_max_brightness(backlight);
309 if (backlight->max_brightness < 0)
310 goto err;
311
312 backlight->brightness = backlight_get_actual_brightness(backlight);
313 if (backlight->brightness < 0)
314 goto err;
315
316 return backlight;
317err:
318 if (chosen_path)
319 free(chosen_path);
320 free (backlight);
321 return NULL;
322}