backlight.c revision 42542f5f
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} 85 86#ifdef __OpenBSD__ 87 88#include <dev/wscons/wsconsio.h> 89#include <xf86Priv.h> 90 91int backlight_set(struct backlight *b, int level) 92{ 93 struct wsdisplay_param param; 94 95 if (b->iface == NULL) 96 return -1; 97 98 if ((unsigned)level > b->max) 99 level = b->max; 100 101 memset(¶m, 0, sizeof(param)); 102 param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 103 param.curval = level; 104 105 return ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, ¶m); 106} 107 108int backlight_get(struct backlight *b) 109{ 110 struct wsdisplay_param param; 111 112 if (b->iface == NULL) 113 return -1; 114 115 memset(¶m, 0, sizeof(param)); 116 param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 117 118 if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m)) 119 return -1; 120 121 return param.curval; 122} 123 124int backlight_open(struct backlight *b, char *iface) 125{ 126 struct wsdisplay_param param; 127 128 if (iface != NULL) 129 return -1; 130 131 memset(¶m, 0, sizeof(param)); 132 param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 133 134 if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m) == -1) 135 return -1; 136 137 b->iface = strdup("wscons"); 138 if (b->iface == NULL) 139 return -1; 140 141 b->max = param.max; 142 b->fd = -1; 143 b->type = BL_PLATFORM; 144 145 return param.curval; 146} 147 148enum backlight_type backlight_exists(const char *iface) 149{ 150 if (iface != NULL) 151 return BL_NONE; 152 153 return BL_PLATFORM; 154} 155 156#else 157 158static int 159is_sysfs_fd(int fd) 160{ 161 struct stat st; 162 return fstat(fd, &st) == 0 && major(st.st_dev) == 0; 163} 164 165static int 166__backlight_open(const char *iface, const char *file, int mode) 167{ 168 char buf[1024]; 169 int fd; 170 171 snprintf(buf, sizeof(buf), BACKLIGHT_CLASS "/%s/%s", iface, file); 172 fd = open(buf, mode); 173 if (fd == -1) 174 return -1; 175 176 if (!is_sysfs_fd(fd)) { 177 close(fd); 178 return -1; 179 } 180 181 return fd; 182} 183 184static int 185__backlight_read(const char *iface, const char *file) 186{ 187 char buf[BACKLIGHT_VALUE_LEN]; 188 int fd, val; 189 190 fd = __backlight_open(iface, file, O_RDONLY); 191 if (fd < 0) 192 return -1; 193 194 val = read(fd, buf, BACKLIGHT_VALUE_LEN - 1); 195 if (val > 0) { 196 buf[val] = '\0'; 197 val = atoi(buf); 198 } else 199 val = -1; 200 close(fd); 201 202 return val; 203} 204 205/* List of available kernel interfaces in priority order */ 206static const char *known_interfaces[] = { 207 "dell_backlight", 208 "gmux_backlight", 209 "asus-laptop", 210 "asus-nb-wmi", 211 "eeepc", 212 "thinkpad_screen", 213 "mbp_backlight", 214 "fujitsu-laptop", 215 "sony", 216 "samsung", 217 "acpi_video1", 218 "acpi_video0", 219 "intel_backlight", 220}; 221 222static enum backlight_type __backlight_type(const char *iface) 223{ 224 char buf[1024]; 225 int fd, v; 226 227 v = -1; 228 fd = __backlight_open(iface, "type", O_RDONLY); 229 if (fd >= 0) { 230 v = read(fd, buf, sizeof(buf)-1); 231 close(fd); 232 } 233 if (v > 0) { 234 while (v > 0 && isspace(buf[v-1])) 235 v--; 236 buf[v] = '\0'; 237 238 if (strcmp(buf, "raw") == 0) 239 v = BL_RAW; 240 else if (strcmp(buf, "platform") == 0) 241 v = BL_PLATFORM; 242 else if (strcmp(buf, "firmware") == 0) 243 v = BL_FIRMWARE; 244 else 245 v = BL_NAMED; 246 } else 247 v = BL_NAMED; 248 249 if (v == BL_NAMED) { 250 int i; 251 for (i = 0; i < ARRAY_SIZE(known_interfaces); i++) { 252 if (strcmp(iface, known_interfaces[i]) == 0) 253 break; 254 } 255 v += i; 256 } 257 258 return v; 259} 260 261enum backlight_type backlight_exists(const char *iface) 262{ 263 if (__backlight_read(iface, "brightness") < 0) 264 return BL_NONE; 265 266 if (__backlight_read(iface, "max_brightness") <= 0) 267 return BL_NONE; 268 269 return __backlight_type(iface); 270} 271 272static int __backlight_init(struct backlight *b, char *iface, int fd) 273{ 274 b->fd = fd_move_cloexec(fd_set_nonblock(fd)); 275 b->iface = iface; 276 return 1; 277} 278 279static int __backlight_direct_init(struct backlight *b, char *iface) 280{ 281 int fd; 282 283 fd = __backlight_open(iface, "brightness", O_RDWR); 284 if (fd < 0) 285 return 0; 286 287 return __backlight_init(b, iface, fd); 288} 289 290static int __backlight_helper_init(struct backlight *b, char *iface) 291{ 292#if USE_BACKLIGHT_HELPER 293 struct stat st; 294 char *env[] = { NULL }; 295 int use_pkexec = 0; 296 int fds[2]; 297 298 /* 299 * Some systems may prefer using PolicyKit's pkexec over 300 * making the helper suid root, since the suid option will allow 301 * anyone to control the backlight. However, as pkexec 302 * is quite troublesome and not universally available, we 303 * still try the old fashioned and simple method first. 304 * Either way, we have to trust that it is our backlight-helper 305 * that is run and that we have scrutinised it carefully. 306 */ 307 if (stat(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", &st)) 308 return 0; 309 310 if ((st.st_mode & (S_IFREG | S_ISUID | S_IXUSR)) != (S_IFREG | S_ISUID | S_IXUSR)) { 311 if (System("pkexec --version")) 312 return 0; 313 314 use_pkexec = 1; 315 } 316 317 if (pipe(fds)) 318 return 0; 319 320 switch ((b->pid = fork())) { 321 case 0: 322 if (setgid(getgid()) || setuid(getuid())) 323 _exit(127); 324 325 close(fds[1]); 326 if (dup2(fds[0], 0)) 327 _exit(127); 328 close(fds[0]); 329 330 if (use_pkexec) { 331 execlp("pkexec", "pkexec", 332 LIBEXEC_PATH "/xf86-video-intel-backlight-helper", 333 iface, (char *)0); 334 } else { 335 execle(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", 336 "xf86-video-intel-backlight-helper", 337 iface, (char *)0, env); 338 } 339 _exit(1); 340 /* unreachable fallthrough */ 341 case -1: 342 close(fds[1]); 343 close(fds[0]); 344 return 0; 345 346 default: 347 close(fds[0]); 348 return __backlight_init(b, iface, fds[1]); 349 } 350#else 351 return 0; 352#endif 353} 354 355static char * 356__backlight_find(void) 357{ 358 char *best_iface = NULL; 359 unsigned best_type = INT_MAX; 360 DIR *dir; 361 struct dirent *de; 362 363 dir = opendir(BACKLIGHT_CLASS); 364 if (dir == NULL) 365 return NULL; 366 367 while ((de = readdir(dir))) { 368 int v; 369 370 if (*de->d_name == '.') 371 continue; 372 373 /* Fallback to priority list of known iface for old kernels */ 374 v = backlight_exists(de->d_name); 375 if (v < best_type) { 376 char *copy = strdup(de->d_name); 377 if (copy) { 378 free(best_iface); 379 best_iface = copy; 380 best_type = v; 381 } 382 } 383 } 384 closedir(dir); 385 386 return best_iface; 387} 388 389int backlight_open(struct backlight *b, char *iface) 390{ 391 int level; 392 393 if (iface == NULL) 394 iface = __backlight_find(); 395 if (iface == NULL) 396 goto err; 397 398 b->type = __backlight_type(iface); 399 400 b->max = __backlight_read(iface, "max_brightness"); 401 if (b->max <= 0) 402 goto err; 403 404 level = __backlight_read(iface, "brightness"); 405 if (level < 0) 406 goto err; 407 408 if (!__backlight_direct_init(b, iface) && 409 !__backlight_helper_init(b, iface)) 410 goto err; 411 412 return level; 413 414err: 415 backlight_init(b); 416 return -1; 417} 418 419int backlight_set(struct backlight *b, int level) 420{ 421 char val[BACKLIGHT_VALUE_LEN]; 422 int len, ret = 0; 423 424 if (b->iface == NULL) 425 return 0; 426 427 if ((unsigned)level > b->max) 428 level = b->max; 429 430 len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level); 431 if (write(b->fd, val, len) != len) 432 ret = -1; 433 434 return ret; 435} 436 437int backlight_get(struct backlight *b) 438{ 439 int level; 440 441 if (b->iface == NULL) 442 return -1; 443 444 level = __backlight_read(b->iface, "brightness"); 445 if (level > b->max) 446 level = b->max; 447 else if (level < 0) 448 level = -1; 449 return level; 450} 451#endif 452 453void backlight_disable(struct backlight *b) 454{ 455 if (b->iface == NULL) 456 return; 457 458 if (b->fd != -1) 459 close(b->fd); 460 461 free(b->iface); 462 b->iface = NULL; 463} 464 465void backlight_close(struct backlight *b) 466{ 467 backlight_disable(b); 468 if (b->pid) 469 waitpid(b->pid, NULL, 0); 470} 471 472char *backlight_find_for_device(struct pci_device *pci) 473{ 474 char path[200]; 475 unsigned best_type = INT_MAX; 476 char *best_iface = NULL; 477 DIR *dir; 478 struct dirent *de; 479 480 snprintf(path, sizeof(path), 481 "/sys/bus/pci/devices/%04x:%02x:%02x.%d/backlight", 482 pci->domain, pci->bus, pci->dev, pci->func); 483 484 dir = opendir(path); 485 if (dir == NULL) 486 return NULL; 487 488 while ((de = readdir(dir))) { 489 int v; 490 491 if (*de->d_name == '.') 492 continue; 493 494 v = backlight_exists(de->d_name); 495 if (v < best_type) { 496 char *copy = strdup(de->d_name); 497 if (copy) { 498 free(best_iface); 499 best_iface = copy; 500 best_type = v; 501 } 502 } 503 } 504 closedir(dir); 505 506 return best_iface; 507} 508