backlight.c revision 934ae16baf543af03f3f521277d14524ca772d17
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier This file is part of systemd.
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier Copyright 2013 Lennart Poettering
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 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 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 Chevalierstatic struct udev_device *find_pci_or_platform_parent(struct udev_device *device) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier subsystem = udev_device_get_subsystem(parent);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier const char *c;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (*c == '-') {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier /* A connector DRM device, let's ignore all but LVDS and eDP! */
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier value = udev_device_get_sysattr_value(parent, "class");
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier unsigned long class = 0;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier log_warning("Cannot parse PCI class %s of device %s:%s.",
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier /* Graphics card */
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalierstatic bool same_device(struct udev_device *a, struct udev_device *b) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (!streq_ptr(udev_device_get_subsystem(a), udev_device_get_subsystem(b)))
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return false;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (!streq_ptr(udev_device_get_sysname(a), udev_device_get_sysname(b)))
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return false;
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 const char *v, *subsystem;
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 subsystem = udev_device_get_subsystem(device);
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier v = udev_device_get_sysattr_value(device, "type");
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier parent = find_pci_or_platform_parent(device);
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier subsystem = udev_device_get_subsystem(parent);
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier r = udev_enumerate_add_match_subsystem(enumerate, "backlight");
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier first = udev_enumerate_get_list_entry(enumerate);
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier _cleanup_udev_device_unref_ struct udev_device *other;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier other = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier v = udev_device_get_sysattr_value(other, "type");
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (!streq_ptr(v, "platform") && !streq_ptr(v, "firmware"))
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 other_parent = find_pci_or_platform_parent(other);
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 return false;
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 return false;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalierstatic unsigned get_max_brightness(struct udev_device *device) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier max_brightness_str = udev_device_get_sysattr_value(device, "max_brightness");
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier log_warning("Failed to read 'max_brightness' attribute.");
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier r = safe_atou(max_brightness_str, &max_brightness);
4630bbb766d564eafee3bfcd36342fbae8534c15Ronny Chevalier log_warning_errno(r, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str);
if (max_brightness <= 0) {
return max_brightness;
const char *subsystem;
min_brightness = 0;
log_oom();
*value);
_cleanup_free_ char *saved = NULL, *ss = NULL, *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL;
unsigned max_brightness;
return EXIT_FAILURE;
log_open();
return EXIT_FAILURE;
if (!udev) {
log_oom();
return EXIT_FAILURE;
if (!sysname) {
return EXIT_FAILURE;
if (!ss) {
log_oom();
return EXIT_FAILURE;
sysname++;
return EXIT_FAILURE;
errno = 0;
if (!device) {
if (errno != 0)
log_oom();
return EXIT_FAILURE;
if (max_brightness == 0)
return EXIT_SUCCESS;
if (!escaped_ss) {
log_oom();
return EXIT_FAILURE;
if (!escaped_sysname) {
log_oom();
return EXIT_FAILURE;
if (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);
if (!saved) {
log_oom();
return EXIT_FAILURE;
const char *clamp;
if (shall_restore_state() == 0)
return EXIT_SUCCESS;
return EXIT_SUCCESS;
if (r == -ENOENT)
return EXIT_SUCCESS;
return EXIT_FAILURE;
return EXIT_FAILURE;
const char *value;
return EXIT_SUCCESS;
if (!value) {
return EXIT_FAILURE;
return EXIT_FAILURE;
return EXIT_FAILURE;
return EXIT_SUCCESS;