[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#779529: xserver-xorg-video-intel: Built-in display completely black if external monitor is plugged in



Dear Maintainer(s),

I am attaching a Git patch that backports the fixes needed in order to solve the backlight problem. I have tested this on a Dell Latitude E5540 (Intel Haswell i7-4600U with GPU HD Graphics 4400) running Debian Jessie. I hope this can be useful.

Kind regards,
Luca Boccassi


From f29b80b3f1cba1138284c5c28ed01aed5376d037 Mon Sep 17 00:00:00 2001
From: Luca Boccassi <luca.boccassi@gmail.com>
Date: Sun, 1 Mar 2015 12:14:13 +0000
Subject: [PATCH] Backport backlight fix (and other necessary commits)

commit a1717fe5ab0180f82a77a777c4ef870d54654ded
Author: Chris Wilson <chris@chris-wilson.co.uk>
Date:   Thu Aug 21 07:21:59 2014 +0100

    backlight: Move the fd out of the select range

commit 631b4e4c78a807e61214026bf9a1461aadbd59b5
Author: maximilian attems <maks@***>
Date:   Thu May 29 17:07:16 2014 +0200
    install add new helper

commit b71f3d8bd4d6773899c1bdc903911cf240e68ead
Author: Jan Alexander Steffens (heftig) <jan.steffens@***>
Date:   Sat Feb 15 17:53:16 2014 +0100
    Backlight helper build fixes

commit 3d629c91cfa98b75c6685c2a2003e64fd1b612c4
Author: Chris Wilson <chris@***>
Date:   Sat Feb 15 14:55:09 2014 +0000
    intel: Add a helper for setting backlight without root rights

commit a01548ccf192a5b1fa1f4a3e31e1634db39f6b39
Author: Hans de Goede <hdegoede@redhat.com>
Date:   Sat Feb 15 00:02:36 2014 +0100

    intel: export fd_set_cloexec / fd_set_nonblock

Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
---
 Makefile.am                                        |   2 +-
 configure.ac                                       |  20 ++
 debian/xserver-xorg-video-intel.install            |   2 +
 src/Makefile.am                                    |   4 +
 src/backlight.c                                    | 318 +++++++++++++++++++++
 src/backlight.h                                    |  46 +++
 src/fd.c                                           |  93 ++++++
 src/fd.h                                           |  34 +++
 src/intel_device.c                                 |  19 +-
 src/sna/sna_display.c                              | 239 +++-------------
 src/uxa/intel_display.c                            | 224 ++++-----------
 tools/.gitignore                                   |   3 +
 tools/Makefile.am                                  |  48 ++++
 tools/backlight_helper.c                           |  51 ++++
 ...g.x.xf86-video-intel.backlight-helper.policy.in |  19 ++
 15 files changed, 728 insertions(+), 394 deletions(-)
 create mode 100644 src/backlight.c
 create mode 100644 src/backlight.h
 create mode 100644 src/fd.c
 create mode 100644 src/fd.h
 create mode 100644 tools/.gitignore
 create mode 100644 tools/Makefile.am
 create mode 100644 tools/backlight_helper.c
 create mode 100644 tools/org.x.xf86-video-intel.backlight-helper.policy.in

diff --git a/Makefile.am b/Makefile.am
index 2b3b5d4..6bb4854 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -20,7 +20,7 @@
 
 ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4
 
-SUBDIRS = man xvmc src
+SUBDIRS = man xvmc src tools
 
 MAINTAINERCLEANFILES = ChangeLog INSTALL
 
diff --git a/configure.ac b/configure.ac
index 9fc011e..2cda018 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,6 +62,23 @@ AC_DISABLE_STATIC
 AC_PROG_LIBTOOL
 AC_SYS_LARGEFILE
 
+# Platform specific settings
+case $host_os in
+  *linux*)
+    backlight_helper=yes
+    ;;
+esac
+
+AC_ARG_ENABLE(backlight-helper,
+              AS_HELP_STRING([--disable-backlight-helper],
+			     [Enable building the backlight helper executable for running X under a normal user [default=auto]]),
+              [backlight_helper="$enableval"],)
+AM_CONDITIONAL(BUILD_BACKLIGHT_HELPER, [test "x$backlight_helper" = "xyes"])
+if test "x$backlight_helper" = "xyes"; then
+	tools_msg="$tools_msg xf86-video-intel-backlight-helper"
+	AC_DEFINE(USE_BACKLIGHT_HELPER, 1, [Enable use of the backlight helper interfaces])
+fi
+
 # Are we in a git checkout?
 dot_git=no
 if test -e .git; then
@@ -545,6 +562,7 @@ fi
 DRIVER_NAME=intel
 AC_SUBST([DRIVER_NAME])
 AC_SUBST([moduledir])
+AC_DEFINE_DIR([LIBEXEC_PATH], libexecdir, [libexec directory])
 
 AC_CONFIG_FILES([
                 Makefile
@@ -563,6 +581,8 @@ AC_CONFIG_FILES([
                 xvmc/shader/mc/Makefile
                 xvmc/shader/vld/Makefile
 		test/Makefile
+		tools/Makefile
+		tools/org.x.xf86-video-intel.backlight-helper.policy
 ])
 AC_OUTPUT
 
diff --git a/debian/xserver-xorg-video-intel.install b/debian/xserver-xorg-video-intel.install
index 48c5ed0..fdefaad 100644
--- a/debian/xserver-xorg-video-intel.install
+++ b/debian/xserver-xorg-video-intel.install
@@ -1,4 +1,6 @@
 usr/lib/xorg/modules/drivers/*.so
 usr/lib/libI810XvMC.so*
 usr/lib/libIntelXvMC.so*
+usr/lib/xserver-xorg-video-intel/xf86-video-intel-backlight-helper
+usr/share/polkit-1/actions/org.x.xf86-video-intel.backlight-helper.policy
 usr/share/man
diff --git a/src/Makefile.am b/src/Makefile.am
index b0781ca..6c4d835 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,6 +52,10 @@ endif
 NULL:=#
 
 intel_drv_la_SOURCES = \
+	fd.c \
+	fd.h \
+	backlight.c \
+	backlight.h \
 	i915_pciids.h \
 	intel_list.h \
 	intel_options.h \
diff --git a/src/backlight.c b/src/backlight.c
new file mode 100644
index 0000000..cc5d187
--- /dev/null
+++ b/src/backlight.c
@@ -0,0 +1,318 @@
+/***************************************************************************
+
+ Copyright 2014 Intel Corporation.  All Rights Reserved.
+ Copyright 2014 Red Hat, Inc.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sub license, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice (including the
+ next paragraph) shall be included in all copies or substantial portions
+ of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "backlight.h"
+#include "fd.h"
+
+/* Enough for 10 digits of backlight + '\n' + '\0' */
+#define BACKLIGHT_VALUE_LEN 12
+
+/*
+ * Unfortunately this is not as simple as I would like it to be. If selinux is
+ * dropping dbus messages pkexec may block *forever*.
+ *
+ * Backgrounding pkexec by doing System("pkexec ...&") does not work because
+ * that detaches pkexec from its parent at which point its security checks
+ * fail and it refuses to execute the helper.
+ *
+ * So we're left with spawning a helper child which gets levels to set written
+ * to it through a pipe. This turns the blocking forever problem from a hung
+ * machine problem into a simple backlight control not working problem.
+ */
+
+#ifdef __OpenBSD__
+
+#include <dev/wscons/wsconsio.h>
+
+int backlight_set(struct backlight *b, int level)
+{
+	struct wsdisplay_param param;
+
+	if (b->iface == NULL)
+		return;
+
+	if ((unsigned)level > b->max)
+		level = b->max;
+
+	memset(&param, 0, sizeof(param));
+	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
+	param.curval = level;
+
+	return ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &param);
+}
+
+int backlight_get(struct backlight *b)
+{
+	struct wsdisplay_param param;
+
+	if (b->iface == NULL)
+		return -1;
+
+	memset(&param, 0, sizeof(param));
+	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
+
+	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param))
+		return -1;
+
+	return param.curval;
+}
+
+int backlight_open(struct backlight *b, char *iface)
+{
+	struct wsdisplay_param param;
+
+	memset(&param, 0, sizeof(param));
+	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
+
+	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param) == -1)
+		return -1;
+
+	b->iface = strdup("wscons");
+	if (b->iface == NULL)
+		return -1;
+
+	b->max = param.max;
+	b->fd = -1;
+
+	return param.curval;
+}
+
+#else
+
+static int
+is_sysfs_fd(int fd)
+{
+	struct stat st;
+	return fstat(fd, &st) == 0 && major(st.st_dev) == 0;
+}
+
+static int
+__backlight_read(const char *iface, const char *file)
+{
+	char buf[1024];
+	int fd, val;
+
+	snprintf(buf, sizeof(buf), "%s/%s/%s", BACKLIGHT_CLASS, iface, file);
+	fd = open(buf, O_RDONLY);
+	if (fd == -1)
+		return -1;
+
+	if (is_sysfs_fd(fd)) {
+		val = read(fd, buf, BACKLIGHT_VALUE_LEN - 1);
+		if (val > 0) {
+			buf[val] = '\0';
+			val = atoi(buf);
+		} else
+			val = -1;
+	} else
+		val = -1;
+	close(fd);
+
+	return val;
+}
+
+int backlight_exists(const char *iface)
+{
+	if (__backlight_read(iface, "brightness") < 0)
+		return 0;
+
+	if (__backlight_read(iface, "max_brightness") <= 0)
+		return 0;
+
+	return 1;
+}
+
+static int __backlight_init(struct backlight *b, char *iface, int fd)
+{
+	b->fd = fd_move_cloexec(fd_set_nonblock(fd));
+	b->iface = iface;
+	return 1;
+}
+
+static int __backlight_direct_init(struct backlight *b, char *iface)
+{
+	char path[1024];
+	int fd;
+
+	snprintf(path, sizeof(path), "%s/%s/brightness", BACKLIGHT_CLASS, iface);
+	fd = open(path, O_RDWR);
+	if (fd < 0)
+		return 0;
+
+	if (!is_sysfs_fd(fd)) {
+		close(fd);
+		return 0;
+	}
+
+	return __backlight_init(b, iface, fd);
+}
+
+static int __backlight_helper_init(struct backlight *b, char *iface)
+{
+#if USE_BACKLIGHT_HELPER
+	struct stat st;
+	char *env[] = { NULL };
+	int use_pkexec = 0;
+	int fds[2];
+
+	/* If system policy is to disallow setuid helpers,
+	 * we fallback to invoking PolicyKit. However, as pkexec
+	 * is quite troublesome and not universally available, we
+	 * still try the old fashioned and simple method first.
+	 * Either way, we have to trust that it is our backlight-helper
+	 * that is run and that we have scrutinised it carefully.
+	 */
+	if (stat(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", &st))
+		return 0;
+
+	if ((st.st_mode & (S_IFREG | S_ISUID | S_IXUSR)) != (S_IFREG | S_ISUID | S_IXUSR)) {
+		if (system("pkexec --version"))
+			return 0;
+
+		use_pkexec = 1;
+	}
+
+	if (pipe(fds))
+		return 0;
+
+	switch ((b->pid = fork())) {
+	case 0:
+		close(fds[1]);
+		dup2(fds[0], 0);
+		close(fds[0]);
+		if (use_pkexec) {
+			execlp("pkexec", "pkexec",
+			       LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
+			       iface, (char *)0);
+		} else {
+			execle(LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
+			       "xf86-video-intel-backlight-helper",
+			       iface, (char *)0, env);
+		}
+		_exit(1);
+		/* unreachable fallthrough */
+	case -1:
+		close(fds[1]);
+		close(fds[0]);
+		return 0;
+
+	default:
+		close(fds[0]);
+		return __backlight_init(b, iface, fds[1]);
+	}
+#else
+	return 0;
+#endif
+}
+
+int backlight_open(struct backlight *b, char *iface)
+{
+	int level;
+
+	if (iface == NULL)
+		return -1;
+
+	b->max = __backlight_read(iface, "max_brightness");
+	if (b->max <= 0)
+		return -1;
+
+	level = __backlight_read(iface, "brightness");
+	if (level < 0)
+		return -1;
+
+	if (!__backlight_direct_init(b, iface) &&
+	    !__backlight_helper_init(b, iface))
+		return -1;
+
+	return level;
+}
+
+int backlight_set(struct backlight *b, int level)
+{
+	char val[BACKLIGHT_VALUE_LEN];
+	int len, ret = 0;
+
+	if (b->iface == NULL)
+		return 0;
+
+	if ((unsigned)level > b->max)
+		level = b->max;
+
+	len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level);
+	if (write(b->fd, val, len) != len)
+		ret = -1;
+
+	return ret;
+}
+
+int backlight_get(struct backlight *b)
+{
+	int level;
+
+	if (b->iface == NULL)
+		return -1;
+
+	level = __backlight_read(b->iface, "brightness");
+	if (level > b->max)
+		level = b->max;
+	else if (level < 0)
+		level = -1;
+	return level;
+}
+#endif
+
+void backlight_disable(struct backlight *b)
+{
+	if (b->iface == NULL)
+		return;
+
+	if (b->fd != -1)
+		close(b->fd);
+
+	free(b->iface);
+	b->iface = NULL;
+}
+
+void backlight_close(struct backlight *b)
+{
+	backlight_disable(b);
+	if (b->pid)
+		waitpid(b->pid, NULL, 0);
+}
diff --git a/src/backlight.h b/src/backlight.h
new file mode 100644
index 0000000..deecbd0
--- /dev/null
+++ b/src/backlight.h
@@ -0,0 +1,46 @@
+/***************************************************************************
+
+ Copyright 2014 Intel Corporation.  All Rights Reserved.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sub license, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice (including the
+ next paragraph) shall be included in all copies or substantial portions
+ of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **************************************************************************/
+
+#ifndef BACKLIGHT_H
+#define BACKLIGHT_H
+
+struct backlight {
+	char *iface;
+	int max;
+	int pid, fd;
+};
+
+#define BACKLIGHT_CLASS "/sys/class/backlight"
+
+int backlight_exists(const char *iface);
+
+int backlight_open(struct backlight *backlight, char *iface);
+int backlight_set(struct backlight *backlight, int level);
+int backlight_get(struct backlight *backlight);
+void backlight_disable(struct backlight *backlight);
+void backlight_close(struct backlight *backlight);
+
+#endif /* BACKLIGHT_H */
diff --git a/src/fd.c b/src/fd.c
new file mode 100644
index 0000000..445e3f4
--- /dev/null
+++ b/src/fd.c
@@ -0,0 +1,93 @@
+/***************************************************************************
+
+ Copyright 2013 Intel Corporation.  All Rights Reserved.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sub license, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice (including the
+ next paragraph) shall be included in all copies or substantial portions
+ of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <misc.h> /* MAXCLIENTS */
+
+#include "fd.h"
+
+int fd_move_cloexec(int fd)
+{
+	int newfd;
+
+	newfd = fcntl(fd,
+#ifdef F_DUPFD_CLOEXEC
+		      F_DUPFD_CLOEXEC,
+#else
+		      F_DUPFD,
+#endif
+		      MAXCLIENTS);
+	if (newfd < 0)
+		return fd;
+
+#ifndef F_DUPFD_CLOEXEC
+	newfd = fd_set_cloexec(newfd);
+#endif
+
+	close(fd);
+	return newfd;
+}
+
+int fd_set_cloexec(int fd)
+{
+	int flags;
+
+	if (fd == -1)
+		return fd;
+
+#ifdef FD_CLOEXEC
+	flags = fcntl(fd, F_GETFD);
+	if (flags != -1) {
+		flags |= FD_CLOEXEC;
+		fcntl(fd, F_SETFD, flags);
+	}
+#endif
+
+	return fd;
+}
+
+int fd_set_nonblock(int fd)
+{
+	int flags;
+
+	if (fd == -1)
+		return fd;
+
+	flags = fcntl(fd, F_GETFL);
+	if (flags != -1) {
+		flags |= O_NONBLOCK;
+		fcntl(fd, F_SETFL, flags);
+	}
+
+	return fd;
+}
+
diff --git a/src/fd.h b/src/fd.h
new file mode 100644
index 0000000..d71fa7b
--- /dev/null
+++ b/src/fd.h
@@ -0,0 +1,34 @@
+/***************************************************************************
+
+ Copyright 2013 Intel Corporation.  All Rights Reserved.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sub license, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice (including the
+ next paragraph) shall be included in all copies or substantial portions
+ of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **************************************************************************/
+
+#ifndef FD_H
+#define FD_H
+
+int fd_move_cloexec(int fd);
+int fd_set_cloexec(int fd);
+int fd_set_nonblock(int fd);
+
+#endif /* FD_H */
diff --git a/src/intel_device.c b/src/intel_device.c
index d9ff8bc..8728398 100644
--- a/src/intel_device.c
+++ b/src/intel_device.c
@@ -41,6 +41,7 @@
 #include <i915_drm.h>
 
 #include "intel_driver.h"
+#include "fd.h"
 
 struct intel_device {
 	char *path;
@@ -103,24 +104,6 @@ static int __intel_check_device(int fd)
 	return ret;
 }
 
-static int fd_set_cloexec(int fd)
-{
-	int flags;
-
-	if (fd == -1)
-		return fd;
-
-#ifdef FD_CLOEXEC
-	flags = fcntl(fd, F_GETFD);
-	if (flags != -1) {
-		flags |= FD_CLOEXEC;
-		fcntl(fd, F_SETFD, flags);
-	}
-#endif
-
-	return fd;
-}
-
 static int __intel_open_device(const struct pci_device *pci, char **path)
 {
 	int fd;
diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c
index 3d42eb9..2d49b81 100644
--- a/src/sna/sna_display.c
+++ b/src/sna/sna_display.c
@@ -51,6 +51,7 @@
 #include "fb/fbpict.h"
 
 #include "intel_options.h"
+#include "backlight.h"
 
 #define KNOWN_MODE_FLAGS ((1<<14)-1)
 
@@ -103,9 +104,8 @@ struct sna_output {
 
 	uint32_t dpms_id;
 	int dpms_mode;
-	char *backlight_iface;
+	struct backlight backlight;
 	int backlight_active_level;
-	int backlight_max;
 
 	int num_modes;
 	struct drm_mode_modeinfo *modes;
@@ -140,11 +140,6 @@ static bool sna_mode_has_pending_events(struct sna *sna)
 	return poll(&pfd, 1, 0) == 1;
 }
 
-#define BACKLIGHT_CLASS "/sys/class/backlight"
-
-/* Enough for 10 digits of backlight + '\n' + '\0' */
-#define BACKLIGHT_VALUE_LEN 12
-
 static inline uint32_t fb_id(struct kgem_bo *bo)
 {
 	return bo->delta;
@@ -252,178 +247,39 @@ static void gem_close(int fd, uint32_t handle)
 	(void)drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close);
 }
 
-#ifdef __OpenBSD__
-
-#include <dev/wscons/wsconsio.h>
-#include <xf86Priv.h>
+#define BACKLIGHT_NAME             "Backlight"
+#define BACKLIGHT_DEPRECATED_NAME  "BACKLIGHT"
+static Atom backlight_atom, backlight_deprecated_atom;
 
 static void
 sna_output_backlight_set(xf86OutputPtr output, int level)
 {
 	struct sna_output *sna_output = output->driver_private;
-	struct wsdisplay_param param;
-
-	DBG(("%s: level=%d, max=%d\n", __FUNCTION__,
-	     level, sna_output->backlight_max));
-
-	if (!sna_output->backlight_iface)
-		return;
-
-	if ((unsigned)level > sna_output->backlight_max)
-		level = sna_output->backlight_max;
-
-	VG_CLEAR(param);
-	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
-	param.curval = level;
-
-	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &param) == -1) {
-		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
-			   "Failed to set backlight level: %s\n",
-			   strerror(errno));
-	}
-}
-
-static int
-sna_output_backlight_get(xf86OutputPtr output)
-{
-	struct wsdisplay_param param;
-
-	VG_CLEAR(param);
-	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
+	DBG(("%s(%s) level=%d, max=%d\n", __FUNCTION__,
+	     output->name, level, sna_output->backlight.max));
 
-	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param) == -1) {
+	if (backlight_set(&sna_output->backlight, level)) {
 		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
-			   "Failed to get backlight level: %s\n",
-			   strerror(errno));
-		return -1;
-	}
-
-	DBG(("%s: level=%d (max=%d)\n", __FUNCTION__, param.curval, param.max));
-
-	return param.curval;
-}
-
-static void
-sna_output_backlight_init(xf86OutputPtr output)
-{
-	struct sna_output *sna_output = output->driver_private;
-	struct wsdisplay_param param;
-
-	VG_CLEAR(param);
-	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
-
-	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param) == -1)
-		return;
-
-	DBG(("%s: found 'wscons'\n", __FUNCTION__));
-
-	sna_output->backlight_iface = "wscons";
-	sna_output->backlight_max = param.max;
-	sna_output->backlight_active_level = param.curval;
-}
-
-#else
-
-static void
-sna_output_backlight_set(xf86OutputPtr output, int level)
-{
-	struct sna_output *sna_output = output->driver_private;
-	char path[1024], val[BACKLIGHT_VALUE_LEN];
-	int fd, len, ret;
-
-	DBG(("%s: level=%d, max=%d\n", __FUNCTION__,
-	     level, sna_output->backlight_max));
-
-	if (!sna_output->backlight_iface)
-		return;
-
-	if ((unsigned)level > sna_output->backlight_max)
-		level = sna_output->backlight_max;
-
-	len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level);
-	sprintf(path, "%s/%s/brightness",
-		BACKLIGHT_CLASS, sna_output->backlight_iface);
-	fd = open(path, O_RDWR);
-	if (fd == -1) {
-		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "failed to open %s for backlight "
-			   "control: %s\n", path, strerror(errno));
-		return;
-	}
-
-	ret = write(fd, val, len);
-	if (ret == -1) {
-		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "write to %s for backlight "
-			   "control failed: %s\n", path, strerror(errno));
+			   "Failed to set backlight %s for output %s to brightness level %d, disabling\n",
+			   sna_output->backlight.iface, output->name, level);
+		backlight_disable(&sna_output->backlight);
+		if (output->randr_output) {
+			RRDeleteOutputProperty(output->randr_output, backlight_atom);
+			RRDeleteOutputProperty(output->randr_output, backlight_deprecated_atom);
+		}
 	}
-
-	close(fd);
 }
 
 static int
 sna_output_backlight_get(xf86OutputPtr output)
 {
 	struct sna_output *sna_output = output->driver_private;
-	char path[1024], val[BACKLIGHT_VALUE_LEN];
-	int fd, level;
-
-	sprintf(path, "%s/%s/actual_brightness",
-		BACKLIGHT_CLASS, sna_output->backlight_iface);
-	fd = open(path, O_RDONLY);
-	if (fd == -1) {
-		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "failed to open %s "
-			   "for backlight control: %s\n", path, strerror(errno));
-		return -1;
-	}
-
-	memset(val, 0, sizeof(val));
-	if (read(fd, val, BACKLIGHT_VALUE_LEN) == -1) {
-		close(fd);
-		return -1;
-	}
-
-	close(fd);
-
-	level = atoi(val);
-	DBG(("%s: level=%d (max=%d)\n",
-	     __FUNCTION__, level, sna_output->backlight_max));
-
-	if (level > sna_output->backlight_max)
-		level = sna_output->backlight_max;
-	else if (level < 0)
-		level = -1;
+	int level = backlight_get(&sna_output->backlight);
+	DBG(("%s(%s) level=%d, max=%d\n", __FUNCTION__,
+	     output->name, level, sna_output->backlight.max));
 	return level;
 }
 
-static int
-sna_output_backlight_get_max(xf86OutputPtr output)
-{
-	struct sna_output *sna_output = output->driver_private;
-	char path[1024], val[BACKLIGHT_VALUE_LEN];
-	int fd, max = 0;
-
-	sprintf(path, "%s/%s/max_brightness",
-		BACKLIGHT_CLASS, sna_output->backlight_iface);
-	fd = open(path, O_RDONLY);
-	if (fd == -1) {
-		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "failed to open %s "
-			   "for backlight control: %s\n", path, strerror(errno));
-		return -1;
-	}
-
-	memset(val, 0, sizeof(val));
-	if (read(fd, val, BACKLIGHT_VALUE_LEN) == -1) {
-		close(fd);
-		return -1;
-	}
-
-	close(fd);
-
-	max = atoi(val);
-	if (max <= 0)
-		max = -1;
-	return max;
-}
-
 enum {
 	PLATFORM,
 	FIRMWARE,
@@ -434,21 +290,16 @@ enum {
 static char *
 has_user_backlight_override(xf86OutputPtr output)
 {
-	struct sna_output *sna_output = output->driver_private;
 	struct sna *sna = to_sna(output->scrn);
 	char *str;
-	int max;
 
 	str = xf86GetOptValString(sna->Options, OPTION_BACKLIGHT);
 	if (str == NULL)
 		return NULL;
 
-	sna_output->backlight_iface = str;
-	max = sna_output_backlight_get_max(output);
-	sna_output->backlight_iface = NULL;
-	if (max <= 0) {
+	if (!backlight_exists(str)) {
 		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
-			   "unrecognised backlight control interface '%s'\n",
+			   "Unrecognised backlight control interface '%s'\n",
 			   str);
 		return NULL;
 	}
@@ -459,7 +310,6 @@ has_user_backlight_override(xf86OutputPtr output)
 static char *
 has_device_backlight(xf86OutputPtr output, int *best_type)
 {
-	struct sna_output *sna_output = output->driver_private;
 	struct sna *sna = to_sna(output->scrn);
 	struct pci_device *pci = sna->PciInfo;
 	char path[1024];
@@ -511,12 +361,8 @@ has_device_backlight(xf86OutputPtr output, int *best_type)
 
 		if (v < *best_type) {
 			char *copy;
-			int max;
 
-			sna_output->backlight_iface = de->d_name;
-			max = sna_output_backlight_get_max(output);
-			sna_output->backlight_iface = NULL;
-			if (max <= 0)
+			if (!backlight_exists(de->d_name))
 				continue;
 
 			copy = strdup(de->d_name);
@@ -550,7 +396,6 @@ has_backlight(xf86OutputPtr output, int *best_type)
 		"acpi_video0",
 		"intel_backlight",
 	};
-	struct sna_output *sna_output = output->driver_private;
 	char *best_iface = NULL;
 	DIR *dir;
 	struct dirent *de;
@@ -604,14 +449,10 @@ has_backlight(xf86OutputPtr output, int *best_type)
 
 		if (v < *best_type) {
 			char *copy;
-			int max;
 
 			/* XXX detect right backlight for multi-GPU/panels */
 
-			sna_output->backlight_iface = de->d_name;
-			max = sna_output_backlight_get_max(output);
-			sna_output->backlight_iface = NULL;
-			if (max <= 0)
+			if (!backlight_exists(de->d_name))
 				continue;
 
 			copy = strdup(de->d_name);
@@ -648,12 +489,15 @@ sna_output_backlight_init(xf86OutputPtr output)
 	if (best_iface)
 		goto done;
 
-	return;
+	best_type = PLATFORM;
+	best_iface = NULL;
 
 done:
-	sna_output->backlight_iface = best_iface;
-	sna_output->backlight_max = sna_output_backlight_get_max(output);
-	sna_output->backlight_active_level = sna_output_backlight_get(output);
+	sna_output->backlight_active_level =
+		backlight_open(&sna_output->backlight, best_iface);
+	if (sna_output->backlight_active_level < 0)
+		return;
+
 	switch (best_type) {
 	case INT_MAX: best_iface = (char *)"user"; from = X_CONFIG; break;
 	case FIRMWARE: best_iface = (char *)"firmware"; break;
@@ -662,10 +506,9 @@ done:
 	default: best_iface = (char *)"unknown"; break;
 	}
 	xf86DrvMsg(output->scrn->scrnIndex, from,
-		   "found backlight control interface %s (type '%s')\n",
-		   sna_output->backlight_iface, best_iface);
+		   "Found backlight control interface %s (type '%s') for output %s\n",
+		   sna_output->backlight.iface, best_iface, output->name);
 }
-#endif
 
 static DisplayModePtr
 mode_from_kmode(ScrnInfoPtr scrn,
@@ -2049,7 +1892,7 @@ sna_output_destroy(xf86OutputPtr output)
 	free(sna_output->prop_ids);
 	free(sna_output->prop_values);
 
-	free(sna_output->backlight_iface);
+	backlight_close(&sna_output->backlight);
 
 	free(sna_output);
 	output->driver_private = NULL;
@@ -2060,7 +1903,7 @@ sna_output_dpms_backlight(xf86OutputPtr output, int oldmode, int mode)
 {
 	struct sna_output *sna_output = output->driver_private;
 
-	if (!sna_output->backlight_iface)
+	if (!sna_output->backlight.iface)
 		return;
 
 	if (mode == DPMSModeOn) {
@@ -2154,10 +1997,6 @@ sna_output_create_ranged_atom(xf86OutputPtr output, Atom *atom,
 			   "RRChangeOutputProperty error, %d\n", err);
 }
 
-#define BACKLIGHT_NAME             "Backlight"
-#define BACKLIGHT_DEPRECATED_NAME  "BACKLIGHT"
-static Atom backlight_atom, backlight_deprecated_atom;
-
 static void
 sna_output_create_resources(xf86OutputPtr output)
 {
@@ -2228,20 +2067,20 @@ sna_output_create_resources(xf86OutputPtr output)
 		}
 	}
 
-	if (sna_output->backlight_iface) {
+	if (sna_output->backlight.iface) {
 		/* Set up the backlight property, which takes effect
 		 * immediately and accepts values only within the
 		 * backlight_range.
 		 */
 		sna_output_create_ranged_atom(output, &backlight_atom,
 					      BACKLIGHT_NAME, 0,
-					      sna_output->backlight_max,
+					      sna_output->backlight.max,
 					      sna_output->backlight_active_level,
 					      FALSE);
 		sna_output_create_ranged_atom(output,
 					      &backlight_deprecated_atom,
 					      BACKLIGHT_DEPRECATED_NAME, 0,
-					      sna_output->backlight_max,
+					      sna_output->backlight.max,
 					      sna_output->backlight_active_level,
 					      FALSE);
 	}
@@ -2265,7 +2104,9 @@ sna_output_set_property(xf86OutputPtr output, Atom property,
 		}
 
 		val = *(INT32 *)value->data;
-		if (val < 0 || val > sna_output->backlight_max)
+ 		DBG(("%s: setting backlight to %d (max=%d)\n",
+		     __FUNCTION__, (int)val, sna_output->backlight.max));
+		if (val < 0 || val > sna_output->backlight.max)
 			return FALSE;
 
 		if (sna_output->dpms_mode == DPMSModeOn)
@@ -2331,7 +2172,7 @@ sna_output_get_property(xf86OutputPtr output, Atom property)
 	if (property == backlight_atom || property == backlight_deprecated_atom) {
 		INT32 val;
 
-		if (! sna_output->backlight_iface)
+		if (! sna_output->backlight.iface)
 			return FALSE;
 
 		val = sna_output_backlight_get(output);
diff --git a/src/uxa/intel_display.c b/src/uxa/intel_display.c
index b4f7e87..74455c1 100644
--- a/src/uxa/intel_display.c
+++ b/src/uxa/intel_display.c
@@ -43,6 +43,7 @@
 #include "intel.h"
 #include "intel_bufmgr.h"
 #include "intel_options.h"
+#include "backlight.h"
 #include "xf86drm.h"
 #include "xf86drmMode.h"
 #include "X11/Xatom.h"
@@ -115,9 +116,8 @@ struct intel_output {
 	int panel_vdisplay;
 
 	int dpms_mode;
-	const char *backlight_iface;
+	struct backlight backlight;
 	int backlight_active_level;
-	int backlight_max;
 	xf86OutputPtr output;
 	struct list link;
 };
@@ -134,68 +134,6 @@ crtc_id(struct intel_crtc *crtc)
 	return crtc->mode_crtc->crtc_id;
 }
 
-#ifdef __OpenBSD__
-
-#include <dev/wscons/wsconsio.h>
-#include "xf86Priv.h"
-
-static void
-intel_output_backlight_set(xf86OutputPtr output, int level)
-{
-	struct intel_output *intel_output = output->driver_private;
-	struct wsdisplay_param param;
-
-	if (level > intel_output->backlight_max)
-		level = intel_output->backlight_max;
-	if (! intel_output->backlight_iface || level < 0)
-		return;
-
-	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
-	param.curval = level;
-	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &param) == -1) {
-		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
-			   "Failed to set backlight level: %s\n",
-			   strerror(errno));
-	}
-}
-
-static int
-intel_output_backlight_get(xf86OutputPtr output)
-{
-	struct wsdisplay_param param;
-
-	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
-	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param) == -1) {
-		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
-			   "Failed to get backlight level: %s\n",
-			   strerror(errno));
-		return -1;
-	}
-
-	return param.curval;
-}
-
-static void
-intel_output_backlight_init(xf86OutputPtr output)
-{
-	struct intel_output *intel_output = output->driver_private;
-	struct wsdisplay_param param;
-
-	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
-	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param) == -1) {
-		intel_output->backlight_iface = NULL;
-		return;
-	}
-
-	intel_output->backlight_iface = "wscons";
-	intel_output->backlight_max = param.max;
-	intel_output->backlight_active_level = param.curval;
-}
-
-#else
-
-#define BACKLIGHT_CLASS "/sys/class/backlight"
-
 /*
  * List of available kernel interfaces in priority order
  */
@@ -215,127 +153,50 @@ static const char *backlight_interfaces[] = {
 	"intel_backlight",
 	NULL,
 };
-/*
- * Must be long enough for BACKLIGHT_CLASS + '/' + longest in above table +
- * '/' + "max_backlight"
- */
-#define BACKLIGHT_PATH_LEN 80
-/* Enough for 10 digits of backlight + '\n' + '\0' */
-#define BACKLIGHT_VALUE_LEN 12
 
 static void
 intel_output_backlight_set(xf86OutputPtr output, int level)
 {
 	struct intel_output *intel_output = output->driver_private;
-	char path[BACKLIGHT_PATH_LEN], val[BACKLIGHT_VALUE_LEN];
-	int fd, len, ret;
-
-	if (level > intel_output->backlight_max)
-		level = intel_output->backlight_max;
-	if (! intel_output->backlight_iface || level < 0)
-		return;
-
-	len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level);
-	sprintf(path, "%s/%s/brightness",
-		BACKLIGHT_CLASS, intel_output->backlight_iface);
-	fd = open(path, O_RDWR);
-	if (fd == -1) {
-		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "failed to open %s for backlight "
-			   "control: %s\n", path, strerror(errno));
-		return;
-	}
-
-	ret = write(fd, val, len);
-	if (ret == -1) {
-		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "write to %s for backlight "
-			   "control failed: %s\n", path, strerror(errno));
+	if (backlight_set(&intel_output->backlight, level) < 0) {
+		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+			   "failed to set backlight %s to brightness level %d, disabling\n",
+			   intel_output->backlight.iface, level);
+		backlight_disable(&intel_output->backlight);
 	}
-
-	close(fd);
 }
 
 static int
 intel_output_backlight_get(xf86OutputPtr output)
 {
 	struct intel_output *intel_output = output->driver_private;
-	char path[BACKLIGHT_PATH_LEN], val[BACKLIGHT_VALUE_LEN];
-	int fd, level;
-
-	sprintf(path, "%s/%s/actual_brightness",
-		BACKLIGHT_CLASS, intel_output->backlight_iface);
-	fd = open(path, O_RDONLY);
-	if (fd == -1) {
-		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "failed to open %s "
-			   "for backlight control: %s\n", path, strerror(errno));
-		return -1;
-	}
-
-	memset(val, 0, sizeof(val));
-	if (read(fd, val, BACKLIGHT_VALUE_LEN) == -1) {
-		close(fd);
-		return -1;
-	}
-
-	close(fd);
-
-	level = atoi(val);
-	if (level > intel_output->backlight_max)
-		level = intel_output->backlight_max;
-	if (level < 0)
-		level = -1;
-	return level;
-}
-
-static int
-intel_output_backlight_get_max(xf86OutputPtr output)
-{
-	struct intel_output *intel_output = output->driver_private;
-	char path[BACKLIGHT_PATH_LEN], val[BACKLIGHT_VALUE_LEN];
-	int fd, max = 0;
-
-	sprintf(path, "%s/%s/max_brightness",
-		BACKLIGHT_CLASS, intel_output->backlight_iface);
-	fd = open(path, O_RDONLY);
-	if (fd == -1) {
-		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "failed to open %s "
-			   "for backlight control: %s\n", path, strerror(errno));
-		return -1;
-	}
-
-	memset(val, 0, sizeof(val));
-	if (read(fd, val, BACKLIGHT_VALUE_LEN) == -1) {
-		close(fd);
-		return -1;
-	}
-
-	close(fd);
-
-	max = atoi(val);
-	if (max <= 0)
-		max = -1;
-	return max;
+	return backlight_get(&intel_output->backlight);
 }
 
 static void
 intel_output_backlight_init(xf86OutputPtr output)
 {
+#ifdef __OpenBSD__
+	intel_output->backlight_active_level =
+		backlight_init(&intel_output->backlight, NULL);
+	if (intel_output->backlight_active_level != -1) {
+		xf86DrvMsg(output->scrn->scrnIndex, X_PROBED,
+			   "found backlight control interface\n");
+	}
+#else
 	struct intel_output *intel_output = output->driver_private;
 	intel_screen_private *intel = intel_get_screen_private(output->scrn);
-	char path[BACKLIGHT_PATH_LEN];
-	struct stat buf;
 	char *str;
 	int i;
 
 	str = xf86GetOptValString(intel->Options, OPTION_BACKLIGHT);
 	if (str != NULL) {
-		sprintf(path, "%s/%s", BACKLIGHT_CLASS, str);
-		if (!stat(path, &buf)) {
-			intel_output->backlight_iface = str;
-			intel_output->backlight_max = intel_output_backlight_get_max(output);
-			if (intel_output->backlight_max > 0) {
-				intel_output->backlight_active_level = intel_output_backlight_get(output);
+		if (backlight_exists(str)) {
+			intel_output->backlight_active_level =
+				backlight_open(&intel_output->backlight, strdup(str));
+			if (intel_output->backlight_active_level != -1) {
 				xf86DrvMsg(output->scrn->scrnIndex, X_CONFIG,
-					   "found backlight control interface %s\n", path);
+					   "found backlight control interface %s\n", str);
 				return;
 			}
 		}
@@ -344,22 +205,18 @@ intel_output_backlight_init(xf86OutputPtr output)
 	}
 
 	for (i = 0; backlight_interfaces[i] != NULL; i++) {
-		sprintf(path, "%s/%s", BACKLIGHT_CLASS, backlight_interfaces[i]);
-		if (!stat(path, &buf)) {
-			intel_output->backlight_iface = backlight_interfaces[i];
-			intel_output->backlight_max = intel_output_backlight_get_max(output);
-			if (intel_output->backlight_max > 0) {
-				intel_output->backlight_active_level = intel_output_backlight_get(output);
+		if (backlight_exists(backlight_interfaces[i])) {
+			intel_output->backlight_active_level =
+				backlight_open(&intel_output->backlight, strdup(backlight_interfaces[i]));
+			if (intel_output->backlight_active_level != -1) {
 				xf86DrvMsg(output->scrn->scrnIndex, X_PROBED,
-					   "found backlight control interface %s\n", path);
+					   "found backlight control interface %s\n", backlight_interfaces[i]);
 				return;
 			}
 		}
 	}
-	intel_output->backlight_iface = NULL;
-}
-
 #endif
+}
 
 static void
 mode_from_kmode(ScrnInfoPtr scrn,
@@ -468,6 +325,9 @@ intel_crtc_apply(xf86CrtcPtr crtc)
 			continue;
 
 		intel_output = output->driver_private;
+		if (!intel_output->mode_output)
+			return FALSE;
+
 		output_ids[output_count] =
 			intel_output->mode_output->connector_id;
 		output_count++;
@@ -924,6 +784,11 @@ intel_output_attach_edid(xf86OutputPtr output)
 	xf86MonPtr mon = NULL;
 	int i;
 
+	if (!koutput) {
+		xf86OutputSetEDID(output, mon);
+		return;
+	}
+
 	/* look for an EDID property */
 	for (i = 0; i < koutput->count_props; i++) {
 		drmModePropertyPtr props;
@@ -1013,6 +878,9 @@ intel_output_get_modes(xf86OutputPtr output)
 
 	intel_output_attach_edid(output);
 
+	if (!koutput)
+		return;
+
 	/* modes should already be available */
 	for (i = 0; i < koutput->count_modes; i++) {
 		DisplayModePtr Mode;
@@ -1070,6 +938,7 @@ intel_output_destroy(xf86OutputPtr output)
 	intel_output->mode_output = NULL;
 
 	list_del(&intel_output->link);
+	backlight_close(&intel_output->backlight);
 	free(intel_output);
 
 	output->driver_private = NULL;
@@ -1080,7 +949,7 @@ intel_output_dpms_backlight(xf86OutputPtr output, int oldmode, int mode)
 {
 	struct intel_output *intel_output = output->driver_private;
 
-	if (!intel_output->backlight_iface)
+	if (!intel_output->backlight.iface)
 		return;
 
 	if (mode == DPMSModeOn) {
@@ -1104,6 +973,9 @@ intel_output_dpms(xf86OutputPtr output, int dpms)
 	struct intel_mode *mode = intel_output->mode;
 	int i;
 
+	if (!koutput)
+		return;
+
 	for (i = 0; i < koutput->count_props; i++) {
 		drmModePropertyPtr props;
 
@@ -1271,20 +1143,20 @@ intel_output_create_resources(xf86OutputPtr output)
 		}
 	}
 
-	if (intel_output->backlight_iface) {
+	if (intel_output->backlight.iface) {
 		/* Set up the backlight property, which takes effect
 		 * immediately and accepts values only within the
 		 * backlight_range.
 		 */
 		intel_output_create_ranged_atom(output, &backlight_atom,
 					BACKLIGHT_NAME, 0,
-					intel_output->backlight_max,
+					intel_output->backlight.max,
 					intel_output->backlight_active_level,
 					FALSE);
 		intel_output_create_ranged_atom(output,
 					&backlight_deprecated_atom,
 					BACKLIGHT_DEPRECATED_NAME, 0,
-					intel_output->backlight_max,
+					intel_output->backlight.max,
 					intel_output->backlight_active_level,
 					FALSE);
 	}
@@ -1308,7 +1180,7 @@ intel_output_set_property(xf86OutputPtr output, Atom property,
 		}
 
 		val = *(INT32 *)value->data;
-		if (val < 0 || val > intel_output->backlight_max)
+		if (val < 0 || val > intel_output->backlight.max)
 			return FALSE;
 
 		if (intel_output->dpms_mode == DPMSModeOn)
@@ -1374,7 +1246,7 @@ intel_output_get_property(xf86OutputPtr output, Atom property)
 	if (property == backlight_atom || property == backlight_deprecated_atom) {
 		INT32 val;
 
-		if (! intel_output->backlight_iface)
+		if (!intel_output->backlight.iface)
 			return FALSE;
 
 		val = intel_output_backlight_get(output);
diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644
index 0000000..36868c6
--- /dev/null
+++ b/tools/.gitignore
@@ -0,0 +1,3 @@
+intel-virtual-output
+xf86-video-intel-backlight-helper
+org.x.xf86-video-intel.backlight-helper.policy
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 0000000..c82c422
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,48 @@
+#  Copyright 2005 Adam Jackson.
+#
+#  Permission is hereby granted, free of charge, to any person obtaining a
+#  copy of this software and associated documentation files (the "Software"),
+#  to deal in the Software without restriction, including without limitation
+#  on the rights to use, copy, modify, merge, publish, distribute, sub
+#  license, and/or sell copies of the Software, and to permit persons to whom
+#  the Software is furnished to do so, subject to the following conditions:
+#
+#  The above copyright notice and this permission notice (including the next
+#  paragraph) shall be included in all copies or substantial portions of the
+#  Software.
+#
+#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#  FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
+#  ADAM JACKSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+#  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+#  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+AM_CFLAGS = \
+	@CWARNFLAGS@ \
+	@NOWARNFLAGS@ \
+	$(NULL)
+
+drivermandir = $(DRIVER_MAN_DIR)
+policydir = $(datarootdir)/polkit-1/actions
+
+if BUILD_BACKLIGHT_HELPER
+libexec_PROGRAMS = xf86-video-intel-backlight-helper
+nodist_policy_DATA = org.x.xf86-video-intel.backlight-helper.policy
+
+backlight_helper = $(libexecdir)/xf86-video-intel-backlight-helper
+install-exec-hook:
+	-chown root $(DESTDIR)$(backlight_helper) && chmod u+s $(DESTDIR)$(backlight_helper)
+endif
+
+xf86_video_intel_backlight_helper_SOURCES = \
+	backlight_helper.c \
+	$(NULL)
+
+EXTRA_DIST = org.x.xf86-video-intel.backlight-helper.policy.in
+CLEANFILES = $(nodist_policy_DATA)
+
+# String replacements in MAN_SUBSTS now come from xorg-macros.m4 via configure
+SUFFIXES = .$(DRIVER_MAN_SUFFIX) .man
+.man.$(DRIVER_MAN_SUFFIX):
+	$(AM_V_GEN)$(SED) $(MAN_SUBSTS) < $< > $@
diff --git a/tools/backlight_helper.c b/tools/backlight_helper.c
new file mode 100644
index 0000000..fc16fce
--- /dev/null
+++ b/tools/backlight_helper.c
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+int main(int argc, char *argv[])
+{
+	struct stat st;
+	char buf[1024], *b = buf;
+	int len, fd;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s <iface>\n", argv[0]);
+		return 1;
+	}
+
+	snprintf(buf, sizeof(buf), "/sys/class/backlight/%s/brightness", argv[1]);
+	fd = open(buf, O_RDWR);
+	if (fd < 0 || fstat(fd, &st) || major(st.st_dev)) {
+		fprintf(stderr, "Cannot access backlight interface '%s'\n", argv[1]);
+		return 1;
+	}
+
+	while ((len = read(0, b, sizeof(buf) - (b - buf) - 1)) > 0) {
+		len += b - buf;
+		buf[len] = '\0';
+
+		b = buf;
+		do {
+			char *end = strchr(b, '\n');
+			if (end == NULL)
+				break;
+
+			++end;
+			if (write(fd, b, end - b) != end - b) {
+				fprintf(stderr, "Failed to update backlight interface '%s'\n", argv[1]);
+				return 2;
+			}
+
+			b = end;
+		} while (1);
+
+		memmove(buf, b, len = buf + len - b);
+		b = buf + len;
+	}
+
+	return 0;
+}
diff --git a/tools/org.x.xf86-video-intel.backlight-helper.policy.in b/tools/org.x.xf86-video-intel.backlight-helper.policy.in
new file mode 100644
index 0000000..96e772d
--- /dev/null
+++ b/tools/org.x.xf86-video-intel.backlight-helper.policy.in
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd";>
+<policyconfig>
+  <vendor>The X.Org project</vendor>
+  <vendor_url>https://01.org/linuxgraphics/community/xf86-video-intel</vendor_url>
+  <icon_name>brightness</icon_name>
+  <action id="org.x.xf86-video-intel.backlight-helper">
+    <description>Modify lcd panel brightness</description>
+    <message>Authentication is required to modify the lcd panel brightness</message>
+    <defaults>
+      <allow_any>no</allow_any>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>yes</allow_active>
+    </defaults>
+    <annotate key="org.freedesktop.policykit.exec.path">@LIBEXEC_PATH@/xf86-video-intel-backlight-helper</annotate>
+  </action>
+</policyconfig>
-- 
2.1.4

Attachment: signature.asc
Description: Digital signature


Reply to: