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