]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/backlight/backlight.c
basic: rename util.h to logarithm.h
[thirdparty/systemd.git] / src / backlight / backlight.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
3731acf1 2
ca78ad1d
ZJS
3#include <sys/stat.h>
4#include <sys/types.h>
5#include <unistd.h>
6
9aadd281 7#include "sd-device.h"
b4bbcaa9 8
b5efdb8a 9#include "alloc-util.h"
8437c059 10#include "device-util.h"
4f5dd394 11#include "escape.h"
3731acf1 12#include "fileio.h"
1ddae15a 13#include "main-func.h"
4f5dd394 14#include "mkdir.h"
4e731273 15#include "parse-util.h"
b23728ec 16#include "pretty-print.h"
542bb9be 17#include "process-util.h"
2bfa8466 18#include "reboot-util.h"
07630cea 19#include "string-util.h"
9aadd281 20#include "strv.h"
542bb9be 21#include "terminal-util.h"
3731acf1 22
e0504dd0
YW
23#define PCI_CLASS_GRAPHICS_CARD 0x30000
24
b23728ec
P
25static int help(void) {
26 _cleanup_free_ char *link = NULL;
27 int r;
28
29 r = terminal_urlify_man("systemd-backlight", "8", &link);
30 if (r < 0)
31 return log_oom();
32
33 printf("%s save [backlight|leds]:DEVICE\n"
34 "%s load [backlight|leds]:DEVICE\n"
35 "\n%sSave and restore backlight brightness at shutdown and boot.%s\n\n"
36 " save Save current brightness\n"
37 " load Set brightness to be the previously saved value\n"
bc556335
DDM
38 "\nSee the %s for details.\n",
39 program_invocation_short_name,
40 program_invocation_short_name,
41 ansi_highlight(),
42 ansi_normal(),
43 link);
b23728ec
P
44
45 return 0;
46}
47
e0504dd0
YW
48static int has_multiple_graphics_cards(void) {
49 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
50 sd_device *dev;
51 bool found = false;
52 int r;
53
54 r = sd_device_enumerator_new(&e);
55 if (r < 0)
56 return r;
57
58 r = sd_device_enumerator_add_match_subsystem(e, "pci", /* match = */ true);
59 if (r < 0)
60 return r;
61
62 /* class is an unsigned number, let's validate the value later. */
63 r = sd_device_enumerator_add_match_sysattr(e, "class", NULL, /* match = */ true);
64 if (r < 0)
65 return r;
66
67 FOREACH_DEVICE(e, dev) {
68 const char *s;
69 unsigned long c;
70
71 if (sd_device_get_sysattr_value(dev, "class", &s) < 0)
72 continue;
73
74 if (safe_atolu(s, &c) < 0)
75 continue;
76
77 if (c != PCI_CLASS_GRAPHICS_CARD)
78 continue;
79
80 if (found)
81 return true; /* This is the second device. */
82
83 found = true; /* Found the first device. */
84 }
85
86 return false;
87}
88
9aadd281
YW
89static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
90 const char *subsystem, *sysname, *value;
91 sd_device *parent;
92 int r;
0f4ba83c
LP
93
94 assert(device);
9aadd281 95 assert(ret);
0f4ba83c 96
9aadd281
YW
97 r = sd_device_get_parent(device, &parent);
98 if (r < 0)
99 return r;
0f4ba83c 100
9aadd281
YW
101 r = sd_device_get_subsystem(parent, &subsystem);
102 if (r < 0)
103 return r;
0f4ba83c 104
9aadd281
YW
105 r = sd_device_get_sysname(parent, &sysname);
106 if (r < 0)
107 return r;
0f4ba83c
LP
108
109 if (streq(subsystem, "drm")) {
110 const char *c;
111
112 c = startswith(sysname, "card");
113 if (!c)
9aadd281 114 return -ENODATA;
0f4ba83c 115
938d2699 116 c += strspn(c, DIGITS);
85ff4150 117 if (*c == '-' && !STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-", "-eDP-"))
0f4ba83c 118 /* A connector DRM device, let's ignore all but LVDS and eDP! */
d17d66f0 119 return -EOPNOTSUPP;
0f4ba83c 120
9aadd281
YW
121 } else if (streq(subsystem, "pci") &&
122 sd_device_get_sysattr_value(parent, "class", &value) >= 0) {
afa8ffae 123 unsigned long class;
0f4ba83c 124
9aadd281
YW
125 r = safe_atolu(value, &class);
126 if (r < 0)
127 return log_warning_errno(r, "Cannot parse PCI class '%s' of device %s:%s: %m",
128 value, subsystem, sysname);
0f4ba83c 129
9aadd281 130 /* Graphics card */
e0504dd0 131 if (class == PCI_CLASS_GRAPHICS_CARD) {
e8596ca5 132 *ret = parent;
9aadd281 133 return 0;
0f4ba83c
LP
134 }
135
9aadd281 136 } else if (streq(subsystem, "platform")) {
e8596ca5 137 *ret = parent;
9aadd281
YW
138 return 0;
139 }
0f4ba83c 140
9aadd281 141 return find_pci_or_platform_parent(parent, ret);
0f4ba83c
LP
142}
143
9aadd281
YW
144static int same_device(sd_device *a, sd_device *b) {
145 const char *a_val, *b_val;
146 int r;
147
0f4ba83c
LP
148 assert(a);
149 assert(b);
150
9aadd281
YW
151 r = sd_device_get_subsystem(a, &a_val);
152 if (r < 0)
153 return r;
154
155 r = sd_device_get_subsystem(b, &b_val);
156 if (r < 0)
157 return r;
0f4ba83c 158
403660c5 159 if (!streq(a_val, b_val))
0f4ba83c
LP
160 return false;
161
9aadd281
YW
162 r = sd_device_get_sysname(a, &a_val);
163 if (r < 0)
164 return r;
165
166 r = sd_device_get_sysname(b, &b_val);
167 if (r < 0)
168 return r;
169
403660c5 170 return streq(a_val, b_val);
0f4ba83c
LP
171}
172
9aadd281
YW
173static int validate_device(sd_device *device) {
174 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerate = NULL;
b2f77b5e 175 const char *v, *sysname, *subsystem;
e8596ca5 176 sd_device *parent, *other;
0f4ba83c
LP
177 int r;
178
0f4ba83c
LP
179 assert(device);
180
b77c9299
YW
181 /* Verify whether we should actually care for a specific backlight device. For backlight devices
182 * there might be multiple ways to access the same control: "firmware" (i.e. ACPI), "platform"
183 * (i.e. via the machine's EC) and "raw" (via the graphics card). In general we should prefer
184 * "firmware" (i.e. ACPI) or "platform" access over "raw" access, in order not to confuse the
185 * BIOS/EC, and compatibility with possible low-level hotkey handling of screen brightness. The
186 * kernel will already make sure to expose only one of "firmware" and "platform" for the same
187 * device to userspace. However, we still need to make sure that we use "raw" only if no
188 * "firmware" or "platform" device for the same device exists. */
0f4ba83c 189
b2f77b5e
YW
190 r = sd_device_get_sysname(device, &sysname);
191 if (r < 0)
192 return log_device_debug_errno(device, r, "Failed to get sysname: %m");
193
9aadd281
YW
194 r = sd_device_get_subsystem(device, &subsystem);
195 if (r < 0)
b2f77b5e 196 return log_device_debug_errno(device, r, "Failed to get subsystem: %m");
9aadd281 197 if (!streq(subsystem, "backlight"))
0f4ba83c
LP
198 return true;
199
9aadd281
YW
200 r = sd_device_get_sysattr_value(device, "type", &v);
201 if (r < 0)
b2f77b5e 202 return log_device_debug_errno(device, r, "Failed to read 'type' sysattr: %m");
9aadd281 203 if (!streq(v, "raw"))
0f4ba83c
LP
204 return true;
205
9aadd281
YW
206 r = find_pci_or_platform_parent(device, &parent);
207 if (r < 0)
b2f77b5e 208 return log_device_debug_errno(device, r, "Failed to find PCI or platform parent: %m");
0f4ba83c 209
9aadd281
YW
210 r = sd_device_get_subsystem(parent, &subsystem);
211 if (r < 0)
b2f77b5e
YW
212 return log_device_debug_errno(parent, r, "Failed to get subsystem: %m");
213
214 if (DEBUG_LOGGING) {
215 const char *s = NULL;
216
217 (void) sd_device_get_syspath(parent, &s);
218 log_device_debug(device, "Found %s parent device: %s", subsystem, strna(s));
219 }
0f4ba83c 220
9aadd281
YW
221 r = sd_device_enumerator_new(&enumerate);
222 if (r < 0)
b2f77b5e 223 return log_oom_debug();
0f4ba83c 224
9aadd281 225 r = sd_device_enumerator_allow_uninitialized(enumerate);
0f4ba83c 226 if (r < 0)
b2f77b5e 227 return log_debug_errno(r, "Failed to allow uninitialized devices: %m");
0f4ba83c 228
b2f77b5e 229 r = sd_device_enumerator_add_match_subsystem(enumerate, "backlight", /* match = */ true);
0f4ba83c 230 if (r < 0)
b2f77b5e 231 return log_debug_errno(r, "Failed to add subsystem match: %m");
0f4ba83c 232
f8ff4b60
YW
233 r = sd_device_enumerator_add_nomatch_sysname(enumerate, sysname);
234 if (r < 0)
235 return log_debug_errno(r, "Failed to add sysname unmatch: %m");
236
237 r = sd_device_enumerator_add_match_sysattr(enumerate, "type", "platform", /* match = */ true);
238 if (r < 0)
239 return log_debug_errno(r, "Failed to add sysattr match: %m");
240
241 r = sd_device_enumerator_add_match_sysattr(enumerate, "type", "firmware", /* match = */ true);
242 if (r < 0)
243 return log_debug_errno(r, "Failed to add sysattr match: %m");
244
e0504dd0
YW
245 if (streq(subsystem, "pci")) {
246 r = has_multiple_graphics_cards();
247 if (r < 0)
248 return log_debug_errno(r, "Failed to check if the system has multiple graphics cards: %m");
249 if (r > 0) {
250 /* If the system has multiple graphics cards, then we cannot associate platform
251 * devices on non-PCI bus (especially WMI bus) with PCI devices. Let's ignore all
252 * backlight devices that do not have the same parent PCI device. */
253 log_debug("Found multiple graphics cards on PCI bus. "
254 "Skipping to associate platform backlight devices on non-PCI bus.");
255
256 r = sd_device_enumerator_add_match_parent(enumerate, parent);
257 if (r < 0)
258 return log_debug_errno(r, "Failed to add parent match: %m");
259 }
260 }
261
8437c059 262 FOREACH_DEVICE(enumerate, other) {
9aadd281 263 const char *other_subsystem;
e8596ca5 264 sd_device *other_parent;
0f4ba83c 265
b77c9299
YW
266 /* OK, so there's another backlight device, and it's a platform or firmware device.
267 * Let's see if we can verify it belongs to the same device as ours. */
b2f77b5e
YW
268 r = find_pci_or_platform_parent(other, &other_parent);
269 if (r < 0) {
270 log_device_debug_errno(other, r, "Failed to get PCI or platform parent, ignoring: %m");
0f4ba83c 271 continue;
b2f77b5e 272 }
0f4ba83c 273
ba6c9b79 274 if (same_device(parent, other_parent) > 0) {
9aadd281 275 /* Both have the same PCI parent, that means we are out. */
b2f77b5e
YW
276 if (DEBUG_LOGGING) {
277 const char *other_sysname = NULL, *other_type = NULL;
278
279 (void) sd_device_get_sysname(other, &other_sysname);
280 (void) sd_device_get_sysattr_value(other, "type", &other_type);
281 log_device_debug(device,
282 "Found another %s backlight device %s on the same PCI, skipping.",
283 strna(other_type), strna(other_sysname));
284 }
0f4ba83c
LP
285 return false;
286 }
287
b2f77b5e
YW
288 r = sd_device_get_subsystem(other_parent, &other_subsystem);
289 if (r < 0) {
290 log_device_debug_errno(other_parent, r, "Failed to get subsystem, ignoring: %m");
9aadd281 291 continue;
b2f77b5e 292 }
9aadd281
YW
293
294 if (streq(other_subsystem, "platform") && streq(subsystem, "pci")) {
9aadd281 295 /* The other is connected to the platform bus and we are a PCI device, that also means we are out. */
b2f77b5e
YW
296 if (DEBUG_LOGGING) {
297 const char *other_sysname = NULL, *other_type = NULL;
298
299 (void) sd_device_get_sysname(other, &other_sysname);
300 (void) sd_device_get_sysattr_value(other, "type", &other_type);
301 log_device_debug(device,
302 "Found another %s backlight device %s, which has higher precedence, skipping.",
303 strna(other_type), strna(other_sysname));
304 }
0f4ba83c
LP
305 return false;
306 }
307 }
308
309 return true;
310}
311
9aadd281 312static int get_max_brightness(sd_device *device, unsigned *ret) {
c0391616 313 const char *s;
9aadd281 314 int r;
7b909d74 315
9aadd281
YW
316 assert(device);
317 assert(ret);
318
c0391616 319 r = sd_device_get_sysattr_value(device, "max_brightness", &s);
9aadd281 320 if (r < 0)
87a9a197 321 return log_device_warning_errno(device, r, "Failed to read 'max_brightness' attribute: %m");
7b909d74 322
c0391616 323 r = safe_atou(s, ret);
9aadd281 324 if (r < 0)
c0391616 325 return log_device_warning_errno(device, r, "Failed to parse 'max_brightness' \"%s\": %m", s);
7b909d74 326
9aadd281 327 return 0;
3cadce7d
TB
328}
329
3bacb7e7
YW
330static int clamp_brightness(sd_device *device, bool saved, unsigned max_brightness, unsigned *brightness) {
331 unsigned new_brightness, min_brightness;
4cd2b2cf 332 const char *subsystem;
3e44d24d 333 int r;
3cadce7d 334
3bacb7e7
YW
335 assert(device);
336 assert(brightness);
7b909d74 337
b77c9299
YW
338 /* Some systems turn the backlight all the way off at the lowest levels. This clamps the saved
339 * brightness to at least 1 or 5% of max_brightness in case of 'backlight' subsystem. This
340 * avoids preserving an unreadably dim screen, which would otherwise force the user to disable
341 * state restoration. */
342
9aadd281
YW
343 r = sd_device_get_subsystem(device, &subsystem);
344 if (r < 0)
87a9a197 345 return log_device_warning_errno(device, r, "Failed to get device subsystem: %m");
9aadd281
YW
346
347 if (streq(subsystem, "backlight"))
4cd2b2cf
DT
348 min_brightness = MAX(1U, max_brightness/20);
349 else
350 min_brightness = 0;
351
3bacb7e7
YW
352 new_brightness = CLAMP(*brightness, min_brightness, max_brightness);
353 if (new_brightness != *brightness)
354 log_device_info(device, "%s brightness %u is %s to %u.",
355 saved ? "Saved" : "Current",
356 *brightness,
357 new_brightness > *brightness ?
87a9a197 358 "too low; increasing" : "too high; decreasing",
3bacb7e7 359 new_brightness);
9aadd281 360
3bacb7e7 361 *brightness = new_brightness;
9aadd281 362 return 0;
7b909d74
JT
363}
364
9aadd281 365static bool shall_clamp(sd_device *d) {
4432b941
SR
366 const char *s;
367 int r;
368
369 assert(d);
370
9aadd281 371 r = sd_device_get_property_value(d, "ID_BACKLIGHT_CLAMP", &s);
87a9a197 372 if (r < 0) {
06d98bdc
YW
373 if (r != -ENOENT)
374 log_device_debug_errno(d, r, "Failed to get ID_BACKLIGHT_CLAMP property, ignoring: %m");
4432b941 375 return true;
87a9a197 376 }
4432b941
SR
377
378 r = parse_boolean(s);
379 if (r < 0) {
87a9a197 380 log_device_debug_errno(d, r, "Failed to parse ID_BACKLIGHT_CLAMP property, ignoring: %m");
4432b941
SR
381 return true;
382 }
383
384 return r;
385}
386
3bacb7e7
YW
387static int read_brightness(sd_device *device, unsigned max_brightness, unsigned *ret_brightness) {
388 const char *subsystem, *value;
389 unsigned brightness;
437b9a7f
YW
390 int r;
391
392 assert(device);
3bacb7e7 393 assert(ret_brightness);
437b9a7f
YW
394
395 r = sd_device_get_subsystem(device, &subsystem);
396 if (r < 0)
397 return log_device_debug_errno(device, r, "Failed to get subsystem: %m");
398
399 if (streq(subsystem, "backlight")) {
3bacb7e7
YW
400 r = sd_device_get_sysattr_value(device, "actual_brightness", &value);
401 if (r == -ENOENT) {
402 log_device_debug_errno(device, r, "Failed to read 'actual_brightness' attribute, "
403 "fall back to use 'brightness' attribute: %m");
404 goto use_brightness;
405 }
406 if (r < 0)
437b9a7f
YW
407 return log_device_debug_errno(device, r, "Failed to read 'actual_brightness' attribute: %m");
408
3bacb7e7
YW
409 r = safe_atou(value, &brightness);
410 if (r < 0) {
411 log_device_debug_errno(device, r, "Failed to parse 'actual_brightness' attribute, "
412 "fall back to use 'brightness' attribute: %s", value);
413 goto use_brightness;
414 }
415
416 if (brightness > max_brightness) {
417 log_device_debug(device, "actual_brightness=%u is larger than max_brightness=%u, "
418 "fall back to use 'brightness' attribute", brightness, max_brightness);
419 goto use_brightness;
420 }
421
8dc1ad04 422 log_device_debug(device, "Current actual_brightness is %u", brightness);
3bacb7e7
YW
423 *ret_brightness = brightness;
424 return 0;
437b9a7f
YW
425 }
426
3bacb7e7
YW
427use_brightness:
428 r = sd_device_get_sysattr_value(device, "brightness", &value);
437b9a7f
YW
429 if (r < 0)
430 return log_device_debug_errno(device, r, "Failed to read 'brightness' attribute: %m");
431
3bacb7e7
YW
432 r = safe_atou(value, &brightness);
433 if (r < 0)
434 return log_device_debug_errno(device, r, "Failed to parse 'brightness' attribute: %s", value);
435
436 if (brightness > max_brightness)
437 return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
438 "brightness=%u is larger than max_brightness=%u",
439 brightness, max_brightness);
440
8dc1ad04 441 log_device_debug(device, "Current brightness is %u", brightness);
3bacb7e7 442 *ret_brightness = brightness;
437b9a7f
YW
443 return 0;
444}
445
1ddae15a 446static int run(int argc, char *argv[]) {
9aadd281 447 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
3e44d24d
LP
448 _cleanup_free_ char *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL;
449 const char *sysname, *path_id, *ss, *saved;
3bacb7e7 450 unsigned max_brightness, brightness;
3731acf1
LP
451 int r;
452
d2acb93d 453 log_setup();
daa227a3 454
542bb9be 455 if (argv_looks_like_help(argc, argv))
b23728ec
P
456 return help();
457
74f1bb5c
YW
458 if (argc != 3)
459 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires two arguments.");
3731acf1 460
7a9737bc
YW
461 if (!STR_IN_SET(argv[1], "load", "save"))
462 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb %s.", argv[1]);
463
3731acf1
LP
464 umask(0022);
465
ef5bfcf6 466 r = mkdir_p("/var/lib/systemd/backlight", 0755);
1ddae15a
YW
467 if (r < 0)
468 return log_error_errno(r, "Failed to create backlight directory /var/lib/systemd/backlight: %m");
3731acf1 469
0f4ba83c 470 sysname = strchr(argv[2], ':');
74f1bb5c
YW
471 if (!sysname)
472 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Requires a subsystem and sysname pair specifying a backlight device.");
0f4ba83c 473
2f82562b 474 ss = strndupa_safe(argv[2], sysname - argv[2]);
0f4ba83c
LP
475
476 sysname++;
477
74f1bb5c
YW
478 if (!STR_IN_SET(ss, "backlight", "leds"))
479 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a backlight or LED device: '%s:%s'", ss, sysname);
0f4ba83c 480
9aadd281 481 r = sd_device_new_from_subsystem_sysname(&device, ss, sysname);
f0f65087
YW
482 if (r < 0) {
483 bool ignore = r == -ENODEV;
484
485 /* Some drivers, e.g. for AMD GPU, removes acpi backlight device soon after it is added.
486 * See issue #21997. */
487 log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r,
488 "Failed to get backlight or LED device '%s:%s'%s: %m",
489 ss, sysname, ignore ? ", ignoring" : "");
490 return ignore ? 0 : r;
491 }
3731acf1 492
b77c9299
YW
493 /* If max_brightness is 0, then there is no actual backlight device. This happens on desktops
494 * with Asus mainboards that load the eeepc-wmi module. */
1ddae15a
YW
495 if (get_max_brightness(device, &max_brightness) < 0)
496 return 0;
3cadce7d 497
c0391616
ZJS
498 if (max_brightness == 0) {
499 log_device_warning(device, "Maximum brightness is 0, ignoring device.");
500 return 0;
501 }
502
503 log_device_debug(device, "Maximum brightness is %u", max_brightness);
504
be3f52f4 505 escaped_ss = cescape(ss);
1ddae15a
YW
506 if (!escaped_ss)
507 return log_oom();
be3f52f4
LP
508
509 escaped_sysname = cescape(sysname);
1ddae15a
YW
510 if (!escaped_sysname)
511 return log_oom();
be3f52f4 512
9aadd281 513 if (sd_device_get_property_value(device, "ID_PATH", &path_id) >= 0) {
be3f52f4 514 escaped_path_id = cescape(path_id);
1ddae15a
YW
515 if (!escaped_path_id)
516 return log_oom();
be3f52f4 517
3e44d24d 518 saved = strjoina("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname);
be3f52f4 519 } else
3e44d24d 520 saved = strjoina("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname);
3731acf1 521
b77c9299
YW
522 /* If there are multiple conflicting backlight devices, then their probing at boot-time might
523 * happen in any order. This means the validity checking of the device then is not reliable,
524 * since it might not see other devices conflicting with a specific backlight. To deal with
525 * this, we will actively delete backlight state files at shutdown (where device probing should
526 * be complete), so that the validity check at boot time doesn't have to be reliable. */
0f4ba83c 527
b76388e1 528 if (streq(argv[1], "load")) {
3731acf1 529 _cleanup_free_ char *value = NULL;
4432b941 530 bool clamp;
3731acf1 531
934ae16b 532 if (shall_restore_state() == 0)
1ddae15a 533 return 0;
b76388e1 534
9aadd281 535 if (validate_device(device) == 0)
1ddae15a 536 return 0;
0f4ba83c 537
4432b941
SR
538 clamp = shall_clamp(device);
539
3731acf1 540 r = read_one_line_file(saved, &value);
3bacb7e7
YW
541 if (r < 0 && r != -ENOENT)
542 return log_error_errno(r, "Failed to read %s: %m", saved);
543 if (r > 0) {
544 r = safe_atou(value, &brightness);
545 if (r < 0) {
8dc1ad04
YW
546 log_warning_errno(r, "Failed to parse saved brightness '%s', removing %s.",
547 value, saved);
3bacb7e7
YW
548 (void) unlink(saved);
549 } else {
8dc1ad04 550 log_debug("Using saved brightness %u.", brightness);
3bacb7e7
YW
551 if (clamp)
552 (void) clamp_brightness(device, true, max_brightness, &brightness);
553
554 /* Do not fall back to read current brightness below. */
555 r = 1;
556 }
557 }
558 if (r <= 0) {
559 /* Fallback to clamping current brightness or exit early if clamping is not
560 * supported/enabled. */
4432b941 561 if (!clamp)
1ddae15a 562 return 0;
3731acf1 563
3bacb7e7 564 r = read_brightness(device, max_brightness, &brightness);
1ddae15a 565 if (r < 0)
437b9a7f 566 return log_device_error_errno(device, r, "Failed to read current brightness: %m");
4432b941 567
3bacb7e7
YW
568 (void) clamp_brightness(device, false, max_brightness, &brightness);
569 }
7b909d74 570
3bacb7e7 571 r = sd_device_set_sysattr_valuef(device, "brightness", "%u", brightness);
1ddae15a
YW
572 if (r < 0)
573 return log_device_error_errno(device, r, "Failed to write system 'brightness' attribute: %m");
3731acf1
LP
574
575 } else if (streq(argv[1], "save")) {
9aadd281 576 if (validate_device(device) == 0) {
6990fb6b 577 (void) unlink(saved);
1ddae15a 578 return 0;
0f4ba83c
LP
579 }
580
3bacb7e7 581 r = read_brightness(device, max_brightness, &brightness);
1ddae15a 582 if (r < 0)
437b9a7f 583 return log_device_error_errno(device, r, "Failed to read current brightness: %m");
3731acf1 584
3bacb7e7 585 r = write_string_filef(saved, WRITE_STRING_FILE_CREATE, "%u", brightness);
1ddae15a
YW
586 if (r < 0)
587 return log_device_error_errno(device, r, "Failed to write %s: %m", saved);
3731acf1 588
74f1bb5c 589 } else
04499a70 590 assert_not_reached();
3731acf1 591
1ddae15a 592 return 0;
3731acf1 593}
1ddae15a
YW
594
595DEFINE_MAIN_FUNCTION(run);