Add a sysfs attribute to allow informing the kernel about the current standby state of the device depending on user involvement, those being: "active", "inactive", "sleep", and "resume" (in "sleep" but preparing for presenting to the user faster).
Signed-off-by: Antheas Kapenekakis <[email protected]> --- kernel/power/main.c | 84 ++++++++++++++++++++++++++++++++++++++++++++ kernel/power/power.h | 1 + 2 files changed, 85 insertions(+) diff --git a/kernel/power/main.c b/kernel/power/main.c index 03b2c5495c77..30494be41557 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -830,6 +830,89 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, power_attr(state); +#ifdef CONFIG_SUSPEND +/* + * standby - control system s2idle standby state. + * + * show() returns available standby states, which may be "active", "screen_off", + * "sleep" and "resume" (still in sleep but preparing to present to user). + * See Documentation/admin-guide/pm/standby-states.rst for a description of + * what they mean. + * + * store() accepts one of those strings and initiates a transition to that + * standby state. + * + * For backwards compatibility, when the system suspends, it first enters the + * state "sleep", regardless of what was written into store() and then during + * resume restores the previous value. + */ +static ssize_t standby_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + unsigned int sleep_flags; + standby_state_t i, curr; + char *s = buf; + + sleep_flags = lock_system_sleep(); + pm_standby_refresh_states(); + curr = pm_standby_get_state(); + unlock_system_sleep(sleep_flags); + + if (curr < 0) + return -EBUSY; + + for (i = PM_STANDBY_MIN; i < PM_STANDBY_MAX; i++) + if (standby_states[i]) + s += sprintf(s, curr == i ? "[%s] " : "%s ", standby_states[i]); + + if (s != buf) + /* convert the last space to a newline */ + *(s - 1) = '\n'; + return (s - buf); +} + +static standby_state_t decode_standby_state(const char *buf, size_t n) +{ + standby_state_t state; + char *p; + int len; + + p = memchr(buf, '\n', n); + len = p ? p - buf : n; + + for (state = PM_STANDBY_MIN; state < PM_STANDBY_MAX; state++) { + const char *label = standby_states[state]; + + if (label && len == strlen(label) && !strncmp(buf, label, len)) + return state; + } + + return PM_STANDBY_MAX; +} + +static ssize_t standby_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned int sleep_flags; + standby_state_t state; + int error; + + sleep_flags = lock_system_sleep(); + pm_standby_refresh_states(); + state = decode_standby_state(buf, n); + + if (state >= PM_STANDBY_MAX) + return -EINVAL; + + error = pm_standby_transition(state); + unlock_system_sleep(sleep_flags); + + return error ? error : n; +} + +power_attr(standby); +#endif + #ifdef CONFIG_PM_SLEEP /* * The 'wakeup_count' attribute, along with the functions defined in @@ -1084,6 +1167,7 @@ static struct attribute * g[] = { #ifdef CONFIG_SUSPEND &mem_sleep_attr.attr, &sync_on_suspend_attr.attr, + &standby_attr.attr, #endif #ifdef CONFIG_PM_AUTOSLEEP &autosleep_attr.attr, diff --git a/kernel/power/power.h b/kernel/power/power.h index 75b63843886e..2327a1ce2b05 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -216,6 +216,7 @@ extern void swsusp_show_speed(ktime_t, ktime_t, unsigned int, char *); extern const char * const pm_labels[]; extern const char *pm_states[]; extern const char *mem_sleep_states[]; +extern const char *standby_states[]; extern int suspend_devices_and_enter(suspend_state_t state); #else /* !CONFIG_SUSPEND */ -- 2.52.0
