blob: 4039575c8bc7cc1014ad13136bf799b64abcb09f [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;
51 int fd;
52 long value, ret;
53
54 if (asprintf(&path, "%s/%s", backlight->path, node) < 0)
Bryce W. Harrington991f2712014-01-14 21:58:34 +000055 return -ENOMEM;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -050056 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
U. Artie Eoff981fa332014-01-15 08:12:19 -0800214 if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) {
215 free(backlight_path);
Rob Bradford546c8562012-12-05 18:47:09 +0000216 goto err;
U. Artie Eoff981fa332014-01-15 08:12:19 -0800217 }
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500218
219 fd = open(path, O_RDONLY);
220
221 if (fd < 0)
222 goto out;
223
224 ret = read (fd, &buffer, sizeof(buffer));
225 close (fd);
226
227 if (ret < 1)
228 goto out;
229
230 buffer[ret] = '\0';
231
232 if (!strncmp(buffer, "raw\n", sizeof(buffer)))
233 entry_type = BACKLIGHT_RAW;
234 else if (!strncmp(buffer, "platform\n", sizeof(buffer)))
235 entry_type = BACKLIGHT_PLATFORM;
236 else if (!strncmp(buffer, "firmware\n", sizeof(buffer)))
237 entry_type = BACKLIGHT_FIRMWARE;
238 else
239 goto out;
240
241 if (connector_type != DRM_MODE_CONNECTOR_LVDS &&
242 connector_type != DRM_MODE_CONNECTOR_eDP) {
243 /* External displays are assumed to require
244 gpu control at the moment */
245 if (entry_type != BACKLIGHT_RAW)
246 goto out;
247 }
248
249 free (path);
250
251 if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
Rob Bradford546c8562012-12-05 18:47:09 +0000252 goto err;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500253
Rob Bradford273fec82012-10-09 18:44:33 +0100254 ret = readlink(path, buffer, sizeof(buffer) - 1);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500255
256 if (ret < 0)
257 goto out;
258
259 buffer[ret] = '\0';
260
261 parent = basename(buffer);
262
Murray Calavera883ac022015-06-06 13:02:22 +0000263 /* Perform matching for raw and firmware backlights -
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500264 platform backlights have to be assumed to match */
265 if (entry_type == BACKLIGHT_RAW ||
266 entry_type == BACKLIGHT_FIRMWARE) {
267 if (!(pci_name && !strcmp(pci_name, parent)))
268 goto out;
269 }
270
271 if (entry_type < type)
272 goto out;
273
274 type = entry_type;
275
276 if (chosen_path)
277 free(chosen_path);
278 chosen_path = strdup(backlight_path);
279
280 out:
281 free(backlight_path);
282 free(path);
283 }
284
285 if (!chosen_path)
Rob Bradford546c8562012-12-05 18:47:09 +0000286 goto err;
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500287
288 backlight = malloc(sizeof(struct backlight));
289
290 if (!backlight)
291 goto err;
292
293 backlight->path = chosen_path;
294 backlight->type = type;
295
296 backlight->max_brightness = backlight_get_max_brightness(backlight);
297 if (backlight->max_brightness < 0)
298 goto err;
299
300 backlight->brightness = backlight_get_actual_brightness(backlight);
301 if (backlight->brightness < 0)
302 goto err;
303
Rob Bradford546c8562012-12-05 18:47:09 +0000304 closedir(backlights);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500305 return backlight;
306err:
Rob Bradford546c8562012-12-05 18:47:09 +0000307 closedir(backlights);
308 free (chosen_path);
Kristian Høgsberg51cba3c2012-02-29 14:23:51 -0500309 free (backlight);
310 return NULL;
311}