LCOV - code coverage report
Current view: top level - backlight - backlight.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 222 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 8 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 288 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <sys/stat.h>
       4                 :            : #include <sys/types.h>
       5                 :            : #include <unistd.h>
       6                 :            : 
       7                 :            : #include "sd-device.h"
       8                 :            : 
       9                 :            : #include "alloc-util.h"
      10                 :            : #include "device-util.h"
      11                 :            : #include "escape.h"
      12                 :            : #include "fileio.h"
      13                 :            : #include "main-func.h"
      14                 :            : #include "mkdir.h"
      15                 :            : #include "parse-util.h"
      16                 :            : #include "proc-cmdline.h"
      17                 :            : #include "string-util.h"
      18                 :            : #include "strv.h"
      19                 :            : #include "util.h"
      20                 :            : 
      21                 :          0 : static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
      22                 :            :         const char *subsystem, *sysname, *value;
      23                 :            :         sd_device *parent;
      24                 :            :         int r;
      25                 :            : 
      26         [ #  # ]:          0 :         assert(device);
      27         [ #  # ]:          0 :         assert(ret);
      28                 :            : 
      29                 :          0 :         r = sd_device_get_parent(device, &parent);
      30         [ #  # ]:          0 :         if (r < 0)
      31                 :          0 :                 return r;
      32                 :            : 
      33                 :          0 :         r = sd_device_get_subsystem(parent, &subsystem);
      34         [ #  # ]:          0 :         if (r < 0)
      35                 :          0 :                 return r;
      36                 :            : 
      37                 :          0 :         r = sd_device_get_sysname(parent, &sysname);
      38         [ #  # ]:          0 :         if (r < 0)
      39                 :          0 :                 return r;
      40                 :            : 
      41         [ #  # ]:          0 :         if (streq(subsystem, "drm")) {
      42                 :            :                 const char *c;
      43                 :            : 
      44                 :          0 :                 c = startswith(sysname, "card");
      45         [ #  # ]:          0 :                 if (!c)
      46                 :          0 :                         return -ENODATA;
      47                 :            : 
      48                 :          0 :                 c += strspn(c, DIGITS);
      49         [ #  # ]:          0 :                 if (*c == '-') {
      50                 :            :                         /* A connector DRM device, let's ignore all but LVDS and eDP! */
      51   [ #  #  #  #  :          0 :                         if (!STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-"))
             #  #  #  # ]
      52                 :          0 :                                 return -EOPNOTSUPP;
      53                 :            :                 }
      54                 :            : 
      55   [ #  #  #  # ]:          0 :         } else if (streq(subsystem, "pci") &&
      56                 :          0 :                    sd_device_get_sysattr_value(parent, "class", &value) >= 0) {
      57                 :          0 :                 unsigned long class = 0;
      58                 :            : 
      59                 :          0 :                 r = safe_atolu(value, &class);
      60         [ #  # ]:          0 :                 if (r < 0)
      61         [ #  # ]:          0 :                         return log_warning_errno(r, "Cannot parse PCI class '%s' of device %s:%s: %m",
      62                 :            :                                                  value, subsystem, sysname);
      63                 :            : 
      64                 :            :                 /* Graphics card */
      65         [ #  # ]:          0 :                 if (class == 0x30000) {
      66                 :          0 :                         *ret = parent;
      67                 :          0 :                         return 0;
      68                 :            :                 }
      69                 :            : 
      70         [ #  # ]:          0 :         } else if (streq(subsystem, "platform")) {
      71                 :          0 :                 *ret = parent;
      72                 :          0 :                 return 0;
      73                 :            :         }
      74                 :            : 
      75                 :          0 :         return find_pci_or_platform_parent(parent, ret);
      76                 :            : }
      77                 :            : 
      78                 :          0 : static int same_device(sd_device *a, sd_device *b) {
      79                 :            :         const char *a_val, *b_val;
      80                 :            :         int r;
      81                 :            : 
      82         [ #  # ]:          0 :         assert(a);
      83         [ #  # ]:          0 :         assert(b);
      84                 :            : 
      85                 :          0 :         r = sd_device_get_subsystem(a, &a_val);
      86         [ #  # ]:          0 :         if (r < 0)
      87                 :          0 :                 return r;
      88                 :            : 
      89                 :          0 :         r = sd_device_get_subsystem(b, &b_val);
      90         [ #  # ]:          0 :         if (r < 0)
      91                 :          0 :                 return r;
      92                 :            : 
      93         [ #  # ]:          0 :         if (!streq(a_val, b_val))
      94                 :          0 :                 return false;
      95                 :            : 
      96                 :          0 :         r = sd_device_get_sysname(a, &a_val);
      97         [ #  # ]:          0 :         if (r < 0)
      98                 :          0 :                 return r;
      99                 :            : 
     100                 :          0 :         r = sd_device_get_sysname(b, &b_val);
     101         [ #  # ]:          0 :         if (r < 0)
     102                 :          0 :                 return r;
     103                 :            : 
     104                 :          0 :         return streq(a_val, b_val);
     105                 :            : }
     106                 :            : 
     107                 :          0 : static int validate_device(sd_device *device) {
     108                 :          0 :         _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerate = NULL;
     109                 :            :         const char *v, *subsystem;
     110                 :            :         sd_device *parent, *other;
     111                 :            :         int r;
     112                 :            : 
     113         [ #  # ]:          0 :         assert(device);
     114                 :            : 
     115                 :            :         /* Verify whether we should actually care for a specific
     116                 :            :          * backlight device. For backlight devices there might be
     117                 :            :          * multiple ways to access the same control: "firmware"
     118                 :            :          * (i.e. ACPI), "platform" (i.e. via the machine's EC) and
     119                 :            :          * "raw" (via the graphics card). In general we should prefer
     120                 :            :          * "firmware" (i.e. ACPI) or "platform" access over "raw"
     121                 :            :          * access, in order not to confuse the BIOS/EC, and
     122                 :            :          * compatibility with possible low-level hotkey handling of
     123                 :            :          * screen brightness. The kernel will already make sure to
     124                 :            :          * expose only one of "firmware" and "platform" for the same
     125                 :            :          * device to userspace. However, we still need to make sure
     126                 :            :          * that we use "raw" only if no "firmware" or "platform"
     127                 :            :          * device for the same device exists. */
     128                 :            : 
     129                 :          0 :         r = sd_device_get_subsystem(device, &subsystem);
     130         [ #  # ]:          0 :         if (r < 0)
     131                 :          0 :                 return r;
     132         [ #  # ]:          0 :         if (!streq(subsystem, "backlight"))
     133                 :          0 :                 return true;
     134                 :            : 
     135                 :          0 :         r = sd_device_get_sysattr_value(device, "type", &v);
     136         [ #  # ]:          0 :         if (r < 0)
     137                 :          0 :                 return r;
     138         [ #  # ]:          0 :         if (!streq(v, "raw"))
     139                 :          0 :                 return true;
     140                 :            : 
     141                 :          0 :         r = find_pci_or_platform_parent(device, &parent);
     142         [ #  # ]:          0 :         if (r < 0)
     143                 :          0 :                 return r;
     144                 :            : 
     145                 :          0 :         r = sd_device_get_subsystem(parent, &subsystem);
     146         [ #  # ]:          0 :         if (r < 0)
     147                 :          0 :                 return r;
     148                 :            : 
     149                 :          0 :         r = sd_device_enumerator_new(&enumerate);
     150         [ #  # ]:          0 :         if (r < 0)
     151                 :          0 :                 return r;
     152                 :            : 
     153                 :          0 :         r = sd_device_enumerator_allow_uninitialized(enumerate);
     154         [ #  # ]:          0 :         if (r < 0)
     155                 :          0 :                 return r;
     156                 :            : 
     157                 :          0 :         r = sd_device_enumerator_add_match_subsystem(enumerate, "backlight", true);
     158         [ #  # ]:          0 :         if (r < 0)
     159                 :          0 :                 return r;
     160                 :            : 
     161         [ #  # ]:          0 :         FOREACH_DEVICE(enumerate, other) {
     162                 :            :                 const char *other_subsystem;
     163                 :            :                 sd_device *other_parent;
     164                 :            : 
     165         [ #  # ]:          0 :                 if (same_device(device, other) > 0)
     166                 :          0 :                         continue;
     167                 :            : 
     168         [ #  # ]:          0 :                 if (sd_device_get_sysattr_value(other, "type", &v) < 0 ||
     169         [ #  # ]:          0 :                     !STR_IN_SET(v, "platform", "firmware"))
     170                 :          0 :                         continue;
     171                 :            : 
     172                 :            :                 /* OK, so there's another backlight device, and it's a
     173                 :            :                  * platform or firmware device, so, let's see if we
     174                 :            :                  * can verify it belongs to the same device as ours. */
     175         [ #  # ]:          0 :                 if (find_pci_or_platform_parent(other, &other_parent) < 0)
     176                 :          0 :                         continue;
     177                 :            : 
     178         [ #  # ]:          0 :                 if (same_device(parent, other_parent)) {
     179                 :          0 :                         const char *device_sysname = NULL, *other_sysname = NULL;
     180                 :            : 
     181                 :            :                         /* Both have the same PCI parent, that means we are out. */
     182                 :            : 
     183                 :          0 :                         (void) sd_device_get_sysname(device, &device_sysname);
     184                 :          0 :                         (void) sd_device_get_sysname(other, &other_sysname);
     185                 :            : 
     186         [ #  # ]:          0 :                         log_debug("Skipping backlight device %s, since device %s is on same PCI device and takes precedence.",
     187                 :            :                                   device_sysname, other_sysname);
     188                 :          0 :                         return false;
     189                 :            :                 }
     190                 :            : 
     191         [ #  # ]:          0 :                 if (sd_device_get_subsystem(other_parent, &other_subsystem) < 0)
     192                 :          0 :                         continue;
     193                 :            : 
     194   [ #  #  #  # ]:          0 :                 if (streq(other_subsystem, "platform") && streq(subsystem, "pci")) {
     195                 :          0 :                         const char *device_sysname = NULL, *other_sysname = NULL;
     196                 :            : 
     197                 :            :                         /* The other is connected to the platform bus and we are a PCI device, that also means we are out. */
     198                 :            : 
     199                 :          0 :                         (void) sd_device_get_sysname(device, &device_sysname);
     200                 :          0 :                         (void) sd_device_get_sysname(other, &other_sysname);
     201                 :            : 
     202         [ #  # ]:          0 :                         log_debug("Skipping backlight device %s, since device %s is a platform device and takes precedence.",
     203                 :            :                                   device_sysname, other_sysname);
     204                 :          0 :                         return false;
     205                 :            :                 }
     206                 :            :         }
     207                 :            : 
     208                 :          0 :         return true;
     209                 :            : }
     210                 :            : 
     211                 :          0 : static int get_max_brightness(sd_device *device, unsigned *ret) {
     212                 :            :         const char *max_brightness_str;
     213                 :            :         unsigned max_brightness;
     214                 :            :         int r;
     215                 :            : 
     216         [ #  # ]:          0 :         assert(device);
     217         [ #  # ]:          0 :         assert(ret);
     218                 :            : 
     219                 :          0 :         r = sd_device_get_sysattr_value(device, "max_brightness", &max_brightness_str);
     220         [ #  # ]:          0 :         if (r < 0)
     221   [ #  #  #  #  :          0 :                 return log_device_warning_errno(device, r, "Failed to read 'max_brightness' attribute: %m");
                   #  # ]
     222                 :            : 
     223                 :          0 :         r = safe_atou(max_brightness_str, &max_brightness);
     224         [ #  # ]:          0 :         if (r < 0)
     225   [ #  #  #  #  :          0 :                 return log_device_warning_errno(device, r, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str);
                   #  # ]
     226                 :            : 
     227         [ #  # ]:          0 :         if (max_brightness <= 0) {
     228   [ #  #  #  #  :          0 :                 log_device_warning(device, "Maximum brightness is 0, ignoring device.");
                   #  # ]
     229                 :          0 :                 return -EINVAL;
     230                 :            :         }
     231                 :            : 
     232                 :          0 :         *ret = max_brightness;
     233                 :          0 :         return 0;
     234                 :            : }
     235                 :            : 
     236                 :            : /* Some systems turn the backlight all the way off at the lowest levels.
     237                 :            :  * clamp_brightness clamps the saved brightness to at least 1 or 5% of
     238                 :            :  * max_brightness in case of 'backlight' subsystem. This avoids preserving
     239                 :            :  * an unreadably dim screen, which would otherwise force the user to
     240                 :            :  * disable state restoration. */
     241                 :          0 : static int clamp_brightness(sd_device *device, char **value, unsigned max_brightness) {
     242                 :            :         unsigned brightness, new_brightness, min_brightness;
     243                 :            :         const char *subsystem;
     244                 :            :         int r;
     245                 :            : 
     246         [ #  # ]:          0 :         assert(value);
     247         [ #  # ]:          0 :         assert(*value);
     248                 :            : 
     249                 :          0 :         r = safe_atou(*value, &brightness);
     250         [ #  # ]:          0 :         if (r < 0)
     251   [ #  #  #  #  :          0 :                 return log_device_warning_errno(device, r, "Failed to parse brightness \"%s\": %m", *value);
                   #  # ]
     252                 :            : 
     253                 :          0 :         r = sd_device_get_subsystem(device, &subsystem);
     254         [ #  # ]:          0 :         if (r < 0)
     255   [ #  #  #  #  :          0 :                 return log_device_warning_errno(device, r, "Failed to get device subsystem: %m");
                   #  # ]
     256                 :            : 
     257         [ #  # ]:          0 :         if (streq(subsystem, "backlight"))
     258                 :          0 :                 min_brightness = MAX(1U, max_brightness/20);
     259                 :            :         else
     260                 :          0 :                 min_brightness = 0;
     261                 :            : 
     262         [ #  # ]:          0 :         new_brightness = CLAMP(brightness, min_brightness, max_brightness);
     263         [ #  # ]:          0 :         if (new_brightness != brightness) {
     264                 :            :                 char *new_value;
     265                 :            : 
     266                 :          0 :                 r = asprintf(&new_value, "%u", new_brightness);
     267         [ #  # ]:          0 :                 if (r < 0)
     268                 :          0 :                         return log_oom();
     269                 :            : 
     270   [ #  #  #  #  :          0 :                 log_device_info(device, "Saved brightness %s %s to %s.", *value,
             #  #  #  # ]
     271                 :            :                                 new_brightness > brightness ?
     272                 :            :                                 "too low; increasing" : "too high; decreasing",
     273                 :            :                                 new_value);
     274                 :            : 
     275                 :          0 :                 free_and_replace(*value, new_value);
     276                 :            :         }
     277                 :            : 
     278                 :          0 :         return 0;
     279                 :            : }
     280                 :            : 
     281                 :          0 : static bool shall_clamp(sd_device *d) {
     282                 :            :         const char *s;
     283                 :            :         int r;
     284                 :            : 
     285         [ #  # ]:          0 :         assert(d);
     286                 :            : 
     287                 :          0 :         r = sd_device_get_property_value(d, "ID_BACKLIGHT_CLAMP", &s);
     288         [ #  # ]:          0 :         if (r < 0) {
     289   [ #  #  #  #  :          0 :                 log_device_debug_errno(d, r, "Failed to get ID_BACKLIGHT_CLAMP property, ignoring: %m");
                   #  # ]
     290                 :          0 :                 return true;
     291                 :            :         }
     292                 :            : 
     293                 :          0 :         r = parse_boolean(s);
     294         [ #  # ]:          0 :         if (r < 0) {
     295   [ #  #  #  #  :          0 :                 log_device_debug_errno(d, r, "Failed to parse ID_BACKLIGHT_CLAMP property, ignoring: %m");
                   #  # ]
     296                 :          0 :                 return true;
     297                 :            :         }
     298                 :            : 
     299                 :          0 :         return r;
     300                 :            : }
     301                 :            : 
     302                 :          0 : static int run(int argc, char *argv[]) {
     303                 :          0 :         _cleanup_(sd_device_unrefp) sd_device *device = NULL;
     304                 :          0 :         _cleanup_free_ char *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL;
     305                 :            :         const char *sysname, *path_id, *ss, *saved;
     306                 :            :         unsigned max_brightness;
     307                 :            :         int r;
     308                 :            : 
     309         [ #  # ]:          0 :         if (argc != 3) {
     310         [ #  # ]:          0 :                 log_error("This program requires two arguments.");
     311                 :          0 :                 return -EINVAL;
     312                 :            :         }
     313                 :            : 
     314                 :          0 :         log_setup_service();
     315                 :            : 
     316                 :          0 :         umask(0022);
     317                 :            : 
     318                 :          0 :         r = mkdir_p("/var/lib/systemd/backlight", 0755);
     319         [ #  # ]:          0 :         if (r < 0)
     320         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to create backlight directory /var/lib/systemd/backlight: %m");
     321                 :            : 
     322                 :          0 :         sysname = strchr(argv[2], ':');
     323         [ #  # ]:          0 :         if (!sysname) {
     324         [ #  # ]:          0 :                 log_error("Requires a subsystem and sysname pair specifying a backlight device.");
     325                 :          0 :                 return -EINVAL;
     326                 :            :         }
     327                 :            : 
     328                 :          0 :         ss = strndupa(argv[2], sysname - argv[2]);
     329                 :            : 
     330                 :          0 :         sysname++;
     331                 :            : 
     332         [ #  # ]:          0 :         if (!STR_IN_SET(ss, "backlight", "leds")) {
     333         [ #  # ]:          0 :                 log_error("Not a backlight or LED device: '%s:%s'", ss, sysname);
     334                 :          0 :                 return -EINVAL;
     335                 :            :         }
     336                 :            : 
     337                 :          0 :         r = sd_device_new_from_subsystem_sysname(&device, ss, sysname);
     338         [ #  # ]:          0 :         if (r < 0)
     339         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname);
     340                 :            : 
     341                 :            :         /* If max_brightness is 0, then there is no actual backlight
     342                 :            :          * device. This happens on desktops with Asus mainboards
     343                 :            :          * that load the eeepc-wmi module. */
     344         [ #  # ]:          0 :         if (get_max_brightness(device, &max_brightness) < 0)
     345                 :          0 :                 return 0;
     346                 :            : 
     347                 :          0 :         escaped_ss = cescape(ss);
     348         [ #  # ]:          0 :         if (!escaped_ss)
     349                 :          0 :                 return log_oom();
     350                 :            : 
     351                 :          0 :         escaped_sysname = cescape(sysname);
     352         [ #  # ]:          0 :         if (!escaped_sysname)
     353                 :          0 :                 return log_oom();
     354                 :            : 
     355         [ #  # ]:          0 :         if (sd_device_get_property_value(device, "ID_PATH", &path_id) >= 0) {
     356                 :          0 :                 escaped_path_id = cescape(path_id);
     357         [ #  # ]:          0 :                 if (!escaped_path_id)
     358                 :          0 :                         return log_oom();
     359                 :            : 
     360   [ #  #  #  #  :          0 :                 saved = strjoina("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname);
          #  #  #  #  #  
                #  #  # ]
     361                 :            :         } else
     362   [ #  #  #  #  :          0 :                 saved = strjoina("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname);
          #  #  #  #  #  
                #  #  # ]
     363                 :            : 
     364                 :            :         /* If there are multiple conflicting backlight devices, then
     365                 :            :          * their probing at boot-time might happen in any order. This
     366                 :            :          * means the validity checking of the device then is not
     367                 :            :          * reliable, since it might not see other devices conflicting
     368                 :            :          * with a specific backlight. To deal with this, we will
     369                 :            :          * actively delete backlight state files at shutdown (where
     370                 :            :          * device probing should be complete), so that the validity
     371                 :            :          * check at boot time doesn't have to be reliable. */
     372                 :            : 
     373         [ #  # ]:          0 :         if (streq(argv[1], "load")) {
     374         [ #  # ]:          0 :                 _cleanup_free_ char *value = NULL;
     375                 :            :                 bool clamp;
     376                 :            : 
     377         [ #  # ]:          0 :                 if (shall_restore_state() == 0)
     378                 :          0 :                         return 0;
     379                 :            : 
     380         [ #  # ]:          0 :                 if (validate_device(device) == 0)
     381                 :          0 :                         return 0;
     382                 :            : 
     383                 :          0 :                 clamp = shall_clamp(device);
     384                 :            : 
     385                 :          0 :                 r = read_one_line_file(saved, &value);
     386   [ #  #  #  # ]:          0 :                 if (IN_SET(r, -ENOENT, 0)) {
     387                 :            :                         const char *curval;
     388                 :            : 
     389                 :            :                         /* Fallback to clamping current brightness or exit early if
     390                 :            :                          * clamping is not supported/enabled. */
     391         [ #  # ]:          0 :                         if (!clamp)
     392                 :          0 :                                 return 0;
     393                 :            : 
     394                 :          0 :                         r = sd_device_get_sysattr_value(device, "brightness", &curval);
     395         [ #  # ]:          0 :                         if (r < 0)
     396   [ #  #  #  #  :          0 :                                 return log_device_warning_errno(device, r, "Failed to read 'brightness' attribute: %m");
                   #  # ]
     397                 :            : 
     398                 :          0 :                         value = strdup(curval);
     399         [ #  # ]:          0 :                         if (!value)
     400                 :          0 :                                 return log_oom();
     401         [ #  # ]:          0 :                 } else if (r < 0)
     402         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to read %s: %m", saved);
     403                 :            : 
     404         [ #  # ]:          0 :                 if (clamp)
     405                 :          0 :                         (void) clamp_brightness(device, &value, max_brightness);
     406                 :            : 
     407                 :          0 :                 r = sd_device_set_sysattr_value(device, "brightness", value);
     408         [ #  # ]:          0 :                 if (r < 0)
     409   [ #  #  #  #  :          0 :                         return log_device_error_errno(device, r, "Failed to write system 'brightness' attribute: %m");
                   #  # ]
     410                 :            : 
     411         [ #  # ]:          0 :         } else if (streq(argv[1], "save")) {
     412                 :            :                 const char *value;
     413                 :            : 
     414         [ #  # ]:          0 :                 if (validate_device(device) == 0) {
     415                 :          0 :                         (void) unlink(saved);
     416                 :          0 :                         return 0;
     417                 :            :                 }
     418                 :            : 
     419                 :          0 :                 r = sd_device_get_sysattr_value(device, "brightness", &value);
     420         [ #  # ]:          0 :                 if (r < 0)
     421   [ #  #  #  #  :          0 :                         return log_device_error_errno(device, r, "Failed to read system 'brightness' attribute: %m");
                   #  # ]
     422                 :            : 
     423                 :          0 :                 r = write_string_file(saved, value, WRITE_STRING_FILE_CREATE);
     424         [ #  # ]:          0 :                 if (r < 0)
     425   [ #  #  #  #  :          0 :                         return log_device_error_errno(device, r, "Failed to write %s: %m", saved);
                   #  # ]
     426                 :            : 
     427                 :            :         } else {
     428         [ #  # ]:          0 :                 log_error("Unknown verb %s.", argv[1]);
     429                 :          0 :                 return -EINVAL;
     430                 :            :         }
     431                 :            : 
     432                 :          0 :         return 0;
     433                 :            : }
     434                 :            : 
     435                 :          0 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14