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