blob: 37f4bccf7fde8e10fd2c42991b31cdf74e30a9e9 [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
114 if (asprintf(&buffer, "%ld", brightness) < 0)
115 return -ENOMEM;
116
117 ret = write(fd, buffer, strlen(buffer));
118 if (ret < 0) {
119 ret = -1;
120 goto out;
121 }
122
123 ret = backlight_get_brightness(backlight);
124 backlight->brightness = ret;
125out:
126 if (buffer)
127 free(buffer);
128 if (path)
129 free(path);
130 close(fd);
131 return ret;
132}
133
134void backlight_destroy(struct backlight *backlight)
135{
136 if (!backlight)
137 return;
138
139 if (backlight->path)
140 free(backlight->path);
141
142 free(backlight);
143}
144
145struct backlight *backlight_init(struct udev_device *drm_device,
146 uint32_t connector_type)
147{
148 const char *syspath = NULL;
149 char *pci_name = NULL;
150 char *chosen_path = NULL;
151 char *path = NULL;
152 DIR *backlights;
153 struct dirent *entry;
154 enum backlight_type type = 0;
155 char buffer[100];
156 struct backlight *backlight;
Pekka Paalanen4ac32ab2012-03-02 17:33:17 +0200157 int ret;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500158
159 if (!drm_device)
160 return NULL;
161
162 syspath = udev_device_get_syspath(drm_device);
163 if (!syspath)
164 return NULL;
165
166 if (asprintf(&path, "%s/%s", syspath, "device") < 0)
167 return NULL;
168
169 ret = readlink(path, buffer, sizeof(buffer));
170 free(path);
171 if (ret < 0)
172 return NULL;
173
174 buffer[ret] = '\0';
175 pci_name = basename(buffer);
176
177 if (connector_type <= 0)
178 return NULL;
179
180 backlights = opendir("/sys/class/backlight");
181 if (!backlights)
182 return NULL;
183
184 /* Find the "best" backlight for the device. Firmware
185 interfaces are preferred over platform interfaces are
186 preferred over raw interfaces. For raw interfaces we'll
187 check if the device ID in the form of pci match, while
188 for firmware interfaces we require the pci ID to
189 match. It's assumed that platform interfaces always match,
190 since we can't actually associate them with IDs.
191
192 A further awkwardness is that, while it's theoretically
193 possible for an ACPI interface to include support for
194 changing the backlight of external devices, it's unlikely
195 to ever be done. It's effectively impossible for a platform
196 interface to do so. So if we get asked about anything that
197 isn't LVDS or eDP, we pretty much have to require that the
198 control be supplied via a raw interface */
199
200 while ((entry = readdir(backlights))) {
201 char *backlight_path;
202 char *parent;
203 enum backlight_type entry_type;
204 int fd;
205
206 if (entry->d_name[0] == '.')
207 continue;
208
209 if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
210 entry->d_name) < 0)
211 return NULL;
212
213 if (asprintf(&path, "%s/%s", backlight_path, "type") < 0)
214 return NULL;
215
216 fd = open(path, O_RDONLY);
217
218 if (fd < 0)
219 goto out;
220
221 ret = read (fd, &buffer, sizeof(buffer));
222 close (fd);
223
224 if (ret < 1)
225 goto out;
226
227 buffer[ret] = '\0';
228
229 if (!strncmp(buffer, "raw\n", sizeof(buffer)))
230 entry_type = BACKLIGHT_RAW;
231 else if (!strncmp(buffer, "platform\n", sizeof(buffer)))
232 entry_type = BACKLIGHT_PLATFORM;
233 else if (!strncmp(buffer, "firmware\n", sizeof(buffer)))
234 entry_type = BACKLIGHT_FIRMWARE;
235 else
236 goto out;
237
238 if (connector_type != DRM_MODE_CONNECTOR_LVDS &&
239 connector_type != DRM_MODE_CONNECTOR_eDP) {
240 /* External displays are assumed to require
241 gpu control at the moment */
242 if (entry_type != BACKLIGHT_RAW)
243 goto out;
244 }
245
246 free (path);
247
248 if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
249 return NULL;
250
251 ret = readlink(path, buffer, sizeof(buffer));
252
253 if (ret < 0)
254 goto out;
255
256 buffer[ret] = '\0';
257
258 parent = basename(buffer);
259
260 /* Perform matching for raw and firmware backlights -
261 platform backlights have to be assumed to match */
262 if (entry_type == BACKLIGHT_RAW ||
263 entry_type == BACKLIGHT_FIRMWARE) {
264 if (!(pci_name && !strcmp(pci_name, parent)))
265 goto out;
266 }
267
268 if (entry_type < type)
269 goto out;
270
271 type = entry_type;
272
273 if (chosen_path)
274 free(chosen_path);
275 chosen_path = strdup(backlight_path);
276
277 out:
278 free(backlight_path);
279 free(path);
280 }
281
282 if (!chosen_path)
283 return NULL;
284
285 backlight = malloc(sizeof(struct backlight));
286
287 if (!backlight)
288 goto err;
289
290 backlight->path = chosen_path;
291 backlight->type = type;
292
293 backlight->max_brightness = backlight_get_max_brightness(backlight);
294 if (backlight->max_brightness < 0)
295 goto err;
296
297 backlight->brightness = backlight_get_actual_brightness(backlight);
298 if (backlight->brightness < 0)
299 goto err;
300
301 return backlight;
302err:
303 if (chosen_path)
304 free(chosen_path);
305 free (backlight);
306 return NULL;
307}