diff options
Diffstat (limited to 'src/libbacklight.c')
-rw-r--r-- | src/libbacklight.c | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/src/libbacklight.c b/src/libbacklight.c new file mode 100644 index 00000000..b3acc63f --- /dev/null +++ b/src/libbacklight.c @@ -0,0 +1,309 @@ +/* + * libbacklight - userspace interface to Linux backlight control + * + * Copyright © 2012 Intel Corporation + * Copyright 2010 Red Hat <mjg@redhat.com> + * + * 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, sublicense, + * 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 + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS 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. + * + * Authors: + * Matthew Garrett <mjg@redhat.com> + * Tiago Vignatti <vignatti@freedesktop.org> + */ + +#include "config.h" + +#include "libbacklight.h" +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <linux/types.h> +#include <dirent.h> +#include <drm.h> +#include <fcntl.h> +#include <malloc.h> +#include <string.h> +#include <errno.h> + +static long backlight_get(struct backlight *backlight, char *node) +{ + char buffer[100]; + char *path; + int fd; + long value, ret; + + if (asprintf(&path, "%s/%s", backlight->path, node) < 0) + return -ENOMEM +; + fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -1; + goto out; + } + + ret = read(fd, &buffer, sizeof(buffer)); + if (ret < 1) { + ret = -1; + goto out; + } + + value = strtol(buffer, NULL, 10); + ret = value; +out: + if (fd >= 0) + close(fd); + free(path); + return ret; +} + +long backlight_get_brightness(struct backlight *backlight) +{ + return backlight_get(backlight, "brightness"); +} + +long backlight_get_max_brightness(struct backlight *backlight) +{ + return backlight_get(backlight, "max_brightness"); +} + +long backlight_get_actual_brightness(struct backlight *backlight) +{ + return backlight_get(backlight, "actual_brightness"); +} + +long backlight_set_brightness(struct backlight *backlight, long brightness) +{ + char *path; + char *buffer = NULL; + int fd; + long ret; + + if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0) + return -ENOMEM; + + fd = open(path, O_RDWR); + if (fd < 0) { + ret = -1; + goto out; + } + + ret = read(fd, &buffer, sizeof(buffer)); + if (ret < 1) { + ret = -1; + goto out; + } + + if (asprintf(&buffer, "%ld", brightness) < 0) { + ret = -1; + goto out; + } + + ret = write(fd, buffer, strlen(buffer)); + if (ret < 0) { + ret = -1; + goto out; + } + + ret = backlight_get_brightness(backlight); + backlight->brightness = ret; +out: + free(buffer); + free(path); + if (fd >= 0) + close(fd); + return ret; +} + +void backlight_destroy(struct backlight *backlight) +{ + if (!backlight) + return; + + if (backlight->path) + free(backlight->path); + + free(backlight); +} + +struct backlight *backlight_init(struct udev_device *drm_device, + uint32_t connector_type) +{ + const char *syspath = NULL; + char *pci_name = NULL; + char *chosen_path = NULL; + char *path = NULL; + DIR *backlights = NULL; + struct dirent *entry; + enum backlight_type type = 0; + char buffer[100]; + struct backlight *backlight = NULL; + int ret; + + if (!drm_device) + return NULL; + + syspath = udev_device_get_syspath(drm_device); + if (!syspath) + return NULL; + + if (asprintf(&path, "%s/%s", syspath, "device") < 0) + return NULL; + + ret = readlink(path, buffer, sizeof(buffer) - 1); + free(path); + if (ret < 0) + return NULL; + + buffer[ret] = '\0'; + pci_name = basename(buffer); + + if (connector_type <= 0) + return NULL; + + backlights = opendir("/sys/class/backlight"); + if (!backlights) + return NULL; + + /* Find the "best" backlight for the device. Firmware + interfaces are preferred over platform interfaces are + preferred over raw interfaces. For raw interfaces we'll + check if the device ID in the form of pci match, while + for firmware interfaces we require the pci ID to + match. It's assumed that platform interfaces always match, + since we can't actually associate them with IDs. + + A further awkwardness is that, while it's theoretically + possible for an ACPI interface to include support for + changing the backlight of external devices, it's unlikely + to ever be done. It's effectively impossible for a platform + interface to do so. So if we get asked about anything that + isn't LVDS or eDP, we pretty much have to require that the + control be supplied via a raw interface */ + + while ((entry = readdir(backlights))) { + char *backlight_path; + char *parent; + enum backlight_type entry_type; + int fd; + + if (entry->d_name[0] == '.') + continue; + + if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight", + entry->d_name) < 0) + goto err; + + if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) + goto err; + + fd = open(path, O_RDONLY); + + if (fd < 0) + goto out; + + ret = read (fd, &buffer, sizeof(buffer)); + close (fd); + + if (ret < 1) + goto out; + + buffer[ret] = '\0'; + + if (!strncmp(buffer, "raw\n", sizeof(buffer))) + entry_type = BACKLIGHT_RAW; + else if (!strncmp(buffer, "platform\n", sizeof(buffer))) + entry_type = BACKLIGHT_PLATFORM; + else if (!strncmp(buffer, "firmware\n", sizeof(buffer))) + entry_type = BACKLIGHT_FIRMWARE; + else + goto out; + + if (connector_type != DRM_MODE_CONNECTOR_LVDS && + connector_type != DRM_MODE_CONNECTOR_eDP) { + /* External displays are assumed to require + gpu control at the moment */ + if (entry_type != BACKLIGHT_RAW) + goto out; + } + + free (path); + + if (asprintf(&path, "%s/%s", backlight_path, "device") < 0) + goto err; + + ret = readlink(path, buffer, sizeof(buffer) - 1); + + if (ret < 0) + goto out; + + buffer[ret] = '\0'; + + parent = basename(buffer); + + /* Perform matching for raw and firmware backlights - + platform backlights have to be assumed to match */ + if (entry_type == BACKLIGHT_RAW || + entry_type == BACKLIGHT_FIRMWARE) { + if (!(pci_name && !strcmp(pci_name, parent))) + goto out; + } + + if (entry_type < type) + goto out; + + type = entry_type; + + if (chosen_path) + free(chosen_path); + chosen_path = strdup(backlight_path); + + out: + free(backlight_path); + free(path); + } + + if (!chosen_path) + goto err; + + backlight = malloc(sizeof(struct backlight)); + + if (!backlight) + goto err; + + backlight->path = chosen_path; + backlight->type = type; + + backlight->max_brightness = backlight_get_max_brightness(backlight); + if (backlight->max_brightness < 0) + goto err; + + backlight->brightness = backlight_get_actual_brightness(backlight); + if (backlight->brightness < 0) + goto err; + + closedir(backlights); + return backlight; +err: + closedir(backlights); + free (chosen_path); + free (backlight); + return NULL; +} |