1428d7b3dSmrg/***************************************************************************
2428d7b3dSmrg
3428d7b3dSmrg Copyright 2014 Intel Corporation.  All Rights Reserved.
4428d7b3dSmrg Copyright 2014 Red Hat, Inc.
5428d7b3dSmrg
6428d7b3dSmrg Permission is hereby granted, free of charge, to any person obtaining a
7428d7b3dSmrg copy of this software and associated documentation files (the
8428d7b3dSmrg "Software"), to deal in the Software without restriction, including
9428d7b3dSmrg without limitation the rights to use, copy, modify, merge, publish,
10428d7b3dSmrg distribute, sub license, and/or sell copies of the Software, and to
11428d7b3dSmrg permit persons to whom the Software is furnished to do so, subject to
12428d7b3dSmrg the following conditions:
13428d7b3dSmrg
14428d7b3dSmrg The above copyright notice and this permission notice (including the
15428d7b3dSmrg next paragraph) shall be included in all copies or substantial portions
16428d7b3dSmrg of the Software.
17428d7b3dSmrg
18428d7b3dSmrg THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19428d7b3dSmrg OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20428d7b3dSmrg MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21428d7b3dSmrg IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
22428d7b3dSmrg DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23428d7b3dSmrg OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
24428d7b3dSmrg THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25428d7b3dSmrg
26428d7b3dSmrg **************************************************************************/
27428d7b3dSmrg
28428d7b3dSmrg#ifdef HAVE_CONFIG_H
29428d7b3dSmrg#include "config.h"
30428d7b3dSmrg#endif
31428d7b3dSmrg
32428d7b3dSmrg#include <sys/types.h>
33428d7b3dSmrg#include <sys/wait.h>
34428d7b3dSmrg#include <sys/stat.h>
35428d7b3dSmrg#include <sys/ioctl.h>
36428d7b3dSmrg
37428d7b3dSmrg#include <stdio.h>
38428d7b3dSmrg#include <stdlib.h>
39428d7b3dSmrg#include <string.h>
40428d7b3dSmrg#include <ctype.h>
41428d7b3dSmrg#include <limits.h>
42428d7b3dSmrg#include <fcntl.h>
43428d7b3dSmrg#include <unistd.h>
44428d7b3dSmrg#include <dirent.h>
45428d7b3dSmrg
46428d7b3dSmrg#include <xorg-server.h>
47428d7b3dSmrg#include <xf86.h>
48428d7b3dSmrg#include <pciaccess.h>
49428d7b3dSmrg
50428d7b3dSmrg#include "backlight.h"
51428d7b3dSmrg#include "fd.h"
52428d7b3dSmrg
53428d7b3dSmrg#define BACKLIGHT_CLASS "/sys/class/backlight"
54428d7b3dSmrg
55428d7b3dSmrg/* Enough for 10 digits of backlight + '\n' + '\0' */
56428d7b3dSmrg#define BACKLIGHT_VALUE_LEN 12
57428d7b3dSmrg
58428d7b3dSmrg#ifndef ARRAY_SIZE
59428d7b3dSmrg#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
60428d7b3dSmrg#endif
61428d7b3dSmrg
62428d7b3dSmrg/*
63428d7b3dSmrg * Unfortunately this is not as simple as I would like it to be. If selinux is
64428d7b3dSmrg * dropping dbus messages pkexec may block *forever*.
65428d7b3dSmrg *
66428d7b3dSmrg * Backgrounding pkexec by doing System("pkexec ...&") does not work because
67428d7b3dSmrg * that detaches pkexec from its parent at which point its security checks
68428d7b3dSmrg * fail and it refuses to execute the helper.
69428d7b3dSmrg *
70428d7b3dSmrg * So we're left with spawning a helper child which gets levels to set written
71428d7b3dSmrg * to it through a pipe. This turns the blocking forever problem from a hung
72428d7b3dSmrg * machine problem into a simple backlight control not working problem.
73428d7b3dSmrg *
74428d7b3dSmrg * If only things were as simple as on OpenBSD! :)
75428d7b3dSmrg */
76428d7b3dSmrg
77428d7b3dSmrgvoid backlight_init(struct backlight *b)
78428d7b3dSmrg{
79428d7b3dSmrg	b->type = BL_NONE;
80428d7b3dSmrg	b->iface = NULL;
81428d7b3dSmrg	b->fd = -1;
82428d7b3dSmrg	b->pid = -1;
83428d7b3dSmrg	b->max = -1;
84428d7b3dSmrg	b->has_power = 0;
85428d7b3dSmrg}
86428d7b3dSmrg
87428d7b3dSmrg#if defined(__OpenBSD__) || defined(__NetBSD__)
88428d7b3dSmrg
89428d7b3dSmrg#include <dev/wscons/wsconsio.h>
90428d7b3dSmrg#include <xf86Priv.h>
91428d7b3dSmrg
92428d7b3dSmrgint backlight_set(struct backlight *b, int level)
93428d7b3dSmrg{
94428d7b3dSmrg	struct wsdisplay_param param;
95428d7b3dSmrg
96428d7b3dSmrg	if (b->iface == NULL)
97428d7b3dSmrg		return -1;
98428d7b3dSmrg
99428d7b3dSmrg	if ((unsigned)level > b->max)
100428d7b3dSmrg		level = b->max;
101428d7b3dSmrg
102428d7b3dSmrg	memset(&param, 0, sizeof(param));
103428d7b3dSmrg	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
104428d7b3dSmrg	param.curval = level;
105428d7b3dSmrg
106428d7b3dSmrg	return ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &param);
107428d7b3dSmrg}
108428d7b3dSmrg
109428d7b3dSmrgint backlight_get(struct backlight *b)
110428d7b3dSmrg{
111428d7b3dSmrg	struct wsdisplay_param param;
112428d7b3dSmrg
113428d7b3dSmrg	if (b->iface == NULL)
114428d7b3dSmrg		return -1;
115428d7b3dSmrg
116428d7b3dSmrg	memset(&param, 0, sizeof(param));
117428d7b3dSmrg	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
118428d7b3dSmrg
119428d7b3dSmrg	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param))
120428d7b3dSmrg		return -1;
121428d7b3dSmrg
122428d7b3dSmrg	return param.curval;
123428d7b3dSmrg}
124428d7b3dSmrg
125428d7b3dSmrgint backlight_open(struct backlight *b, char *iface)
126428d7b3dSmrg{
127428d7b3dSmrg	struct wsdisplay_param param;
128428d7b3dSmrg
129428d7b3dSmrg	if (iface != NULL)
130428d7b3dSmrg		return -1;
131428d7b3dSmrg
132428d7b3dSmrg	memset(&param, 0, sizeof(param));
133428d7b3dSmrg	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
134428d7b3dSmrg
135428d7b3dSmrg	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param) == -1)
136428d7b3dSmrg		return -1;
137428d7b3dSmrg
138428d7b3dSmrg	b->iface = strdup("wscons");
139428d7b3dSmrg	if (b->iface == NULL)
140428d7b3dSmrg		return -1;
141428d7b3dSmrg
142428d7b3dSmrg	b->max = param.max;
143428d7b3dSmrg	b->fd = -1;
144428d7b3dSmrg	b->type = BL_PLATFORM;
145428d7b3dSmrg
146428d7b3dSmrg	return param.curval;
147428d7b3dSmrg}
148428d7b3dSmrg
149428d7b3dSmrgenum backlight_type backlight_exists(const char *iface)
150428d7b3dSmrg{
151428d7b3dSmrg	if (iface != NULL)
152428d7b3dSmrg		return BL_NONE;
153428d7b3dSmrg
154428d7b3dSmrg	return BL_PLATFORM;
155428d7b3dSmrg}
156428d7b3dSmrg
157428d7b3dSmrgint backlight_on(struct backlight *b)
158428d7b3dSmrg{
159428d7b3dSmrg	return 0;
160428d7b3dSmrg}
161428d7b3dSmrg
162428d7b3dSmrgint backlight_off(struct backlight *b)
163428d7b3dSmrg{
164428d7b3dSmrg	return 0;
165428d7b3dSmrg}
166428d7b3dSmrg#else
167428d7b3dSmrg
168428d7b3dSmrgstatic int
169428d7b3dSmrgis_sysfs_fd(int fd)
170428d7b3dSmrg{
171428d7b3dSmrg	struct stat st;
172428d7b3dSmrg	return fstat(fd, &st) == 0 && major(st.st_dev) == 0;
173428d7b3dSmrg}
174428d7b3dSmrg
175428d7b3dSmrgstatic int
176428d7b3dSmrg__backlight_open(const char *iface, const char *file, int mode)
177428d7b3dSmrg{
178428d7b3dSmrg	char buf[1024];
179428d7b3dSmrg	int fd;
180428d7b3dSmrg
181428d7b3dSmrg	snprintf(buf, sizeof(buf), BACKLIGHT_CLASS "/%s/%s", iface, file);
182428d7b3dSmrg	fd = open(buf, mode);
183428d7b3dSmrg	if (fd == -1)
184428d7b3dSmrg		return -1;
185428d7b3dSmrg
186428d7b3dSmrg	if (!is_sysfs_fd(fd)) {
187428d7b3dSmrg		close(fd);
188428d7b3dSmrg		return -1;
189428d7b3dSmrg	}
190428d7b3dSmrg
191428d7b3dSmrg	return fd;
192428d7b3dSmrg}
193428d7b3dSmrg
194428d7b3dSmrgstatic int
195428d7b3dSmrg__backlight_read(const char *iface, const char *file)
196428d7b3dSmrg{
197428d7b3dSmrg	char buf[BACKLIGHT_VALUE_LEN];
198428d7b3dSmrg	int fd, val;
199428d7b3dSmrg
200428d7b3dSmrg	fd = __backlight_open(iface, file, O_RDONLY);
201428d7b3dSmrg	if (fd < 0)
202428d7b3dSmrg		return -1;
203428d7b3dSmrg
204428d7b3dSmrg	val = read(fd, buf, BACKLIGHT_VALUE_LEN - 1);
205428d7b3dSmrg	if (val > 0) {
206428d7b3dSmrg		buf[val] = '\0';
207428d7b3dSmrg		val = atoi(buf);
208428d7b3dSmrg	} else
209428d7b3dSmrg		val = -1;
210428d7b3dSmrg	close(fd);
211428d7b3dSmrg
212428d7b3dSmrg	return val;
213428d7b3dSmrg}
214428d7b3dSmrg
215428d7b3dSmrgstatic int
216428d7b3dSmrg__backlight_write(const char *iface, const char *file, const char *value)
217428d7b3dSmrg{
218428d7b3dSmrg	int fd, ret;
219428d7b3dSmrg
220428d7b3dSmrg	fd = __backlight_open(iface, file, O_WRONLY);
221428d7b3dSmrg	if (fd < 0)
222428d7b3dSmrg		return -1;
223428d7b3dSmrg
224428d7b3dSmrg	ret = write(fd, value, strlen(value)+1);
225428d7b3dSmrg	close(fd);
226428d7b3dSmrg
227428d7b3dSmrg	return ret;
228428d7b3dSmrg}
229428d7b3dSmrg
230428d7b3dSmrg/* List of available kernel interfaces in priority order */
231428d7b3dSmrgstatic const char *known_interfaces[] = {
232428d7b3dSmrg	"dell_backlight",
233428d7b3dSmrg	"gmux_backlight",
234428d7b3dSmrg	"asus-laptop",
235428d7b3dSmrg	"asus-nb-wmi",
236428d7b3dSmrg	"eeepc",
237428d7b3dSmrg	"thinkpad_screen",
238428d7b3dSmrg	"mbp_backlight",
239428d7b3dSmrg	"fujitsu-laptop",
240428d7b3dSmrg	"sony",
241428d7b3dSmrg	"samsung",
242428d7b3dSmrg	"acpi_video1",
243428d7b3dSmrg	"acpi_video0",
244428d7b3dSmrg	"intel_backlight",
245428d7b3dSmrg};
246428d7b3dSmrg
247428d7b3dSmrgstatic enum backlight_type __backlight_type(const char *iface)
248428d7b3dSmrg{
249428d7b3dSmrg	char buf[1024];
250428d7b3dSmrg	int fd, v;
251428d7b3dSmrg
252428d7b3dSmrg	v = -1;
253428d7b3dSmrg	fd = __backlight_open(iface, "type", O_RDONLY);
254428d7b3dSmrg	if (fd >= 0) {
255428d7b3dSmrg		v = read(fd, buf, sizeof(buf)-1);
256428d7b3dSmrg		close(fd);
257428d7b3dSmrg	}
258428d7b3dSmrg	if (v > 0) {
259428d7b3dSmrg		while (v > 0 && isspace(buf[v-1]))
260428d7b3dSmrg			v--;
261428d7b3dSmrg		buf[v] = '\0';
262428d7b3dSmrg
263428d7b3dSmrg		if (strcmp(buf, "raw") == 0)
264428d7b3dSmrg			v = BL_RAW;
265428d7b3dSmrg		else if (strcmp(buf, "platform") == 0)
266428d7b3dSmrg			v = BL_PLATFORM;
267428d7b3dSmrg		else if (strcmp(buf, "firmware") == 0)
268428d7b3dSmrg			v = BL_FIRMWARE;
269428d7b3dSmrg		else
270428d7b3dSmrg			v = BL_NAMED;
271428d7b3dSmrg	} else
272428d7b3dSmrg		v = BL_NAMED;
273428d7b3dSmrg
274428d7b3dSmrg	if (v == BL_NAMED) {
275428d7b3dSmrg		int i;
276428d7b3dSmrg		for (i = 0; i < ARRAY_SIZE(known_interfaces); i++) {
277428d7b3dSmrg			if (strcmp(iface, known_interfaces[i]) == 0)
278428d7b3dSmrg				break;
279428d7b3dSmrg		}
280428d7b3dSmrg		v += i;
281428d7b3dSmrg	}
282428d7b3dSmrg
283428d7b3dSmrg	return v;
284428d7b3dSmrg}
285428d7b3dSmrg
286428d7b3dSmrgenum backlight_type backlight_exists(const char *iface)
287428d7b3dSmrg{
288428d7b3dSmrg	if (__backlight_read(iface, "brightness") < 0)
289428d7b3dSmrg		return BL_NONE;
290428d7b3dSmrg
291428d7b3dSmrg	if (__backlight_read(iface, "max_brightness") <= 0)
292428d7b3dSmrg		return BL_NONE;
293428d7b3dSmrg
294428d7b3dSmrg	return __backlight_type(iface);
295428d7b3dSmrg}
296428d7b3dSmrg
297428d7b3dSmrgstatic int __backlight_init(struct backlight *b, char *iface, int fd)
298428d7b3dSmrg{
299428d7b3dSmrg	b->fd = fd_move_cloexec(fd_set_nonblock(fd));
300428d7b3dSmrg	b->iface = iface;
301428d7b3dSmrg	return 1;
302428d7b3dSmrg}
303428d7b3dSmrg
304428d7b3dSmrgstatic int __backlight_direct_init(struct backlight *b, char *iface)
305428d7b3dSmrg{
306428d7b3dSmrg	int fd;
307428d7b3dSmrg
308428d7b3dSmrg	fd = __backlight_open(iface, "brightness", O_RDWR);
309428d7b3dSmrg	if (fd < 0)
310428d7b3dSmrg		return 0;
311428d7b3dSmrg
312428d7b3dSmrg	if (__backlight_read(iface, "bl_power") != -1)
313428d7b3dSmrg		b->has_power = 1;
314428d7b3dSmrg
315428d7b3dSmrg	return __backlight_init(b, iface, fd);
316428d7b3dSmrg}
317428d7b3dSmrg
318428d7b3dSmrgstatic int __backlight_helper_init(struct backlight *b, char *iface)
319428d7b3dSmrg{
320428d7b3dSmrg#if USE_BACKLIGHT_HELPER
321428d7b3dSmrg	struct stat st;
322428d7b3dSmrg	char *env[] = { NULL };
323428d7b3dSmrg	int use_pkexec = 0;
324428d7b3dSmrg	int fds[2];
325428d7b3dSmrg
326428d7b3dSmrg	/*
327428d7b3dSmrg	 * Some systems may prefer using PolicyKit's pkexec over
328428d7b3dSmrg	 * making the helper suid root, since the suid option will allow
329428d7b3dSmrg	 * anyone to control the backlight.  However, as pkexec
330428d7b3dSmrg	 * is quite troublesome and not universally available, we
331428d7b3dSmrg	 * still try the old fashioned and simple method first.
332428d7b3dSmrg	 * Either way, we have to trust that it is our backlight-helper
333428d7b3dSmrg	 * that is run and that we have scrutinised it carefully.
334428d7b3dSmrg	 */
335428d7b3dSmrg	if (stat(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", &st))
336428d7b3dSmrg		return 0;
337428d7b3dSmrg
338428d7b3dSmrg	if ((st.st_mode & (S_IFREG | S_ISUID | S_IXUSR)) != (S_IFREG | S_ISUID | S_IXUSR)) {
339428d7b3dSmrg		if (System("pkexec --version"))
340428d7b3dSmrg			return 0;
341428d7b3dSmrg
342428d7b3dSmrg		use_pkexec = 1;
343428d7b3dSmrg	}
344428d7b3dSmrg
345428d7b3dSmrg	if (pipe(fds))
346428d7b3dSmrg		return 0;
347428d7b3dSmrg
348428d7b3dSmrg	switch ((b->pid = fork())) {
349428d7b3dSmrg	case 0:
350428d7b3dSmrg		if (setgid(getgid()) || setuid(getuid()))
351428d7b3dSmrg			_exit(127);
352428d7b3dSmrg
353428d7b3dSmrg		close(fds[1]);
354428d7b3dSmrg		if (dup2(fds[0], 0))
355428d7b3dSmrg			_exit(127);
356428d7b3dSmrg		close(fds[0]);
357428d7b3dSmrg
358428d7b3dSmrg		if (use_pkexec) {
359428d7b3dSmrg			execlp("pkexec", "pkexec",
360428d7b3dSmrg			       LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
361428d7b3dSmrg			       iface, (char *)0);
362428d7b3dSmrg		} else {
363428d7b3dSmrg			execle(LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
364428d7b3dSmrg			       "xf86-video-intel-backlight-helper",
365428d7b3dSmrg			       iface, (char *)0, env);
366428d7b3dSmrg		}
367428d7b3dSmrg		_exit(1);
368428d7b3dSmrg		/* unreachable fallthrough */
369428d7b3dSmrg	case -1:
370428d7b3dSmrg		close(fds[1]);
371428d7b3dSmrg		close(fds[0]);
372428d7b3dSmrg		return 0;
373428d7b3dSmrg
374428d7b3dSmrg	default:
375428d7b3dSmrg		close(fds[0]);
376428d7b3dSmrg		return __backlight_init(b, iface, fds[1]);
377428d7b3dSmrg	}
378428d7b3dSmrg#else
379428d7b3dSmrg	return 0;
380428d7b3dSmrg#endif
381428d7b3dSmrg}
382428d7b3dSmrg
383428d7b3dSmrgstatic char *
384428d7b3dSmrg__backlight_find(void)
385428d7b3dSmrg{
386428d7b3dSmrg	char *best_iface = NULL;
387428d7b3dSmrg	unsigned best_type = INT_MAX;
388428d7b3dSmrg	DIR *dir;
389428d7b3dSmrg	struct dirent *de;
390428d7b3dSmrg
391428d7b3dSmrg	dir = opendir(BACKLIGHT_CLASS);
392428d7b3dSmrg	if (dir == NULL)
393428d7b3dSmrg		return NULL;
394428d7b3dSmrg
395428d7b3dSmrg	while ((de = readdir(dir))) {
396428d7b3dSmrg		int v;
397428d7b3dSmrg
398428d7b3dSmrg		if (*de->d_name == '.')
399428d7b3dSmrg			continue;
400428d7b3dSmrg
401428d7b3dSmrg		/* Fallback to priority list of known iface for old kernels */
402428d7b3dSmrg		v = backlight_exists(de->d_name);
403428d7b3dSmrg		if (v < best_type) {
404428d7b3dSmrg			char *copy = strdup(de->d_name);
405428d7b3dSmrg			if (copy) {
406428d7b3dSmrg				free(best_iface);
407428d7b3dSmrg				best_iface = copy;
408428d7b3dSmrg				best_type = v;
409428d7b3dSmrg			}
410428d7b3dSmrg		}
411428d7b3dSmrg	}
412428d7b3dSmrg	closedir(dir);
413428d7b3dSmrg
414428d7b3dSmrg	return best_iface;
415428d7b3dSmrg}
416428d7b3dSmrg
417428d7b3dSmrgint backlight_open(struct backlight *b, char *iface)
418428d7b3dSmrg{
419428d7b3dSmrg	int level;
420428d7b3dSmrg
421428d7b3dSmrg	if (iface == NULL)
422428d7b3dSmrg		iface = __backlight_find();
423428d7b3dSmrg	if (iface == NULL)
424428d7b3dSmrg		goto err;
425428d7b3dSmrg
426428d7b3dSmrg	b->type = __backlight_type(iface);
427428d7b3dSmrg
428428d7b3dSmrg	b->max = __backlight_read(iface, "max_brightness");
429428d7b3dSmrg	if (b->max <= 0)
430428d7b3dSmrg		goto err;
431428d7b3dSmrg
432428d7b3dSmrg	level = __backlight_read(iface, "brightness");
433428d7b3dSmrg	if (level < 0)
434428d7b3dSmrg		goto err;
435428d7b3dSmrg
436428d7b3dSmrg	if (!__backlight_direct_init(b, iface) &&
437428d7b3dSmrg	    !__backlight_helper_init(b, iface))
438428d7b3dSmrg		goto err;
439428d7b3dSmrg
440428d7b3dSmrg	return level;
441428d7b3dSmrg
442428d7b3dSmrgerr:
443428d7b3dSmrg	backlight_init(b);
444428d7b3dSmrg	return -1;
445428d7b3dSmrg}
446428d7b3dSmrg
447428d7b3dSmrgint backlight_set(struct backlight *b, int level)
448428d7b3dSmrg{
449428d7b3dSmrg	char val[BACKLIGHT_VALUE_LEN];
450428d7b3dSmrg	int len, ret = 0;
451428d7b3dSmrg
452428d7b3dSmrg	if (b->iface == NULL)
453428d7b3dSmrg		return 0;
454428d7b3dSmrg
455428d7b3dSmrg	if ((unsigned)level > b->max)
456428d7b3dSmrg		level = b->max;
457428d7b3dSmrg
458428d7b3dSmrg	len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level);
459428d7b3dSmrg	if (write(b->fd, val, len) != len)
460428d7b3dSmrg		ret = -1;
461428d7b3dSmrg
462428d7b3dSmrg	return ret;
463428d7b3dSmrg}
464428d7b3dSmrg
465428d7b3dSmrgint backlight_get(struct backlight *b)
466428d7b3dSmrg{
467428d7b3dSmrg	int level;
468428d7b3dSmrg
469428d7b3dSmrg	if (b->iface == NULL)
470428d7b3dSmrg		return -1;
471428d7b3dSmrg
472428d7b3dSmrg	level = __backlight_read(b->iface, "brightness");
473428d7b3dSmrg	if (level > b->max)
474428d7b3dSmrg		level = b->max;
475428d7b3dSmrg	else if (level < 0)
476428d7b3dSmrg		level = -1;
477428d7b3dSmrg	return level;
478428d7b3dSmrg}
479428d7b3dSmrg
480428d7b3dSmrgint backlight_off(struct backlight *b)
481428d7b3dSmrg{
482428d7b3dSmrg	if (b->iface == NULL)
483428d7b3dSmrg		return 0;
484428d7b3dSmrg
485428d7b3dSmrg	if (!b->has_power)
486428d7b3dSmrg		return 0;
487428d7b3dSmrg
488428d7b3dSmrg	/* 4 -> FB_BLANK_POWERDOWN */
489428d7b3dSmrg	return __backlight_write(b->iface, "bl_power", "4");
490428d7b3dSmrg}
491428d7b3dSmrg
492428d7b3dSmrgint backlight_on(struct backlight *b)
493428d7b3dSmrg{
494428d7b3dSmrg	if (b->iface == NULL)
495428d7b3dSmrg		return 0;
496428d7b3dSmrg
497428d7b3dSmrg	if (!b->has_power)
498428d7b3dSmrg		return 0;
499428d7b3dSmrg
500428d7b3dSmrg	/* 0 -> FB_BLANK_UNBLANK */
501428d7b3dSmrg	return __backlight_write(b->iface, "bl_power", "0");
502428d7b3dSmrg}
503428d7b3dSmrg#endif
504428d7b3dSmrg
505428d7b3dSmrgvoid backlight_disable(struct backlight *b)
506428d7b3dSmrg{
507428d7b3dSmrg	if (b->iface == NULL)
508428d7b3dSmrg		return;
509428d7b3dSmrg
510428d7b3dSmrg	if (b->fd != -1)
511428d7b3dSmrg		close(b->fd);
512428d7b3dSmrg
513428d7b3dSmrg	free(b->iface);
514428d7b3dSmrg	b->iface = NULL;
515428d7b3dSmrg}
516428d7b3dSmrg
517428d7b3dSmrgvoid backlight_close(struct backlight *b)
518428d7b3dSmrg{
519428d7b3dSmrg	backlight_disable(b);
520428d7b3dSmrg	if (b->pid)
521428d7b3dSmrg		waitpid(b->pid, NULL, 0);
522428d7b3dSmrg}
523428d7b3dSmrg
524428d7b3dSmrgchar *backlight_find_for_device(struct pci_device *pci)
525428d7b3dSmrg{
526428d7b3dSmrg	char path[200];
527428d7b3dSmrg	unsigned best_type = INT_MAX;
528428d7b3dSmrg	char *best_iface = NULL;
529428d7b3dSmrg	DIR *dir;
530428d7b3dSmrg	struct dirent *de;
531428d7b3dSmrg
532428d7b3dSmrg	snprintf(path, sizeof(path),
533428d7b3dSmrg		 "/sys/bus/pci/devices/%04x:%02x:%02x.%d/backlight",
534428d7b3dSmrg		 pci->domain, pci->bus, pci->dev, pci->func);
535428d7b3dSmrg
536428d7b3dSmrg	dir = opendir(path);
537428d7b3dSmrg	if (dir == NULL)
538428d7b3dSmrg		return NULL;
539428d7b3dSmrg
540428d7b3dSmrg	while ((de = readdir(dir))) {
541428d7b3dSmrg		int v;
542428d7b3dSmrg
543428d7b3dSmrg		if (*de->d_name == '.')
544428d7b3dSmrg			continue;
545428d7b3dSmrg
546428d7b3dSmrg		v = backlight_exists(de->d_name);
547428d7b3dSmrg		if (v < best_type) {
548428d7b3dSmrg			char *copy = strdup(de->d_name);
549428d7b3dSmrg			if (copy) {
550428d7b3dSmrg				free(best_iface);
551428d7b3dSmrg				best_iface = copy;
552428d7b3dSmrg				best_type = v;
553428d7b3dSmrg			}
554428d7b3dSmrg		}
555428d7b3dSmrg	}
556428d7b3dSmrg	closedir(dir);
557428d7b3dSmrg
558428d7b3dSmrg	return best_iface;
559428d7b3dSmrg}
560