1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Lennart Poettering
10 #include "alloc-util.h"
15 #include "parse-util.h"
16 #include "proc-cmdline.h"
17 #include "string-util.h"
18 #include "udev-util.h"
21 static struct udev_device
*find_pci_or_platform_parent(struct udev_device
*device
) {
22 struct udev_device
*parent
;
23 const char *subsystem
, *sysname
;
27 parent
= udev_device_get_parent(device
);
31 subsystem
= udev_device_get_subsystem(parent
);
35 sysname
= udev_device_get_sysname(parent
);
39 if (streq(subsystem
, "drm")) {
42 c
= startswith(sysname
, "card");
46 c
+= strspn(c
, DIGITS
);
48 /* A connector DRM device, let's ignore all but LVDS and eDP! */
50 if (!startswith(c
, "-LVDS-") &&
51 !startswith(c
, "-Embedded DisplayPort-"))
55 } else if (streq(subsystem
, "pci")) {
58 value
= udev_device_get_sysattr_value(parent
, "class");
60 unsigned long class = 0;
62 if (safe_atolu(value
, &class) < 0) {
63 log_warning("Cannot parse PCI class %s of device %s:%s.",
64 value
, subsystem
, sysname
);
73 } else if (streq(subsystem
, "platform"))
76 return find_pci_or_platform_parent(parent
);
79 static bool same_device(struct udev_device
*a
, struct udev_device
*b
) {
83 if (!streq_ptr(udev_device_get_subsystem(a
), udev_device_get_subsystem(b
)))
86 if (!streq_ptr(udev_device_get_sysname(a
), udev_device_get_sysname(b
)))
92 static bool validate_device(struct udev
*udev
, struct udev_device
*device
) {
93 _cleanup_udev_enumerate_unref_
struct udev_enumerate
*enumerate
= NULL
;
94 struct udev_list_entry
*item
= NULL
, *first
= NULL
;
95 struct udev_device
*parent
;
96 const char *v
, *subsystem
;
102 /* Verify whether we should actually care for a specific
103 * backlight device. For backlight devices there might be
104 * multiple ways to access the same control: "firmware"
105 * (i.e. ACPI), "platform" (i.e. via the machine's EC) and
106 * "raw" (via the graphics card). In general we should prefer
107 * "firmware" (i.e. ACPI) or "platform" access over "raw"
108 * access, in order not to confuse the BIOS/EC, and
109 * compatibility with possible low-level hotkey handling of
110 * screen brightness. The kernel will already make sure to
111 * expose only one of "firmware" and "platform" for the same
112 * device to userspace. However, we still need to make sure
113 * that we use "raw" only if no "firmware" or "platform"
114 * device for the same device exists. */
116 subsystem
= udev_device_get_subsystem(device
);
117 if (!streq_ptr(subsystem
, "backlight"))
120 v
= udev_device_get_sysattr_value(device
, "type");
121 if (!streq_ptr(v
, "raw"))
124 parent
= find_pci_or_platform_parent(device
);
128 subsystem
= udev_device_get_subsystem(parent
);
132 enumerate
= udev_enumerate_new(udev
);
136 r
= udev_enumerate_add_match_subsystem(enumerate
, "backlight");
140 r
= udev_enumerate_scan_devices(enumerate
);
144 first
= udev_enumerate_get_list_entry(enumerate
);
145 udev_list_entry_foreach(item
, first
) {
146 _cleanup_udev_device_unref_
struct udev_device
*other
;
147 struct udev_device
*other_parent
;
148 const char *other_subsystem
;
150 other
= udev_device_new_from_syspath(udev
, udev_list_entry_get_name(item
));
154 if (same_device(device
, other
))
157 v
= udev_device_get_sysattr_value(other
, "type");
158 if (!STRPTR_IN_SET(v
, "platform", "firmware"))
161 /* OK, so there's another backlight device, and it's a
162 * platform or firmware device, so, let's see if we
163 * can verify it belongs to the same device as
165 other_parent
= find_pci_or_platform_parent(other
);
169 if (same_device(parent
, other_parent
)) {
170 /* Both have the same PCI parent, that means
172 log_debug("Skipping backlight device %s, since device %s is on same PCI device and takes precedence.",
173 udev_device_get_sysname(device
),
174 udev_device_get_sysname(other
));
178 other_subsystem
= udev_device_get_subsystem(other_parent
);
179 if (streq_ptr(other_subsystem
, "platform") && streq_ptr(subsystem
, "pci")) {
180 /* The other is connected to the platform bus
181 * and we are a PCI device, that also means we
183 log_debug("Skipping backlight device %s, since device %s is a platform device and takes precedence.",
184 udev_device_get_sysname(device
),
185 udev_device_get_sysname(other
));
193 static unsigned get_max_brightness(struct udev_device
*device
) {
195 const char *max_brightness_str
;
196 unsigned max_brightness
;
198 max_brightness_str
= udev_device_get_sysattr_value(device
, "max_brightness");
199 if (!max_brightness_str
) {
200 log_warning("Failed to read 'max_brightness' attribute.");
204 r
= safe_atou(max_brightness_str
, &max_brightness
);
206 log_warning_errno(r
, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str
);
210 if (max_brightness
<= 0) {
211 log_warning("Maximum brightness is 0, ignoring device.");
215 return max_brightness
;
218 /* Some systems turn the backlight all the way off at the lowest levels.
219 * clamp_brightness clamps the saved brightness to at least 1 or 5% of
220 * max_brightness in case of 'backlight' subsystem. This avoids preserving
221 * an unreadably dim screen, which would otherwise force the user to
222 * disable state restoration. */
223 static void clamp_brightness(struct udev_device
*device
, char **value
, unsigned max_brightness
) {
224 unsigned brightness
, new_brightness
, min_brightness
;
225 const char *subsystem
;
228 r
= safe_atou(*value
, &brightness
);
230 log_warning_errno(r
, "Failed to parse brightness \"%s\": %m", *value
);
234 subsystem
= udev_device_get_subsystem(device
);
235 if (streq_ptr(subsystem
, "backlight"))
236 min_brightness
= MAX(1U, max_brightness
/20);
240 new_brightness
= CLAMP(brightness
, min_brightness
, max_brightness
);
241 if (new_brightness
!= brightness
) {
242 char *old_value
= *value
;
244 r
= asprintf(value
, "%u", new_brightness
);
250 log_info("Saved brightness %s %s to %s.", old_value
,
251 new_brightness
> brightness
?
252 "too low; increasing" : "too high; decreasing",
259 static bool shall_clamp(struct udev_device
*d
) {
265 s
= udev_device_get_property_value(d
, "ID_BACKLIGHT_CLAMP");
269 r
= parse_boolean(s
);
271 log_debug_errno(r
, "Failed to parse ID_BACKLIGHT_CLAMP property, ignoring: %m");
278 int main(int argc
, char *argv
[]) {
279 _cleanup_udev_unref_
struct udev
*udev
= NULL
;
280 _cleanup_udev_device_unref_
struct udev_device
*device
= NULL
;
281 _cleanup_free_
char *escaped_ss
= NULL
, *escaped_sysname
= NULL
, *escaped_path_id
= NULL
;
282 const char *sysname
, *path_id
, *ss
, *saved
;
283 unsigned max_brightness
;
287 log_error("This program requires two arguments.");
291 log_set_target(LOG_TARGET_AUTO
);
292 log_parse_environment();
297 r
= mkdir_p("/var/lib/systemd/backlight", 0755);
299 log_error_errno(r
, "Failed to create backlight directory /var/lib/systemd/backlight: %m");
309 sysname
= strchr(argv
[2], ':');
311 log_error("Requires a subsystem and sysname pair specifying a backlight device.");
315 ss
= strndupa(argv
[2], sysname
- argv
[2]);
319 if (!STR_IN_SET(ss
, "backlight", "leds")) {
320 log_error("Not a backlight or LED device: '%s:%s'", ss
, sysname
);
325 device
= udev_device_new_from_subsystem_sysname(udev
, ss
, sysname
);
328 log_error_errno(errno
, "Failed to get backlight or LED device '%s:%s': %m", ss
, sysname
);
335 /* If max_brightness is 0, then there is no actual backlight
336 * device. This happens on desktops with Asus mainboards
337 * that load the eeepc-wmi module.
339 max_brightness
= get_max_brightness(device
);
340 if (max_brightness
== 0)
343 escaped_ss
= cescape(ss
);
349 escaped_sysname
= cescape(sysname
);
350 if (!escaped_sysname
) {
355 path_id
= udev_device_get_property_value(device
, "ID_PATH");
357 escaped_path_id
= cescape(path_id
);
358 if (!escaped_path_id
) {
363 saved
= strjoina("/var/lib/systemd/backlight/", escaped_path_id
, ":", escaped_ss
, ":", escaped_sysname
);
365 saved
= strjoina("/var/lib/systemd/backlight/", escaped_ss
, ":", escaped_sysname
);
367 /* If there are multiple conflicting backlight devices, then
368 * their probing at boot-time might happen in any order. This
369 * means the validity checking of the device then is not
370 * reliable, since it might not see other devices conflicting
371 * with a specific backlight. To deal with this, we will
372 * actively delete backlight state files at shutdown (where
373 * device probing should be complete), so that the validity
374 * check at boot time doesn't have to be reliable. */
376 if (streq(argv
[1], "load")) {
377 _cleanup_free_
char *value
= NULL
;
380 if (shall_restore_state() == 0)
383 if (!validate_device(udev
, device
))
386 clamp
= shall_clamp(device
);
388 r
= read_one_line_file(saved
, &value
);
392 /* Fallback to clamping current brightness or exit early if
393 * clamping is not supported/enabled. */
397 curval
= udev_device_get_sysattr_value(device
, "brightness");
399 log_warning("Failed to read 'brightness' attribute.");
403 value
= strdup(curval
);
409 log_error_errno(r
, "Failed to read %s: %m", saved
);
414 clamp_brightness(device
, &value
, max_brightness
);
416 r
= udev_device_set_sysattr_value(device
, "brightness", value
);
418 log_error_errno(r
, "Failed to write system 'brightness' attribute: %m");
422 } else if (streq(argv
[1], "save")) {
425 if (!validate_device(udev
, device
)) {
430 value
= udev_device_get_sysattr_value(device
, "brightness");
432 log_error("Failed to read system 'brightness' attribute");
436 r
= write_string_file(saved
, value
, WRITE_STRING_FILE_CREATE
);
438 log_error_errno(r
, "Failed to write %s: %m", saved
);
443 log_error("Unknown verb %s.", argv
[1]);