Skip to content

Commit

Permalink
sleep: support acpi_btp and suspend system if enabled, skipping custo…
Browse files Browse the repository at this point in the history
…m timer
  • Loading branch information
cerebro1 committed Aug 23, 2022
1 parent 628998e commit 6ac2366
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 3 deletions.
63 changes: 63 additions & 0 deletions src/shared/sleep-config.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/shared/sleep-config.h
Expand Up @@ -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_;
52 changes: 49 additions & 3 deletions src/sleep/sleep.c
Expand Up @@ -262,12 +262,10 @@ 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;

assert(sleep_config);

while (battery_is_low() == 0) {
_cleanup_close_ int tfd = -1;
struct itimerspec ts = {};
Expand Down Expand Up @@ -335,7 +333,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)
log_debug_errno(r, "Failed to set custom timer suspend");
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);
Expand Down

0 comments on commit 6ac2366

Please sign in to comment.