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#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40#include <ctype.h> 41#include <limits.h> 42#include <fcntl.h> 43#include <unistd.h> 44#include <dirent.h> 45 46#include <xorg-server.h> 47#include <xf86.h> 48#include <pciaccess.h> 49 50#include "backlight.h" 51#include "fd.h" 52 53#define BACKLIGHT_CLASS "/sys/class/backlight" 54 55/* Enough for 10 digits of backlight + '\n' + '\0' */ 56#define BACKLIGHT_VALUE_LEN 12 57 58#ifndef ARRAY_SIZE 59#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0])) 60#endif 61 62/* 63 * Unfortunately this is not as simple as I would like it to be. If selinux is 64 * dropping dbus messages pkexec may block *forever*. 65 * 66 * Backgrounding pkexec by doing System("pkexec ...&") does not work because 67 * that detaches pkexec from its parent at which point its security checks 68 * fail and it refuses to execute the helper. 69 * 70 * So we're left with spawning a helper child which gets levels to set written 71 * to it through a pipe. This turns the blocking forever problem from a hung 72 * machine problem into a simple backlight control not working problem. 73 * 74 * If only things were as simple as on OpenBSD! :) 75 */ 76 77void backlight_init(struct backlight *b) 78{ 79 b->type = BL_NONE; 80 b->iface = NULL; 81 b->fd = -1; 82 b->pid = -1; 83 b->max = -1; 84 b->has_power = 0; 85} 86 87#if defined(__OpenBSD__) || defined(__NetBSD__) 88 89#include <dev/wscons/wsconsio.h> 90#include <xf86Priv.h> 91 92int backlight_set(struct backlight *b, int level) 93{ 94 struct wsdisplay_param param; 95 96 if (b->iface == NULL) 97 return -1; 98 99 if ((unsigned)level > b->max) 100 level = b->max; 101 102 memset(¶m, 0, sizeof(param)); 103 param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 104 param.curval = level; 105 106 return ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, ¶m); 107} 108 109int backlight_get(struct backlight *b) 110{ 111 struct wsdisplay_param param; 112 113 if (b->iface == NULL) 114 return -1; 115 116 memset(¶m, 0, sizeof(param)); 117 param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 118 119 if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m)) 120 return -1; 121 122 return param.curval; 123} 124 125int backlight_open(struct backlight *b, char *iface) 126{ 127 struct wsdisplay_param param; 128 129 if (iface != NULL) 130 return -1; 131 132 memset(¶m, 0, sizeof(param)); 133 param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 134 135 if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m) == -1) 136 return -1; 137 138 b->iface = strdup("wscons"); 139 if (b->iface == NULL) 140 return -1; 141 142 b->max = param.max; 143 b->fd = -1; 144 b->type = BL_PLATFORM; 145 146 return param.curval; 147} 148 149enum backlight_type backlight_exists(const char *iface) 150{ 151 if (iface != NULL) 152 return BL_NONE; 153 154 return BL_PLATFORM; 155} 156 157int backlight_on(struct backlight *b) 158{ 159 return 0; 160} 161 162int backlight_off(struct backlight *b) 163{ 164 return 0; 165} 166#else 167 168static int 169is_sysfs_fd(int fd) 170{ 171 struct stat st; 172 return fstat(fd, &st) == 0 && major(st.st_dev) == 0; 173} 174 175static int 176__backlight_open(const char *iface, const char *file, int mode) 177{ 178 char buf[1024]; 179 int fd; 180 181 snprintf(buf, sizeof(buf), BACKLIGHT_CLASS "/%s/%s", iface, file); 182 fd = open(buf, mode); 183 if (fd == -1) 184 return -1; 185 186 if (!is_sysfs_fd(fd)) { 187 close(fd); 188 return -1; 189 } 190 191 return fd; 192} 193 194static int 195__backlight_read(const char *iface, const char *file) 196{ 197 char buf[BACKLIGHT_VALUE_LEN]; 198 int fd, val; 199 200 fd = __backlight_open(iface, file, O_RDONLY); 201 if (fd < 0) 202 return -1; 203 204 val = read(fd, buf, BACKLIGHT_VALUE_LEN - 1); 205 if (val > 0) { 206 buf[val] = '\0'; 207 val = atoi(buf); 208 } else 209 val = -1; 210 close(fd); 211 212 return val; 213} 214 215static int 216__backlight_write(const char *iface, const char *file, const char *value) 217{ 218 int fd, ret; 219 220 fd = __backlight_open(iface, file, O_WRONLY); 221 if (fd < 0) 222 return -1; 223 224 ret = write(fd, value, strlen(value)+1); 225 close(fd); 226 227 return ret; 228} 229 230/* List of available kernel interfaces in priority order */ 231static const char *known_interfaces[] = { 232 "dell_backlight", 233 "gmux_backlight", 234 "asus-laptop", 235 "asus-nb-wmi", 236 "eeepc", 237 "thinkpad_screen", 238 "mbp_backlight", 239 "fujitsu-laptop", 240 "sony", 241 "samsung", 242 "acpi_video1", 243 "acpi_video0", 244 "intel_backlight", 245}; 246 247static enum backlight_type __backlight_type(const char *iface) 248{ 249 char buf[1024]; 250 int fd, v; 251 252 v = -1; 253 fd = __backlight_open(iface, "type", O_RDONLY); 254 if (fd >= 0) { 255 v = read(fd, buf, sizeof(buf)-1); 256 close(fd); 257 } 258 if (v > 0) { 259 while (v > 0 && isspace(buf[v-1])) 260 v--; 261 buf[v] = '\0'; 262 263 if (strcmp(buf, "raw") == 0) 264 v = BL_RAW; 265 else if (strcmp(buf, "platform") == 0) 266 v = BL_PLATFORM; 267 else if (strcmp(buf, "firmware") == 0) 268 v = BL_FIRMWARE; 269 else 270 v = BL_NAMED; 271 } else 272 v = BL_NAMED; 273 274 if (v == BL_NAMED) { 275 int i; 276 for (i = 0; i < ARRAY_SIZE(known_interfaces); i++) { 277 if (strcmp(iface, known_interfaces[i]) == 0) 278 break; 279 } 280 v += i; 281 } 282 283 return v; 284} 285 286enum backlight_type backlight_exists(const char *iface) 287{ 288 if (__backlight_read(iface, "brightness") < 0) 289 return BL_NONE; 290 291 if (__backlight_read(iface, "max_brightness") <= 0) 292 return BL_NONE; 293 294 return __backlight_type(iface); 295} 296 297static int __backlight_init(struct backlight *b, char *iface, int fd) 298{ 299 b->fd = fd_move_cloexec(fd_set_nonblock(fd)); 300 b->iface = iface; 301 return 1; 302} 303 304static int __backlight_direct_init(struct backlight *b, char *iface) 305{ 306 int fd; 307 308 fd = __backlight_open(iface, "brightness", O_RDWR); 309 if (fd < 0) 310 return 0; 311 312 if (__backlight_read(iface, "bl_power") != -1) 313 b->has_power = 1; 314 315 return __backlight_init(b, iface, fd); 316} 317 318static int __backlight_helper_init(struct backlight *b, char *iface) 319{ 320#if USE_BACKLIGHT_HELPER 321 struct stat st; 322 char *env[] = { NULL }; 323 int use_pkexec = 0; 324 int fds[2]; 325 326 /* 327 * Some systems may prefer using PolicyKit's pkexec over 328 * making the helper suid root, since the suid option will allow 329 * anyone to control the backlight. However, as pkexec 330 * is quite troublesome and not universally available, we 331 * still try the old fashioned and simple method first. 332 * Either way, we have to trust that it is our backlight-helper 333 * that is run and that we have scrutinised it carefully. 334 */ 335 if (stat(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", &st)) 336 return 0; 337 338 if ((st.st_mode & (S_IFREG | S_ISUID | S_IXUSR)) != (S_IFREG | S_ISUID | S_IXUSR)) { 339 if (System("pkexec --version")) 340 return 0; 341 342 use_pkexec = 1; 343 } 344 345 if (pipe(fds)) 346 return 0; 347 348 switch ((b->pid = fork())) { 349 case 0: 350 if (setgid(getgid()) || setuid(getuid())) 351 _exit(127); 352 353 close(fds[1]); 354 if (dup2(fds[0], 0)) 355 _exit(127); 356 close(fds[0]); 357 358 if (use_pkexec) { 359 execlp("pkexec", "pkexec", 360 LIBEXEC_PATH "/xf86-video-intel-backlight-helper", 361 iface, (char *)0); 362 } else { 363 execle(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", 364 "xf86-video-intel-backlight-helper", 365 iface, (char *)0, env); 366 } 367 _exit(1); 368 /* unreachable fallthrough */ 369 case -1: 370 close(fds[1]); 371 close(fds[0]); 372 return 0; 373 374 default: 375 close(fds[0]); 376 return __backlight_init(b, iface, fds[1]); 377 } 378#else 379 return 0; 380#endif 381} 382 383static char * 384__backlight_find(void) 385{ 386 char *best_iface = NULL; 387 unsigned best_type = INT_MAX; 388 DIR *dir; 389 struct dirent *de; 390 391 dir = opendir(BACKLIGHT_CLASS); 392 if (dir == NULL) 393 return NULL; 394 395 while ((de = readdir(dir))) { 396 int v; 397 398 if (*de->d_name == '.') 399 continue; 400 401 /* Fallback to priority list of known iface for old kernels */ 402 v = backlight_exists(de->d_name); 403 if (v < best_type) { 404 char *copy = strdup(de->d_name); 405 if (copy) { 406 free(best_iface); 407 best_iface = copy; 408 best_type = v; 409 } 410 } 411 } 412 closedir(dir); 413 414 return best_iface; 415} 416 417int backlight_open(struct backlight *b, char *iface) 418{ 419 int level; 420 421 if (iface == NULL) 422 iface = __backlight_find(); 423 if (iface == NULL) 424 goto err; 425 426 b->type = __backlight_type(iface); 427 428 b->max = __backlight_read(iface, "max_brightness"); 429 if (b->max <= 0) 430 goto err; 431 432 level = __backlight_read(iface, "brightness"); 433 if (level < 0) 434 goto err; 435 436 if (!__backlight_direct_init(b, iface) && 437 !__backlight_helper_init(b, iface)) 438 goto err; 439 440 return level; 441 442err: 443 backlight_init(b); 444 return -1; 445} 446 447int backlight_set(struct backlight *b, int level) 448{ 449 char val[BACKLIGHT_VALUE_LEN]; 450 int len, ret = 0; 451 452 if (b->iface == NULL) 453 return 0; 454 455 if ((unsigned)level > b->max) 456 level = b->max; 457 458 len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level); 459 if (write(b->fd, val, len) != len) 460 ret = -1; 461 462 return ret; 463} 464 465int backlight_get(struct backlight *b) 466{ 467 int level; 468 469 if (b->iface == NULL) 470 return -1; 471 472 level = __backlight_read(b->iface, "brightness"); 473 if (level > b->max) 474 level = b->max; 475 else if (level < 0) 476 level = -1; 477 return level; 478} 479 480int backlight_off(struct backlight *b) 481{ 482 if (b->iface == NULL) 483 return 0; 484 485 if (!b->has_power) 486 return 0; 487 488 /* 4 -> FB_BLANK_POWERDOWN */ 489 return __backlight_write(b->iface, "bl_power", "4"); 490} 491 492int backlight_on(struct backlight *b) 493{ 494 if (b->iface == NULL) 495 return 0; 496 497 if (!b->has_power) 498 return 0; 499 500 /* 0 -> FB_BLANK_UNBLANK */ 501 return __backlight_write(b->iface, "bl_power", "0"); 502} 503#endif 504 505void backlight_disable(struct backlight *b) 506{ 507 if (b->iface == NULL) 508 return; 509 510 if (b->fd != -1) 511 close(b->fd); 512 513 free(b->iface); 514 b->iface = NULL; 515} 516 517void backlight_close(struct backlight *b) 518{ 519 backlight_disable(b); 520 if (b->pid) 521 waitpid(b->pid, NULL, 0); 522} 523 524char *backlight_find_for_device(struct pci_device *pci) 525{ 526 char path[200]; 527 unsigned best_type = INT_MAX; 528 char *best_iface = NULL; 529 DIR *dir; 530 struct dirent *de; 531 532 snprintf(path, sizeof(path), 533 "/sys/bus/pci/devices/%04x:%02x:%02x.%d/backlight", 534 pci->domain, pci->bus, pci->dev, pci->func); 535 536 dir = opendir(path); 537 if (dir == NULL) 538 return NULL; 539 540 while ((de = readdir(dir))) { 541 int v; 542 543 if (*de->d_name == '.') 544 continue; 545 546 v = backlight_exists(de->d_name); 547 if (v < best_type) { 548 char *copy = strdup(de->d_name); 549 if (copy) { 550 free(best_iface); 551 best_iface = copy; 552 best_type = v; 553 } 554 } 555 } 556 closedir(dir); 557 558 return best_iface; 559} 560