backlight.c revision 813957e3
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; 84813957e3Ssnj b->has_power = 0; 8542542f5fSchristos} 8642542f5fSchristos 879a906b70Schristos#if defined(__OpenBSD__) || defined(__NetBSD__) 8842542f5fSchristos 8942542f5fSchristos#include <dev/wscons/wsconsio.h> 9042542f5fSchristos#include <xf86Priv.h> 9142542f5fSchristos 9242542f5fSchristosint backlight_set(struct backlight *b, int level) 9342542f5fSchristos{ 9442542f5fSchristos struct wsdisplay_param param; 9542542f5fSchristos 9642542f5fSchristos if (b->iface == NULL) 9742542f5fSchristos return -1; 9842542f5fSchristos 9942542f5fSchristos if ((unsigned)level > b->max) 10042542f5fSchristos level = b->max; 10142542f5fSchristos 10242542f5fSchristos memset(¶m, 0, sizeof(param)); 10342542f5fSchristos param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 10442542f5fSchristos param.curval = level; 10542542f5fSchristos 10642542f5fSchristos return ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, ¶m); 10742542f5fSchristos} 10842542f5fSchristos 10942542f5fSchristosint backlight_get(struct backlight *b) 11042542f5fSchristos{ 11142542f5fSchristos struct wsdisplay_param param; 11242542f5fSchristos 11342542f5fSchristos if (b->iface == NULL) 11442542f5fSchristos return -1; 11542542f5fSchristos 11642542f5fSchristos memset(¶m, 0, sizeof(param)); 11742542f5fSchristos param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 11842542f5fSchristos 11942542f5fSchristos if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m)) 12042542f5fSchristos return -1; 12142542f5fSchristos 12242542f5fSchristos return param.curval; 12342542f5fSchristos} 12442542f5fSchristos 12542542f5fSchristosint backlight_open(struct backlight *b, char *iface) 12642542f5fSchristos{ 12742542f5fSchristos struct wsdisplay_param param; 12842542f5fSchristos 12942542f5fSchristos if (iface != NULL) 13042542f5fSchristos return -1; 13142542f5fSchristos 13242542f5fSchristos memset(¶m, 0, sizeof(param)); 13342542f5fSchristos param.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 13442542f5fSchristos 13542542f5fSchristos if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m) == -1) 13642542f5fSchristos return -1; 13742542f5fSchristos 13842542f5fSchristos b->iface = strdup("wscons"); 13942542f5fSchristos if (b->iface == NULL) 14042542f5fSchristos return -1; 14142542f5fSchristos 14242542f5fSchristos b->max = param.max; 14342542f5fSchristos b->fd = -1; 14442542f5fSchristos b->type = BL_PLATFORM; 14542542f5fSchristos 14642542f5fSchristos return param.curval; 14742542f5fSchristos} 14842542f5fSchristos 14942542f5fSchristosenum backlight_type backlight_exists(const char *iface) 15042542f5fSchristos{ 15142542f5fSchristos if (iface != NULL) 15242542f5fSchristos return BL_NONE; 15342542f5fSchristos 15442542f5fSchristos return BL_PLATFORM; 15542542f5fSchristos} 15642542f5fSchristos 157813957e3Ssnjint backlight_on(struct backlight *b) 158813957e3Ssnj{ 159813957e3Ssnj return 0; 160813957e3Ssnj} 161813957e3Ssnj 162813957e3Ssnjint backlight_off(struct backlight *b) 163813957e3Ssnj{ 164813957e3Ssnj return 0; 165813957e3Ssnj} 16642542f5fSchristos#else 16742542f5fSchristos 16842542f5fSchristosstatic int 16942542f5fSchristosis_sysfs_fd(int fd) 17042542f5fSchristos{ 17142542f5fSchristos struct stat st; 17242542f5fSchristos return fstat(fd, &st) == 0 && major(st.st_dev) == 0; 17342542f5fSchristos} 17442542f5fSchristos 17542542f5fSchristosstatic int 17642542f5fSchristos__backlight_open(const char *iface, const char *file, int mode) 17742542f5fSchristos{ 17842542f5fSchristos char buf[1024]; 17942542f5fSchristos int fd; 18042542f5fSchristos 18142542f5fSchristos snprintf(buf, sizeof(buf), BACKLIGHT_CLASS "/%s/%s", iface, file); 18242542f5fSchristos fd = open(buf, mode); 18342542f5fSchristos if (fd == -1) 18442542f5fSchristos return -1; 18542542f5fSchristos 18642542f5fSchristos if (!is_sysfs_fd(fd)) { 18742542f5fSchristos close(fd); 18842542f5fSchristos return -1; 18942542f5fSchristos } 19042542f5fSchristos 19142542f5fSchristos return fd; 19242542f5fSchristos} 19342542f5fSchristos 19442542f5fSchristosstatic int 19542542f5fSchristos__backlight_read(const char *iface, const char *file) 19642542f5fSchristos{ 19742542f5fSchristos char buf[BACKLIGHT_VALUE_LEN]; 19842542f5fSchristos int fd, val; 19942542f5fSchristos 20042542f5fSchristos fd = __backlight_open(iface, file, O_RDONLY); 20142542f5fSchristos if (fd < 0) 20242542f5fSchristos return -1; 20342542f5fSchristos 20442542f5fSchristos val = read(fd, buf, BACKLIGHT_VALUE_LEN - 1); 20542542f5fSchristos if (val > 0) { 20642542f5fSchristos buf[val] = '\0'; 20742542f5fSchristos val = atoi(buf); 20842542f5fSchristos } else 20942542f5fSchristos val = -1; 21042542f5fSchristos close(fd); 21142542f5fSchristos 21242542f5fSchristos return val; 21342542f5fSchristos} 21442542f5fSchristos 215813957e3Ssnjstatic int 216813957e3Ssnj__backlight_write(const char *iface, const char *file, const char *value) 217813957e3Ssnj{ 218813957e3Ssnj int fd, ret; 219813957e3Ssnj 220813957e3Ssnj fd = __backlight_open(iface, file, O_WRONLY); 221813957e3Ssnj if (fd < 0) 222813957e3Ssnj return -1; 223813957e3Ssnj 224813957e3Ssnj ret = write(fd, value, strlen(value)+1); 225813957e3Ssnj close(fd); 226813957e3Ssnj 227813957e3Ssnj return ret; 228813957e3Ssnj} 229813957e3Ssnj 23042542f5fSchristos/* List of available kernel interfaces in priority order */ 23142542f5fSchristosstatic const char *known_interfaces[] = { 23242542f5fSchristos "dell_backlight", 23342542f5fSchristos "gmux_backlight", 23442542f5fSchristos "asus-laptop", 23542542f5fSchristos "asus-nb-wmi", 23642542f5fSchristos "eeepc", 23742542f5fSchristos "thinkpad_screen", 23842542f5fSchristos "mbp_backlight", 23942542f5fSchristos "fujitsu-laptop", 24042542f5fSchristos "sony", 24142542f5fSchristos "samsung", 24242542f5fSchristos "acpi_video1", 24342542f5fSchristos "acpi_video0", 24442542f5fSchristos "intel_backlight", 24542542f5fSchristos}; 24642542f5fSchristos 24742542f5fSchristosstatic enum backlight_type __backlight_type(const char *iface) 24842542f5fSchristos{ 24942542f5fSchristos char buf[1024]; 25042542f5fSchristos int fd, v; 25142542f5fSchristos 25242542f5fSchristos v = -1; 25342542f5fSchristos fd = __backlight_open(iface, "type", O_RDONLY); 25442542f5fSchristos if (fd >= 0) { 25542542f5fSchristos v = read(fd, buf, sizeof(buf)-1); 25642542f5fSchristos close(fd); 25742542f5fSchristos } 25842542f5fSchristos if (v > 0) { 25942542f5fSchristos while (v > 0 && isspace(buf[v-1])) 26042542f5fSchristos v--; 26142542f5fSchristos buf[v] = '\0'; 26242542f5fSchristos 26342542f5fSchristos if (strcmp(buf, "raw") == 0) 26442542f5fSchristos v = BL_RAW; 26542542f5fSchristos else if (strcmp(buf, "platform") == 0) 26642542f5fSchristos v = BL_PLATFORM; 26742542f5fSchristos else if (strcmp(buf, "firmware") == 0) 26842542f5fSchristos v = BL_FIRMWARE; 26942542f5fSchristos else 27042542f5fSchristos v = BL_NAMED; 27142542f5fSchristos } else 27242542f5fSchristos v = BL_NAMED; 27342542f5fSchristos 27442542f5fSchristos if (v == BL_NAMED) { 27542542f5fSchristos int i; 27642542f5fSchristos for (i = 0; i < ARRAY_SIZE(known_interfaces); i++) { 27742542f5fSchristos if (strcmp(iface, known_interfaces[i]) == 0) 27842542f5fSchristos break; 27942542f5fSchristos } 28042542f5fSchristos v += i; 28142542f5fSchristos } 28242542f5fSchristos 28342542f5fSchristos return v; 28442542f5fSchristos} 28542542f5fSchristos 28642542f5fSchristosenum backlight_type backlight_exists(const char *iface) 28742542f5fSchristos{ 28842542f5fSchristos if (__backlight_read(iface, "brightness") < 0) 28942542f5fSchristos return BL_NONE; 29042542f5fSchristos 29142542f5fSchristos if (__backlight_read(iface, "max_brightness") <= 0) 29242542f5fSchristos return BL_NONE; 29342542f5fSchristos 29442542f5fSchristos return __backlight_type(iface); 29542542f5fSchristos} 29642542f5fSchristos 29742542f5fSchristosstatic int __backlight_init(struct backlight *b, char *iface, int fd) 29842542f5fSchristos{ 29942542f5fSchristos b->fd = fd_move_cloexec(fd_set_nonblock(fd)); 30042542f5fSchristos b->iface = iface; 30142542f5fSchristos return 1; 30242542f5fSchristos} 30342542f5fSchristos 30442542f5fSchristosstatic int __backlight_direct_init(struct backlight *b, char *iface) 30542542f5fSchristos{ 30642542f5fSchristos int fd; 30742542f5fSchristos 30842542f5fSchristos fd = __backlight_open(iface, "brightness", O_RDWR); 30942542f5fSchristos if (fd < 0) 31042542f5fSchristos return 0; 31142542f5fSchristos 312813957e3Ssnj if (__backlight_read(iface, "bl_power") != -1) 313813957e3Ssnj b->has_power = 1; 314813957e3Ssnj 31542542f5fSchristos return __backlight_init(b, iface, fd); 31642542f5fSchristos} 31742542f5fSchristos 31842542f5fSchristosstatic int __backlight_helper_init(struct backlight *b, char *iface) 31942542f5fSchristos{ 32042542f5fSchristos#if USE_BACKLIGHT_HELPER 32142542f5fSchristos struct stat st; 32242542f5fSchristos char *env[] = { NULL }; 32342542f5fSchristos int use_pkexec = 0; 32442542f5fSchristos int fds[2]; 32542542f5fSchristos 32642542f5fSchristos /* 32742542f5fSchristos * Some systems may prefer using PolicyKit's pkexec over 32842542f5fSchristos * making the helper suid root, since the suid option will allow 32942542f5fSchristos * anyone to control the backlight. However, as pkexec 33042542f5fSchristos * is quite troublesome and not universally available, we 33142542f5fSchristos * still try the old fashioned and simple method first. 33242542f5fSchristos * Either way, we have to trust that it is our backlight-helper 33342542f5fSchristos * that is run and that we have scrutinised it carefully. 33442542f5fSchristos */ 33542542f5fSchristos if (stat(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", &st)) 33642542f5fSchristos return 0; 33742542f5fSchristos 33842542f5fSchristos if ((st.st_mode & (S_IFREG | S_ISUID | S_IXUSR)) != (S_IFREG | S_ISUID | S_IXUSR)) { 33942542f5fSchristos if (System("pkexec --version")) 34042542f5fSchristos return 0; 34142542f5fSchristos 34242542f5fSchristos use_pkexec = 1; 34342542f5fSchristos } 34442542f5fSchristos 34542542f5fSchristos if (pipe(fds)) 34642542f5fSchristos return 0; 34742542f5fSchristos 34842542f5fSchristos switch ((b->pid = fork())) { 34942542f5fSchristos case 0: 35042542f5fSchristos if (setgid(getgid()) || setuid(getuid())) 35142542f5fSchristos _exit(127); 35242542f5fSchristos 35342542f5fSchristos close(fds[1]); 35442542f5fSchristos if (dup2(fds[0], 0)) 35542542f5fSchristos _exit(127); 35642542f5fSchristos close(fds[0]); 35742542f5fSchristos 35842542f5fSchristos if (use_pkexec) { 35942542f5fSchristos execlp("pkexec", "pkexec", 36042542f5fSchristos LIBEXEC_PATH "/xf86-video-intel-backlight-helper", 36142542f5fSchristos iface, (char *)0); 36242542f5fSchristos } else { 36342542f5fSchristos execle(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", 36442542f5fSchristos "xf86-video-intel-backlight-helper", 36542542f5fSchristos iface, (char *)0, env); 36642542f5fSchristos } 36742542f5fSchristos _exit(1); 36842542f5fSchristos /* unreachable fallthrough */ 36942542f5fSchristos case -1: 37042542f5fSchristos close(fds[1]); 37142542f5fSchristos close(fds[0]); 37242542f5fSchristos return 0; 37342542f5fSchristos 37442542f5fSchristos default: 37542542f5fSchristos close(fds[0]); 37642542f5fSchristos return __backlight_init(b, iface, fds[1]); 37742542f5fSchristos } 37842542f5fSchristos#else 37942542f5fSchristos return 0; 38042542f5fSchristos#endif 38142542f5fSchristos} 38242542f5fSchristos 38342542f5fSchristosstatic char * 38442542f5fSchristos__backlight_find(void) 38542542f5fSchristos{ 38642542f5fSchristos char *best_iface = NULL; 38742542f5fSchristos unsigned best_type = INT_MAX; 38842542f5fSchristos DIR *dir; 38942542f5fSchristos struct dirent *de; 39042542f5fSchristos 39142542f5fSchristos dir = opendir(BACKLIGHT_CLASS); 39242542f5fSchristos if (dir == NULL) 39342542f5fSchristos return NULL; 39442542f5fSchristos 39542542f5fSchristos while ((de = readdir(dir))) { 39642542f5fSchristos int v; 39742542f5fSchristos 39842542f5fSchristos if (*de->d_name == '.') 39942542f5fSchristos continue; 40042542f5fSchristos 40142542f5fSchristos /* Fallback to priority list of known iface for old kernels */ 40242542f5fSchristos v = backlight_exists(de->d_name); 40342542f5fSchristos if (v < best_type) { 40442542f5fSchristos char *copy = strdup(de->d_name); 40542542f5fSchristos if (copy) { 40642542f5fSchristos free(best_iface); 40742542f5fSchristos best_iface = copy; 40842542f5fSchristos best_type = v; 40942542f5fSchristos } 41042542f5fSchristos } 41142542f5fSchristos } 41242542f5fSchristos closedir(dir); 41342542f5fSchristos 41442542f5fSchristos return best_iface; 41542542f5fSchristos} 41642542f5fSchristos 41742542f5fSchristosint backlight_open(struct backlight *b, char *iface) 41842542f5fSchristos{ 41942542f5fSchristos int level; 42042542f5fSchristos 42142542f5fSchristos if (iface == NULL) 42242542f5fSchristos iface = __backlight_find(); 42342542f5fSchristos if (iface == NULL) 42442542f5fSchristos goto err; 42542542f5fSchristos 42642542f5fSchristos b->type = __backlight_type(iface); 42742542f5fSchristos 42842542f5fSchristos b->max = __backlight_read(iface, "max_brightness"); 42942542f5fSchristos if (b->max <= 0) 43042542f5fSchristos goto err; 43142542f5fSchristos 43242542f5fSchristos level = __backlight_read(iface, "brightness"); 43342542f5fSchristos if (level < 0) 43442542f5fSchristos goto err; 43542542f5fSchristos 43642542f5fSchristos if (!__backlight_direct_init(b, iface) && 43742542f5fSchristos !__backlight_helper_init(b, iface)) 43842542f5fSchristos goto err; 43942542f5fSchristos 44042542f5fSchristos return level; 44142542f5fSchristos 44242542f5fSchristoserr: 44342542f5fSchristos backlight_init(b); 44442542f5fSchristos return -1; 44542542f5fSchristos} 44642542f5fSchristos 44742542f5fSchristosint backlight_set(struct backlight *b, int level) 44842542f5fSchristos{ 44942542f5fSchristos char val[BACKLIGHT_VALUE_LEN]; 45042542f5fSchristos int len, ret = 0; 45142542f5fSchristos 45242542f5fSchristos if (b->iface == NULL) 45342542f5fSchristos return 0; 45442542f5fSchristos 45542542f5fSchristos if ((unsigned)level > b->max) 45642542f5fSchristos level = b->max; 45742542f5fSchristos 45842542f5fSchristos len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level); 45942542f5fSchristos if (write(b->fd, val, len) != len) 46042542f5fSchristos ret = -1; 46142542f5fSchristos 46242542f5fSchristos return ret; 46342542f5fSchristos} 46442542f5fSchristos 46542542f5fSchristosint backlight_get(struct backlight *b) 46642542f5fSchristos{ 46742542f5fSchristos int level; 46842542f5fSchristos 46942542f5fSchristos if (b->iface == NULL) 47042542f5fSchristos return -1; 47142542f5fSchristos 47242542f5fSchristos level = __backlight_read(b->iface, "brightness"); 47342542f5fSchristos if (level > b->max) 47442542f5fSchristos level = b->max; 47542542f5fSchristos else if (level < 0) 47642542f5fSchristos level = -1; 47742542f5fSchristos return level; 47842542f5fSchristos} 479813957e3Ssnj 480813957e3Ssnjint backlight_off(struct backlight *b) 481813957e3Ssnj{ 482813957e3Ssnj if (b->iface == NULL) 483813957e3Ssnj return 0; 484813957e3Ssnj 485813957e3Ssnj if (!b->has_power) 486813957e3Ssnj return 0; 487813957e3Ssnj 488813957e3Ssnj /* 4 -> FB_BLANK_POWERDOWN */ 489813957e3Ssnj return __backlight_write(b->iface, "bl_power", "4"); 490813957e3Ssnj} 491813957e3Ssnj 492813957e3Ssnjint backlight_on(struct backlight *b) 493813957e3Ssnj{ 494813957e3Ssnj if (b->iface == NULL) 495813957e3Ssnj return 0; 496813957e3Ssnj 497813957e3Ssnj if (!b->has_power) 498813957e3Ssnj return 0; 499813957e3Ssnj 500813957e3Ssnj /* 0 -> FB_BLANK_UNBLANK */ 501813957e3Ssnj return __backlight_write(b->iface, "bl_power", "0"); 502813957e3Ssnj} 50342542f5fSchristos#endif 50442542f5fSchristos 50542542f5fSchristosvoid backlight_disable(struct backlight *b) 50642542f5fSchristos{ 50742542f5fSchristos if (b->iface == NULL) 50842542f5fSchristos return; 50942542f5fSchristos 51042542f5fSchristos if (b->fd != -1) 51142542f5fSchristos close(b->fd); 51242542f5fSchristos 51342542f5fSchristos free(b->iface); 51442542f5fSchristos b->iface = NULL; 51542542f5fSchristos} 51642542f5fSchristos 51742542f5fSchristosvoid backlight_close(struct backlight *b) 51842542f5fSchristos{ 51942542f5fSchristos backlight_disable(b); 52042542f5fSchristos if (b->pid) 52142542f5fSchristos waitpid(b->pid, NULL, 0); 52242542f5fSchristos} 52342542f5fSchristos 52442542f5fSchristoschar *backlight_find_for_device(struct pci_device *pci) 52542542f5fSchristos{ 52642542f5fSchristos char path[200]; 52742542f5fSchristos unsigned best_type = INT_MAX; 52842542f5fSchristos char *best_iface = NULL; 52942542f5fSchristos DIR *dir; 53042542f5fSchristos struct dirent *de; 53142542f5fSchristos 53242542f5fSchristos snprintf(path, sizeof(path), 53342542f5fSchristos "/sys/bus/pci/devices/%04x:%02x:%02x.%d/backlight", 53442542f5fSchristos pci->domain, pci->bus, pci->dev, pci->func); 53542542f5fSchristos 53642542f5fSchristos dir = opendir(path); 53742542f5fSchristos if (dir == NULL) 53842542f5fSchristos return NULL; 53942542f5fSchristos 54042542f5fSchristos while ((de = readdir(dir))) { 54142542f5fSchristos int v; 54242542f5fSchristos 54342542f5fSchristos if (*de->d_name == '.') 54442542f5fSchristos continue; 54542542f5fSchristos 54642542f5fSchristos v = backlight_exists(de->d_name); 54742542f5fSchristos if (v < best_type) { 54842542f5fSchristos char *copy = strdup(de->d_name); 54942542f5fSchristos if (copy) { 55042542f5fSchristos free(best_iface); 55142542f5fSchristos best_iface = copy; 55242542f5fSchristos best_type = v; 55342542f5fSchristos } 55442542f5fSchristos } 55542542f5fSchristos } 55642542f5fSchristos closedir(dir); 55742542f5fSchristos 55842542f5fSchristos return best_iface; 55942542f5fSchristos} 560