blob: 59f4e44014389c9a32abec4dd213394cb78eabcf [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
Daniel Stonec228e232013-05-22 18:03:19 +030032#include "config.h"
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050033
Pekka Paalanen4ac32ab2012-03-02 17:33:17 +020034#include "libbacklight.h"
Jussi Kukkonen649bbce2016-07-19 14:16:27 +030035#include <stdint.h>
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050036#include <stdio.h>
Pekka Paalanen4ac32ab2012-03-02 17:33:17 +020037#include <stdlib.h>
38#include <unistd.h>
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050039#include <linux/types.h>
40#include <dirent.h>
Pekka Paalanen4ac32ab2012-03-02 17:33:17 +020041#include <drm.h>
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050042#include <fcntl.h>
43#include <malloc.h>
44#include <string.h>
45#include <errno.h>
46
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050047static long backlight_get(struct backlight *backlight, char *node)
48{
49 char buffer[100];
50 char *path;
Bryce Harrington913d7c12016-08-03 17:40:48 -070051 char *end;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050052 int fd;
53 long value, ret;
54
55 if (asprintf(&path, "%s/%s", backlight->path, node) < 0)
Bryce W. Harrington991f2712014-01-14 21:58:34 +000056 return -ENOMEM;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050057 fd = open(path, O_RDONLY);
58 if (fd < 0) {
59 ret = -1;
60 goto out;
61 }
62
63 ret = read(fd, &buffer, sizeof(buffer));
64 if (ret < 1) {
65 ret = -1;
66 goto out;
67 }
68
Bryce Harrington913d7c12016-08-03 17:40:48 -070069 errno = 0;
70 value = strtol(buffer, &end, 10);
71 if (errno != 0 || end == buffer || *end != '\0') {
72 ret = -1;
73 goto out;
74 }
75
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050076 ret = value;
Bryce Harrington913d7c12016-08-03 17:40:48 -070077
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050078out:
Rob Bradford23d330e2013-01-10 19:48:56 +000079 if (fd >= 0)
80 close(fd);
Rob Bradford0b0be8e2012-12-05 18:47:06 +000081 free(path);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050082 return ret;
83}
84
85long backlight_get_brightness(struct backlight *backlight)
86{
87 return backlight_get(backlight, "brightness");
88}
89
90long backlight_get_max_brightness(struct backlight *backlight)
91{
92 return backlight_get(backlight, "max_brightness");
93}
94
95long backlight_get_actual_brightness(struct backlight *backlight)
96{
97 return backlight_get(backlight, "actual_brightness");
98}
99
100long backlight_set_brightness(struct backlight *backlight, long brightness)
101{
102 char *path;
103 char *buffer = NULL;
104 int fd;
105 long ret;
106
107 if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0)
108 return -ENOMEM;
109
110 fd = open(path, O_RDWR);
111 if (fd < 0) {
112 ret = -1;
113 goto out;
114 }
115
116 ret = read(fd, &buffer, sizeof(buffer));
117 if (ret < 1) {
118 ret = -1;
119 goto out;
120 }
121
Rob Bradforda6b63d02012-10-09 18:44:36 +0100122 if (asprintf(&buffer, "%ld", brightness) < 0) {
123 ret = -1;
124 goto out;
125 }
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500126
127 ret = write(fd, buffer, strlen(buffer));
128 if (ret < 0) {
129 ret = -1;
130 goto out;
131 }
132
133 ret = backlight_get_brightness(backlight);
134 backlight->brightness = ret;
135out:
Rob Bradford23d330e2013-01-10 19:48:56 +0000136 free(buffer);
Rob Bradford0b0be8e2012-12-05 18:47:06 +0000137 free(path);
Rob Bradford23d330e2013-01-10 19:48:56 +0000138 if (fd >= 0)
139 close(fd);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500140 return ret;
141}
142
143void backlight_destroy(struct backlight *backlight)
144{
145 if (!backlight)
146 return;
147
148 if (backlight->path)
149 free(backlight->path);
150
151 free(backlight);
152}
153
154struct backlight *backlight_init(struct udev_device *drm_device,
155 uint32_t connector_type)
156{
157 const char *syspath = NULL;
158 char *pci_name = NULL;
159 char *chosen_path = NULL;
160 char *path = NULL;
Rob Bradford546c8562012-12-05 18:47:09 +0000161 DIR *backlights = NULL;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500162 struct dirent *entry;
163 enum backlight_type type = 0;
164 char buffer[100];
Rob Bradford546c8562012-12-05 18:47:09 +0000165 struct backlight *backlight = NULL;
Pekka Paalanen4ac32ab2012-03-02 17:33:17 +0200166 int ret;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500167
168 if (!drm_device)
169 return NULL;
170
171 syspath = udev_device_get_syspath(drm_device);
172 if (!syspath)
173 return NULL;
174
175 if (asprintf(&path, "%s/%s", syspath, "device") < 0)
176 return NULL;
177
Rob Bradford273fec82012-10-09 18:44:33 +0100178 ret = readlink(path, buffer, sizeof(buffer) - 1);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500179 free(path);
180 if (ret < 0)
181 return NULL;
182
183 buffer[ret] = '\0';
184 pci_name = basename(buffer);
185
186 if (connector_type <= 0)
187 return NULL;
188
189 backlights = opendir("/sys/class/backlight");
190 if (!backlights)
191 return NULL;
192
193 /* Find the "best" backlight for the device. Firmware
194 interfaces are preferred over platform interfaces are
195 preferred over raw interfaces. For raw interfaces we'll
196 check if the device ID in the form of pci match, while
197 for firmware interfaces we require the pci ID to
198 match. It's assumed that platform interfaces always match,
199 since we can't actually associate them with IDs.
200
201 A further awkwardness is that, while it's theoretically
202 possible for an ACPI interface to include support for
203 changing the backlight of external devices, it's unlikely
204 to ever be done. It's effectively impossible for a platform
205 interface to do so. So if we get asked about anything that
206 isn't LVDS or eDP, we pretty much have to require that the
207 control be supplied via a raw interface */
208
209 while ((entry = readdir(backlights))) {
210 char *backlight_path;
211 char *parent;
212 enum backlight_type entry_type;
213 int fd;
214
215 if (entry->d_name[0] == '.')
216 continue;
217
218 if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
219 entry->d_name) < 0)
Rob Bradford546c8562012-12-05 18:47:09 +0000220 goto err;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500221
U. Artie Eoff981fa332014-01-15 08:12:19 -0800222 if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) {
223 free(backlight_path);
Rob Bradford546c8562012-12-05 18:47:09 +0000224 goto err;
U. Artie Eoff981fa332014-01-15 08:12:19 -0800225 }
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500226
227 fd = open(path, O_RDONLY);
228
229 if (fd < 0)
230 goto out;
231
232 ret = read (fd, &buffer, sizeof(buffer));
233 close (fd);
234
235 if (ret < 1)
236 goto out;
237
238 buffer[ret] = '\0';
239
240 if (!strncmp(buffer, "raw\n", sizeof(buffer)))
241 entry_type = BACKLIGHT_RAW;
242 else if (!strncmp(buffer, "platform\n", sizeof(buffer)))
243 entry_type = BACKLIGHT_PLATFORM;
244 else if (!strncmp(buffer, "firmware\n", sizeof(buffer)))
245 entry_type = BACKLIGHT_FIRMWARE;
246 else
247 goto out;
248
249 if (connector_type != DRM_MODE_CONNECTOR_LVDS &&
250 connector_type != DRM_MODE_CONNECTOR_eDP) {
251 /* External displays are assumed to require
252 gpu control at the moment */
253 if (entry_type != BACKLIGHT_RAW)
254 goto out;
255 }
256
257 free (path);
258
259 if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
Rob Bradford546c8562012-12-05 18:47:09 +0000260 goto err;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500261
Rob Bradford273fec82012-10-09 18:44:33 +0100262 ret = readlink(path, buffer, sizeof(buffer) - 1);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500263
264 if (ret < 0)
265 goto out;
266
267 buffer[ret] = '\0';
268
269 parent = basename(buffer);
270
Murray Calavera883ac022015-06-06 13:02:22 +0000271 /* Perform matching for raw and firmware backlights -
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500272 platform backlights have to be assumed to match */
273 if (entry_type == BACKLIGHT_RAW ||
274 entry_type == BACKLIGHT_FIRMWARE) {
275 if (!(pci_name && !strcmp(pci_name, parent)))
276 goto out;
277 }
278
279 if (entry_type < type)
280 goto out;
281
282 type = entry_type;
283
284 if (chosen_path)
285 free(chosen_path);
286 chosen_path = strdup(backlight_path);
287
288 out:
289 free(backlight_path);
290 free(path);
291 }
292
293 if (!chosen_path)
Rob Bradford546c8562012-12-05 18:47:09 +0000294 goto err;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500295
296 backlight = malloc(sizeof(struct backlight));
297
298 if (!backlight)
299 goto err;
300
301 backlight->path = chosen_path;
302 backlight->type = type;
303
304 backlight->max_brightness = backlight_get_max_brightness(backlight);
305 if (backlight->max_brightness < 0)
306 goto err;
307
308 backlight->brightness = backlight_get_actual_brightness(backlight);
309 if (backlight->brightness < 0)
310 goto err;
311
Rob Bradford546c8562012-12-05 18:47:09 +0000312 closedir(backlights);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500313 return backlight;
314err:
Rob Bradford546c8562012-12-05 18:47:09 +0000315 closedir(backlights);
316 free (chosen_path);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500317 free (backlight);
318 return NULL;
319}