diff options
-rw-r--r-- | src/shared/sleep-config.c | 63 | ||||
-rw-r--r-- | src/shared/sleep-config.h | 2 | ||||
-rw-r--r-- | src/sleep/sleep.c | 50 |
3 files changed, 114 insertions, 1 deletions
diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index d9923e9de8..efc066c4f2 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -45,6 +45,7 @@ #define BATTERY_LOW_CAPACITY_LEVEL 5 #define DISCHARGE_RATE_FILEPATH "/var/lib/systemd/sleep/battery_discharge_percentage_rate_per_hour" #define BATTERY_DISCHARGE_RATE_HASH_KEY SD_ID128_MAKE(5f,9a,20,18,38,76,46,07,8d,36,58,0b,bb,c4,e0,63) +#define SYS_ENTRY_RAW_FILE_TYPE1 "/sys/firmware/dmi/entries/1-0/raw" static void *CAPACITY_TO_PTR(int capacity) { assert(capacity >= 0); @@ -526,6 +527,68 @@ int get_total_suspend_interval(Hashmap *last_capacity, usec_t *ret) { return 0; } +/* Return true if all batteries have acpi_btp support */ +int battery_trip_point_alarm_exists(void) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + sd_device *dev; + int r; + + r = battery_enumerator_new(&e); + if (r < 0) + return log_debug_errno(r, "Failed to initialize battery enumerator: %m"); + + FOREACH_DEVICE(e, dev) { + int battery_alarm; + const char *s; + + r = sd_device_get_sysattr_value(dev, "alarm", &s); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to read battery alarm: %m"); + + r = safe_atoi(s, &battery_alarm); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to parse battery alarm: %m"); + if (battery_alarm <= 0) + return false; + } + + return true; +} + +/* Return true if wakeup type is APM timer */ +int check_wakeup_type(void) { + _cleanup_free_ char *s = NULL; + uint8_t wakeup_type_byte, tablesize; + size_t readsize; + int r; + + /* implementation via dmi/entries */ + r = read_full_virtual_file(SYS_ENTRY_RAW_FILE_TYPE1, &s, &readsize); + if (r < 0) + return log_debug_errno(r, "Unable to read %s: %m", SYS_ENTRY_RAW_FILE_TYPE1); + + if (readsize < 25) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Only read %zu bytes from %s (expected 25)", readsize, SYS_ENTRY_RAW_FILE_TYPE1); + + /* index 1 stores the size of table */ + tablesize = (uint8_t) s[1]; + if (tablesize < 25) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Table size lesser than the index[0x18] where waketype byte is available."); + + wakeup_type_byte = (uint8_t) s[24]; + /* 0 is Reserved and 8 is AC Power Restored. As per table 12 in + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.4.0.pdf */ + if (wakeup_type_byte >= 128) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Expected value in range 0-127"); + + if (wakeup_type_byte == 3) { + log_debug("DMI BIOS System Information indicates wakeup type is APM Timer"); + return true; + } + + return false; +} + int can_sleep_state(char **types) { _cleanup_free_ char *text = NULL; int r; diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h index 54fe65007e..6645c3e596 100644 --- a/src/shared/sleep-config.h +++ b/src/shared/sleep-config.h @@ -65,6 +65,8 @@ int estimate_battery_discharge_rate_per_hour( Hashmap *current_capacity, usec_t before_timestamp, usec_t after_timestamp); +int check_wakeup_type(void); +int battery_trip_point_alarm_exists(void); const char* sleep_operation_to_string(SleepOperation s) _const_; SleepOperation sleep_operation_from_string(const char *s) _pure_; diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 14191cfc61..d8e6380d0a 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -262,7 +262,7 @@ static int execute( return r; } -static int execute_s2h(const SleepConfig *sleep_config) { +static int custom_timer_suspend(const SleepConfig *sleep_config) { _cleanup_hashmap_free_ Hashmap *last_capacity = NULL, *current_capacity = NULL; int r; @@ -335,7 +335,55 @@ static int execute_s2h(const SleepConfig *sleep_config) { if (!woken_by_timer) /* Return as manual wakeup done. This also will return in case battery was charged during suspension */ return 0; + + r = check_wakeup_type(); + if (r < 0) + log_debug_errno(r, "Failed to check hardware wakeup type, ignoring: %m"); + if (r > 0) { + log_debug("wakeup type is APM timer"); + /* system should hibernate */ + break; + } + } + + return 1; +} + +static int execute_s2h(const SleepConfig *sleep_config) { + int r, k; + + assert(sleep_config); + + r = check_wakeup_type(); + if (r < 0) + log_debug_errno(r, "Failed to check hardware wakeup type, ignoring: %m"); + + k = battery_trip_point_alarm_exists(); + if (k < 0) + log_debug_errno(k, "Failed to check whether acpi_btp support is enabled or not, ignoring: %m"); + + if (r >= 0 && k > 0) { + log_debug("Attempting to suspend..."); + r = execute(sleep_config, SLEEP_SUSPEND, NULL); + if (r < 0) + return r; + + r = check_wakeup_type(); + if (r < 0) + return log_debug_errno(r, "Failed to check hardware wakeup type: %m"); + + if (r == 0) + /* For APM Timer wakeup, system should hibernate else wakeup */ + return 0; + } else { + r = custom_timer_suspend(sleep_config); + if(r < 0) + return log_debug_errno(r, "Suspend cycle with manual battery discharge rate estimation failed: %m"); + if(r == 0) + /* manual wakeup */ + return 0; } + /* For above custom timer, if 1 is returned, system will directly hibernate */ log_debug("Attempting to hibernate"); r = execute(sleep_config, SLEEP_HIBERNATE, NULL); |