backlight.c revision 63ef14f0
11.1Sriastrad/***************************************************************************
21.1Sriastrad
31.1Sriastrad Copyright 2014 Intel Corporation.  All Rights Reserved.
41.1Sriastrad Copyright 2014 Red Hat, Inc.
51.1Sriastrad
61.1Sriastrad Permission is hereby granted, free of charge, to any person obtaining a
71.1Sriastrad copy of this software and associated documentation files (the
81.1Sriastrad "Software"), to deal in the Software without restriction, including
91.1Sriastrad without limitation the rights to use, copy, modify, merge, publish,
101.1Sriastrad distribute, sub license, and/or sell copies of the Software, and to
111.1Sriastrad permit persons to whom the Software is furnished to do so, subject to
121.1Sriastrad the following conditions:
131.1Sriastrad
141.1Sriastrad The above copyright notice and this permission notice (including the
151.1Sriastrad next paragraph) shall be included in all copies or substantial portions
161.1Sriastrad of the Software.
171.1Sriastrad
181.1Sriastrad THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
191.1Sriastrad OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
201.1Sriastrad MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
211.1Sriastrad IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
221.1Sriastrad DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
231.1Sriastrad OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
241.1Sriastrad THE USE OR OTHER DEALINGS IN THE SOFTWARE.
251.1Sriastrad
261.1Sriastrad **************************************************************************/
271.1Sriastrad
281.1Sriastrad#ifdef HAVE_CONFIG_H
291.1Sriastrad#include "config.h"
301.1Sriastrad#endif
311.1Sriastrad
321.1Sriastrad#include <sys/types.h>
331.1Sriastrad#include <sys/wait.h>
341.1Sriastrad#include <sys/stat.h>
351.1Sriastrad#include <sys/ioctl.h>
361.1Sriastrad
371.1Sriastrad#if MAJOR_IN_MKDEV
381.1Sriastrad#include <sys/mkdev.h>
391.1Sriastrad#elif MAJOR_IN_SYSMACROS
401.3Sriastrad#include <sys/sysmacros.h>
411.1Sriastrad#endif
421.1Sriastrad
431.1Sriastrad#include <stdio.h>
441.1Sriastrad#include <stdlib.h>
451.1Sriastrad#include <string.h>
461.1Sriastrad#include <ctype.h>
471.1Sriastrad#include <limits.h>
481.1Sriastrad#include <fcntl.h>
491.1Sriastrad#include <unistd.h>
501.1Sriastrad#include <dirent.h>
511.1Sriastrad#include <errno.h>
521.1Sriastrad
531.3Sriastrad#include <xorg-server.h>
541.1Sriastrad#include <xf86.h>
551.1Sriastrad#include <pciaccess.h>
561.1Sriastrad
571.1Sriastrad#include "backlight.h"
581.1Sriastrad#include "fd.h"
591.1Sriastrad
601.1Sriastrad#define BACKLIGHT_CLASS "/sys/class/backlight"
611.1Sriastrad
621.1Sriastrad/* Enough for 10 digits of backlight + '\n' + '\0' */
631.1Sriastrad#define BACKLIGHT_VALUE_LEN 12
641.1Sriastrad
651.1Sriastrad#ifndef ARRAY_SIZE
661.1Sriastrad#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
671.1Sriastrad#endif
68
69/*
70 * Unfortunately this is not as simple as I would like it to be. If selinux is
71 * dropping dbus messages pkexec may block *forever*.
72 *
73 * Backgrounding pkexec by doing System("pkexec ...&") does not work because
74 * that detaches pkexec from its parent at which point its security checks
75 * fail and it refuses to execute the helper.
76 *
77 * So we're left with spawning a helper child which gets levels to set written
78 * to it through a pipe. This turns the blocking forever problem from a hung
79 * machine problem into a simple backlight control not working problem.
80 *
81 * If only things were as simple as on OpenBSD! :)
82 */
83
84void backlight_init(struct backlight *b)
85{
86	b->type = BL_NONE;
87	b->iface = NULL;
88	b->fd = -1;
89	b->pid = -1;
90	b->max = -1;
91	b->has_power = 0;
92}
93
94#ifdef HAVE_DEV_WSCONS_WSCONSIO_H
95
96#include <dev/wscons/wsconsio.h>
97#include <xf86Priv.h>
98
99int backlight_set(struct backlight *b, int level)
100{
101	struct wsdisplay_param param;
102
103	if (b->iface == NULL)
104		return -1;
105
106	if ((unsigned)level > b->max)
107		level = b->max;
108
109	memset(&param, 0, sizeof(param));
110	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
111	param.curval = level;
112
113	return ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &param);
114}
115
116int backlight_get(struct backlight *b)
117{
118	struct wsdisplay_param param;
119
120	if (b->iface == NULL)
121		return -1;
122
123	memset(&param, 0, sizeof(param));
124	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
125
126	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param))
127		return -1;
128
129	return param.curval;
130}
131
132char *backlight_find_for_device(struct pci_device *pci)
133{
134	return NULL;
135}
136
137int backlight_open(struct backlight *b, char *iface)
138{
139	struct wsdisplay_param param;
140
141	if (iface != NULL)
142		return -1;
143
144	memset(&param, 0, sizeof(param));
145	param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
146
147	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &param) == -1)
148		return -1;
149
150	b->iface = strdup("wscons");
151	if (b->iface == NULL)
152		return -1;
153
154	b->max = param.max;
155	b->fd = -1;
156	b->type = BL_PLATFORM;
157
158	return param.curval;
159}
160
161int backlight_exists(const char *iface)
162{
163	return iface == NULL;
164}
165
166int backlight_on(struct backlight *b)
167{
168	return 0;
169}
170
171int backlight_off(struct backlight *b)
172{
173	return 0;
174}
175
176#else
177
178static int
179is_sysfs_fd(int fd)
180{
181	struct stat st;
182	return fstat(fd, &st) == 0 && major(st.st_dev) == 0;
183}
184
185static int
186__backlight_open(const char *iface, const char *file, int mode)
187{
188	char buf[1024];
189	int fd;
190
191	snprintf(buf, sizeof(buf), BACKLIGHT_CLASS "/%s/%s", iface, file);
192	fd = open(buf, mode);
193	if (fd == -1)
194		return -1;
195
196	if (!is_sysfs_fd(fd)) {
197		close(fd);
198		return -1;
199	}
200
201	return fd;
202}
203
204static int
205__backlight_read(const char *iface, const char *file)
206{
207	char buf[BACKLIGHT_VALUE_LEN];
208	int fd, val;
209
210	fd = __backlight_open(iface, file, O_RDONLY);
211	if (fd < 0)
212		return -1;
213
214	val = read(fd, buf, BACKLIGHT_VALUE_LEN - 1);
215	if (val > 0) {
216		buf[val] = '\0';
217		val = atoi(buf);
218	} else
219		val = -1;
220	close(fd);
221
222	return val;
223}
224
225static int
226writen(int fd, const char *value, int len)
227{
228	int ret;
229
230	do {
231		ret = write(fd, value, len);
232		if (ret < 0) {
233			if (errno == EAGAIN || errno == EINTR)
234				continue;
235
236			return ret;
237		}
238	} while (value += ret, len -= ret);
239
240	return 0;
241}
242
243static int
244__backlight_write(const char *iface, const char *file, const char *value)
245{
246	int fd, ret;
247
248	fd = __backlight_open(iface, file, O_WRONLY);
249	if (fd < 0)
250		return -1;
251
252	ret = writen(fd, value, strlen(value)+1);
253	close(fd);
254
255	return ret;
256}
257
258/* List of available kernel interfaces in priority order */
259static const char *known_interfaces[] = {
260	"dell_backlight",
261	"gmux_backlight",
262	"asus-laptop",
263	"asus-nb-wmi",
264	"eeepc",
265	"thinkpad_screen",
266	"mbp_backlight",
267	"fujitsu-laptop",
268	"sony",
269	"samsung",
270	"acpi_video1",
271	"acpi_video0",
272	"intel_backlight",
273};
274
275static int __backlight_type(const char *iface)
276{
277	char buf[1024];
278	int fd, v, i;
279
280	v = -1;
281	fd = __backlight_open(iface, "type", O_RDONLY);
282	if (fd >= 0) {
283		v = read(fd, buf, sizeof(buf)-1);
284		close(fd);
285	}
286	if (v > 0) {
287		while (v > 0 && isspace(buf[v-1]))
288			v--;
289		buf[v] = '\0';
290
291		if (strcmp(buf, "raw") == 0)
292			v = BL_RAW << 8;
293		else if (strcmp(buf, "platform") == 0)
294			v = BL_PLATFORM << 8;
295		else if (strcmp(buf, "firmware") == 0)
296			v = BL_FIRMWARE << 8;
297		else
298			v = BL_NAMED << 8;
299	} else
300		v = BL_NAMED << 8;
301
302	for (i = 0; i < ARRAY_SIZE(known_interfaces); i++) {
303		if (strcmp(iface, known_interfaces[i]) == 0)
304			break;
305	}
306	v += i;
307
308	return v;
309}
310
311static int __backlight_exists(const char *iface)
312{
313	if (__backlight_read(iface, "brightness") < 0)
314		return -1;
315
316	if (__backlight_read(iface, "max_brightness") <= 0)
317		return -1;
318
319	return __backlight_type(iface);
320}
321
322int backlight_exists(const char *iface)
323{
324	return __backlight_exists(iface) != -1;
325}
326
327static int __backlight_init(struct backlight *b, char *iface, int fd)
328{
329	b->fd = fd_move_cloexec(fd_set_nonblock(fd));
330	b->iface = iface;
331	return 1;
332}
333
334static int __backlight_direct_init(struct backlight *b, char *iface)
335{
336	int fd;
337
338	fd = __backlight_open(iface, "brightness", O_RDWR);
339	if (fd < 0)
340		return 0;
341
342	if (__backlight_read(iface, "bl_power") != -1)
343		b->has_power = 1;
344
345	return __backlight_init(b, iface, fd);
346}
347
348static int __backlight_helper_init(struct backlight *b, char *iface)
349{
350#if USE_BACKLIGHT_HELPER
351	struct stat st;
352	char *env[] = { NULL };
353	int use_pkexec = 0;
354	int fds[2];
355
356	/*
357	 * Some systems may prefer using PolicyKit's pkexec over
358	 * making the helper suid root, since the suid option will allow
359	 * anyone to control the backlight.  However, as pkexec
360	 * is quite troublesome and not universally available, we
361	 * still try the old fashioned and simple method first.
362	 * Either way, we have to trust that it is our backlight-helper
363	 * that is run and that we have scrutinised it carefully.
364	 */
365	if (stat(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", &st))
366		return 0;
367
368	if ((st.st_mode & (S_IFREG | S_ISUID | S_IXUSR)) != (S_IFREG | S_ISUID | S_IXUSR)) {
369		if (System("pkexec --version"))
370			return 0;
371
372		use_pkexec = 1;
373	}
374
375	if (pipe(fds))
376		return 0;
377
378	switch ((b->pid = fork())) {
379	case 0:
380		if (setgid(getgid()) || setuid(getuid()))
381			_exit(127);
382
383		close(fds[1]);
384		if (dup2(fds[0], 0))
385			_exit(127);
386		close(fds[0]);
387
388		if (use_pkexec) {
389			execlp("pkexec", "pkexec",
390			       LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
391			       iface, (char *)0);
392		} else {
393			execle(LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
394			       "xf86-video-intel-backlight-helper",
395			       iface, (char *)0, env);
396		}
397		_exit(1);
398		/* unreachable fallthrough */
399	case -1:
400		close(fds[1]);
401		close(fds[0]);
402		return 0;
403
404	default:
405		close(fds[0]);
406		return __backlight_init(b, iface, fds[1]);
407	}
408#else
409	return 0;
410#endif
411}
412
413static char *
414__backlight_find(void)
415{
416	char *best_iface = NULL;
417	unsigned best_type = INT_MAX;
418	DIR *dir;
419	struct dirent *de;
420
421	dir = opendir(BACKLIGHT_CLASS);
422	if (dir == NULL)
423		return NULL;
424
425	while ((de = readdir(dir))) {
426		int v;
427
428		if (*de->d_name == '.')
429			continue;
430
431		/* Fallback to priority list of known iface for old kernels */
432		v = __backlight_exists(de->d_name);
433		if (v < 0)
434			continue;
435
436		if (v < best_type) {
437			char *copy = strdup(de->d_name);
438			if (copy) {
439				free(best_iface);
440				best_iface = copy;
441				best_type = v;
442			}
443		}
444	}
445	closedir(dir);
446
447	return best_iface;
448}
449
450char *backlight_find_for_device(struct pci_device *pci)
451{
452	char path[200];
453	unsigned best_type = INT_MAX;
454	char *best_iface = NULL;
455	DIR *dir;
456	struct dirent *de;
457
458	snprintf(path, sizeof(path),
459		 "/sys/bus/pci/devices/%04x:%02x:%02x.%d/backlight",
460		 pci->domain, pci->bus, pci->dev, pci->func);
461
462	dir = opendir(path);
463	if (dir == NULL)
464		return NULL;
465
466	while ((de = readdir(dir))) {
467		int v;
468
469		if (*de->d_name == '.')
470			continue;
471
472		v = __backlight_exists(de->d_name);
473		if (v < 0)
474			continue;
475
476		if (v < best_type) {
477			char *copy = strdup(de->d_name);
478			if (copy) {
479				free(best_iface);
480				best_iface = copy;
481				best_type = v;
482			}
483		}
484	}
485	closedir(dir);
486
487	return best_iface;
488}
489
490int backlight_open(struct backlight *b, char *iface)
491{
492	int level, type;
493
494	if (iface == NULL)
495		iface = __backlight_find();
496	if (iface == NULL)
497		goto err;
498
499	type = __backlight_type(iface);
500	if (type < 0)
501		goto err;
502	b->type = type >> 8;
503
504	b->max = __backlight_read(iface, "max_brightness");
505	if (b->max <= 0)
506		goto err;
507
508	level = __backlight_read(iface, "brightness");
509	if (level < 0)
510		goto err;
511
512	if (!__backlight_direct_init(b, iface) &&
513	    !__backlight_helper_init(b, iface))
514		goto err;
515
516	return level;
517
518err:
519	backlight_init(b);
520	return -1;
521}
522
523int backlight_set(struct backlight *b, int level)
524{
525	char val[BACKLIGHT_VALUE_LEN];
526	int len;
527
528	if (b->iface == NULL)
529		return 0;
530
531	if ((unsigned)level > b->max)
532		level = b->max;
533
534	len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level);
535	return writen(b->fd, val, len);
536}
537
538int backlight_get(struct backlight *b)
539{
540	int level;
541
542	if (b->iface == NULL)
543		return -1;
544
545	level = __backlight_read(b->iface, "brightness");
546	if (level > b->max)
547		level = b->max;
548	else if (level < 0)
549		level = -1;
550	return level;
551}
552
553int backlight_off(struct backlight *b)
554{
555	if (b->iface == NULL)
556		return 0;
557
558	if (!b->has_power)
559		return 0;
560
561	/* 4 -> FB_BLANK_POWERDOWN */
562	return __backlight_write(b->iface, "bl_power", "4");
563}
564
565int backlight_on(struct backlight *b)
566{
567	if (b->iface == NULL)
568		return 0;
569
570	if (!b->has_power)
571		return 0;
572
573	/* 0 -> FB_BLANK_UNBLANK */
574	return __backlight_write(b->iface, "bl_power", "0");
575}
576#endif
577
578void backlight_disable(struct backlight *b)
579{
580	if (b->iface == NULL)
581		return;
582
583	if (b->fd != -1)
584		close(b->fd);
585
586	free(b->iface);
587	b->iface = NULL;
588}
589
590void backlight_close(struct backlight *b)
591{
592	backlight_disable(b);
593	if (b->pid > 0)
594		waitpid(b->pid, NULL, 0);
595}
596