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, "0123456789");
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");
70 if (safe_atolu(value, &class) < 0) {
71 log_warning("Cannot parse PCI class %s of device %s:%s.", value, subsystem, sysname);
80 } else if (streq(subsystem, "platform"))
83 return find_pci_or_platform_parent(parent);
86 static bool same_device(struct udev_device *a, struct udev_device *b) {
90 if (!streq_ptr(udev_device_get_subsystem(a), udev_device_get_subsystem(b)))
93 if (!streq_ptr(udev_device_get_sysname(a), udev_device_get_sysname(b)))
99 static bool validate_device(struct udev *udev, struct udev_device *device) {
100 _cleanup_udev_enumerate_unref_ struct udev_enumerate *enumerate = NULL;
101 struct udev_list_entry *item = NULL, *first = NULL;
102 struct udev_device *parent;
103 const char *v, *subsystem;
109 /* Verify whether we should actually care for a specific
110 * backlight device. For backlight devices there might be
111 * multiple ways to access the same control: "firmware"
112 * (i.e. ACPI), "platform" (i.e. via the machine's EC) and
113 * "raw" (via the graphics card). In general we should prefer
114 * "firmware" (i.e. ACPI) or "platform" access over "raw"
115 * access, in order not to confuse the BIOS/EC, and
116 * compatibility with possible low-level hotkey handling of
117 * screen brightness. The kernel will already make sure to
118 * expose only one of "firmware" and "platform" for the same
119 * device to userspace. However, we still need to make sure
120 * that we use "raw" only if no "firmware" or "platform"
121 * device for the same device exists. */
123 subsystem = udev_device_get_subsystem(device);
124 if (!streq_ptr(subsystem, "backlight"))
127 v = udev_device_get_sysattr_value(device, "type");
128 if (!streq_ptr(v, "raw"))
131 parent = find_pci_or_platform_parent(device);
135 subsystem = udev_device_get_subsystem(parent);
139 enumerate = udev_enumerate_new(udev);
143 r = udev_enumerate_add_match_subsystem(enumerate, "backlight");
147 r = udev_enumerate_scan_devices(enumerate);
151 first = udev_enumerate_get_list_entry(enumerate);
152 udev_list_entry_foreach(item, first) {
153 _cleanup_udev_device_unref_ struct udev_device *other;
154 struct udev_device *other_parent;
155 const char *other_subsystem;
157 other = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
161 if (same_device(device, other))
164 v = udev_device_get_sysattr_value(other, "type");
165 if (!streq_ptr(v, "platform") && !streq_ptr(v, "firmware"))
168 /* OK, so there's another backlight device, and it's a
169 * platform or firmware device, so, let's see if we
170 * can verify it belongs to the same device as
172 other_parent = find_pci_or_platform_parent(other);
176 if (same_device(parent, other_parent)) {
177 /* Both have the same PCI parent, that means
179 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));
183 other_subsystem = udev_device_get_subsystem(other_parent);
184 if (streq_ptr(other_subsystem, "platform") && streq_ptr(subsystem, "pci")) {
185 /* The other is connected to the platform bus
186 * and we are a PCI device, that also means we
188 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));
196 int main(int argc, char *argv[]) {
197 _cleanup_udev_unref_ struct udev *udev = NULL;
198 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
199 _cleanup_free_ char *saved = NULL, *ss = NULL, *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL;
200 const char *sysname, *path_id;
204 log_error("This program requires two arguments.");
208 log_set_target(LOG_TARGET_AUTO);
209 log_parse_environment();
214 r = mkdir_p("/var/lib/systemd/backlight", 0755);
216 log_error("Failed to create backlight directory: %s", strerror(-r));
226 sysname = strchr(argv[2], ':');
228 log_error("Requires pair of subsystem and sysname for specifying backlight device.");
232 ss = strndup(argv[2], sysname - argv[2]);
240 if (!streq(ss, "backlight") && !streq(ss, "leds")) {
241 log_error("Not a backlight or LED device: '%s:%s'", ss, sysname);
246 device = udev_device_new_from_subsystem_sysname(udev, ss, sysname);
249 log_error("Failed to get backlight or LED device '%s:%s': %m", ss, sysname);
256 escaped_ss = cescape(ss);
262 escaped_sysname = cescape(sysname);
263 if (!escaped_sysname) {
268 path_id = udev_device_get_property_value(device, "ID_PATH");
270 escaped_path_id = cescape(path_id);
271 if (!escaped_path_id) {
276 saved = strjoin("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname, NULL);
278 saved = strjoin("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname, NULL);
285 /* If there are multiple conflicting backlight devices, then
286 * their probing at boot-time might happen in any order. This
287 * means the validity checking of the device then is not
288 * reliable, since it might not see other devices conflicting
289 * with a specific backlight. To deal with this we will
290 * actively delete backlight state files at shutdown (where
291 * device probing should be complete), so that the validity
292 * check at boot time doesn't have to be reliable. */
294 if (streq(argv[1], "load") && shall_restore_state()) {
295 _cleanup_free_ char *value = NULL;
297 if (!validate_device(udev, device))
300 r = read_one_line_file(saved, &value);
306 log_error("Failed to read %s: %s", saved, strerror(-r));
310 r = udev_device_set_sysattr_value(device, "brightness", value);
312 log_error("Failed to write system attribute: %s", strerror(-r));
316 } else if (streq(argv[1], "save")) {
319 if (!validate_device(udev, device)) {
324 value = udev_device_get_sysattr_value(device, "brightness");
326 log_error("Failed to read system attribute: %s", strerror(-r));
330 r = write_string_file(saved, value);
332 log_error("Failed to write %s: %s", saved, strerror(-r));
337 log_error("Unknown verb %s.", argv[1]);