backlight.c revision 813957e3
142542f5fSchristos/***************************************************************************
242542f5fSchristos
342542f5fSchristos Copyright 2014 Intel Corporation.  All Rights Reserved.
442542f5fSchristos Copyright 2014 Red Hat, Inc.
542542f5fSchristos
642542f5fSchristos Permission is hereby granted, free of charge, to any person obtaining a
742542f5fSchristos copy of this software and associated documentation files (the
842542f5fSchristos "Software"), to deal in the Software without restriction, including
942542f5fSchristos without limitation the rights to use, copy, modify, merge, publish,
1042542f5fSchristos distribute, sub license, and/or sell copies of the Software, and to
1142542f5fSchristos permit persons to whom the Software is furnished to do so, subject to
1242542f5fSchristos the following conditions:
1342542f5fSchristos
1442542f5fSchristos The above copyright notice and this permission notice (including the
1542542f5fSchristos next paragraph) shall be included in all copies or substantial portions
1642542f5fSchristos of the Software.
1742542f5fSchristos
1842542f5fSchristos THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1942542f5fSchristos OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2042542f5fSchristos MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
2142542f5fSchristos IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
2242542f5fSchristos DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
2342542f5fSchristos OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
2442542f5fSchristos THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2542542f5fSchristos
2642542f5fSchristos **************************************************************************/
2742542f5fSchristos
2842542f5fSchristos#ifdef HAVE_CONFIG_H
2942542f5fSchristos#include "config.h"
3042542f5fSchristos#endif
3142542f5fSchristos
3242542f5fSchristos#include <sys/types.h>
3342542f5fSchristos#include <sys/wait.h>
3442542f5fSchristos#include <sys/stat.h>
3542542f5fSchristos#include <sys/ioctl.h>
3642542f5fSchristos
3742542f5fSchristos#include <stdio.h>
3842542f5fSchristos#include <stdlib.h>
3942542f5fSchristos#include <string.h>
4042542f5fSchristos#include <ctype.h>
4142542f5fSchristos#include <limits.h>
4242542f5fSchristos#include <fcntl.h>
4342542f5fSchristos#include <unistd.h>
4442542f5fSchristos#include <dirent.h>
4542542f5fSchristos
4642542f5fSchristos#include <xorg-server.h>
4742542f5fSchristos#include <xf86.h>
4842542f5fSchristos#include <pciaccess.h>
4942542f5fSchristos
5042542f5fSchristos#include "backlight.h"
5142542f5fSchristos#include "fd.h"
5242542f5fSchristos
5342542f5fSchristos#define BACKLIGHT_CLASS "/sys/class/backlight"
5442542f5fSchristos
5542542f5fSchristos/* Enough for 10 digits of backlight + '\n' + '\0' */
5642542f5fSchristos#define BACKLIGHT_VALUE_LEN 12
5742542f5fSchristos
5842542f5fSchristos#ifndef ARRAY_SIZE
5942542f5fSchristos#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
6042542f5fSchristos#endif
6142542f5fSchristos
6242542f5fSchristos/*
6342542f5fSchristos * Unfortunately this is not as simple as I would like it to be. If selinux is
6442542f5fSchristos * dropping dbus messages pkexec may block *forever*.
6542542f5fSchristos *
6642542f5fSchristos * Backgrounding pkexec by doing System("pkexec ...&") does not work because
6742542f5fSchristos * that detaches pkexec from its parent at which point its security checks
6842542f5fSchristos * fail and it refuses to execute the helper.
6942542f5fSchristos *
7042542f5fSchristos * So we're left with spawning a helper child which gets levels to set written
7142542f5fSchristos * to it through a pipe. This turns the blocking forever problem from a hung
7242542f5fSchristos * machine problem into a simple backlight control not working problem.
7342542f5fSchristos *
7442542f5fSchristos * If only things were as simple as on OpenBSD! :)
7542542f5fSchristos */
7642542f5fSchristos
7742542f5fSchristosvoid backlight_init(struct backlight *b)
7842542f5fSchristos{
7942542f5fSchristos	b->type = BL_NONE;
8042542f5fSchristos	b->iface = NULL;
8142542f5fSchristos	b->fd = -1;
8242542f5fSchristos	b->pid = -1;
8342542f5fSchristos	b->max = -1;
84813957e3Ssnj	b->has_power = 0;
8542542f5fSchristos}
8642542f5fSchristos
879a906b70Schristos#if defined(__OpenBSD__) || defined(__NetBSD__)
8842542f5fSchristos
8942542f5fSchristos#include <dev/wscons/wsconsio.h>
9042542f5fSchristos#include <xf86Priv.h>
9142542f5fSchristos
9242542f5fSchristosint backlight_set(struct backlight *b, int level)
9342542f5fSchristos{
9442542f5fSchristos	struct wsdisplay_param param;
9542542f5fSchristos
9642542f5fSchristos	if (b->iface == NULL)
9742542f5fSchristos		return -1;
9842542f5fSchristos
9942542f5fSchristos	if ((unsigned)level > b->max)
10042542f5fSchristos		level = b->max;
10142542f5fSchristos
10242542f5fSchristos	memset(&param, 0, sizeof(param));
10342542f5fSchristos	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
10442542f5fSchristos	param.curval = level;
10542542f5fSchristos
10642542f5fSchristos	return ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &param);
10742542f5fSchristos}
10842542f5fSchristos
10942542f5fSchristosint backlight_get(struct backlight *b)
11042542f5fSchristos{
11142542f5fSchristos	struct wsdisplay_param param;
11242542f5fSchristos
11342542f5fSchristos	if (b->iface == NULL)
11442542f5fSchristos		return -1;
11542542f5fSchristos
11642542f5fSchristos	memset(&param, 0, sizeof(param));
11742542f5fSchristos	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
11842542f5fSchristos
11942542f5fSchristos	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param))
12042542f5fSchristos		return -1;
12142542f5fSchristos
12242542f5fSchristos	return param.curval;
12342542f5fSchristos}
12442542f5fSchristos
12542542f5fSchristosint backlight_open(struct backlight *b, char *iface)
12642542f5fSchristos{
12742542f5fSchristos	struct wsdisplay_param param;
12842542f5fSchristos
12942542f5fSchristos	if (iface != NULL)
13042542f5fSchristos		return -1;
13142542f5fSchristos
13242542f5fSchristos	memset(&param, 0, sizeof(param));
13342542f5fSchristos	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
13442542f5fSchristos
13542542f5fSchristos	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param) == -1)
13642542f5fSchristos		return -1;
13742542f5fSchristos
13842542f5fSchristos	b->iface = strdup("wscons");
13942542f5fSchristos	if (b->iface == NULL)
14042542f5fSchristos		return -1;
14142542f5fSchristos
14242542f5fSchristos	b->max = param.max;
14342542f5fSchristos	b->fd = -1;
14442542f5fSchristos	b->type = BL_PLATFORM;
14542542f5fSchristos
14642542f5fSchristos	return param.curval;
14742542f5fSchristos}
14842542f5fSchristos
14942542f5fSchristosenum backlight_type backlight_exists(const char *iface)
15042542f5fSchristos{
15142542f5fSchristos	if (iface != NULL)
15242542f5fSchristos		return BL_NONE;
15342542f5fSchristos
15442542f5fSchristos	return BL_PLATFORM;
15542542f5fSchristos}
15642542f5fSchristos
157813957e3Ssnjint backlight_on(struct backlight *b)
158813957e3Ssnj{
159813957e3Ssnj	return 0;
160813957e3Ssnj}
161813957e3Ssnj
162813957e3Ssnjint backlight_off(struct backlight *b)
163813957e3Ssnj{
164813957e3Ssnj	return 0;
165813957e3Ssnj}
16642542f5fSchristos#else
16742542f5fSchristos
16842542f5fSchristosstatic int
16942542f5fSchristosis_sysfs_fd(int fd)
17042542f5fSchristos{
17142542f5fSchristos	struct stat st;
17242542f5fSchristos	return fstat(fd, &st) == 0 && major(st.st_dev) == 0;
17342542f5fSchristos}
17442542f5fSchristos
17542542f5fSchristosstatic int
17642542f5fSchristos__backlight_open(const char *iface, const char *file, int mode)
17742542f5fSchristos{
17842542f5fSchristos	char buf[1024];
17942542f5fSchristos	int fd;
18042542f5fSchristos
18142542f5fSchristos	snprintf(buf, sizeof(buf), BACKLIGHT_CLASS "/%s/%s", iface, file);
18242542f5fSchristos	fd = open(buf, mode);
18342542f5fSchristos	if (fd == -1)
18442542f5fSchristos		return -1;
18542542f5fSchristos
18642542f5fSchristos	if (!is_sysfs_fd(fd)) {
18742542f5fSchristos		close(fd);
18842542f5fSchristos		return -1;
18942542f5fSchristos	}
19042542f5fSchristos
19142542f5fSchristos	return fd;
19242542f5fSchristos}
19342542f5fSchristos
19442542f5fSchristosstatic int
19542542f5fSchristos__backlight_read(const char *iface, const char *file)
19642542f5fSchristos{
19742542f5fSchristos	char buf[BACKLIGHT_VALUE_LEN];
19842542f5fSchristos	int fd, val;
19942542f5fSchristos
20042542f5fSchristos	fd = __backlight_open(iface, file, O_RDONLY);
20142542f5fSchristos	if (fd < 0)
20242542f5fSchristos		return -1;
20342542f5fSchristos
20442542f5fSchristos	val = read(fd, buf, BACKLIGHT_VALUE_LEN - 1);
20542542f5fSchristos	if (val > 0) {
20642542f5fSchristos		buf[val] = '\0';
20742542f5fSchristos		val = atoi(buf);
20842542f5fSchristos	} else
20942542f5fSchristos		val = -1;
21042542f5fSchristos	close(fd);
21142542f5fSchristos
21242542f5fSchristos	return val;
21342542f5fSchristos}
21442542f5fSchristos
215813957e3Ssnjstatic int
216813957e3Ssnj__backlight_write(const char *iface, const char *file, const char *value)
217813957e3Ssnj{
218813957e3Ssnj	int fd, ret;
219813957e3Ssnj
220813957e3Ssnj	fd = __backlight_open(iface, file, O_WRONLY);
221813957e3Ssnj	if (fd < 0)
222813957e3Ssnj		return -1;
223813957e3Ssnj
224813957e3Ssnj	ret = write(fd, value, strlen(value)+1);
225813957e3Ssnj	close(fd);
226813957e3Ssnj
227813957e3Ssnj	return ret;
228813957e3Ssnj}
229813957e3Ssnj
23042542f5fSchristos/* List of available kernel interfaces in priority order */
23142542f5fSchristosstatic const char *known_interfaces[] = {
23242542f5fSchristos	"dell_backlight",
23342542f5fSchristos	"gmux_backlight",
23442542f5fSchristos	"asus-laptop",
23542542f5fSchristos	"asus-nb-wmi",
23642542f5fSchristos	"eeepc",
23742542f5fSchristos	"thinkpad_screen",
23842542f5fSchristos	"mbp_backlight",
23942542f5fSchristos	"fujitsu-laptop",
24042542f5fSchristos	"sony",
24142542f5fSchristos	"samsung",
24242542f5fSchristos	"acpi_video1",
24342542f5fSchristos	"acpi_video0",
24442542f5fSchristos	"intel_backlight",
24542542f5fSchristos};
24642542f5fSchristos
24742542f5fSchristosstatic enum backlight_type __backlight_type(const char *iface)
24842542f5fSchristos{
24942542f5fSchristos	char buf[1024];
25042542f5fSchristos	int fd, v;
25142542f5fSchristos
25242542f5fSchristos	v = -1;
25342542f5fSchristos	fd = __backlight_open(iface, "type", O_RDONLY);
25442542f5fSchristos	if (fd >= 0) {
25542542f5fSchristos		v = read(fd, buf, sizeof(buf)-1);
25642542f5fSchristos		close(fd);
25742542f5fSchristos	}
25842542f5fSchristos	if (v > 0) {
25942542f5fSchristos		while (v > 0 && isspace(buf[v-1]))
26042542f5fSchristos			v--;
26142542f5fSchristos		buf[v] = '\0';
26242542f5fSchristos
26342542f5fSchristos		if (strcmp(buf, "raw") == 0)
26442542f5fSchristos			v = BL_RAW;
26542542f5fSchristos		else if (strcmp(buf, "platform") == 0)
26642542f5fSchristos			v = BL_PLATFORM;
26742542f5fSchristos		else if (strcmp(buf, "firmware") == 0)
26842542f5fSchristos			v = BL_FIRMWARE;
26942542f5fSchristos		else
27042542f5fSchristos			v = BL_NAMED;
27142542f5fSchristos	} else
27242542f5fSchristos		v = BL_NAMED;
27342542f5fSchristos
27442542f5fSchristos	if (v == BL_NAMED) {
27542542f5fSchristos		int i;
27642542f5fSchristos		for (i = 0; i < ARRAY_SIZE(known_interfaces); i++) {
27742542f5fSchristos			if (strcmp(iface, known_interfaces[i]) == 0)
27842542f5fSchristos				break;
27942542f5fSchristos		}
28042542f5fSchristos		v += i;
28142542f5fSchristos	}
28242542f5fSchristos
28342542f5fSchristos	return v;
28442542f5fSchristos}
28542542f5fSchristos
28642542f5fSchristosenum backlight_type backlight_exists(const char *iface)
28742542f5fSchristos{
28842542f5fSchristos	if (__backlight_read(iface, "brightness") < 0)
28942542f5fSchristos		return BL_NONE;
29042542f5fSchristos
29142542f5fSchristos	if (__backlight_read(iface, "max_brightness") <= 0)
29242542f5fSchristos		return BL_NONE;
29342542f5fSchristos
29442542f5fSchristos	return __backlight_type(iface);
29542542f5fSchristos}
29642542f5fSchristos
29742542f5fSchristosstatic int __backlight_init(struct backlight *b, char *iface, int fd)
29842542f5fSchristos{
29942542f5fSchristos	b->fd = fd_move_cloexec(fd_set_nonblock(fd));
30042542f5fSchristos	b->iface = iface;
30142542f5fSchristos	return 1;
30242542f5fSchristos}
30342542f5fSchristos
30442542f5fSchristosstatic int __backlight_direct_init(struct backlight *b, char *iface)
30542542f5fSchristos{
30642542f5fSchristos	int fd;
30742542f5fSchristos
30842542f5fSchristos	fd = __backlight_open(iface, "brightness", O_RDWR);
30942542f5fSchristos	if (fd < 0)
31042542f5fSchristos		return 0;
31142542f5fSchristos
312813957e3Ssnj	if (__backlight_read(iface, "bl_power") != -1)
313813957e3Ssnj		b->has_power = 1;
314813957e3Ssnj
31542542f5fSchristos	return __backlight_init(b, iface, fd);
31642542f5fSchristos}
31742542f5fSchristos
31842542f5fSchristosstatic int __backlight_helper_init(struct backlight *b, char *iface)
31942542f5fSchristos{
32042542f5fSchristos#if USE_BACKLIGHT_HELPER
32142542f5fSchristos	struct stat st;
32242542f5fSchristos	char *env[] = { NULL };
32342542f5fSchristos	int use_pkexec = 0;
32442542f5fSchristos	int fds[2];
32542542f5fSchristos
32642542f5fSchristos	/*
32742542f5fSchristos	 * Some systems may prefer using PolicyKit's pkexec over
32842542f5fSchristos	 * making the helper suid root, since the suid option will allow
32942542f5fSchristos	 * anyone to control the backlight.  However, as pkexec
33042542f5fSchristos	 * is quite troublesome and not universally available, we
33142542f5fSchristos	 * still try the old fashioned and simple method first.
33242542f5fSchristos	 * Either way, we have to trust that it is our backlight-helper
33342542f5fSchristos	 * that is run and that we have scrutinised it carefully.
33442542f5fSchristos	 */
33542542f5fSchristos	if (stat(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", &st))
33642542f5fSchristos		return 0;
33742542f5fSchristos
33842542f5fSchristos	if ((st.st_mode & (S_IFREG | S_ISUID | S_IXUSR)) != (S_IFREG | S_ISUID | S_IXUSR)) {
33942542f5fSchristos		if (System("pkexec --version"))
34042542f5fSchristos			return 0;
34142542f5fSchristos
34242542f5fSchristos		use_pkexec = 1;
34342542f5fSchristos	}
34442542f5fSchristos
34542542f5fSchristos	if (pipe(fds))
34642542f5fSchristos		return 0;
34742542f5fSchristos
34842542f5fSchristos	switch ((b->pid = fork())) {
34942542f5fSchristos	case 0:
35042542f5fSchristos		if (setgid(getgid()) || setuid(getuid()))
35142542f5fSchristos			_exit(127);
35242542f5fSchristos
35342542f5fSchristos		close(fds[1]);
35442542f5fSchristos		if (dup2(fds[0], 0))
35542542f5fSchristos			_exit(127);
35642542f5fSchristos		close(fds[0]);
35742542f5fSchristos
35842542f5fSchristos		if (use_pkexec) {
35942542f5fSchristos			execlp("pkexec", "pkexec",
36042542f5fSchristos			       LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
36142542f5fSchristos			       iface, (char *)0);
36242542f5fSchristos		} else {
36342542f5fSchristos			execle(LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
36442542f5fSchristos			       "xf86-video-intel-backlight-helper",
36542542f5fSchristos			       iface, (char *)0, env);
36642542f5fSchristos		}
36742542f5fSchristos		_exit(1);
36842542f5fSchristos		/* unreachable fallthrough */
36942542f5fSchristos	case -1:
37042542f5fSchristos		close(fds[1]);
37142542f5fSchristos		close(fds[0]);
37242542f5fSchristos		return 0;
37342542f5fSchristos
37442542f5fSchristos	default:
37542542f5fSchristos		close(fds[0]);
37642542f5fSchristos		return __backlight_init(b, iface, fds[1]);
37742542f5fSchristos	}
37842542f5fSchristos#else
37942542f5fSchristos	return 0;
38042542f5fSchristos#endif
38142542f5fSchristos}
38242542f5fSchristos
38342542f5fSchristosstatic char *
38442542f5fSchristos__backlight_find(void)
38542542f5fSchristos{
38642542f5fSchristos	char *best_iface = NULL;
38742542f5fSchristos	unsigned best_type = INT_MAX;
38842542f5fSchristos	DIR *dir;
38942542f5fSchristos	struct dirent *de;
39042542f5fSchristos
39142542f5fSchristos	dir = opendir(BACKLIGHT_CLASS);
39242542f5fSchristos	if (dir == NULL)
39342542f5fSchristos		return NULL;
39442542f5fSchristos
39542542f5fSchristos	while ((de = readdir(dir))) {
39642542f5fSchristos		int v;
39742542f5fSchristos
39842542f5fSchristos		if (*de->d_name == '.')
39942542f5fSchristos			continue;
40042542f5fSchristos
40142542f5fSchristos		/* Fallback to priority list of known iface for old kernels */
40242542f5fSchristos		v = backlight_exists(de->d_name);
40342542f5fSchristos		if (v < best_type) {
40442542f5fSchristos			char *copy = strdup(de->d_name);
40542542f5fSchristos			if (copy) {
40642542f5fSchristos				free(best_iface);
40742542f5fSchristos				best_iface = copy;
40842542f5fSchristos				best_type = v;
40942542f5fSchristos			}
41042542f5fSchristos		}
41142542f5fSchristos	}
41242542f5fSchristos	closedir(dir);
41342542f5fSchristos
41442542f5fSchristos	return best_iface;
41542542f5fSchristos}
41642542f5fSchristos
41742542f5fSchristosint backlight_open(struct backlight *b, char *iface)
41842542f5fSchristos{
41942542f5fSchristos	int level;
42042542f5fSchristos
42142542f5fSchristos	if (iface == NULL)
42242542f5fSchristos		iface = __backlight_find();
42342542f5fSchristos	if (iface == NULL)
42442542f5fSchristos		goto err;
42542542f5fSchristos
42642542f5fSchristos	b->type = __backlight_type(iface);
42742542f5fSchristos
42842542f5fSchristos	b->max = __backlight_read(iface, "max_brightness");
42942542f5fSchristos	if (b->max <= 0)
43042542f5fSchristos		goto err;
43142542f5fSchristos
43242542f5fSchristos	level = __backlight_read(iface, "brightness");
43342542f5fSchristos	if (level < 0)
43442542f5fSchristos		goto err;
43542542f5fSchristos
43642542f5fSchristos	if (!__backlight_direct_init(b, iface) &&
43742542f5fSchristos	    !__backlight_helper_init(b, iface))
43842542f5fSchristos		goto err;
43942542f5fSchristos
44042542f5fSchristos	return level;
44142542f5fSchristos
44242542f5fSchristoserr:
44342542f5fSchristos	backlight_init(b);
44442542f5fSchristos	return -1;
44542542f5fSchristos}
44642542f5fSchristos
44742542f5fSchristosint backlight_set(struct backlight *b, int level)
44842542f5fSchristos{
44942542f5fSchristos	char val[BACKLIGHT_VALUE_LEN];
45042542f5fSchristos	int len, ret = 0;
45142542f5fSchristos
45242542f5fSchristos	if (b->iface == NULL)
45342542f5fSchristos		return 0;
45442542f5fSchristos
45542542f5fSchristos	if ((unsigned)level > b->max)
45642542f5fSchristos		level = b->max;
45742542f5fSchristos
45842542f5fSchristos	len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level);
45942542f5fSchristos	if (write(b->fd, val, len) != len)
46042542f5fSchristos		ret = -1;
46142542f5fSchristos
46242542f5fSchristos	return ret;
46342542f5fSchristos}
46442542f5fSchristos
46542542f5fSchristosint backlight_get(struct backlight *b)
46642542f5fSchristos{
46742542f5fSchristos	int level;
46842542f5fSchristos
46942542f5fSchristos	if (b->iface == NULL)
47042542f5fSchristos		return -1;
47142542f5fSchristos
47242542f5fSchristos	level = __backlight_read(b->iface, "brightness");
47342542f5fSchristos	if (level > b->max)
47442542f5fSchristos		level = b->max;
47542542f5fSchristos	else if (level < 0)
47642542f5fSchristos		level = -1;
47742542f5fSchristos	return level;
47842542f5fSchristos}
479813957e3Ssnj
480813957e3Ssnjint backlight_off(struct backlight *b)
481813957e3Ssnj{
482813957e3Ssnj	if (b->iface == NULL)
483813957e3Ssnj		return 0;
484813957e3Ssnj
485813957e3Ssnj	if (!b->has_power)
486813957e3Ssnj		return 0;
487813957e3Ssnj
488813957e3Ssnj	/* 4 -> FB_BLANK_POWERDOWN */
489813957e3Ssnj	return __backlight_write(b->iface, "bl_power", "4");
490813957e3Ssnj}
491813957e3Ssnj
492813957e3Ssnjint backlight_on(struct backlight *b)
493813957e3Ssnj{
494813957e3Ssnj	if (b->iface == NULL)
495813957e3Ssnj		return 0;
496813957e3Ssnj
497813957e3Ssnj	if (!b->has_power)
498813957e3Ssnj		return 0;
499813957e3Ssnj
500813957e3Ssnj	/* 0 -> FB_BLANK_UNBLANK */
501813957e3Ssnj	return __backlight_write(b->iface, "bl_power", "0");
502813957e3Ssnj}
50342542f5fSchristos#endif
50442542f5fSchristos
50542542f5fSchristosvoid backlight_disable(struct backlight *b)
50642542f5fSchristos{
50742542f5fSchristos	if (b->iface == NULL)
50842542f5fSchristos		return;
50942542f5fSchristos
51042542f5fSchristos	if (b->fd != -1)
51142542f5fSchristos		close(b->fd);
51242542f5fSchristos
51342542f5fSchristos	free(b->iface);
51442542f5fSchristos	b->iface = NULL;
51542542f5fSchristos}
51642542f5fSchristos
51742542f5fSchristosvoid backlight_close(struct backlight *b)
51842542f5fSchristos{
51942542f5fSchristos	backlight_disable(b);
52042542f5fSchristos	if (b->pid)
52142542f5fSchristos		waitpid(b->pid, NULL, 0);
52242542f5fSchristos}
52342542f5fSchristos
52442542f5fSchristoschar *backlight_find_for_device(struct pci_device *pci)
52542542f5fSchristos{
52642542f5fSchristos	char path[200];
52742542f5fSchristos	unsigned best_type = INT_MAX;
52842542f5fSchristos	char *best_iface = NULL;
52942542f5fSchristos	DIR *dir;
53042542f5fSchristos	struct dirent *de;
53142542f5fSchristos
53242542f5fSchristos	snprintf(path, sizeof(path),
53342542f5fSchristos		 "/sys/bus/pci/devices/%04x:%02x:%02x.%d/backlight",
53442542f5fSchristos		 pci->domain, pci->bus, pci->dev, pci->func);
53542542f5fSchristos
53642542f5fSchristos	dir = opendir(path);
53742542f5fSchristos	if (dir == NULL)
53842542f5fSchristos		return NULL;
53942542f5fSchristos
54042542f5fSchristos	while ((de = readdir(dir))) {
54142542f5fSchristos		int v;
54242542f5fSchristos
54342542f5fSchristos		if (*de->d_name == '.')
54442542f5fSchristos			continue;
54542542f5fSchristos
54642542f5fSchristos		v = backlight_exists(de->d_name);
54742542f5fSchristos		if (v < best_type) {
54842542f5fSchristos			char *copy = strdup(de->d_name);
54942542f5fSchristos			if (copy) {
55042542f5fSchristos				free(best_iface);
55142542f5fSchristos				best_iface = copy;
55242542f5fSchristos				best_type = v;
55342542f5fSchristos			}
55442542f5fSchristos		}
55542542f5fSchristos	}
55642542f5fSchristos	closedir(dir);
55742542f5fSchristos
55842542f5fSchristos	return best_iface;
55942542f5fSchristos}
560