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(¶m, 0, sizeof(param));
110 param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
111 param.curval = level;
112
113 return ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, ¶m);
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(¶m, 0, sizeof(param));
124 param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
125
126 if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m))
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(¶m, 0, sizeof(param));
145 param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
146
147 if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m) == -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