blob: 722d66f07ba87f3d76f0109fa1232786f77a5bc8 [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"
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)
Bryce W. Harrington991f2712014-01-14 21:58:34 +000054 return -ENOMEM;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050055 fd = open(path, O_RDONLY);
56 if (fd < 0) {
57 ret = -1;
58 goto out;
59 }
60
61 ret = read(fd, &buffer, sizeof(buffer));
62 if (ret < 1) {
63 ret = -1;
64 goto out;
65 }
66
67 value = strtol(buffer, NULL, 10);
68 ret = value;
69out:
Rob Bradford23d330e2013-01-10 19:48:56 +000070 if (fd >= 0)
71 close(fd);
Rob Bradford0b0be8e2012-12-05 18:47:06 +000072 free(path);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050073 return ret;
74}
75
76long backlight_get_brightness(struct backlight *backlight)
77{
78 return backlight_get(backlight, "brightness");
79}
80
81long backlight_get_max_brightness(struct backlight *backlight)
82{
83 return backlight_get(backlight, "max_brightness");
84}
85
86long backlight_get_actual_brightness(struct backlight *backlight)
87{
88 return backlight_get(backlight, "actual_brightness");
89}
90
91long backlight_set_brightness(struct backlight *backlight, long brightness)
92{
93 char *path;
94 char *buffer = NULL;
95 int fd;
96 long ret;
97
98 if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0)
99 return -ENOMEM;
100
101 fd = open(path, O_RDWR);
102 if (fd < 0) {
103 ret = -1;
104 goto out;
105 }
106
107 ret = read(fd, &buffer, sizeof(buffer));
108 if (ret < 1) {
109 ret = -1;
110 goto out;
111 }
112
Rob Bradforda6b63d02012-10-09 18:44:36 +0100113 if (asprintf(&buffer, "%ld", brightness) < 0) {
114 ret = -1;
115 goto out;
116 }
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500117
118 ret = write(fd, buffer, strlen(buffer));
119 if (ret < 0) {
120 ret = -1;
121 goto out;
122 }
123
124 ret = backlight_get_brightness(backlight);
125 backlight->brightness = ret;
126out:
Rob Bradford23d330e2013-01-10 19:48:56 +0000127 free(buffer);
Rob Bradford0b0be8e2012-12-05 18:47:06 +0000128 free(path);
Rob Bradford23d330e2013-01-10 19:48:56 +0000129 if (fd >= 0)
130 close(fd);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500131 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;
Rob Bradford546c8562012-12-05 18:47:09 +0000152 DIR *backlights = NULL;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500153 struct dirent *entry;
154 enum backlight_type type = 0;
155 char buffer[100];
Rob Bradford546c8562012-12-05 18:47:09 +0000156 struct backlight *backlight = NULL;
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
Rob Bradford273fec82012-10-09 18:44:33 +0100169 ret = readlink(path, buffer, sizeof(buffer) - 1);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500170 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)
Rob Bradford546c8562012-12-05 18:47:09 +0000211 goto err;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500212
U. Artie Eoff981fa332014-01-15 08:12:19 -0800213 if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) {
214 free(backlight_path);
Rob Bradford546c8562012-12-05 18:47:09 +0000215 goto err;
U. Artie Eoff981fa332014-01-15 08:12:19 -0800216 }
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500217
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)
Rob Bradford546c8562012-12-05 18:47:09 +0000251 goto err;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500252
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
Murray Calavera883ac022015-06-06 13:02:22 +0000262 /* Perform matching for raw and firmware backlights -
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500263 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)
Rob Bradford546c8562012-12-05 18:47:09 +0000285 goto err;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500286
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
Rob Bradford546c8562012-12-05 18:47:09 +0000303 closedir(backlights);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500304 return backlight;
305err:
Rob Bradford546c8562012-12-05 18:47:09 +0000306 closedir(backlights);
307 free (chosen_path);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500308 free (backlight);
309 return NULL;
310}