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"
29 static struct udev_device *find_pci_or_platform_parent(struct udev_device *device) {
30 struct udev_device *parent;
31 const char *subsystem, *sysname;
35 parent = udev_device_get_parent(device);
39 subsystem = udev_device_get_subsystem(parent);
43 sysname = udev_device_get_sysname(parent);
47 if (streq(subsystem, "drm")) {
50 c = startswith(sysname, "card");
54 c += strspn(c, DIGITS);
56 /* A connector DRM device, let's ignore all but LVDS and eDP! */
58 if (!startswith(c, "-LVDS-") &&
59 !startswith(c, "-Embedded DisplayPort-"))
63 } else if (streq(subsystem, "pci")) {
66 value = udev_device_get_sysattr_value(parent, "class");
68 unsigned long class = 0;
70 if (safe_atolu(value, &class) < 0) {
71 log_warning("Cannot parse PCI class %s of device %s:%s.",
72 value, subsystem, sysname);
81 } else if (streq(subsystem, "platform"))
84 return find_pci_or_platform_parent(parent);
87 static bool same_device(struct udev_device *a, struct udev_device *b) {
91 if (!streq_ptr(udev_device_get_subsystem(a), udev_device_get_subsystem(b)))
94 if (!streq_ptr(udev_device_get_sysname(a), udev_device_get_sysname(b)))
100 static bool validate_device(struct udev *udev, struct udev_device *device) {
101 _cleanup_udev_enumerate_unref_ struct udev_enumerate *enumerate = NULL;
102 struct udev_list_entry *item = NULL, *first = NULL;
103 struct udev_device *parent;
104 const char *v, *subsystem;
110 /* Verify whether we should actually care for a specific
111 * backlight device. For backlight devices there might be
112 * multiple ways to access the same control: "firmware"
113 * (i.e. ACPI), "platform" (i.e. via the machine's EC) and
114 * "raw" (via the graphics card). In general we should prefer
115 * "firmware" (i.e. ACPI) or "platform" access over "raw"
116 * access, in order not to confuse the BIOS/EC, and
117 * compatibility with possible low-level hotkey handling of
118 * screen brightness. The kernel will already make sure to
119 * expose only one of "firmware" and "platform" for the same
120 * device to userspace. However, we still need to make sure
121 * that we use "raw" only if no "firmware" or "platform"
122 * device for the same device exists. */
124 subsystem = udev_device_get_subsystem(device);
125 if (!streq_ptr(subsystem, "backlight"))
128 v = udev_device_get_sysattr_value(device, "type");
129 if (!streq_ptr(v, "raw"))
132 parent = find_pci_or_platform_parent(device);
136 subsystem = udev_device_get_subsystem(parent);
140 enumerate = udev_enumerate_new(udev);
144 r = udev_enumerate_add_match_subsystem(enumerate, "backlight");
148 r = udev_enumerate_scan_devices(enumerate);
152 first = udev_enumerate_get_list_entry(enumerate);
153 udev_list_entry_foreach(item, first) {
154 _cleanup_udev_device_unref_ struct udev_device *other;
155 struct udev_device *other_parent;
156 const char *other_subsystem;
158 other = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
162 if (same_device(device, other))
165 v = udev_device_get_sysattr_value(other, "type");
166 if (!streq_ptr(v, "platform") && !streq_ptr(v, "firmware"))
169 /* OK, so there's another backlight device, and it's a
170 * platform or firmware device, so, let's see if we
171 * can verify it belongs to the same device as
173 other_parent = find_pci_or_platform_parent(other);
177 if (same_device(parent, other_parent)) {
178 /* Both have the same PCI parent, that means
180 log_debug("Skipping backlight device %s, since device %s is on same PCI device and takes precedence.",
181 udev_device_get_sysname(device),
182 udev_device_get_sysname(other));
186 other_subsystem = udev_device_get_subsystem(other_parent);
187 if (streq_ptr(other_subsystem, "platform") && streq_ptr(subsystem, "pci")) {
188 /* The other is connected to the platform bus
189 * and we are a PCI device, that also means we
191 log_debug("Skipping backlight device %s, since device %s is a platform device and takes precedence.",
192 udev_device_get_sysname(device),
193 udev_device_get_sysname(other));
201 static unsigned get_max_brightness(struct udev_device *device) {
203 const char *max_brightness_str;
204 unsigned max_brightness;
206 max_brightness_str = udev_device_get_sysattr_value(device, "max_brightness");
207 if (!max_brightness_str) {
208 log_warning("Failed to read 'max_brightness' attribute.");
212 r = safe_atou(max_brightness_str, &max_brightness);
214 log_warning("Failed to parse 'max_brightness' \"%s\": %s", max_brightness_str, strerror(-r));
218 if (max_brightness <= 0) {
219 log_warning("Maximum brightness is 0, ignoring device.");
223 return max_brightness;
226 /* Some systems turn the backlight all the way off at the lowest levels.
227 * clamp_brightness clamps the saved brightness to at least 1 or 5% of
228 * max_brightness. This avoids preserving an unreadably dim screen, which
229 * would otherwise force the user to disable state restoration. */
230 static void clamp_brightness(struct udev_device *device, char **value, unsigned max_brightness) {
232 unsigned brightness, new_brightness, min_brightness;
234 r = safe_atou(*value, &brightness);
236 log_warning("Failed to parse brightness \"%s\": %s", *value, strerror(-r));
240 min_brightness = MAX(1U, max_brightness/20);
241 new_brightness = CLAMP(brightness, min_brightness, max_brightness);
242 if (new_brightness != brightness) {
243 char *old_value = *value;
245 r = asprintf(value, "%u", new_brightness);
251 log_info("Saved brightness %s %s to %s.", old_value,
252 new_brightness > brightness ?
253 "too low; increasing" : "too high; decreasing",
260 int main(int argc, char *argv[]) {
261 _cleanup_udev_unref_ struct udev *udev = NULL;
262 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
263 _cleanup_free_ char *saved = NULL, *ss = NULL, *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL;
264 const char *sysname, *path_id;
265 unsigned max_brightness;
269 log_error("This program requires two arguments.");
273 log_set_target(LOG_TARGET_AUTO);
274 log_parse_environment();
279 r = mkdir_p("/var/lib/systemd/backlight", 0755);
281 log_error("Failed to create backlight directory /var/lib/systemd/backlight: %s",
292 sysname = strchr(argv[2], ':');
294 log_error("Requires a subsystem and sysname pair specifying a backlight device.");
298 ss = strndup(argv[2], sysname - argv[2]);
306 if (!streq(ss, "backlight") && !streq(ss, "leds")) {
307 log_error("Not a backlight or LED device: '%s:%s'", ss, sysname);
312 device = udev_device_new_from_subsystem_sysname(udev, ss, sysname);
315 log_error("Failed to get backlight or LED device '%s:%s': %m", ss, sysname);
322 /* If max_brightness is 0, then there is no actual backlight
323 * device. This happens on desktops with Asus mainboards
324 * that load the eeepc-wmi module.
326 max_brightness = get_max_brightness(device);
327 if (max_brightness == 0)
330 escaped_ss = cescape(ss);
336 escaped_sysname = cescape(sysname);
337 if (!escaped_sysname) {
342 path_id = udev_device_get_property_value(device, "ID_PATH");
344 escaped_path_id = cescape(path_id);
345 if (!escaped_path_id) {
350 saved = strjoin("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname, NULL);
352 saved = strjoin("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname, NULL);
359 /* If there are multiple conflicting backlight devices, then
360 * their probing at boot-time might happen in any order. This
361 * means the validity checking of the device then is not
362 * reliable, since it might not see other devices conflicting
363 * with a specific backlight. To deal with this, we will
364 * actively delete backlight state files at shutdown (where
365 * device probing should be complete), so that the validity
366 * check at boot time doesn't have to be reliable. */
368 if (streq(argv[1], "load") && shall_restore_state()) {
369 _cleanup_free_ char *value = NULL;
371 if (!validate_device(udev, device))
374 r = read_one_line_file(saved, &value);
380 log_error("Failed to read %s: %s", saved, strerror(-r));
384 clamp_brightness(device, &value, max_brightness);
386 r = udev_device_set_sysattr_value(device, "brightness", value);
388 log_error("Failed to write system 'brightness' attribute: %s",
393 } else if (streq(argv[1], "save")) {
396 if (!validate_device(udev, device)) {
401 value = udev_device_get_sysattr_value(device, "brightness");
403 log_error("Failed to read system 'brightness' attribute");
407 r = write_string_file(saved, value);
409 log_error("Failed to write %s: %s", saved, strerror(-r));
414 log_error("Unknown verb %s.", argv[1]);