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
3763ef14f0Smrg#if MAJOR_IN_MKDEV
3863ef14f0Smrg#include <sys/mkdev.h>
3963ef14f0Smrg#elif MAJOR_IN_SYSMACROS
4063ef14f0Smrg#include <sys/sysmacros.h>
4163ef14f0Smrg#endif
4263ef14f0Smrg
4342542f5fSchristos#include <stdio.h>
4442542f5fSchristos#include <stdlib.h>
4542542f5fSchristos#include <string.h>
4642542f5fSchristos#include <ctype.h>
4742542f5fSchristos#include <limits.h>
4842542f5fSchristos#include <fcntl.h>
4942542f5fSchristos#include <unistd.h>
5042542f5fSchristos#include <dirent.h>
5163ef14f0Smrg#include <errno.h>
5242542f5fSchristos
5342542f5fSchristos#include <xorg-server.h>
5442542f5fSchristos#include <xf86.h>
5542542f5fSchristos#include <pciaccess.h>
5642542f5fSchristos
5742542f5fSchristos#include "backlight.h"
5842542f5fSchristos#include "fd.h"
5942542f5fSchristos
6042542f5fSchristos#define BACKLIGHT_CLASS "/sys/class/backlight"
6142542f5fSchristos
6242542f5fSchristos/* Enough for 10 digits of backlight + '\n' + '\0' */
6342542f5fSchristos#define BACKLIGHT_VALUE_LEN 12
6442542f5fSchristos
6542542f5fSchristos#ifndef ARRAY_SIZE
6642542f5fSchristos#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
6742542f5fSchristos#endif
6842542f5fSchristos
6942542f5fSchristos/*
7042542f5fSchristos * Unfortunately this is not as simple as I would like it to be. If selinux is
7142542f5fSchristos * dropping dbus messages pkexec may block *forever*.
7242542f5fSchristos *
7342542f5fSchristos * Backgrounding pkexec by doing System("pkexec ...&") does not work because
7442542f5fSchristos * that detaches pkexec from its parent at which point its security checks
7542542f5fSchristos * fail and it refuses to execute the helper.
7642542f5fSchristos *
7742542f5fSchristos * So we're left with spawning a helper child which gets levels to set written
7842542f5fSchristos * to it through a pipe. This turns the blocking forever problem from a hung
7942542f5fSchristos * machine problem into a simple backlight control not working problem.
8042542f5fSchristos *
8142542f5fSchristos * If only things were as simple as on OpenBSD! :)
8242542f5fSchristos */
8342542f5fSchristos
8442542f5fSchristosvoid backlight_init(struct backlight *b)
8542542f5fSchristos{
8642542f5fSchristos	b->type = BL_NONE;
8742542f5fSchristos	b->iface = NULL;
8842542f5fSchristos	b->fd = -1;
8942542f5fSchristos	b->pid = -1;
9042542f5fSchristos	b->max = -1;
91813957e3Ssnj	b->has_power = 0;
9242542f5fSchristos}
9342542f5fSchristos
9463ef14f0Smrg#ifdef HAVE_DEV_WSCONS_WSCONSIO_H
9542542f5fSchristos
9642542f5fSchristos#include <dev/wscons/wsconsio.h>
9742542f5fSchristos#include <xf86Priv.h>
9842542f5fSchristos
9942542f5fSchristosint backlight_set(struct backlight *b, int level)
10042542f5fSchristos{
10142542f5fSchristos	struct wsdisplay_param param;
10242542f5fSchristos
10342542f5fSchristos	if (b->iface == NULL)
10442542f5fSchristos		return -1;
10542542f5fSchristos
10642542f5fSchristos	if ((unsigned)level > b->max)
10742542f5fSchristos		level = b->max;
10842542f5fSchristos
10942542f5fSchristos	memset(&param, 0, sizeof(param));
11042542f5fSchristos	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
11142542f5fSchristos	param.curval = level;
11242542f5fSchristos
11342542f5fSchristos	return ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &param);
11442542f5fSchristos}
11542542f5fSchristos
11642542f5fSchristosint backlight_get(struct backlight *b)
11742542f5fSchristos{
11842542f5fSchristos	struct wsdisplay_param param;
11942542f5fSchristos
12042542f5fSchristos	if (b->iface == NULL)
12142542f5fSchristos		return -1;
12242542f5fSchristos
12342542f5fSchristos	memset(&param, 0, sizeof(param));
12442542f5fSchristos	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
12542542f5fSchristos
12642542f5fSchristos	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param))
12742542f5fSchristos		return -1;
12842542f5fSchristos
12942542f5fSchristos	return param.curval;
13042542f5fSchristos}
13142542f5fSchristos
13263ef14f0Smrgchar *backlight_find_for_device(struct pci_device *pci)
13363ef14f0Smrg{
13463ef14f0Smrg	return NULL;
13563ef14f0Smrg}
13663ef14f0Smrg
13742542f5fSchristosint backlight_open(struct backlight *b, char *iface)
13842542f5fSchristos{
13942542f5fSchristos	struct wsdisplay_param param;
14042542f5fSchristos
14142542f5fSchristos	if (iface != NULL)
14242542f5fSchristos		return -1;
14342542f5fSchristos
14442542f5fSchristos	memset(&param, 0, sizeof(param));
14542542f5fSchristos	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
14642542f5fSchristos
14742542f5fSchristos	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param) == -1)
14842542f5fSchristos		return -1;
14942542f5fSchristos
15042542f5fSchristos	b->iface = strdup("wscons");
15142542f5fSchristos	if (b->iface == NULL)
15242542f5fSchristos		return -1;
15342542f5fSchristos
15442542f5fSchristos	b->max = param.max;
15542542f5fSchristos	b->fd = -1;
15642542f5fSchristos	b->type = BL_PLATFORM;
15742542f5fSchristos
15842542f5fSchristos	return param.curval;
15942542f5fSchristos}
16042542f5fSchristos
16163ef14f0Smrgint backlight_exists(const char *iface)
16242542f5fSchristos{
16363ef14f0Smrg	return iface == NULL;
16442542f5fSchristos}
16542542f5fSchristos
166813957e3Ssnjint backlight_on(struct backlight *b)
167813957e3Ssnj{
168813957e3Ssnj	return 0;
169813957e3Ssnj}
170813957e3Ssnj
171813957e3Ssnjint backlight_off(struct backlight *b)
172813957e3Ssnj{
173813957e3Ssnj	return 0;
174813957e3Ssnj}
17563ef14f0Smrg
17642542f5fSchristos#else
17742542f5fSchristos
17842542f5fSchristosstatic int
17942542f5fSchristosis_sysfs_fd(int fd)
18042542f5fSchristos{
18142542f5fSchristos	struct stat st;
18242542f5fSchristos	return fstat(fd, &st) == 0 && major(st.st_dev) == 0;
18342542f5fSchristos}
18442542f5fSchristos
18542542f5fSchristosstatic int
18642542f5fSchristos__backlight_open(const char *iface, const char *file, int mode)
18742542f5fSchristos{
18842542f5fSchristos	char buf[1024];
18942542f5fSchristos	int fd;
19042542f5fSchristos
19142542f5fSchristos	snprintf(buf, sizeof(buf), BACKLIGHT_CLASS "/%s/%s", iface, file);
19242542f5fSchristos	fd = open(buf, mode);
19342542f5fSchristos	if (fd == -1)
19442542f5fSchristos		return -1;
19542542f5fSchristos
19642542f5fSchristos	if (!is_sysfs_fd(fd)) {
19742542f5fSchristos		close(fd);
19842542f5fSchristos		return -1;
19942542f5fSchristos	}
20042542f5fSchristos
20142542f5fSchristos	return fd;
20242542f5fSchristos}
20342542f5fSchristos
20442542f5fSchristosstatic int
20542542f5fSchristos__backlight_read(const char *iface, const char *file)
20642542f5fSchristos{
20742542f5fSchristos	char buf[BACKLIGHT_VALUE_LEN];
20842542f5fSchristos	int fd, val;
20942542f5fSchristos
21042542f5fSchristos	fd = __backlight_open(iface, file, O_RDONLY);
21142542f5fSchristos	if (fd < 0)
21242542f5fSchristos		return -1;
21342542f5fSchristos
21442542f5fSchristos	val = read(fd, buf, BACKLIGHT_VALUE_LEN - 1);
21542542f5fSchristos	if (val > 0) {
21642542f5fSchristos		buf[val] = '\0';
21742542f5fSchristos		val = atoi(buf);
21842542f5fSchristos	} else
21942542f5fSchristos		val = -1;
22042542f5fSchristos	close(fd);
22142542f5fSchristos
22242542f5fSchristos	return val;
22342542f5fSchristos}
22442542f5fSchristos
22563ef14f0Smrgstatic int
22663ef14f0Smrgwriten(int fd, const char *value, int len)
22763ef14f0Smrg{
22863ef14f0Smrg	int ret;
22963ef14f0Smrg
23063ef14f0Smrg	do {
23163ef14f0Smrg		ret = write(fd, value, len);
23263ef14f0Smrg		if (ret < 0) {
23363ef14f0Smrg			if (errno == EAGAIN || errno == EINTR)
23463ef14f0Smrg				continue;
23563ef14f0Smrg
23663ef14f0Smrg			return ret;
23763ef14f0Smrg		}
23863ef14f0Smrg	} while (value += ret, len -= ret);
23963ef14f0Smrg
24063ef14f0Smrg	return 0;
24163ef14f0Smrg}
24263ef14f0Smrg
243813957e3Ssnjstatic int
244813957e3Ssnj__backlight_write(const char *iface, const char *file, const char *value)
245813957e3Ssnj{
246813957e3Ssnj	int fd, ret;
247813957e3Ssnj
248813957e3Ssnj	fd = __backlight_open(iface, file, O_WRONLY);
249813957e3Ssnj	if (fd < 0)
250813957e3Ssnj		return -1;
251813957e3Ssnj
25263ef14f0Smrg	ret = writen(fd, value, strlen(value)+1);
253813957e3Ssnj	close(fd);
254813957e3Ssnj
255813957e3Ssnj	return ret;
256813957e3Ssnj}
257813957e3Ssnj
25842542f5fSchristos/* List of available kernel interfaces in priority order */
25942542f5fSchristosstatic const char *known_interfaces[] = {
26042542f5fSchristos	"dell_backlight",
26142542f5fSchristos	"gmux_backlight",
26242542f5fSchristos	"asus-laptop",
26342542f5fSchristos	"asus-nb-wmi",
26442542f5fSchristos	"eeepc",
26542542f5fSchristos	"thinkpad_screen",
26642542f5fSchristos	"mbp_backlight",
26742542f5fSchristos	"fujitsu-laptop",
26842542f5fSchristos	"sony",
26942542f5fSchristos	"samsung",
27042542f5fSchristos	"acpi_video1",
27142542f5fSchristos	"acpi_video0",
27242542f5fSchristos	"intel_backlight",
27342542f5fSchristos};
27442542f5fSchristos
27563ef14f0Smrgstatic int __backlight_type(const char *iface)
27642542f5fSchristos{
27742542f5fSchristos	char buf[1024];
27863ef14f0Smrg	int fd, v, i;
27942542f5fSchristos
28042542f5fSchristos	v = -1;
28142542f5fSchristos	fd = __backlight_open(iface, "type", O_RDONLY);
28242542f5fSchristos	if (fd >= 0) {
28342542f5fSchristos		v = read(fd, buf, sizeof(buf)-1);
28442542f5fSchristos		close(fd);
28542542f5fSchristos	}
28642542f5fSchristos	if (v > 0) {
28742542f5fSchristos		while (v > 0 && isspace(buf[v-1]))
28842542f5fSchristos			v--;
28942542f5fSchristos		buf[v] = '\0';
29042542f5fSchristos
29142542f5fSchristos		if (strcmp(buf, "raw") == 0)
29263ef14f0Smrg			v = BL_RAW << 8;
29342542f5fSchristos		else if (strcmp(buf, "platform") == 0)
29463ef14f0Smrg			v = BL_PLATFORM << 8;
29542542f5fSchristos		else if (strcmp(buf, "firmware") == 0)
29663ef14f0Smrg			v = BL_FIRMWARE << 8;
29742542f5fSchristos		else
29863ef14f0Smrg			v = BL_NAMED << 8;
29942542f5fSchristos	} else
30063ef14f0Smrg		v = BL_NAMED << 8;
30142542f5fSchristos
30263ef14f0Smrg	for (i = 0; i < ARRAY_SIZE(known_interfaces); i++) {
30363ef14f0Smrg		if (strcmp(iface, known_interfaces[i]) == 0)
30463ef14f0Smrg			break;
30542542f5fSchristos	}
30663ef14f0Smrg	v += i;
30742542f5fSchristos
30842542f5fSchristos	return v;
30942542f5fSchristos}
31042542f5fSchristos
31163ef14f0Smrgstatic int __backlight_exists(const char *iface)
31242542f5fSchristos{
31342542f5fSchristos	if (__backlight_read(iface, "brightness") < 0)
31463ef14f0Smrg		return -1;
31542542f5fSchristos
31642542f5fSchristos	if (__backlight_read(iface, "max_brightness") <= 0)
31763ef14f0Smrg		return -1;
31842542f5fSchristos
31942542f5fSchristos	return __backlight_type(iface);
32042542f5fSchristos}
32142542f5fSchristos
32263ef14f0Smrgint backlight_exists(const char *iface)
32363ef14f0Smrg{
32463ef14f0Smrg	return __backlight_exists(iface) != -1;
32563ef14f0Smrg}
32663ef14f0Smrg
32742542f5fSchristosstatic int __backlight_init(struct backlight *b, char *iface, int fd)
32842542f5fSchristos{
32942542f5fSchristos	b->fd = fd_move_cloexec(fd_set_nonblock(fd));
33042542f5fSchristos	b->iface = iface;
33142542f5fSchristos	return 1;
33242542f5fSchristos}
33342542f5fSchristos
33442542f5fSchristosstatic int __backlight_direct_init(struct backlight *b, char *iface)
33542542f5fSchristos{
33642542f5fSchristos	int fd;
33742542f5fSchristos
33842542f5fSchristos	fd = __backlight_open(iface, "brightness", O_RDWR);
33942542f5fSchristos	if (fd < 0)
34042542f5fSchristos		return 0;
34142542f5fSchristos
342813957e3Ssnj	if (__backlight_read(iface, "bl_power") != -1)
343813957e3Ssnj		b->has_power = 1;
344813957e3Ssnj
34542542f5fSchristos	return __backlight_init(b, iface, fd);
34642542f5fSchristos}
34742542f5fSchristos
34842542f5fSchristosstatic int __backlight_helper_init(struct backlight *b, char *iface)
34942542f5fSchristos{
35042542f5fSchristos#if USE_BACKLIGHT_HELPER
35142542f5fSchristos	struct stat st;
35242542f5fSchristos	char *env[] = { NULL };
35342542f5fSchristos	int use_pkexec = 0;
35442542f5fSchristos	int fds[2];
35542542f5fSchristos
35642542f5fSchristos	/*
35742542f5fSchristos	 * Some systems may prefer using PolicyKit's pkexec over
35842542f5fSchristos	 * making the helper suid root, since the suid option will allow
35942542f5fSchristos	 * anyone to control the backlight.  However, as pkexec
36042542f5fSchristos	 * is quite troublesome and not universally available, we
36142542f5fSchristos	 * still try the old fashioned and simple method first.
36242542f5fSchristos	 * Either way, we have to trust that it is our backlight-helper
36342542f5fSchristos	 * that is run and that we have scrutinised it carefully.
36442542f5fSchristos	 */
36542542f5fSchristos	if (stat(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", &st))
36642542f5fSchristos		return 0;
36742542f5fSchristos
36842542f5fSchristos	if ((st.st_mode & (S_IFREG | S_ISUID | S_IXUSR)) != (S_IFREG | S_ISUID | S_IXUSR)) {
36942542f5fSchristos		if (System("pkexec --version"))
37042542f5fSchristos			return 0;
37142542f5fSchristos
37242542f5fSchristos		use_pkexec = 1;
37342542f5fSchristos	}
37442542f5fSchristos
37542542f5fSchristos	if (pipe(fds))
37642542f5fSchristos		return 0;
37742542f5fSchristos
37842542f5fSchristos	switch ((b->pid = fork())) {
37942542f5fSchristos	case 0:
38042542f5fSchristos		if (setgid(getgid()) || setuid(getuid()))
38142542f5fSchristos			_exit(127);
38242542f5fSchristos
38342542f5fSchristos		close(fds[1]);
38442542f5fSchristos		if (dup2(fds[0], 0))
38542542f5fSchristos			_exit(127);
38642542f5fSchristos		close(fds[0]);
38742542f5fSchristos
38842542f5fSchristos		if (use_pkexec) {
38942542f5fSchristos			execlp("pkexec", "pkexec",
39042542f5fSchristos			       LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
39142542f5fSchristos			       iface, (char *)0);
39242542f5fSchristos		} else {
39342542f5fSchristos			execle(LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
39442542f5fSchristos			       "xf86-video-intel-backlight-helper",
39542542f5fSchristos			       iface, (char *)0, env);
39642542f5fSchristos		}
39742542f5fSchristos		_exit(1);
39842542f5fSchristos		/* unreachable fallthrough */
39942542f5fSchristos	case -1:
40042542f5fSchristos		close(fds[1]);
40142542f5fSchristos		close(fds[0]);
40242542f5fSchristos		return 0;
40342542f5fSchristos
40442542f5fSchristos	default:
40542542f5fSchristos		close(fds[0]);
40642542f5fSchristos		return __backlight_init(b, iface, fds[1]);
40742542f5fSchristos	}
40842542f5fSchristos#else
40942542f5fSchristos	return 0;
41042542f5fSchristos#endif
41142542f5fSchristos}
41242542f5fSchristos
41342542f5fSchristosstatic char *
41442542f5fSchristos__backlight_find(void)
41542542f5fSchristos{
41642542f5fSchristos	char *best_iface = NULL;
41742542f5fSchristos	unsigned best_type = INT_MAX;
41842542f5fSchristos	DIR *dir;
41942542f5fSchristos	struct dirent *de;
42042542f5fSchristos
42142542f5fSchristos	dir = opendir(BACKLIGHT_CLASS);
42242542f5fSchristos	if (dir == NULL)
42342542f5fSchristos		return NULL;
42442542f5fSchristos
42542542f5fSchristos	while ((de = readdir(dir))) {
42642542f5fSchristos		int v;
42742542f5fSchristos
42842542f5fSchristos		if (*de->d_name == '.')
42942542f5fSchristos			continue;
43042542f5fSchristos
43142542f5fSchristos		/* Fallback to priority list of known iface for old kernels */
43263ef14f0Smrg		v = __backlight_exists(de->d_name);
43363ef14f0Smrg		if (v < 0)
43463ef14f0Smrg			continue;
43563ef14f0Smrg
43663ef14f0Smrg		if (v < best_type) {
43763ef14f0Smrg			char *copy = strdup(de->d_name);
43863ef14f0Smrg			if (copy) {
43963ef14f0Smrg				free(best_iface);
44063ef14f0Smrg				best_iface = copy;
44163ef14f0Smrg				best_type = v;
44263ef14f0Smrg			}
44363ef14f0Smrg		}
44463ef14f0Smrg	}
44563ef14f0Smrg	closedir(dir);
44663ef14f0Smrg
44763ef14f0Smrg	return best_iface;
44863ef14f0Smrg}
44963ef14f0Smrg
45063ef14f0Smrgchar *backlight_find_for_device(struct pci_device *pci)
45163ef14f0Smrg{
45263ef14f0Smrg	char path[200];
45363ef14f0Smrg	unsigned best_type = INT_MAX;
45463ef14f0Smrg	char *best_iface = NULL;
45563ef14f0Smrg	DIR *dir;
45663ef14f0Smrg	struct dirent *de;
45763ef14f0Smrg
45863ef14f0Smrg	snprintf(path, sizeof(path),
45963ef14f0Smrg		 "/sys/bus/pci/devices/%04x:%02x:%02x.%d/backlight",
46063ef14f0Smrg		 pci->domain, pci->bus, pci->dev, pci->func);
46163ef14f0Smrg
46263ef14f0Smrg	dir = opendir(path);
46363ef14f0Smrg	if (dir == NULL)
46463ef14f0Smrg		return NULL;
46563ef14f0Smrg
46663ef14f0Smrg	while ((de = readdir(dir))) {
46763ef14f0Smrg		int v;
46863ef14f0Smrg
46963ef14f0Smrg		if (*de->d_name == '.')
47063ef14f0Smrg			continue;
47163ef14f0Smrg
47263ef14f0Smrg		v = __backlight_exists(de->d_name);
47363ef14f0Smrg		if (v < 0)
47463ef14f0Smrg			continue;
47563ef14f0Smrg
47642542f5fSchristos		if (v < best_type) {
47742542f5fSchristos			char *copy = strdup(de->d_name);
47842542f5fSchristos			if (copy) {
47942542f5fSchristos				free(best_iface);
48042542f5fSchristos				best_iface = copy;
48142542f5fSchristos				best_type = v;
48242542f5fSchristos			}
48342542f5fSchristos		}
48442542f5fSchristos	}
48542542f5fSchristos	closedir(dir);
48642542f5fSchristos
48742542f5fSchristos	return best_iface;
48842542f5fSchristos}
48942542f5fSchristos
49042542f5fSchristosint backlight_open(struct backlight *b, char *iface)
49142542f5fSchristos{
49263ef14f0Smrg	int level, type;
49342542f5fSchristos
49442542f5fSchristos	if (iface == NULL)
49542542f5fSchristos		iface = __backlight_find();
49642542f5fSchristos	if (iface == NULL)
49742542f5fSchristos		goto err;
49842542f5fSchristos
49963ef14f0Smrg	type = __backlight_type(iface);
50063ef14f0Smrg	if (type < 0)
50163ef14f0Smrg		goto err;
50263ef14f0Smrg	b->type = type >> 8;
50342542f5fSchristos
50442542f5fSchristos	b->max = __backlight_read(iface, "max_brightness");
50542542f5fSchristos	if (b->max <= 0)
50642542f5fSchristos		goto err;
50742542f5fSchristos
50842542f5fSchristos	level = __backlight_read(iface, "brightness");
50942542f5fSchristos	if (level < 0)
51042542f5fSchristos		goto err;
51142542f5fSchristos
51242542f5fSchristos	if (!__backlight_direct_init(b, iface) &&
51342542f5fSchristos	    !__backlight_helper_init(b, iface))
51442542f5fSchristos		goto err;
51542542f5fSchristos
51642542f5fSchristos	return level;
51742542f5fSchristos
51842542f5fSchristoserr:
51942542f5fSchristos	backlight_init(b);
52042542f5fSchristos	return -1;
52142542f5fSchristos}
52242542f5fSchristos
52342542f5fSchristosint backlight_set(struct backlight *b, int level)
52442542f5fSchristos{
52542542f5fSchristos	char val[BACKLIGHT_VALUE_LEN];
52663ef14f0Smrg	int len;
52742542f5fSchristos
52842542f5fSchristos	if (b->iface == NULL)
52942542f5fSchristos		return 0;
53042542f5fSchristos
53142542f5fSchristos	if ((unsigned)level > b->max)
53242542f5fSchristos		level = b->max;
53342542f5fSchristos
53442542f5fSchristos	len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level);
53563ef14f0Smrg	return writen(b->fd, val, len);
53642542f5fSchristos}
53742542f5fSchristos
53842542f5fSchristosint backlight_get(struct backlight *b)
53942542f5fSchristos{
54042542f5fSchristos	int level;
54142542f5fSchristos
54242542f5fSchristos	if (b->iface == NULL)
54342542f5fSchristos		return -1;
54442542f5fSchristos
54542542f5fSchristos	level = __backlight_read(b->iface, "brightness");
54642542f5fSchristos	if (level > b->max)
54742542f5fSchristos		level = b->max;
54842542f5fSchristos	else if (level < 0)
54942542f5fSchristos		level = -1;
55042542f5fSchristos	return level;
55142542f5fSchristos}
552813957e3Ssnj
553813957e3Ssnjint backlight_off(struct backlight *b)
554813957e3Ssnj{
555813957e3Ssnj	if (b->iface == NULL)
556813957e3Ssnj		return 0;
557813957e3Ssnj
558813957e3Ssnj	if (!b->has_power)
559813957e3Ssnj		return 0;
560813957e3Ssnj
561813957e3Ssnj	/* 4 -> FB_BLANK_POWERDOWN */
562813957e3Ssnj	return __backlight_write(b->iface, "bl_power", "4");
563813957e3Ssnj}
564813957e3Ssnj
565813957e3Ssnjint backlight_on(struct backlight *b)
566813957e3Ssnj{
567813957e3Ssnj	if (b->iface == NULL)
568813957e3Ssnj		return 0;
569813957e3Ssnj
570813957e3Ssnj	if (!b->has_power)
571813957e3Ssnj		return 0;
572813957e3Ssnj
573813957e3Ssnj	/* 0 -> FB_BLANK_UNBLANK */
574813957e3Ssnj	return __backlight_write(b->iface, "bl_power", "0");
575813957e3Ssnj}
57642542f5fSchristos#endif
57742542f5fSchristos
57842542f5fSchristosvoid backlight_disable(struct backlight *b)
57942542f5fSchristos{
58042542f5fSchristos	if (b->iface == NULL)
58142542f5fSchristos		return;
58242542f5fSchristos
58342542f5fSchristos	if (b->fd != -1)
58442542f5fSchristos		close(b->fd);
58542542f5fSchristos
58642542f5fSchristos	free(b->iface);
58742542f5fSchristos	b->iface = NULL;
58842542f5fSchristos}
58942542f5fSchristos
59042542f5fSchristosvoid backlight_close(struct backlight *b)
59142542f5fSchristos{
59242542f5fSchristos	backlight_disable(b);
59363ef14f0Smrg	if (b->pid > 0)
59442542f5fSchristos		waitpid(b->pid, NULL, 0);
59542542f5fSchristos}
596