1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "device-private.h"
6 #include "device-util.h"
7 #include "string-util.h"
8 #include "battery-util.h"
10 #define BATTERY_LOW_CAPACITY_LEVEL 5
12 static int device_is_power_sink(sd_device
*device
) {
13 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
14 bool found_source
= false, found_sink
= false;
20 /* USB-C power supply device has two power roles: source or sink. See,
21 * https://docs.kernel.org/admin-guide/abi-testing.html#abi-file-testing-sysfs-class-typec */
23 r
= sd_device_enumerator_new(&e
);
27 r
= sd_device_enumerator_allow_uninitialized(e
);
31 r
= sd_device_enumerator_add_match_subsystem(e
, "typec", true);
35 r
= sd_device_get_parent(device
, &parent
);
39 r
= sd_device_enumerator_add_match_parent(e
, parent
);
43 FOREACH_DEVICE(e
, d
) {
46 r
= sd_device_get_sysattr_value(d
, "power_role", &val
);
49 log_device_debug_errno(d
, r
, "Failed to read 'power_role' sysfs attribute, ignoring: %m");
53 if (strstr(val
, "[source]")) {
55 log_device_debug(d
, "The USB type-C port is in power source mode.");
56 } else if (strstr(val
, "[sink]")) {
58 log_device_debug(d
, "The USB type-C port is in power sink mode.");
63 log_device_debug(device
, "The USB type-C device has at least one port in power sink mode.");
64 else if (!found_source
)
65 log_device_debug(device
, "The USB type-C device has no port in power source mode, assuming the device is in power sink mode.");
67 log_device_debug(device
, "All USB type-C ports are in power source mode.");
69 return found_sink
|| !found_source
;
72 static bool battery_is_discharging(sd_device
*d
) {
78 r
= sd_device_get_sysattr_value(d
, "scope", &val
);
81 log_device_debug_errno(d
, r
, "Failed to read 'scope' sysfs attribute, ignoring: %m");
82 } else if (streq(val
, "Device")) {
83 log_device_debug(d
, "The power supply is a device battery, ignoring device.");
87 r
= device_get_sysattr_bool(d
, "present");
89 log_device_debug_errno(d
, r
, "Failed to read 'present' sysfs attribute, assuming the battery is present: %m");
91 log_device_debug(d
, "The battery is not present, ignoring the power supply.");
95 /* Possible values: "Unknown", "Charging", "Discharging", "Not charging", "Full" */
96 r
= sd_device_get_sysattr_value(d
, "status", &val
);
98 log_device_debug_errno(d
, r
, "Failed to read 'status' sysfs attribute, assuming the battery is discharging: %m");
101 if (!streq(val
, "Discharging")) {
102 log_device_debug(d
, "The battery status is '%s', assuming the battery is not used as a power source of this machine.", val
);
109 int on_ac_power(void) {
110 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
111 bool found_ac_online
= false, found_discharging_battery
= false;
114 r
= sd_device_enumerator_new(&e
);
118 r
= sd_device_enumerator_allow_uninitialized(e
);
122 r
= sd_device_enumerator_add_match_subsystem(e
, "power_supply", true);
126 FOREACH_DEVICE(e
, d
) {
128 * https://github.com/torvalds/linux/blob/4eef766b7d4d88f0b984781bc1bcb574a6eafdc7/include/linux/power_supply.h#L176
129 * for defined power source types. Also see:
130 * https://docs.kernel.org/admin-guide/abi-testing.html#abi-file-testing-sysfs-class-power */
133 r
= sd_device_get_sysattr_value(d
, "type", &val
);
135 log_device_debug_errno(d
, r
, "Failed to read 'type' sysfs attribute, ignoring device: %m");
139 /* Ignore USB-C power supply in source mode. See issue #21988. */
140 if (streq(val
, "USB")) {
141 r
= device_is_power_sink(d
);
144 log_device_debug_errno(d
, r
, "Failed to determine the current power role, ignoring device: %m");
146 log_device_debug(d
, "USB power supply is in source mode, ignoring device.");
151 if (streq(val
, "Battery")) {
152 if (battery_is_discharging(d
)) {
153 found_discharging_battery
= true;
154 log_device_debug(d
, "The power supply is a battery and currently discharging.");
159 r
= device_get_sysattr_unsigned(d
, "online", NULL
);
161 log_device_debug_errno(d
, r
, "Failed to query 'online' sysfs attribute, ignoring device: %m");
163 } else if (r
> 0) /* At least 1 and 2 are defined as different types of 'online' */
164 found_ac_online
= true;
166 log_device_debug(d
, "The power supply is currently %s.", r
> 0 ? "online" : "offline");
169 if (found_ac_online
) {
170 log_debug("Found at least one online non-battery power supply, system is running on AC.");
172 } else if (found_discharging_battery
) {
173 log_debug("Found at least one discharging battery and no online power sources, assuming system is running from battery.");
176 log_debug("No power supply reported online and no discharging battery found, assuming system is running on AC.");
181 /* Get the list of batteries */
182 int battery_enumerator_new(sd_device_enumerator
**ret
) {
183 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
188 r
= sd_device_enumerator_new(&e
);
192 r
= sd_device_enumerator_add_match_subsystem(e
, "power_supply", /* match = */ true);
196 r
= sd_device_enumerator_allow_uninitialized(e
);
200 r
= sd_device_enumerator_add_match_sysattr(e
, "type", "Battery", /* match = */ true);
204 r
= sd_device_enumerator_add_match_sysattr(e
, "present", "1", /* match = */ true);
208 r
= sd_device_enumerator_add_match_sysattr(e
, "scope", "Device", /* match = */ false);
216 /* Battery percentage capacity fetched from capacity file and if in range 0-100 then returned */
217 int battery_read_capacity_percentage(sd_device
*dev
) {
218 int battery_capacity
, r
;
222 r
= device_get_sysattr_int(dev
, "capacity", &battery_capacity
);
224 return log_device_debug_errno(dev
, r
, "Failed to read/parse POWER_SUPPLY_CAPACITY: %m");
226 if (battery_capacity
< 0 || battery_capacity
> 100)
227 return log_device_debug_errno(dev
, SYNTHETIC_ERRNO(ERANGE
), "Invalid battery capacity: %d", battery_capacity
);
229 return battery_capacity
;
232 /* If a battery whose percentage capacity is <= 5% exists, and we're not on AC power, return success */
233 int battery_is_discharging_and_low(void) {
234 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
235 bool unsure
= false, found_low
= false;
238 /* We have not used battery capacity_level since value is set to full
239 * or Normal in case ACPI is not working properly. In case of no battery
240 * 0 will be returned and system will be suspended for 1st cycle then hibernated */
244 log_warning_errno(r
, "Failed to check if the system is running on AC, assuming it is not: %m");
248 r
= battery_enumerator_new(&e
);
250 return log_error_errno(r
, "Failed to initialize battery enumerator: %m");
252 FOREACH_DEVICE(e
, dev
) {
255 level
= battery_read_capacity_percentage(dev
);
261 if (level
> BATTERY_LOW_CAPACITY_LEVEL
) { /* Found a charged battery */
263 found_low
? LOG_INFO
: LOG_DEBUG
,
264 "Found battery with capacity above threshold (%d%% > %d%%).",
265 level
, BATTERY_LOW_CAPACITY_LEVEL
);
270 "Found battery with capacity below threshold (%d%% <= %d%%).",
271 level
, BATTERY_LOW_CAPACITY_LEVEL
);
275 /* If we found a battery whose state we couldn't read, don't assume we are in low battery state */
277 log_notice("Found battery with unreadable state, assuming not in low battery state.");
281 /* If found neither charged nor low batteries, assume that we aren't in low battery state */