1 /***************************************************************************
2 
3  Copyright 2014 Intel Corporation.  All Rights Reserved.
4  Copyright 2014 Red Hat, Inc.
5 
6  Permission is hereby granted, free of charge, to any person obtaining a
7  copy of this software and associated documentation files (the
8  "Software"), to deal in the Software without restriction, including
9  without limitation the rights to use, copy, modify, merge, publish,
10  distribute, sub license, and/or sell copies of the Software, and to
11  permit persons to whom the Software is furnished to do so, subject to
12  the following conditions:
13 
14  The above copyright notice and this permission notice (including the
15  next paragraph) shall be included in all copies or substantial portions
16  of the Software.
17 
18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21  IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
22  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
24  THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 
26  **************************************************************************/
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 #include <sys/stat.h>
35 #include <sys/ioctl.h>
36 
37 #if MAJOR_IN_MKDEV
38 #include <sys/mkdev.h>
39 #elif MAJOR_IN_SYSMACROS
40 #include <sys/sysmacros.h>
41 #endif
42 
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <ctype.h>
47 #include <limits.h>
48 #include <fcntl.h>
49 #include <unistd.h>
50 #include <dirent.h>
51 #include <errno.h>
52 
53 #include <xorg-server.h>
54 #include <xf86.h>
55 #include <pciaccess.h>
56 
57 #include "backlight.h"
58 #include "fd.h"
59 
60 #define BACKLIGHT_CLASS "/sys/class/backlight"
61 
62 /* Enough for 10 digits of backlight + '\n' + '\0' */
63 #define BACKLIGHT_VALUE_LEN 12
64 
65 #ifndef ARRAY_SIZE
66 #define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
67 #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 
backlight_init(struct backlight * b)84 void 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 
backlight_set(struct backlight * b,int level)99 int 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 
backlight_get(struct backlight * b)116 int 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 
backlight_find_for_device(struct pci_device * pci)132 char *backlight_find_for_device(struct pci_device *pci)
133 {
134 	return NULL;
135 }
136 
backlight_open(struct backlight * b,char * iface)137 int 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 
backlight_exists(const char * iface)161 int backlight_exists(const char *iface)
162 {
163 	return iface == NULL;
164 }
165 
backlight_on(struct backlight * b)166 int backlight_on(struct backlight *b)
167 {
168 	return 0;
169 }
170 
backlight_off(struct backlight * b)171 int backlight_off(struct backlight *b)
172 {
173 	return 0;
174 }
175 
176 #else
177 
178 static int
is_sysfs_fd(int fd)179 is_sysfs_fd(int fd)
180 {
181 	struct stat st;
182 	return fstat(fd, &st) == 0 && major(st.st_dev) == 0;
183 }
184 
185 static int
__backlight_open(const char * iface,const char * file,int mode)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 
204 static int
__backlight_read(const char * iface,const char * file)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 
225 static int
writen(int fd,const char * value,int len)226 writen(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 
243 static int
__backlight_write(const char * iface,const char * file,const char * value)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 */
259 static 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 
__backlight_type(const char * iface)275 static 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 
__backlight_exists(const char * iface)311 static 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 
backlight_exists(const char * iface)322 int backlight_exists(const char *iface)
323 {
324 	return __backlight_exists(iface) != -1;
325 }
326 
__backlight_init(struct backlight * b,char * iface,int fd)327 static 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 
__backlight_direct_init(struct backlight * b,char * iface)334 static 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 
__backlight_helper_init(struct backlight * b,char * iface)348 static 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 
413 static char *
__backlight_find(void)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 
backlight_find_for_device(struct pci_device * pci)450 char *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 
backlight_open(struct backlight * b,char * iface)490 int 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 
518 err:
519 	backlight_init(b);
520 	return -1;
521 }
522 
backlight_set(struct backlight * b,int level)523 int 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 
backlight_get(struct backlight * b)538 int 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 
backlight_off(struct backlight * b)553 int 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 
backlight_on(struct backlight * b)565 int 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 
backlight_disable(struct backlight * b)578 void 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 
backlight_close(struct backlight * b)590 void backlight_close(struct backlight *b)
591 {
592 	backlight_disable(b);
593 	if (b->pid > 0)
594 		waitpid(b->pid, NULL, 0);
595 }
596