]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sleep: support acpi_btp and suspend system if enabled, skipping custom timer
authorSonali Srivastava <srivastava.sonali1@gmail.com>
Sat, 23 Jul 2022 12:00:42 +0000 (17:30 +0530)
committerLuca Boccassi <luca.boccassi@gmail.com>
Tue, 23 Aug 2022 18:36:51 +0000 (19:36 +0100)
src/shared/sleep-config.c
src/shared/sleep-config.h
src/sleep/sleep.c

index d9923e9de8fb367d01fe68cc8814b9338c4cc93d..efc066c4f2e9cf859ebee6f67be46a8dab895f63 100644 (file)
@@ -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;
index 54fe65007ede17aa0eaf3baf659411f1e34d7e92..6645c3e5968f1f52cf3a6eb82a02a5e8391c64af 100644 (file)
@@ -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_;
index 14191cfc61c593c0356d4873235a3b4d9321634a..d8e6380d0a622ee02b3cdf4f5ac9fe6f853b0082 100644 (file)
@@ -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);