1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #include "alloc-util.h"
6 #include "device-util.h"
11 #include "parse-util.h"
12 #include "proc-cmdline.h"
13 #include "string-util.h"
17 static int find_pci_or_platform_parent(sd_device
*device
, sd_device
**ret
) {
18 const char *subsystem
, *sysname
, *value
;
25 r
= sd_device_get_parent(device
, &parent
);
29 r
= sd_device_get_subsystem(parent
, &subsystem
);
33 r
= sd_device_get_sysname(parent
, &sysname
);
37 if (streq(subsystem
, "drm")) {
40 c
= startswith(sysname
, "card");
44 c
+= strspn(c
, DIGITS
);
46 /* A connector DRM device, let's ignore all but LVDS and eDP! */
48 if (!startswith(c
, "-LVDS-") &&
49 !startswith(c
, "-Embedded DisplayPort-"))
53 } else if (streq(subsystem
, "pci") &&
54 sd_device_get_sysattr_value(parent
, "class", &value
) >= 0) {
55 unsigned long class = 0;
57 r
= safe_atolu(value
, &class);
59 return log_warning_errno(r
, "Cannot parse PCI class '%s' of device %s:%s: %m",
60 value
, subsystem
, sysname
);
63 if (class == 0x30000) {
68 } else if (streq(subsystem
, "platform")) {
73 return find_pci_or_platform_parent(parent
, ret
);
76 static int same_device(sd_device
*a
, sd_device
*b
) {
77 const char *a_val
, *b_val
;
83 r
= sd_device_get_subsystem(a
, &a_val
);
87 r
= sd_device_get_subsystem(b
, &b_val
);
91 if (!streq(a_val
, b_val
))
94 r
= sd_device_get_sysname(a
, &a_val
);
98 r
= sd_device_get_sysname(b
, &b_val
);
102 return streq(a_val
, b_val
);
105 static int validate_device(sd_device
*device
) {
106 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*enumerate
= NULL
;
107 const char *v
, *subsystem
;
108 sd_device
*parent
, *other
;
113 /* Verify whether we should actually care for a specific
114 * backlight device. For backlight devices there might be
115 * multiple ways to access the same control: "firmware"
116 * (i.e. ACPI), "platform" (i.e. via the machine's EC) and
117 * "raw" (via the graphics card). In general we should prefer
118 * "firmware" (i.e. ACPI) or "platform" access over "raw"
119 * access, in order not to confuse the BIOS/EC, and
120 * compatibility with possible low-level hotkey handling of
121 * screen brightness. The kernel will already make sure to
122 * expose only one of "firmware" and "platform" for the same
123 * device to userspace. However, we still need to make sure
124 * that we use "raw" only if no "firmware" or "platform"
125 * device for the same device exists. */
127 r
= sd_device_get_subsystem(device
, &subsystem
);
130 if (!streq(subsystem
, "backlight"))
133 r
= sd_device_get_sysattr_value(device
, "type", &v
);
136 if (!streq(v
, "raw"))
139 r
= find_pci_or_platform_parent(device
, &parent
);
143 r
= sd_device_get_subsystem(parent
, &subsystem
);
147 r
= sd_device_enumerator_new(&enumerate
);
151 r
= sd_device_enumerator_allow_uninitialized(enumerate
);
155 r
= sd_device_enumerator_add_match_subsystem(enumerate
, "backlight", true);
159 FOREACH_DEVICE(enumerate
, other
) {
160 const char *other_subsystem
;
161 sd_device
*other_parent
;
163 if (same_device(device
, other
) > 0)
166 if (sd_device_get_sysattr_value(other
, "type", &v
) < 0 ||
167 !STR_IN_SET(v
, "platform", "firmware"))
170 /* OK, so there's another backlight device, and it's a
171 * platform or firmware device, so, let's see if we
172 * can verify it belongs to the same device as ours. */
173 if (find_pci_or_platform_parent(other
, &other_parent
) < 0)
176 if (same_device(parent
, other_parent
)) {
177 const char *device_sysname
= NULL
, *other_sysname
= NULL
;
179 /* Both have the same PCI parent, that means we are out. */
181 (void) sd_device_get_sysname(device
, &device_sysname
);
182 (void) sd_device_get_sysname(other
, &other_sysname
);
184 log_debug("Skipping backlight device %s, since device %s is on same PCI device and takes precedence.",
185 device_sysname
, other_sysname
);
189 if (sd_device_get_subsystem(other_parent
, &other_subsystem
) < 0)
192 if (streq(other_subsystem
, "platform") && streq(subsystem
, "pci")) {
193 const char *device_sysname
= NULL
, *other_sysname
= NULL
;
195 /* The other is connected to the platform bus and we are a PCI device, that also means we are out. */
197 (void) sd_device_get_sysname(device
, &device_sysname
);
198 (void) sd_device_get_sysname(other
, &other_sysname
);
200 log_debug("Skipping backlight device %s, since device %s is a platform device and takes precedence.",
201 device_sysname
, other_sysname
);
209 static int get_max_brightness(sd_device
*device
, unsigned *ret
) {
210 const char *max_brightness_str
;
211 unsigned max_brightness
;
217 r
= sd_device_get_sysattr_value(device
, "max_brightness", &max_brightness_str
);
219 return log_device_warning_errno(device
, r
, "Failed to read 'max_brightness' attribute: %m");
221 r
= safe_atou(max_brightness_str
, &max_brightness
);
223 return log_device_warning_errno(device
, r
, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str
);
225 if (max_brightness
<= 0) {
226 log_device_warning(device
, "Maximum brightness is 0, ignoring device.");
230 *ret
= max_brightness
;
234 /* Some systems turn the backlight all the way off at the lowest levels.
235 * clamp_brightness clamps the saved brightness to at least 1 or 5% of
236 * max_brightness in case of 'backlight' subsystem. This avoids preserving
237 * an unreadably dim screen, which would otherwise force the user to
238 * disable state restoration. */
239 static int clamp_brightness(sd_device
*device
, char **value
, unsigned max_brightness
) {
240 unsigned brightness
, new_brightness
, min_brightness
;
241 const char *subsystem
;
247 r
= safe_atou(*value
, &brightness
);
249 return log_device_warning_errno(device
, r
, "Failed to parse brightness \"%s\": %m", *value
);
251 r
= sd_device_get_subsystem(device
, &subsystem
);
253 return log_device_warning_errno(device
, r
, "Failed to get device subsystem: %m");
255 if (streq(subsystem
, "backlight"))
256 min_brightness
= MAX(1U, max_brightness
/20);
260 new_brightness
= CLAMP(brightness
, min_brightness
, max_brightness
);
261 if (new_brightness
!= brightness
) {
264 r
= asprintf(&new_value
, "%u", new_brightness
);
268 log_device_info(device
, "Saved brightness %s %s to %s.", *value
,
269 new_brightness
> brightness
?
270 "too low; increasing" : "too high; decreasing",
273 free_and_replace(*value
, new_value
);
279 static bool shall_clamp(sd_device
*d
) {
285 r
= sd_device_get_property_value(d
, "ID_BACKLIGHT_CLAMP", &s
);
287 log_device_debug_errno(d
, r
, "Failed to get ID_BACKLIGHT_CLAMP property, ignoring: %m");
291 r
= parse_boolean(s
);
293 log_device_debug_errno(d
, r
, "Failed to parse ID_BACKLIGHT_CLAMP property, ignoring: %m");
300 static int run(int argc
, char *argv
[]) {
301 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
302 _cleanup_free_
char *escaped_ss
= NULL
, *escaped_sysname
= NULL
, *escaped_path_id
= NULL
;
303 const char *sysname
, *path_id
, *ss
, *saved
;
304 unsigned max_brightness
;
308 log_error("This program requires two arguments.");
316 r
= mkdir_p("/var/lib/systemd/backlight", 0755);
318 return log_error_errno(r
, "Failed to create backlight directory /var/lib/systemd/backlight: %m");
320 sysname
= strchr(argv
[2], ':');
322 log_error("Requires a subsystem and sysname pair specifying a backlight device.");
326 ss
= strndupa(argv
[2], sysname
- argv
[2]);
330 if (!STR_IN_SET(ss
, "backlight", "leds")) {
331 log_error("Not a backlight or LED device: '%s:%s'", ss
, sysname
);
335 r
= sd_device_new_from_subsystem_sysname(&device
, ss
, sysname
);
337 return log_error_errno(r
, "Failed to get backlight or LED device '%s:%s': %m", ss
, sysname
);
339 /* If max_brightness is 0, then there is no actual backlight
340 * device. This happens on desktops with Asus mainboards
341 * that load the eeepc-wmi module. */
342 if (get_max_brightness(device
, &max_brightness
) < 0)
345 escaped_ss
= cescape(ss
);
349 escaped_sysname
= cescape(sysname
);
350 if (!escaped_sysname
)
353 if (sd_device_get_property_value(device
, "ID_PATH", &path_id
) >= 0) {
354 escaped_path_id
= cescape(path_id
);
355 if (!escaped_path_id
)
358 saved
= strjoina("/var/lib/systemd/backlight/", escaped_path_id
, ":", escaped_ss
, ":", escaped_sysname
);
360 saved
= strjoina("/var/lib/systemd/backlight/", escaped_ss
, ":", escaped_sysname
);
362 /* If there are multiple conflicting backlight devices, then
363 * their probing at boot-time might happen in any order. This
364 * means the validity checking of the device then is not
365 * reliable, since it might not see other devices conflicting
366 * with a specific backlight. To deal with this, we will
367 * actively delete backlight state files at shutdown (where
368 * device probing should be complete), so that the validity
369 * check at boot time doesn't have to be reliable. */
371 if (streq(argv
[1], "load")) {
372 _cleanup_free_
char *value
= NULL
;
375 if (shall_restore_state() == 0)
378 if (validate_device(device
) == 0)
381 clamp
= shall_clamp(device
);
383 r
= read_one_line_file(saved
, &value
);
387 /* Fallback to clamping current brightness or exit early if
388 * clamping is not supported/enabled. */
392 r
= sd_device_get_sysattr_value(device
, "brightness", &curval
);
394 return log_device_warning_errno(device
, r
, "Failed to read 'brightness' attribute: %m");
396 value
= strdup(curval
);
400 return log_error_errno(r
, "Failed to read %s: %m", saved
);
403 (void) clamp_brightness(device
, &value
, max_brightness
);
405 r
= sd_device_set_sysattr_value(device
, "brightness", value
);
407 return log_device_error_errno(device
, r
, "Failed to write system 'brightness' attribute: %m");
409 } else if (streq(argv
[1], "save")) {
412 if (validate_device(device
) == 0) {
417 r
= sd_device_get_sysattr_value(device
, "brightness", &value
);
419 return log_device_error_errno(device
, r
, "Failed to read system 'brightness' attribute: %m");
421 r
= write_string_file(saved
, value
, WRITE_STRING_FILE_CREATE
);
423 return log_device_error_errno(device
, r
, "Failed to write %s: %m", saved
);
426 log_error("Unknown verb %s.", argv
[1]);
433 DEFINE_MAIN_FUNCTION(run
);