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(¶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 116int 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 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(¶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 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