backlight.c revision 42542f5f
142542f5fSchristos/*************************************************************************** 242542f5fSchristos 342542f5fSchristos Copyright 2014 Intel Corporation. All Rights Reserved. 442542f5fSchristos Copyright 2014 Red Hat, Inc. 542542f5fSchristos 642542f5fSchristos Permission is hereby granted, free of charge, to any person obtaining a 742542f5fSchristos copy of this software and associated documentation files (the 842542f5fSchristos "Software"), to deal in the Software without restriction, including 942542f5fSchristos without limitation the rights to use, copy, modify, merge, publish, 1042542f5fSchristos distribute, sub license, and/or sell copies of the Software, and to 1142542f5fSchristos permit persons to whom the Software is furnished to do so, subject to 1242542f5fSchristos the following conditions: 1342542f5fSchristos 1442542f5fSchristos The above copyright notice and this permission notice (including the 1542542f5fSchristos next paragraph) shall be included in all copies or substantial portions 1642542f5fSchristos of the Software. 1742542f5fSchristos 1842542f5fSchristos THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 1942542f5fSchristos OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2042542f5fSchristos MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 2142542f5fSchristos IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 2242542f5fSchristos DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2342542f5fSchristos OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 2442542f5fSchristos THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2542542f5fSchristos 2642542f5fSchristos **************************************************************************/ 2742542f5fSchristos 2842542f5fSchristos#ifdef HAVE_CONFIG_H 2942542f5fSchristos#include "config.h" 3042542f5fSchristos#endif 3142542f5fSchristos 3242542f5fSchristos#include <sys/types.h> 3342542f5fSchristos#include <sys/wait.h> 3442542f5fSchristos#include <sys/stat.h> 3542542f5fSchristos#include <sys/ioctl.h> 3642542f5fSchristos 3742542f5fSchristos#include <stdio.h> 3842542f5fSchristos#include <stdlib.h> 3942542f5fSchristos#include <string.h> 4042542f5fSchristos#include <ctype.h> 4142542f5fSchristos#include <limits.h> 4242542f5fSchristos#include <fcntl.h> 4342542f5fSchristos#include <unistd.h> 4442542f5fSchristos#include <dirent.h> 4542542f5fSchristos 4642542f5fSchristos#include <xorg-server.h> 4742542f5fSchristos#include <xf86.h> 4842542f5fSchristos#include <pciaccess.h> 4942542f5fSchristos 5042542f5fSchristos#include "backlight.h" 5142542f5fSchristos#include "fd.h" 5242542f5fSchristos 5342542f5fSchristos#define BACKLIGHT_CLASS "/sys/class/backlight" 5442542f5fSchristos 5542542f5fSchristos/* Enough for 10 digits of backlight + '\n' + '\0' */ 5642542f5fSchristos#define BACKLIGHT_VALUE_LEN 12 5742542f5fSchristos 5842542f5fSchristos#ifndef ARRAY_SIZE 5942542f5fSchristos#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0])) 6042542f5fSchristos#endif 6142542f5fSchristos 6242542f5fSchristos/* 6342542f5fSchristos * Unfortunately this is not as simple as I would like it to be. If selinux is 6442542f5fSchristos * dropping dbus messages pkexec may block *forever*. 6542542f5fSchristos * 6642542f5fSchristos * Backgrounding pkexec by doing System("pkexec ...&") does not work because 6742542f5fSchristos * that detaches pkexec from its parent at which point its security checks 6842542f5fSchristos * fail and it refuses to execute the helper. 6942542f5fSchristos * 7042542f5fSchristos * So we're left with spawning a helper child which gets levels to set written 7142542f5fSchristos * to it through a pipe. This turns the blocking forever problem from a hung 7242542f5fSchristos * machine problem into a simple backlight control not working problem. 7342542f5fSchristos * 7442542f5fSchristos * If only things were as simple as on OpenBSD! :) 7542542f5fSchristos */ 7642542f5fSchristos 7742542f5fSchristosvoid backlight_init(struct backlight *b) 7842542f5fSchristos{ 7942542f5fSchristos b->type = BL_NONE; 8042542f5fSchristos b->iface = NULL; 8142542f5fSchristos b->fd = -1; 8242542f5fSchristos b->pid = -1; 8342542f5fSchristos b->max = -1; 8442542f5fSchristos} 8542542f5fSchristos 8642542f5fSchristos#ifdef __OpenBSD__ 8742542f5fSchristos 8842542f5fSchristos#include <dev/wscons/wsconsio.h> 8942542f5fSchristos#include <xf86Priv.h> 9042542f5fSchristos 9142542f5fSchristosint backlight_set(struct backlight *b, int level) 9242542f5fSchristos{ 9342542f5fSchristos struct wsdisplay_param param; 9442542f5fSchristos 9542542f5fSchristos if (b->iface == NULL) 9642542f5fSchristos return -1; 9742542f5fSchristos 9842542f5fSchristos if ((unsigned)level > b->max) 9942542f5fSchristos level = b->max; 10042542f5fSchristos 10142542f5fSchristos memset(¶m, 0, sizeof(param)); 10242542f5fSchristos param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 10342542f5fSchristos param.curval = level; 10442542f5fSchristos 10542542f5fSchristos return ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, ¶m); 10642542f5fSchristos} 10742542f5fSchristos 10842542f5fSchristosint backlight_get(struct backlight *b) 10942542f5fSchristos{ 11042542f5fSchristos struct wsdisplay_param param; 11142542f5fSchristos 11242542f5fSchristos if (b->iface == NULL) 11342542f5fSchristos return -1; 11442542f5fSchristos 11542542f5fSchristos memset(¶m, 0, sizeof(param)); 11642542f5fSchristos param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 11742542f5fSchristos 11842542f5fSchristos if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m)) 11942542f5fSchristos return -1; 12042542f5fSchristos 12142542f5fSchristos return param.curval; 12242542f5fSchristos} 12342542f5fSchristos 12442542f5fSchristosint backlight_open(struct backlight *b, char *iface) 12542542f5fSchristos{ 12642542f5fSchristos struct wsdisplay_param param; 12742542f5fSchristos 12842542f5fSchristos if (iface != NULL) 12942542f5fSchristos return -1; 13042542f5fSchristos 13142542f5fSchristos memset(¶m, 0, sizeof(param)); 13242542f5fSchristos param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 13342542f5fSchristos 13442542f5fSchristos if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m) == -1) 13542542f5fSchristos return -1; 13642542f5fSchristos 13742542f5fSchristos b->iface = strdup("wscons"); 13842542f5fSchristos if (b->iface == NULL) 13942542f5fSchristos return -1; 14042542f5fSchristos 14142542f5fSchristos b->max = param.max; 14242542f5fSchristos b->fd = -1; 14342542f5fSchristos b->type = BL_PLATFORM; 14442542f5fSchristos 14542542f5fSchristos return param.curval; 14642542f5fSchristos} 14742542f5fSchristos 14842542f5fSchristosenum backlight_type backlight_exists(const char *iface) 14942542f5fSchristos{ 15042542f5fSchristos if (iface != NULL) 15142542f5fSchristos return BL_NONE; 15242542f5fSchristos 15342542f5fSchristos return BL_PLATFORM; 15442542f5fSchristos} 15542542f5fSchristos 15642542f5fSchristos#else 15742542f5fSchristos 15842542f5fSchristosstatic int 15942542f5fSchristosis_sysfs_fd(int fd) 16042542f5fSchristos{ 16142542f5fSchristos struct stat st; 16242542f5fSchristos return fstat(fd, &st) == 0 && major(st.st_dev) == 0; 16342542f5fSchristos} 16442542f5fSchristos 16542542f5fSchristosstatic int 16642542f5fSchristos__backlight_open(const char *iface, const char *file, int mode) 16742542f5fSchristos{ 16842542f5fSchristos char buf[1024]; 16942542f5fSchristos int fd; 17042542f5fSchristos 17142542f5fSchristos snprintf(buf, sizeof(buf), BACKLIGHT_CLASS "/%s/%s", iface, file); 17242542f5fSchristos fd = open(buf, mode); 17342542f5fSchristos if (fd == -1) 17442542f5fSchristos return -1; 17542542f5fSchristos 17642542f5fSchristos if (!is_sysfs_fd(fd)) { 17742542f5fSchristos close(fd); 17842542f5fSchristos return -1; 17942542f5fSchristos } 18042542f5fSchristos 18142542f5fSchristos return fd; 18242542f5fSchristos} 18342542f5fSchristos 18442542f5fSchristosstatic int 18542542f5fSchristos__backlight_read(const char *iface, const char *file) 18642542f5fSchristos{ 18742542f5fSchristos char buf[BACKLIGHT_VALUE_LEN]; 18842542f5fSchristos int fd, val; 18942542f5fSchristos 19042542f5fSchristos fd = __backlight_open(iface, file, O_RDONLY); 19142542f5fSchristos if (fd < 0) 19242542f5fSchristos return -1; 19342542f5fSchristos 19442542f5fSchristos val = read(fd, buf, BACKLIGHT_VALUE_LEN - 1); 19542542f5fSchristos if (val > 0) { 19642542f5fSchristos buf[val] = '\0'; 19742542f5fSchristos val = atoi(buf); 19842542f5fSchristos } else 19942542f5fSchristos val = -1; 20042542f5fSchristos close(fd); 20142542f5fSchristos 20242542f5fSchristos return val; 20342542f5fSchristos} 20442542f5fSchristos 20542542f5fSchristos/* List of available kernel interfaces in priority order */ 20642542f5fSchristosstatic const char *known_interfaces[] = { 20742542f5fSchristos "dell_backlight", 20842542f5fSchristos "gmux_backlight", 20942542f5fSchristos "asus-laptop", 21042542f5fSchristos "asus-nb-wmi", 21142542f5fSchristos "eeepc", 21242542f5fSchristos "thinkpad_screen", 21342542f5fSchristos "mbp_backlight", 21442542f5fSchristos "fujitsu-laptop", 21542542f5fSchristos "sony", 21642542f5fSchristos "samsung", 21742542f5fSchristos "acpi_video1", 21842542f5fSchristos "acpi_video0", 21942542f5fSchristos "intel_backlight", 22042542f5fSchristos}; 22142542f5fSchristos 22242542f5fSchristosstatic enum backlight_type __backlight_type(const char *iface) 22342542f5fSchristos{ 22442542f5fSchristos char buf[1024]; 22542542f5fSchristos int fd, v; 22642542f5fSchristos 22742542f5fSchristos v = -1; 22842542f5fSchristos fd = __backlight_open(iface, "type", O_RDONLY); 22942542f5fSchristos if (fd >= 0) { 23042542f5fSchristos v = read(fd, buf, sizeof(buf)-1); 23142542f5fSchristos close(fd); 23242542f5fSchristos } 23342542f5fSchristos if (v > 0) { 23442542f5fSchristos while (v > 0 && isspace(buf[v-1])) 23542542f5fSchristos v--; 23642542f5fSchristos buf[v] = '\0'; 23742542f5fSchristos 23842542f5fSchristos if (strcmp(buf, "raw") == 0) 23942542f5fSchristos v = BL_RAW; 24042542f5fSchristos else if (strcmp(buf, "platform") == 0) 24142542f5fSchristos v = BL_PLATFORM; 24242542f5fSchristos else if (strcmp(buf, "firmware") == 0) 24342542f5fSchristos v = BL_FIRMWARE; 24442542f5fSchristos else 24542542f5fSchristos v = BL_NAMED; 24642542f5fSchristos } else 24742542f5fSchristos v = BL_NAMED; 24842542f5fSchristos 24942542f5fSchristos if (v == BL_NAMED) { 25042542f5fSchristos int i; 25142542f5fSchristos for (i = 0; i < ARRAY_SIZE(known_interfaces); i++) { 25242542f5fSchristos if (strcmp(iface, known_interfaces[i]) == 0) 25342542f5fSchristos break; 25442542f5fSchristos } 25542542f5fSchristos v += i; 25642542f5fSchristos } 25742542f5fSchristos 25842542f5fSchristos return v; 25942542f5fSchristos} 26042542f5fSchristos 26142542f5fSchristosenum backlight_type backlight_exists(const char *iface) 26242542f5fSchristos{ 26342542f5fSchristos if (__backlight_read(iface, "brightness") < 0) 26442542f5fSchristos return BL_NONE; 26542542f5fSchristos 26642542f5fSchristos if (__backlight_read(iface, "max_brightness") <= 0) 26742542f5fSchristos return BL_NONE; 26842542f5fSchristos 26942542f5fSchristos return __backlight_type(iface); 27042542f5fSchristos} 27142542f5fSchristos 27242542f5fSchristosstatic int __backlight_init(struct backlight *b, char *iface, int fd) 27342542f5fSchristos{ 27442542f5fSchristos b->fd = fd_move_cloexec(fd_set_nonblock(fd)); 27542542f5fSchristos b->iface = iface; 27642542f5fSchristos return 1; 27742542f5fSchristos} 27842542f5fSchristos 27942542f5fSchristosstatic int __backlight_direct_init(struct backlight *b, char *iface) 28042542f5fSchristos{ 28142542f5fSchristos int fd; 28242542f5fSchristos 28342542f5fSchristos fd = __backlight_open(iface, "brightness", O_RDWR); 28442542f5fSchristos if (fd < 0) 28542542f5fSchristos return 0; 28642542f5fSchristos 28742542f5fSchristos return __backlight_init(b, iface, fd); 28842542f5fSchristos} 28942542f5fSchristos 29042542f5fSchristosstatic int __backlight_helper_init(struct backlight *b, char *iface) 29142542f5fSchristos{ 29242542f5fSchristos#if USE_BACKLIGHT_HELPER 29342542f5fSchristos struct stat st; 29442542f5fSchristos char *env[] = { NULL }; 29542542f5fSchristos int use_pkexec = 0; 29642542f5fSchristos int fds[2]; 29742542f5fSchristos 29842542f5fSchristos /* 29942542f5fSchristos * Some systems may prefer using PolicyKit's pkexec over 30042542f5fSchristos * making the helper suid root, since the suid option will allow 30142542f5fSchristos * anyone to control the backlight. However, as pkexec 30242542f5fSchristos * is quite troublesome and not universally available, we 30342542f5fSchristos * still try the old fashioned and simple method first. 30442542f5fSchristos * Either way, we have to trust that it is our backlight-helper 30542542f5fSchristos * that is run and that we have scrutinised it carefully. 30642542f5fSchristos */ 30742542f5fSchristos if (stat(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", &st)) 30842542f5fSchristos return 0; 30942542f5fSchristos 31042542f5fSchristos if ((st.st_mode & (S_IFREG | S_ISUID | S_IXUSR)) != (S_IFREG | S_ISUID | S_IXUSR)) { 31142542f5fSchristos if (System("pkexec --version")) 31242542f5fSchristos return 0; 31342542f5fSchristos 31442542f5fSchristos use_pkexec = 1; 31542542f5fSchristos } 31642542f5fSchristos 31742542f5fSchristos if (pipe(fds)) 31842542f5fSchristos return 0; 31942542f5fSchristos 32042542f5fSchristos switch ((b->pid = fork())) { 32142542f5fSchristos case 0: 32242542f5fSchristos if (setgid(getgid()) || setuid(getuid())) 32342542f5fSchristos _exit(127); 32442542f5fSchristos 32542542f5fSchristos close(fds[1]); 32642542f5fSchristos if (dup2(fds[0], 0)) 32742542f5fSchristos _exit(127); 32842542f5fSchristos close(fds[0]); 32942542f5fSchristos 33042542f5fSchristos if (use_pkexec) { 33142542f5fSchristos execlp("pkexec", "pkexec", 33242542f5fSchristos LIBEXEC_PATH "/xf86-video-intel-backlight-helper", 33342542f5fSchristos iface, (char *)0); 33442542f5fSchristos } else { 33542542f5fSchristos execle(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", 33642542f5fSchristos "xf86-video-intel-backlight-helper", 33742542f5fSchristos iface, (char *)0, env); 33842542f5fSchristos } 33942542f5fSchristos _exit(1); 34042542f5fSchristos /* unreachable fallthrough */ 34142542f5fSchristos case -1: 34242542f5fSchristos close(fds[1]); 34342542f5fSchristos close(fds[0]); 34442542f5fSchristos return 0; 34542542f5fSchristos 34642542f5fSchristos default: 34742542f5fSchristos close(fds[0]); 34842542f5fSchristos return __backlight_init(b, iface, fds[1]); 34942542f5fSchristos } 35042542f5fSchristos#else 35142542f5fSchristos return 0; 35242542f5fSchristos#endif 35342542f5fSchristos} 35442542f5fSchristos 35542542f5fSchristosstatic char * 35642542f5fSchristos__backlight_find(void) 35742542f5fSchristos{ 35842542f5fSchristos char *best_iface = NULL; 35942542f5fSchristos unsigned best_type = INT_MAX; 36042542f5fSchristos DIR *dir; 36142542f5fSchristos struct dirent *de; 36242542f5fSchristos 36342542f5fSchristos dir = opendir(BACKLIGHT_CLASS); 36442542f5fSchristos if (dir == NULL) 36542542f5fSchristos return NULL; 36642542f5fSchristos 36742542f5fSchristos while ((de = readdir(dir))) { 36842542f5fSchristos int v; 36942542f5fSchristos 37042542f5fSchristos if (*de->d_name == '.') 37142542f5fSchristos continue; 37242542f5fSchristos 37342542f5fSchristos /* Fallback to priority list of known iface for old kernels */ 37442542f5fSchristos v = backlight_exists(de->d_name); 37542542f5fSchristos if (v < best_type) { 37642542f5fSchristos char *copy = strdup(de->d_name); 37742542f5fSchristos if (copy) { 37842542f5fSchristos free(best_iface); 37942542f5fSchristos best_iface = copy; 38042542f5fSchristos best_type = v; 38142542f5fSchristos } 38242542f5fSchristos } 38342542f5fSchristos } 38442542f5fSchristos closedir(dir); 38542542f5fSchristos 38642542f5fSchristos return best_iface; 38742542f5fSchristos} 38842542f5fSchristos 38942542f5fSchristosint backlight_open(struct backlight *b, char *iface) 39042542f5fSchristos{ 39142542f5fSchristos int level; 39242542f5fSchristos 39342542f5fSchristos if (iface == NULL) 39442542f5fSchristos iface = __backlight_find(); 39542542f5fSchristos if (iface == NULL) 39642542f5fSchristos goto err; 39742542f5fSchristos 39842542f5fSchristos b->type = __backlight_type(iface); 39942542f5fSchristos 40042542f5fSchristos b->max = __backlight_read(iface, "max_brightness"); 40142542f5fSchristos if (b->max <= 0) 40242542f5fSchristos goto err; 40342542f5fSchristos 40442542f5fSchristos level = __backlight_read(iface, "brightness"); 40542542f5fSchristos if (level < 0) 40642542f5fSchristos goto err; 40742542f5fSchristos 40842542f5fSchristos if (!__backlight_direct_init(b, iface) && 40942542f5fSchristos !__backlight_helper_init(b, iface)) 41042542f5fSchristos goto err; 41142542f5fSchristos 41242542f5fSchristos return level; 41342542f5fSchristos 41442542f5fSchristoserr: 41542542f5fSchristos backlight_init(b); 41642542f5fSchristos return -1; 41742542f5fSchristos} 41842542f5fSchristos 41942542f5fSchristosint backlight_set(struct backlight *b, int level) 42042542f5fSchristos{ 42142542f5fSchristos char val[BACKLIGHT_VALUE_LEN]; 42242542f5fSchristos int len, ret = 0; 42342542f5fSchristos 42442542f5fSchristos if (b->iface == NULL) 42542542f5fSchristos return 0; 42642542f5fSchristos 42742542f5fSchristos if ((unsigned)level > b->max) 42842542f5fSchristos level = b->max; 42942542f5fSchristos 43042542f5fSchristos len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level); 43142542f5fSchristos if (write(b->fd, val, len) != len) 43242542f5fSchristos ret = -1; 43342542f5fSchristos 43442542f5fSchristos return ret; 43542542f5fSchristos} 43642542f5fSchristos 43742542f5fSchristosint backlight_get(struct backlight *b) 43842542f5fSchristos{ 43942542f5fSchristos int level; 44042542f5fSchristos 44142542f5fSchristos if (b->iface == NULL) 44242542f5fSchristos return -1; 44342542f5fSchristos 44442542f5fSchristos level = __backlight_read(b->iface, "brightness"); 44542542f5fSchristos if (level > b->max) 44642542f5fSchristos level = b->max; 44742542f5fSchristos else if (level < 0) 44842542f5fSchristos level = -1; 44942542f5fSchristos return level; 45042542f5fSchristos} 45142542f5fSchristos#endif 45242542f5fSchristos 45342542f5fSchristosvoid backlight_disable(struct backlight *b) 45442542f5fSchristos{ 45542542f5fSchristos if (b->iface == NULL) 45642542f5fSchristos return; 45742542f5fSchristos 45842542f5fSchristos if (b->fd != -1) 45942542f5fSchristos close(b->fd); 46042542f5fSchristos 46142542f5fSchristos free(b->iface); 46242542f5fSchristos b->iface = NULL; 46342542f5fSchristos} 46442542f5fSchristos 46542542f5fSchristosvoid backlight_close(struct backlight *b) 46642542f5fSchristos{ 46742542f5fSchristos backlight_disable(b); 46842542f5fSchristos if (b->pid) 46942542f5fSchristos waitpid(b->pid, NULL, 0); 47042542f5fSchristos} 47142542f5fSchristos 47242542f5fSchristoschar *backlight_find_for_device(struct pci_device *pci) 47342542f5fSchristos{ 47442542f5fSchristos char path[200]; 47542542f5fSchristos unsigned best_type = INT_MAX; 47642542f5fSchristos char *best_iface = NULL; 47742542f5fSchristos DIR *dir; 47842542f5fSchristos struct dirent *de; 47942542f5fSchristos 48042542f5fSchristos snprintf(path, sizeof(path), 48142542f5fSchristos "/sys/bus/pci/devices/%04x:%02x:%02x.%d/backlight", 48242542f5fSchristos pci->domain, pci->bus, pci->dev, pci->func); 48342542f5fSchristos 48442542f5fSchristos dir = opendir(path); 48542542f5fSchristos if (dir == NULL) 48642542f5fSchristos return NULL; 48742542f5fSchristos 48842542f5fSchristos while ((de = readdir(dir))) { 48942542f5fSchristos int v; 49042542f5fSchristos 49142542f5fSchristos if (*de->d_name == '.') 49242542f5fSchristos continue; 49342542f5fSchristos 49442542f5fSchristos v = backlight_exists(de->d_name); 49542542f5fSchristos if (v < best_type) { 49642542f5fSchristos char *copy = strdup(de->d_name); 49742542f5fSchristos if (copy) { 49842542f5fSchristos free(best_iface); 49942542f5fSchristos best_iface = copy; 50042542f5fSchristos best_type = v; 50142542f5fSchristos } 50242542f5fSchristos } 50342542f5fSchristos } 50442542f5fSchristos closedir(dir); 50542542f5fSchristos 50642542f5fSchristos return best_iface; 50742542f5fSchristos} 508