blob: 98a20beb5eb0cd9ba701e82b029bec9b0cb0953b [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:
Rob Bradford23d330e2013-01-10 19:48:56 +000071 if (fd >= 0)
72 close(fd);
Rob Bradford0b0be8e2012-12-05 18:47:06 +000073 free(path);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050074 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:
Rob Bradford23d330e2013-01-10 19:48:56 +0000128 free(buffer);
Rob Bradford0b0be8e2012-12-05 18:47:06 +0000129 free(path);
Rob Bradford23d330e2013-01-10 19:48:56 +0000130 if (fd >= 0)
131 close(fd);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500132 return ret;
133}
134
135void backlight_destroy(struct backlight *backlight)
136{
137 if (!backlight)
138 return;
139
140 if (backlight->path)
141 free(backlight->path);
142
143 free(backlight);
144}
145
146struct backlight *backlight_init(struct udev_device *drm_device,
147 uint32_t connector_type)
148{
149 const char *syspath = NULL;
150 char *pci_name = NULL;
151 char *chosen_path = NULL;
152 char *path = NULL;
Rob Bradford546c8562012-12-05 18:47:09 +0000153 DIR *backlights = NULL;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500154 struct dirent *entry;
155 enum backlight_type type = 0;
156 char buffer[100];
Rob Bradford546c8562012-12-05 18:47:09 +0000157 struct backlight *backlight = NULL;
Pekka Paalanen4ac32ab2012-03-02 17:33:17 +0200158 int ret;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500159
160 if (!drm_device)
161 return NULL;
162
163 syspath = udev_device_get_syspath(drm_device);
164 if (!syspath)
165 return NULL;
166
167 if (asprintf(&path, "%s/%s", syspath, "device") < 0)
168 return NULL;
169
Rob Bradford273fec82012-10-09 18:44:33 +0100170 ret = readlink(path, buffer, sizeof(buffer) - 1);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500171 free(path);
172 if (ret < 0)
173 return NULL;
174
175 buffer[ret] = '\0';
176 pci_name = basename(buffer);
177
178 if (connector_type <= 0)
179 return NULL;
180
181 backlights = opendir("/sys/class/backlight");
182 if (!backlights)
183 return NULL;
184
185 /* Find the "best" backlight for the device. Firmware
186 interfaces are preferred over platform interfaces are
187 preferred over raw interfaces. For raw interfaces we'll
188 check if the device ID in the form of pci match, while
189 for firmware interfaces we require the pci ID to
190 match. It's assumed that platform interfaces always match,
191 since we can't actually associate them with IDs.
192
193 A further awkwardness is that, while it's theoretically
194 possible for an ACPI interface to include support for
195 changing the backlight of external devices, it's unlikely
196 to ever be done. It's effectively impossible for a platform
197 interface to do so. So if we get asked about anything that
198 isn't LVDS or eDP, we pretty much have to require that the
199 control be supplied via a raw interface */
200
201 while ((entry = readdir(backlights))) {
202 char *backlight_path;
203 char *parent;
204 enum backlight_type entry_type;
205 int fd;
206
207 if (entry->d_name[0] == '.')
208 continue;
209
210 if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
211 entry->d_name) < 0)
Rob Bradford546c8562012-12-05 18:47:09 +0000212 goto err;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500213
214 if (asprintf(&path, "%s/%s", backlight_path, "type") < 0)
Rob Bradford546c8562012-12-05 18:47:09 +0000215 goto err;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500216
217 fd = open(path, O_RDONLY);
218
219 if (fd < 0)
220 goto out;
221
222 ret = read (fd, &buffer, sizeof(buffer));
223 close (fd);
224
225 if (ret < 1)
226 goto out;
227
228 buffer[ret] = '\0';
229
230 if (!strncmp(buffer, "raw\n", sizeof(buffer)))
231 entry_type = BACKLIGHT_RAW;
232 else if (!strncmp(buffer, "platform\n", sizeof(buffer)))
233 entry_type = BACKLIGHT_PLATFORM;
234 else if (!strncmp(buffer, "firmware\n", sizeof(buffer)))
235 entry_type = BACKLIGHT_FIRMWARE;
236 else
237 goto out;
238
239 if (connector_type != DRM_MODE_CONNECTOR_LVDS &&
240 connector_type != DRM_MODE_CONNECTOR_eDP) {
241 /* External displays are assumed to require
242 gpu control at the moment */
243 if (entry_type != BACKLIGHT_RAW)
244 goto out;
245 }
246
247 free (path);
248
249 if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
Rob Bradford546c8562012-12-05 18:47:09 +0000250 goto err;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500251
Rob Bradford273fec82012-10-09 18:44:33 +0100252 ret = readlink(path, buffer, sizeof(buffer) - 1);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500253
254 if (ret < 0)
255 goto out;
256
257 buffer[ret] = '\0';
258
259 parent = basename(buffer);
260
261 /* Perform matching for raw and firmware backlights -
262 platform backlights have to be assumed to match */
263 if (entry_type == BACKLIGHT_RAW ||
264 entry_type == BACKLIGHT_FIRMWARE) {
265 if (!(pci_name && !strcmp(pci_name, parent)))
266 goto out;
267 }
268
269 if (entry_type < type)
270 goto out;
271
272 type = entry_type;
273
274 if (chosen_path)
275 free(chosen_path);
276 chosen_path = strdup(backlight_path);
277
278 out:
279 free(backlight_path);
280 free(path);
281 }
282
283 if (!chosen_path)
Rob Bradford546c8562012-12-05 18:47:09 +0000284 goto err;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500285
286 backlight = malloc(sizeof(struct backlight));
287
288 if (!backlight)
289 goto err;
290
291 backlight->path = chosen_path;
292 backlight->type = type;
293
294 backlight->max_brightness = backlight_get_max_brightness(backlight);
295 if (backlight->max_brightness < 0)
296 goto err;
297
298 backlight->brightness = backlight_get_actual_brightness(backlight);
299 if (backlight->brightness < 0)
300 goto err;
301
Rob Bradford546c8562012-12-05 18:47:09 +0000302 closedir(backlights);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500303 return backlight;
304err:
Rob Bradford546c8562012-12-05 18:47:09 +0000305 closedir(backlights);
306 free (chosen_path);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500307 free (backlight);
308 return NULL;
309}