1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include "udev-util.h"
28 static struct udev_device *find_pci_or_platform_parent(struct udev_device *device) {
29 struct udev_device *parent;
30 const char *subsystem, *sysname;
34 parent = udev_device_get_parent(device);
38 subsystem = udev_device_get_subsystem(parent);
42 sysname = udev_device_get_sysname(parent);
46 if (streq(subsystem, "drm")) {
49 c = startswith(sysname, "card");
53 c += strspn(c, "0123456789");
55 /* A connector DRM device, let's ignore all but LVDS and eDP! */
57 if (!startswith(c, "-LVDS-") &&
58 !startswith(c, "-Embedded DisplayPort-"))
62 } else if (streq(subsystem, "pci")) {
65 value = udev_device_get_sysattr_value(parent, "class");
67 unsigned long class = 0;
69 if (safe_atolu(value, &class) < 0) {
70 log_warning("Cannot parse PCI class %s of device %s:%s.", value, subsystem, sysname);
79 } else if (streq(subsystem, "platform"))
82 return find_pci_or_platform_parent(parent);
85 static bool same_device(struct udev_device *a, struct udev_device *b) {
89 if (!streq_ptr(udev_device_get_subsystem(a), udev_device_get_subsystem(b)))
92 if (!streq_ptr(udev_device_get_sysname(a), udev_device_get_sysname(b)))
98 static bool validate_device(struct udev *udev, struct udev_device *device) {
99 _cleanup_udev_enumerate_unref_ struct udev_enumerate *enumerate = NULL;
100 struct udev_list_entry *item = NULL, *first = NULL;
101 struct udev_device *parent;
102 const char *v, *subsystem;
108 /* Verify whether we should actually care for a specific
109 * backlight device. For backlight devices there might be
110 * multiple ways to access the same control: "firmware"
111 * (i.e. ACPI), "platform" (i.e. via the machine's EC) and
112 * "raw" (via the graphics card). In general we should prefer
113 * "firmware" (i.e. ACPI) or "platform" access over "raw"
114 * access, in order not to confuse the BIOS/EC, and
115 * compatibility with possible low-level hotkey handling of
116 * screen brightness. The kernel will already make sure to
117 * expose only one of "firmware" and "platform" for the same
118 * device to userspace. However, we still need to make sure
119 * that we use "raw" only if no "firmware" or "platform"
120 * device for the same device exists. */
122 subsystem = udev_device_get_subsystem(device);
123 if (!streq_ptr(subsystem, "backlight"))
126 v = udev_device_get_sysattr_value(device, "type");
127 if (!streq_ptr(v, "raw"))
130 parent = find_pci_or_platform_parent(device);
134 subsystem = udev_device_get_subsystem(parent);
138 enumerate = udev_enumerate_new(udev);
142 r = udev_enumerate_add_match_subsystem(enumerate, "backlight");
146 r = udev_enumerate_scan_devices(enumerate);
150 first = udev_enumerate_get_list_entry(enumerate);
151 udev_list_entry_foreach(item, first) {
152 _cleanup_udev_device_unref_ struct udev_device *other;
153 struct udev_device *other_parent;
154 const char *other_subsystem;
156 other = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
160 if (same_device(device, other))
163 v = udev_device_get_sysattr_value(other, "type");
164 if (!streq_ptr(v, "platform") && !streq_ptr(v, "firmware"))
167 /* OK, so there's another backlight device, and it's a
168 * platform or firmware device, so, let's see if we
169 * can verify it belongs to the same device as
171 other_parent = find_pci_or_platform_parent(other);
175 if (same_device(parent, other_parent)) {
176 /* Both have the same PCI parent, that means
178 log_debug("Skipping backlight device %s, since backlight device %s is on same PCI device and, takes precedence.", udev_device_get_sysname(device), udev_device_get_sysname(other));
182 other_subsystem = udev_device_get_subsystem(other_parent);
183 if (streq_ptr(other_subsystem, "platform") && streq_ptr(subsystem, "pci")) {
184 /* The other is connected to the platform bus
185 * and we are a PCI device, that also means we
187 log_debug("Skipping backlight device %s, since backlight device %s is a platform device and takes precedence.", udev_device_get_sysname(device), udev_device_get_sysname(other));
195 static unsigned get_max_brightness(struct udev_device *device) {
197 const char *max_brightness_str;
198 unsigned max_brightness;
200 max_brightness_str = udev_device_get_sysattr_value(device, "max_brightness");
201 if (!max_brightness_str) {
202 log_warning("Failed to read max_brightness attribute");
206 r = safe_atou(max_brightness_str, &max_brightness);
208 log_warning("Failed to parse max_brightness \"%s\": %s", max_brightness_str, strerror(-r));
212 return max_brightness;
215 /* Some systems turn the backlight all the way off at the lowest levels.
216 * clamp_brightness clamps the saved brightness to at least 1 or 5% of
217 * max_brightness. This avoids preserving an unreadably dim screen, which
218 * would otherwise force the user to disable state restoration. */
219 static void clamp_brightness(struct udev_device *device, char **value, unsigned max_brightness) {
221 unsigned brightness, new_brightness;
223 r = safe_atou(*value, &brightness);
225 log_warning("Failed to parse brightness \"%s\": %s", *value, strerror(-r));
229 new_brightness = MAX3(brightness, 1U, max_brightness/20);
230 if (new_brightness != brightness) {
231 char *old_value = *value;
233 r = asprintf(value, "%u", new_brightness);
239 log_debug("Saved brightness %s too low; increasing to %s.", old_value, *value);
244 int main(int argc, char *argv[]) {
245 _cleanup_udev_unref_ struct udev *udev = NULL;
246 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
247 _cleanup_free_ char *saved = NULL, *ss = NULL, *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL;
248 const char *sysname, *path_id;
249 unsigned max_brightness;
253 log_error("This program requires two arguments.");
257 log_set_target(LOG_TARGET_AUTO);
258 log_parse_environment();
263 r = mkdir_p("/var/lib/systemd/backlight", 0755);
265 log_error("Failed to create backlight directory: %s", strerror(-r));
275 sysname = strchr(argv[2], ':');
277 log_error("Requires pair of subsystem and sysname for specifying backlight device.");
281 ss = strndup(argv[2], sysname - argv[2]);
289 if (!streq(ss, "backlight") && !streq(ss, "leds")) {
290 log_error("Not a backlight or LED device: '%s:%s'", ss, sysname);
295 device = udev_device_new_from_subsystem_sysname(udev, ss, sysname);
298 log_error("Failed to get backlight or LED device '%s:%s': %m", ss, sysname);
305 /* If max_brightness is 0, then there is no actual backlight
306 * device. This happens on desktops with Asus mainboards
307 * that load the eeepc-wmi module.
309 max_brightness = get_max_brightness(device);
310 if (max_brightness == 0)
313 escaped_ss = cescape(ss);
319 escaped_sysname = cescape(sysname);
320 if (!escaped_sysname) {
325 path_id = udev_device_get_property_value(device, "ID_PATH");
327 escaped_path_id = cescape(path_id);
328 if (!escaped_path_id) {
333 saved = strjoin("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname, NULL);
335 saved = strjoin("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname, NULL);
342 /* If there are multiple conflicting backlight devices, then
343 * their probing at boot-time might happen in any order. This
344 * means the validity checking of the device then is not
345 * reliable, since it might not see other devices conflicting
346 * with a specific backlight. To deal with this, we will
347 * actively delete backlight state files at shutdown (where
348 * device probing should be complete), so that the validity
349 * check at boot time doesn't have to be reliable. */
351 if (streq(argv[1], "load") && shall_restore_state()) {
352 _cleanup_free_ char *value = NULL;
354 if (!validate_device(udev, device))
357 r = read_one_line_file(saved, &value);
363 log_error("Failed to read %s: %s", saved, strerror(-r));
367 clamp_brightness(device, &value, max_brightness);
369 r = udev_device_set_sysattr_value(device, "brightness", value);
371 log_error("Failed to write system attribute: %s", strerror(-r));
375 } else if (streq(argv[1], "save")) {
378 if (!validate_device(udev, device)) {
383 value = udev_device_get_sysattr_value(device, "brightness");
385 log_error("Failed to read system attribute");
389 r = write_string_file(saved, value);
391 log_error("Failed to write %s: %s", saved, strerror(-r));
396 log_error("Unknown verb %s.", argv[1]);