sync code with last improvements from OpenBSD
This commit is contained in:
commit
88965415ff
26235 changed files with 29195616 additions and 0 deletions
507
driver/xf86-video-intel/src/backlight.c
Normal file
507
driver/xf86-video-intel/src/backlight.c
Normal file
|
@ -0,0 +1,507 @@
|
|||
/***************************************************************************
|
||||
|
||||
Copyright 2014 Intel Corporation. All Rights Reserved.
|
||||
Copyright 2014 Red Hat, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sub license, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the
|
||||
next paragraph) shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
**************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <xorg-server.h>
|
||||
#include <xf86.h>
|
||||
#include <pciaccess.h>
|
||||
|
||||
#include "backlight.h"
|
||||
#include "fd.h"
|
||||
|
||||
#define BACKLIGHT_CLASS "/sys/class/backlight"
|
||||
|
||||
/* Enough for 10 digits of backlight + '\n' + '\0' */
|
||||
#define BACKLIGHT_VALUE_LEN 12
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Unfortunately this is not as simple as I would like it to be. If selinux is
|
||||
* dropping dbus messages pkexec may block *forever*.
|
||||
*
|
||||
* Backgrounding pkexec by doing System("pkexec ...&") does not work because
|
||||
* that detaches pkexec from its parent at which point its security checks
|
||||
* fail and it refuses to execute the helper.
|
||||
*
|
||||
* So we're left with spawning a helper child which gets levels to set written
|
||||
* to it through a pipe. This turns the blocking forever problem from a hung
|
||||
* machine problem into a simple backlight control not working problem.
|
||||
*
|
||||
* If only things were as simple as on OpenBSD! :)
|
||||
*/
|
||||
|
||||
void backlight_init(struct backlight *b)
|
||||
{
|
||||
b->type = BL_NONE;
|
||||
b->iface = NULL;
|
||||
b->fd = -1;
|
||||
b->pid = -1;
|
||||
b->max = -1;
|
||||
}
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
|
||||
#include <dev/wscons/wsconsio.h>
|
||||
#include <xf86Priv.h>
|
||||
|
||||
int backlight_set(struct backlight *b, int level)
|
||||
{
|
||||
struct wsdisplay_param param;
|
||||
|
||||
if (b->iface == NULL)
|
||||
return -1;
|
||||
|
||||
if ((unsigned)level > b->max)
|
||||
level = b->max;
|
||||
|
||||
memset(¶m, 0, sizeof(param));
|
||||
param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
|
||||
param.curval = level;
|
||||
|
||||
return ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, ¶m);
|
||||
}
|
||||
|
||||
int backlight_get(struct backlight *b)
|
||||
{
|
||||
struct wsdisplay_param param;
|
||||
|
||||
if (b->iface == NULL)
|
||||
return -1;
|
||||
|
||||
memset(¶m, 0, sizeof(param));
|
||||
param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
|
||||
|
||||
if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m))
|
||||
return -1;
|
||||
|
||||
return param.curval;
|
||||
}
|
||||
|
||||
int backlight_open(struct backlight *b, char *iface)
|
||||
{
|
||||
struct wsdisplay_param param;
|
||||
|
||||
if (iface != NULL)
|
||||
return -1;
|
||||
|
||||
memset(¶m, 0, sizeof(param));
|
||||
param.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
|
||||
|
||||
if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, ¶m) == -1)
|
||||
return -1;
|
||||
|
||||
b->iface = strdup("wscons");
|
||||
if (b->iface == NULL)
|
||||
return -1;
|
||||
|
||||
b->max = param.max;
|
||||
b->fd = -1;
|
||||
b->type = BL_PLATFORM;
|
||||
|
||||
return param.curval;
|
||||
}
|
||||
|
||||
enum backlight_type backlight_exists(const char *iface)
|
||||
{
|
||||
if (iface != NULL)
|
||||
return BL_NONE;
|
||||
|
||||
return BL_PLATFORM;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int
|
||||
is_sysfs_fd(int fd)
|
||||
{
|
||||
struct stat st;
|
||||
return fstat(fd, &st) == 0 && major(st.st_dev) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
__backlight_open(const char *iface, const char *file, int mode)
|
||||
{
|
||||
char buf[1024];
|
||||
int fd;
|
||||
|
||||
snprintf(buf, sizeof(buf), BACKLIGHT_CLASS "/%s/%s", iface, file);
|
||||
fd = open(buf, mode);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
if (!is_sysfs_fd(fd)) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int
|
||||
__backlight_read(const char *iface, const char *file)
|
||||
{
|
||||
char buf[BACKLIGHT_VALUE_LEN];
|
||||
int fd, val;
|
||||
|
||||
fd = __backlight_open(iface, file, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
val = read(fd, buf, BACKLIGHT_VALUE_LEN - 1);
|
||||
if (val > 0) {
|
||||
buf[val] = '\0';
|
||||
val = atoi(buf);
|
||||
} else
|
||||
val = -1;
|
||||
close(fd);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/* List of available kernel interfaces in priority order */
|
||||
static const char *known_interfaces[] = {
|
||||
"dell_backlight",
|
||||
"gmux_backlight",
|
||||
"asus-laptop",
|
||||
"asus-nb-wmi",
|
||||
"eeepc",
|
||||
"thinkpad_screen",
|
||||
"mbp_backlight",
|
||||
"fujitsu-laptop",
|
||||
"sony",
|
||||
"samsung",
|
||||
"acpi_video1",
|
||||
"acpi_video0",
|
||||
"intel_backlight",
|
||||
};
|
||||
|
||||
static enum backlight_type __backlight_type(const char *iface)
|
||||
{
|
||||
char buf[1024];
|
||||
int fd, v;
|
||||
|
||||
v = -1;
|
||||
fd = __backlight_open(iface, "type", O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
v = read(fd, buf, sizeof(buf)-1);
|
||||
close(fd);
|
||||
}
|
||||
if (v > 0) {
|
||||
while (v > 0 && isspace(buf[v-1]))
|
||||
v--;
|
||||
buf[v] = '\0';
|
||||
|
||||
if (strcmp(buf, "raw") == 0)
|
||||
v = BL_RAW;
|
||||
else if (strcmp(buf, "platform") == 0)
|
||||
v = BL_PLATFORM;
|
||||
else if (strcmp(buf, "firmware") == 0)
|
||||
v = BL_FIRMWARE;
|
||||
else
|
||||
v = BL_NAMED;
|
||||
} else
|
||||
v = BL_NAMED;
|
||||
|
||||
if (v == BL_NAMED) {
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(known_interfaces); i++) {
|
||||
if (strcmp(iface, known_interfaces[i]) == 0)
|
||||
break;
|
||||
}
|
||||
v += i;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
enum backlight_type backlight_exists(const char *iface)
|
||||
{
|
||||
if (__backlight_read(iface, "brightness") < 0)
|
||||
return BL_NONE;
|
||||
|
||||
if (__backlight_read(iface, "max_brightness") <= 0)
|
||||
return BL_NONE;
|
||||
|
||||
return __backlight_type(iface);
|
||||
}
|
||||
|
||||
static int __backlight_init(struct backlight *b, char *iface, int fd)
|
||||
{
|
||||
b->fd = fd_move_cloexec(fd_set_nonblock(fd));
|
||||
b->iface = iface;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __backlight_direct_init(struct backlight *b, char *iface)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = __backlight_open(iface, "brightness", O_RDWR);
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
|
||||
return __backlight_init(b, iface, fd);
|
||||
}
|
||||
|
||||
static int __backlight_helper_init(struct backlight *b, char *iface)
|
||||
{
|
||||
#if USE_BACKLIGHT_HELPER
|
||||
struct stat st;
|
||||
char *env[] = { NULL };
|
||||
int use_pkexec = 0;
|
||||
int fds[2];
|
||||
|
||||
/*
|
||||
* Some systems may prefer using PolicyKit's pkexec over
|
||||
* making the helper suid root, since the suid option will allow
|
||||
* anyone to control the backlight. However, as pkexec
|
||||
* is quite troublesome and not universally available, we
|
||||
* still try the old fashioned and simple method first.
|
||||
* Either way, we have to trust that it is our backlight-helper
|
||||
* that is run and that we have scrutinised it carefully.
|
||||
*/
|
||||
if (stat(LIBEXEC_PATH "/xf86-video-intel-backlight-helper", &st))
|
||||
return 0;
|
||||
|
||||
if ((st.st_mode & (S_IFREG | S_ISUID | S_IXUSR)) != (S_IFREG | S_ISUID | S_IXUSR)) {
|
||||
if (System("pkexec --version"))
|
||||
return 0;
|
||||
|
||||
use_pkexec = 1;
|
||||
}
|
||||
|
||||
if (pipe(fds))
|
||||
return 0;
|
||||
|
||||
switch ((b->pid = fork())) {
|
||||
case 0:
|
||||
if (setgid(getgid()) || setuid(getuid()))
|
||||
_exit(127);
|
||||
|
||||
close(fds[1]);
|
||||
if (dup2(fds[0], 0))
|
||||
_exit(127);
|
||||
close(fds[0]);
|
||||
|
||||
if (use_pkexec) {
|
||||
execlp("pkexec", "pkexec",
|
||||
LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
|
||||
iface, (char *)0);
|
||||
} else {
|
||||
execle(LIBEXEC_PATH "/xf86-video-intel-backlight-helper",
|
||||
"xf86-video-intel-backlight-helper",
|
||||
iface, (char *)0, env);
|
||||
}
|
||||
_exit(1);
|
||||
/* unreachable fallthrough */
|
||||
case -1:
|
||||
close(fds[1]);
|
||||
close(fds[0]);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
close(fds[0]);
|
||||
return __backlight_init(b, iface, fds[1]);
|
||||
}
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static char *
|
||||
__backlight_find(void)
|
||||
{
|
||||
char *best_iface = NULL;
|
||||
unsigned best_type = INT_MAX;
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
|
||||
dir = opendir(BACKLIGHT_CLASS);
|
||||
if (dir == NULL)
|
||||
return NULL;
|
||||
|
||||
while ((de = readdir(dir))) {
|
||||
int v;
|
||||
|
||||
if (*de->d_name == '.')
|
||||
continue;
|
||||
|
||||
/* Fallback to priority list of known iface for old kernels */
|
||||
v = backlight_exists(de->d_name);
|
||||
if (v < best_type) {
|
||||
char *copy = strdup(de->d_name);
|
||||
if (copy) {
|
||||
free(best_iface);
|
||||
best_iface = copy;
|
||||
best_type = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
return best_iface;
|
||||
}
|
||||
|
||||
int backlight_open(struct backlight *b, char *iface)
|
||||
{
|
||||
int level;
|
||||
|
||||
if (iface == NULL)
|
||||
iface = __backlight_find();
|
||||
if (iface == NULL)
|
||||
goto err;
|
||||
|
||||
b->type = __backlight_type(iface);
|
||||
|
||||
b->max = __backlight_read(iface, "max_brightness");
|
||||
if (b->max <= 0)
|
||||
goto err;
|
||||
|
||||
level = __backlight_read(iface, "brightness");
|
||||
if (level < 0)
|
||||
goto err;
|
||||
|
||||
if (!__backlight_direct_init(b, iface) &&
|
||||
!__backlight_helper_init(b, iface))
|
||||
goto err;
|
||||
|
||||
return level;
|
||||
|
||||
err:
|
||||
backlight_init(b);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int backlight_set(struct backlight *b, int level)
|
||||
{
|
||||
char val[BACKLIGHT_VALUE_LEN];
|
||||
int len, ret = 0;
|
||||
|
||||
if (b->iface == NULL)
|
||||
return 0;
|
||||
|
||||
if ((unsigned)level > b->max)
|
||||
level = b->max;
|
||||
|
||||
len = snprintf(val, BACKLIGHT_VALUE_LEN, "%d\n", level);
|
||||
if (write(b->fd, val, len) != len)
|
||||
ret = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int backlight_get(struct backlight *b)
|
||||
{
|
||||
int level;
|
||||
|
||||
if (b->iface == NULL)
|
||||
return -1;
|
||||
|
||||
level = __backlight_read(b->iface, "brightness");
|
||||
if (level > b->max)
|
||||
level = b->max;
|
||||
else if (level < 0)
|
||||
level = -1;
|
||||
return level;
|
||||
}
|
||||
#endif
|
||||
|
||||
void backlight_disable(struct backlight *b)
|
||||
{
|
||||
if (b->iface == NULL)
|
||||
return;
|
||||
|
||||
if (b->fd != -1)
|
||||
close(b->fd);
|
||||
|
||||
free(b->iface);
|
||||
b->iface = NULL;
|
||||
}
|
||||
|
||||
void backlight_close(struct backlight *b)
|
||||
{
|
||||
backlight_disable(b);
|
||||
if (b->pid)
|
||||
waitpid(b->pid, NULL, 0);
|
||||
}
|
||||
|
||||
char *backlight_find_for_device(struct pci_device *pci)
|
||||
{
|
||||
char path[200];
|
||||
unsigned best_type = INT_MAX;
|
||||
char *best_iface = NULL;
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
|
||||
snprintf(path, sizeof(path),
|
||||
"/sys/bus/pci/devices/%04x:%02x:%02x.%d/backlight",
|
||||
pci->domain, pci->bus, pci->dev, pci->func);
|
||||
|
||||
dir = opendir(path);
|
||||
if (dir == NULL)
|
||||
return NULL;
|
||||
|
||||
while ((de = readdir(dir))) {
|
||||
int v;
|
||||
|
||||
if (*de->d_name == '.')
|
||||
continue;
|
||||
|
||||
v = backlight_exists(de->d_name);
|
||||
if (v < best_type) {
|
||||
char *copy = strdup(de->d_name);
|
||||
if (copy) {
|
||||
free(best_iface);
|
||||
best_iface = copy;
|
||||
best_type = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
return best_iface;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue