backlight.c revision 934ae16baf543af03f3f521277d14524ca772d17
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier/***
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier This file is part of systemd.
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier Copyright 2013 Lennart Poettering
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier systemd is free software; you can redistribute it and/or modify it
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier under the terms of the GNU Lesser General Public License as published by
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier the Free Software Foundation; either version 2.1 of the License, or
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier (at your option) any later version.
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier systemd is distributed in the hope that it will be useful, but
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier WITHOUT ANY WARRANTY; without even the implied warranty of
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier Lesser General Public License for more details.
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier You should have received a copy of the GNU Lesser General Public License
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier along with systemd; If not, see <http://www.gnu.org/licenses/>.
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier***/
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "libudev.h"
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poettering
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "alloc-util.h"
0d39fa9c69b97a2ceb156053deef69c0866c2b97Lennart Poettering#include "def.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "escape.h"
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poettering#include "fileio.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "mkdir.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "parse-util.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "proc-cmdline.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "string-util.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "udev-util.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "util.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalierstatic struct udev_device *find_pci_or_platform_parent(struct udev_device *device) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier struct udev_device *parent;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier const char *subsystem, *sysname;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier assert(device);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier parent = udev_device_get_parent(device);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (!parent)
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return NULL;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier subsystem = udev_device_get_subsystem(parent);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (!subsystem)
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return NULL;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier sysname = udev_device_get_sysname(parent);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (!sysname)
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return NULL;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (streq(subsystem, "drm")) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier const char *c;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier c = startswith(sysname, "card");
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (!c)
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return NULL;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier c += strspn(c, DIGITS);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (*c == '-') {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier /* A connector DRM device, let's ignore all but LVDS and eDP! */
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (!startswith(c, "-LVDS-") &&
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier !startswith(c, "-Embedded DisplayPort-"))
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return NULL;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier }
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier } else if (streq(subsystem, "pci")) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier const char *value;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier value = udev_device_get_sysattr_value(parent, "class");
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (value) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier unsigned long class = 0;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (safe_atolu(value, &class) < 0) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier log_warning("Cannot parse PCI class %s of device %s:%s.",
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier value, subsystem, sysname);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return NULL;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier }
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier /* Graphics card */
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (class == 0x30000)
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return parent;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier }
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier } else if (streq(subsystem, "platform"))
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return parent;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return find_pci_or_platform_parent(parent);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier}
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalierstatic bool same_device(struct udev_device *a, struct udev_device *b) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier assert(a);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier assert(b);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (!streq_ptr(udev_device_get_subsystem(a), udev_device_get_subsystem(b)))
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return false;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (!streq_ptr(udev_device_get_sysname(a), udev_device_get_sysname(b)))
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return false;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return true;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier}
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalierstatic bool validate_device(struct udev *udev, struct udev_device *device) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier _cleanup_udev_enumerate_unref_ struct udev_enumerate *enumerate = NULL;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier struct udev_list_entry *item = NULL, *first = NULL;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier struct udev_device *parent;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier const char *v, *subsystem;
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier int r;
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier assert(udev);
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier assert(device);
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier /* Verify whether we should actually care for a specific
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier * backlight device. For backlight devices there might be
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier * multiple ways to access the same control: "firmware"
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier * (i.e. ACPI), "platform" (i.e. via the machine's EC) and
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier * "raw" (via the graphics card). In general we should prefer
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier * "firmware" (i.e. ACPI) or "platform" access over "raw"
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier * access, in order not to confuse the BIOS/EC, and
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier * compatibility with possible low-level hotkey handling of
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier * screen brightness. The kernel will already make sure to
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier * expose only one of "firmware" and "platform" for the same
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier * device to userspace. However, we still need to make sure
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier * that we use "raw" only if no "firmware" or "platform"
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier * device for the same device exists. */
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier subsystem = udev_device_get_subsystem(device);
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier if (!streq_ptr(subsystem, "backlight"))
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier return true;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier v = udev_device_get_sysattr_value(device, "type");
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier if (!streq_ptr(v, "raw"))
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier return true;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier parent = find_pci_or_platform_parent(device);
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier if (!parent)
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier return true;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier subsystem = udev_device_get_subsystem(parent);
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier if (!subsystem)
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier return true;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier enumerate = udev_enumerate_new(udev);
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier if (!enumerate)
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier return true;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier r = udev_enumerate_add_match_subsystem(enumerate, "backlight");
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier if (r < 0)
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier return true;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier r = udev_enumerate_scan_devices(enumerate);
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier if (r < 0)
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier return true;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier first = udev_enumerate_get_list_entry(enumerate);
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier udev_list_entry_foreach(item, first) {
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier _cleanup_udev_device_unref_ struct udev_device *other;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier struct udev_device *other_parent;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier const char *other_subsystem;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier other = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (!other)
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier return true;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (same_device(device, other))
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier continue;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier v = udev_device_get_sysattr_value(other, "type");
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (!streq_ptr(v, "platform") && !streq_ptr(v, "firmware"))
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier continue;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier /* OK, so there's another backlight device, and it's a
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier * platform or firmware device, so, let's see if we
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier * can verify it belongs to the same device as
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier * ours. */
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier other_parent = find_pci_or_platform_parent(other);
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (!other_parent)
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier continue;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (same_device(parent, other_parent)) {
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier /* Both have the same PCI parent, that means
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier * we are out. */
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier log_debug("Skipping backlight device %s, since device %s is on same PCI device and takes precedence.",
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier udev_device_get_sysname(device),
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier udev_device_get_sysname(other));
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier return false;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier }
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier other_subsystem = udev_device_get_subsystem(other_parent);
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (streq_ptr(other_subsystem, "platform") && streq_ptr(subsystem, "pci")) {
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier /* The other is connected to the platform bus
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier * and we are a PCI device, that also means we
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier * are out. */
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier log_debug("Skipping backlight device %s, since device %s is a platform device and takes precedence.",
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier udev_device_get_sysname(device),
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier udev_device_get_sysname(other));
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier return false;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier }
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier }
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier return true;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier}
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalierstatic unsigned get_max_brightness(struct udev_device *device) {
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier int r;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier const char *max_brightness_str;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier unsigned max_brightness;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier max_brightness_str = udev_device_get_sysattr_value(device, "max_brightness");
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (!max_brightness_str) {
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier log_warning("Failed to read 'max_brightness' attribute.");
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier return 0;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier }
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier r = safe_atou(max_brightness_str, &max_brightness);
4630bbb766d564eafee3bfcd36342fbae8534c15Ronny Chevalier if (r < 0) {
4630bbb766d564eafee3bfcd36342fbae8534c15Ronny Chevalier log_warning_errno(r, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return 0;
}
if (max_brightness <= 0) {
log_warning("Maximum brightness is 0, ignoring device.");
return 0;
}
return max_brightness;
}
/* Some systems turn the backlight all the way off at the lowest levels.
* clamp_brightness clamps the saved brightness to at least 1 or 5% of
* max_brightness in case of 'backlight' subsystem. This avoids preserving
* an unreadably dim screen, which would otherwise force the user to
* disable state restoration. */
static void clamp_brightness(struct udev_device *device, char **value, unsigned max_brightness) {
int r;
unsigned brightness, new_brightness, min_brightness;
const char *subsystem;
r = safe_atou(*value, &brightness);
if (r < 0) {
log_warning_errno(r, "Failed to parse brightness \"%s\": %m", *value);
return;
}
subsystem = udev_device_get_subsystem(device);
if (streq_ptr(subsystem, "backlight"))
min_brightness = MAX(1U, max_brightness/20);
else
min_brightness = 0;
new_brightness = CLAMP(brightness, min_brightness, max_brightness);
if (new_brightness != brightness) {
char *old_value = *value;
r = asprintf(value, "%u", new_brightness);
if (r < 0) {
log_oom();
return;
}
log_info("Saved brightness %s %s to %s.", old_value,
new_brightness > brightness ?
"too low; increasing" : "too high; decreasing",
*value);
free(old_value);
}
}
int main(int argc, char *argv[]) {
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_udev_device_unref_ struct udev_device *device = NULL;
_cleanup_free_ char *saved = NULL, *ss = NULL, *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL;
const char *sysname, *path_id;
unsigned max_brightness;
int r;
if (argc != 3) {
log_error("This program requires two arguments.");
return EXIT_FAILURE;
}
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
umask(0022);
r = mkdir_p("/var/lib/systemd/backlight", 0755);
if (r < 0) {
log_error_errno(r, "Failed to create backlight directory /var/lib/systemd/backlight: %m");
return EXIT_FAILURE;
}
udev = udev_new();
if (!udev) {
log_oom();
return EXIT_FAILURE;
}
sysname = strchr(argv[2], ':');
if (!sysname) {
log_error("Requires a subsystem and sysname pair specifying a backlight device.");
return EXIT_FAILURE;
}
ss = strndup(argv[2], sysname - argv[2]);
if (!ss) {
log_oom();
return EXIT_FAILURE;
}
sysname++;
if (!streq(ss, "backlight") && !streq(ss, "leds")) {
log_error("Not a backlight or LED device: '%s:%s'", ss, sysname);
return EXIT_FAILURE;
}
errno = 0;
device = udev_device_new_from_subsystem_sysname(udev, ss, sysname);
if (!device) {
if (errno != 0)
log_error_errno(errno, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname);
else
log_oom();
return EXIT_FAILURE;
}
/* If max_brightness is 0, then there is no actual backlight
* device. This happens on desktops with Asus mainboards
* that load the eeepc-wmi module.
*/
max_brightness = get_max_brightness(device);
if (max_brightness == 0)
return EXIT_SUCCESS;
escaped_ss = cescape(ss);
if (!escaped_ss) {
log_oom();
return EXIT_FAILURE;
}
escaped_sysname = cescape(sysname);
if (!escaped_sysname) {
log_oom();
return EXIT_FAILURE;
}
path_id = udev_device_get_property_value(device, "ID_PATH");
if (path_id) {
escaped_path_id = cescape(path_id);
if (!escaped_path_id) {
log_oom();
return EXIT_FAILURE;
}
saved = strjoin("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname, NULL);
} else
saved = strjoin("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname, NULL);
if (!saved) {
log_oom();
return EXIT_FAILURE;
}
/* If there are multiple conflicting backlight devices, then
* their probing at boot-time might happen in any order. This
* means the validity checking of the device then is not
* reliable, since it might not see other devices conflicting
* with a specific backlight. To deal with this, we will
* actively delete backlight state files at shutdown (where
* device probing should be complete), so that the validity
* check at boot time doesn't have to be reliable. */
if (streq(argv[1], "load")) {
_cleanup_free_ char *value = NULL;
const char *clamp;
if (shall_restore_state() == 0)
return EXIT_SUCCESS;
if (!validate_device(udev, device))
return EXIT_SUCCESS;
r = read_one_line_file(saved, &value);
if (r < 0) {
if (r == -ENOENT)
return EXIT_SUCCESS;
log_error_errno(r, "Failed to read %s: %m", saved);
return EXIT_FAILURE;
}
clamp = udev_device_get_property_value(device, "ID_BACKLIGHT_CLAMP");
if (!clamp || parse_boolean(clamp) != 0) /* default to clamping */
clamp_brightness(device, &value, max_brightness);
r = udev_device_set_sysattr_value(device, "brightness", value);
if (r < 0) {
log_error_errno(r, "Failed to write system 'brightness' attribute: %m");
return EXIT_FAILURE;
}
} else if (streq(argv[1], "save")) {
const char *value;
if (!validate_device(udev, device)) {
unlink(saved);
return EXIT_SUCCESS;
}
value = udev_device_get_sysattr_value(device, "brightness");
if (!value) {
log_error("Failed to read system 'brightness' attribute");
return EXIT_FAILURE;
}
r = write_string_file(saved, value, WRITE_STRING_FILE_CREATE);
if (r < 0) {
log_error_errno(r, "Failed to write %s: %m", saved);
return EXIT_FAILURE;
}
} else {
log_error("Unknown verb %s.", argv[1]);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}